clean-up: remove gavl_time_t as external dependency
Indeed — this change set is kind of sad.
Because I still admire the design of the GAVL library,
and would love to use it for processing of raw video.
However, up to now, we never got to the point of actually
doing so. For the future, I am not sure if there remains
room to rely on lib-GAVL, since FFmpeg roughly covers
a similar ground (and a lot beyond that). And providing
a plug-in for FFmpeg is unavoidable, practically speaking.
So I still retain the nominal dependency on lib-GAVL
in the Build system (since it is still packaged in Debian).
But it is pointless to rely on this library just for an
external type-def `gavl_time_t`. We owe much to this
inspiration, but it can be expected that we'll wrap
these raw time-values into a dedicated marker type
soon, and we certainly won't be exposing any C-style
interface for time calculations in future, since
we do not want anyone to side-step the Lumiera
time handling framework in favour of working
„just with plain numbers“
NOTE: lib-GAVL hompage has moved to Github:
https://github.com/bplaum/gavl
This commit is contained in:
parent
ee6e720d00
commit
d31d4295a4
22 changed files with 308 additions and 161 deletions
|
|
@ -85,7 +85,7 @@ def configure(env):
|
|||
|
||||
|
||||
if not conf.CheckPkgConfig('gavl', '1.4'):
|
||||
problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].')
|
||||
problems.append('Did not find Gmerlin Audio Video Lib [https://github.com/bplaum/gavl].')
|
||||
else:
|
||||
conf.env.mergeConf('gavl')
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Description
|
|||
* We use an uniform time type. Time is time, not frames, samples etc.
|
||||
* All timings in Lumiera are based on integral datatypes
|
||||
* We use a fixed, very fine grid, something of the sort of microseconds
|
||||
* The internal representation is based on a `typedef int64_t gavl_time_t`
|
||||
* The internal representation is based on a `using raw_time_64 = int64_t`
|
||||
* We use a set of library routines and convenience-methods to
|
||||
- Get time in different formats (fractional seconds, frame counts)
|
||||
- Calculate with time values (overloaded operators)
|
||||
|
|
|
|||
|
|
@ -493,10 +493,10 @@ NOTE: At the time when Lumiera project started, the standard library provided
|
|||
Time
|
||||
~~~~
|
||||
Time values are represented by a family of opaque date types
|
||||
with overloaded operators. The implementation is based on `gavl_time_t`,
|
||||
an integral (µsec) time tick value. Thus, the arithmetic on time values
|
||||
and time spans is limited and any Time handling and conversion is
|
||||
centralised in library routines.
|
||||
with overloaded operators. The implementation was inspired by `gavl_time_t`
|
||||
and is thus based on a µ-seccond time grid, represented as 64-bit integer.
|
||||
Thus, the arithmetic on time values and time spans is limited and any Time
|
||||
handling and conversion is centralised in library routines.
|
||||
|
||||
We distinguish between time values and a _quantisation_ into a frame
|
||||
or sample grid. In any case, quantisation has to be done once, explicitly
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@
|
|||
** this model are defined to be a special kind of timecode, and thus dependent on
|
||||
** a preceding time quantisation.
|
||||
**
|
||||
** @deprecated 2025 this should not be a "simple" C library set aside from the Lumiera
|
||||
** time handling framework, rather it should be clarified that these are
|
||||
** implementation helpers and must not be used by any application code.
|
||||
** It should be checked which of these functions actually need to be
|
||||
** exposed through an interface header, since these are typically
|
||||
** used to implement parts of the time handling framework.
|
||||
**
|
||||
** @see lib::time::Time
|
||||
** @see timequant.hpp
|
||||
|
|
@ -43,11 +49,12 @@
|
|||
#define LUMIERA_TIME_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <gavl/gavl.h>
|
||||
|
||||
#ifdef __cplusplus /*=================== C++ facilities ===================== */
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
using lib::time::raw_time_64;
|
||||
|
||||
|
||||
/**
|
||||
* Converts a fraction of seconds to Lumiera's internal opaque time scale.
|
||||
|
|
@ -56,7 +63,7 @@
|
|||
* here negative fractional micro-ticks are truncated towards zero.
|
||||
* This was deemed irrelevant in practice.
|
||||
*/
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds);
|
||||
|
||||
|
||||
|
|
@ -66,7 +73,7 @@ lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds);
|
|||
* @note handles only positive frame counts and assumes the
|
||||
* origin to be at zero.
|
||||
*/
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps);
|
||||
|
||||
|
||||
|
|
@ -75,7 +82,7 @@ lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps
|
|||
* @param framerate underlying framerate as rational number
|
||||
* @throw error::Logic on zero framerate
|
||||
*/
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_frame_duration (lib::time::FrameRate const& fps);
|
||||
|
||||
|
||||
|
|
@ -91,15 +98,15 @@ extern "C" { /* ===================== C interface ======================== */
|
|||
* The time grid used for quantisation is comprised of equally spaced intervals,
|
||||
* rooted at the given origin. The interval starting with the origin is numbered
|
||||
* as zero. Each interval includes its lower bound, but excludes its upper bound.
|
||||
* @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE
|
||||
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
|
||||
* @return number of the grid interval containing the given time.
|
||||
* @warning the resulting value is limited to (Time::Min, Time::MAX)
|
||||
*/
|
||||
int64_t
|
||||
lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid);
|
||||
lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid);
|
||||
|
||||
int64_t
|
||||
lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate);
|
||||
lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate);
|
||||
|
||||
/**
|
||||
* Similar to #lumiera_quantise_frames, but returns a grid aligned _relative time_.
|
||||
|
|
@ -110,17 +117,17 @@ lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerat
|
|||
* origin = Time::MIN, then all original time values above zero will be
|
||||
* clipped, because the result, relative to origin, needs to be <= Time::MAX
|
||||
*/
|
||||
gavl_time_t
|
||||
lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid);
|
||||
raw_time_64
|
||||
lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid);
|
||||
|
||||
/**
|
||||
* Calculate time of a grid point (frame start)
|
||||
* @param nr index number of the grid point (0 is at origin)
|
||||
* @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE
|
||||
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
|
||||
* @return time point (frame start) on the Lumiera internal time scale
|
||||
*/
|
||||
gavl_time_t
|
||||
lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid);
|
||||
raw_time_64
|
||||
lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid);
|
||||
|
||||
/**
|
||||
* Build a time value by summing up the given components.
|
||||
|
|
@ -129,7 +136,7 @@ lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid);
|
|||
* @param mins number of minutes
|
||||
* @param hours number of hours
|
||||
*/
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_build_time (long millis, uint secs, uint mins, uint hours);
|
||||
|
||||
/**
|
||||
|
|
@ -140,7 +147,7 @@ lumiera_build_time (long millis, uint secs, uint mins, uint hours);
|
|||
* @param mins number of minutes
|
||||
* @param hours number of hours
|
||||
*/
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours);
|
||||
|
||||
/**
|
||||
|
|
@ -148,59 +155,59 @@ lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)
|
|||
* The components are interpreted as a NTSC drop-frame timecode.
|
||||
* @warning take care not to specify time codes that are illegal NTSC drop-frame times.
|
||||
*/
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours);
|
||||
|
||||
|
||||
/** Extract the hour part of given time. */
|
||||
int
|
||||
lumiera_time_hours (gavl_time_t time);
|
||||
lumiera_time_hours (raw_time_64 time);
|
||||
|
||||
|
||||
/** Extract the minute part of given time. */
|
||||
int
|
||||
lumiera_time_minutes (gavl_time_t time);
|
||||
lumiera_time_minutes (raw_time_64 time);
|
||||
|
||||
|
||||
/** Extract the seconds part of given time. */
|
||||
int
|
||||
lumiera_time_seconds (gavl_time_t time);
|
||||
lumiera_time_seconds (raw_time_64 time);
|
||||
|
||||
|
||||
/** Extract the milliseconds part of given time. */
|
||||
int
|
||||
lumiera_time_millis (gavl_time_t time);
|
||||
lumiera_time_millis (raw_time_64 time);
|
||||
|
||||
/**
|
||||
* Extract the remaining frame part of given time.
|
||||
* @param fps frame rate (frames per second)
|
||||
*/
|
||||
int
|
||||
lumiera_time_frames (gavl_time_t time, uint fps);
|
||||
lumiera_time_frames (raw_time_64 time, uint fps);
|
||||
|
||||
/**
|
||||
* Extract the frame part of given time, using NTSC drop-frame timecode.
|
||||
*/
|
||||
int
|
||||
lumiera_time_ntsc_drop_frames (gavl_time_t time);
|
||||
lumiera_time_ntsc_drop_frames (raw_time_64 time);
|
||||
|
||||
/**
|
||||
* Extract the second part of given time, using NTSC drop-frame timecode.
|
||||
*/
|
||||
int
|
||||
lumiera_time_ntsc_drop_seconds (gavl_time_t time);
|
||||
lumiera_time_ntsc_drop_seconds (raw_time_64 time);
|
||||
|
||||
/**
|
||||
* Extract the minute part of given time, using NTSC drop-frame timecode.
|
||||
*/
|
||||
int
|
||||
lumiera_time_ntsc_drop_minutes (gavl_time_t time);
|
||||
lumiera_time_ntsc_drop_minutes (raw_time_64 time);
|
||||
|
||||
/**
|
||||
* Extract the hour part of given time, using NTSC drop-frame timecode.
|
||||
*/
|
||||
int
|
||||
lumiera_time_ntsc_drop_hours (gavl_time_t time);
|
||||
lumiera_time_ntsc_drop_hours (raw_time_64 time);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -210,7 +217,7 @@ lumiera_time_ntsc_drop_hours (gavl_time_t time);
|
|||
* by rendering into a suitable timecode format.
|
||||
*/
|
||||
char*
|
||||
lumiera_tmpbuf_print_time (gavl_time_t time);
|
||||
lumiera_tmpbuf_print_time (raw_time_64 time);
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
/** @file time.cpp
|
||||
** Lumiera time handling core implementation unit.
|
||||
** This translation unit generates code for the Lumiera internal time wrapper,
|
||||
** based on gavl_time_t, associated constants, marker classes for the derived
|
||||
** time entities (TimeVar, Offset, Duration, TimeSpan, FrameRate) and for the
|
||||
** basic time and frame rate conversion functions.
|
||||
** based on 64bit integral µ-tick values, associated constants, marker classes
|
||||
** for the derived time entities (TimeVar, Offset, Duration, TimeSpan, FrameRate)
|
||||
** and for the basic time and frame rate conversion functions.
|
||||
**
|
||||
** Client code includes either time.h (for basics and conversion functions)
|
||||
** or timevalue.hpp (for the time entities), timequant.hpp for grid aligned
|
||||
|
|
@ -75,11 +75,11 @@ namespace meta {
|
|||
namespace time {
|
||||
|
||||
|
||||
const gavl_time_t TimeValue::SCALE = GAVL_TIME_SCALE;
|
||||
const raw_time_64 TimeValue::SCALE = 1'000'000;
|
||||
|
||||
|
||||
/** @note the allowed time range is explicitly limited to help overflow protection */
|
||||
const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits<gavl_time_t>::max() / 30) );
|
||||
const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits<raw_time_64>::max() / 30) );
|
||||
const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
|
||||
const Time Time::ZERO;
|
||||
|
||||
|
|
@ -94,8 +94,8 @@ namespace time {
|
|||
|
||||
|
||||
/** scale factor _used locally within this implementation header_.
|
||||
* GAVL_TIME_SCALE rsp. TimeValue::SCALE is the correct factor or dividend when using
|
||||
* gavl_time_t for display on a scale with seconds. Since we want to use milliseconds,
|
||||
* TimeValue::SCALE (µ-ticks, i.e. 1e6) is the correct factor or dividend when using
|
||||
* raw_time_64 for display on a scale with seconds. Since we want to use milliseconds,
|
||||
* we need to multiply or divide by 1000 to get correct results. */
|
||||
#define TIME_SCALE_MS (lib::time::TimeValue::SCALE / 1000)
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ namespace time {
|
|||
* of any "time-like" value, it is meant to be compact. */
|
||||
TimeValue::operator string() const
|
||||
{
|
||||
gavl_time_t time = t_;
|
||||
raw_time_64 time = t_;
|
||||
int64_t millis, seconds;
|
||||
bool negative = (time < 0);
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ namespace time {
|
|||
*/
|
||||
Time::operator string() const
|
||||
{
|
||||
gavl_time_t time = t_;
|
||||
raw_time_64 time = t_;
|
||||
int millis, seconds, minutes, hours;
|
||||
bool negative = (time < 0);
|
||||
|
||||
|
|
@ -233,7 +233,7 @@ namespace time {
|
|||
/** @internal backdoor to sneak in a raw time value
|
||||
* bypassing any normalisation and limiting */
|
||||
TimeValue
|
||||
TimeValue::buildRaw_ (gavl_time_t raw)
|
||||
TimeValue::buildRaw_ (raw_time_64 raw)
|
||||
{
|
||||
return reinterpret_cast<TimeValue const&> (raw);
|
||||
}
|
||||
|
|
@ -326,7 +326,7 @@ namespace time {
|
|||
{
|
||||
boost::rational<int64_t> distance (this->t_);
|
||||
distance *= factor;
|
||||
gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator());
|
||||
raw_time_64 microTicks = floordiv (distance.numerator(), distance.denominator());
|
||||
return Offset{buildRaw_(microTicks)};
|
||||
}
|
||||
|
||||
|
|
@ -337,7 +337,7 @@ namespace time {
|
|||
{
|
||||
double distance(this->t_);
|
||||
distance *= factor;
|
||||
gavl_time_t microTicks = floor (distance);
|
||||
raw_time_64 microTicks = floor (distance);
|
||||
return Offset{buildRaw_(microTicks)};
|
||||
}
|
||||
|
||||
|
|
@ -383,7 +383,7 @@ namespace util {
|
|||
|
||||
|
||||
char*
|
||||
lumiera_tmpbuf_print_time (gavl_time_t time)
|
||||
lumiera_tmpbuf_print_time (raw_time_64 time)
|
||||
{
|
||||
int milliseconds, seconds, minutes, hours;
|
||||
bool negative = (time < 0);
|
||||
|
|
@ -409,7 +409,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time)
|
|||
|
||||
|
||||
/// @todo this utility function could be factored out into a `FSecs` or `RSec` class ///////////////////////TICKET #1262
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_rational_to_time (FSecs const& fractionalSeconds)
|
||||
{
|
||||
// avoid numeric wrap from values not representable as 64bit µ-ticks
|
||||
|
|
@ -417,22 +417,22 @@ lumiera_rational_to_time (FSecs const& fractionalSeconds)
|
|||
return (fractionalSeconds < 0? -1:+1)
|
||||
* std::numeric_limits<int64_t>::max();
|
||||
|
||||
return gavl_time_t(util::reQuant (fractionalSeconds.numerator()
|
||||
return raw_time_64(util::reQuant (fractionalSeconds.numerator()
|
||||
,fractionalSeconds.denominator()
|
||||
,lib::time::TimeValue::SCALE
|
||||
));
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps)
|
||||
{
|
||||
// convert to 64bit
|
||||
boost::rational<uint64_t> framerate (fps.numerator(), fps.denominator());
|
||||
|
||||
return rational_cast<gavl_time_t> (lib::time::TimeValue::SCALE * frameCount / framerate);
|
||||
return rational_cast<raw_time_64> (lib::time::TimeValue::SCALE * frameCount / framerate);
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_frame_duration (FrameRate const& fps)
|
||||
{
|
||||
if (!fps)
|
||||
|
|
@ -447,20 +447,20 @@ lumiera_frame_duration (FrameRate const& fps)
|
|||
namespace { // implementation: basic frame quantisation....
|
||||
|
||||
inline int64_t
|
||||
calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
||||
calculate_quantisation (raw_time_64 time, raw_time_64 origin, raw_time_64 grid)
|
||||
{
|
||||
time -= origin;
|
||||
return floordiv (time,grid);
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
calculate_quantisation (gavl_time_t time, gavl_time_t origin, uint framerate, uint framerate_divisor=1)
|
||||
calculate_quantisation (raw_time_64 time, raw_time_64 origin, uint framerate, uint framerate_divisor=1)
|
||||
{
|
||||
REQUIRE (framerate);
|
||||
REQUIRE (framerate_divisor);
|
||||
|
||||
const int64_t limit_num = std::numeric_limits<gavl_time_t>::max() / framerate;
|
||||
const int64_t limit_den = std::numeric_limits<gavl_time_t>::max() / framerate_divisor;
|
||||
const int64_t limit_num = std::numeric_limits<raw_time_64>::max() / framerate;
|
||||
const int64_t limit_den = std::numeric_limits<raw_time_64>::max() / framerate_divisor;
|
||||
const int64_t microScale {lib::time::TimeValue::SCALE};
|
||||
|
||||
// protect against numeric overflow
|
||||
|
|
@ -474,7 +474,7 @@ namespace { // implementation: basic frame quantisation....
|
|||
{
|
||||
// direct calculation will overflow.
|
||||
// use the less precise method instead...
|
||||
gavl_time_t frameDuration = microScale / framerate; // truncated to µs
|
||||
raw_time_64 frameDuration = microScale / framerate; // truncated to µs
|
||||
return calculate_quantisation (time,origin, frameDuration);
|
||||
}
|
||||
}
|
||||
|
|
@ -482,37 +482,37 @@ namespace { // implementation: basic frame quantisation....
|
|||
|
||||
|
||||
int64_t
|
||||
lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
||||
lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid)
|
||||
{
|
||||
return calculate_quantisation (time, origin, grid);
|
||||
}
|
||||
|
||||
int64_t
|
||||
lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate)
|
||||
lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate)
|
||||
{
|
||||
return calculate_quantisation (time, origin, framerate);
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
||||
raw_time_64
|
||||
lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid)
|
||||
{
|
||||
int64_t count = calculate_quantisation (time, origin, grid);
|
||||
gavl_time_t alignedTime = count * grid;
|
||||
raw_time_64 alignedTime = count * grid;
|
||||
return alignedTime;
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid)
|
||||
raw_time_64
|
||||
lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid)
|
||||
{
|
||||
gavl_time_t offset = nr * grid;
|
||||
raw_time_64 offset = nr * grid;
|
||||
return origin + offset;
|
||||
}
|
||||
|
||||
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_build_time(long millis, uint secs, uint mins, uint hours)
|
||||
{
|
||||
gavl_time_t time = millis
|
||||
raw_time_64 time = millis
|
||||
+ 1000 * secs
|
||||
+ 1000 * 60 * mins
|
||||
+ 1000 * 60 * 60 * hours;
|
||||
|
|
@ -520,10 +520,10 @@ lumiera_build_time(long millis, uint secs, uint mins, uint hours)
|
|||
return time;
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)
|
||||
{
|
||||
gavl_time_t time = 1000LL * frames/fps
|
||||
raw_time_64 time = 1000LL * frames/fps
|
||||
+ 1000 * secs
|
||||
+ 1000 * 60 * mins
|
||||
+ 1000 * 60 * 60 * hours;
|
||||
|
|
@ -532,31 +532,31 @@ lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)
|
|||
}
|
||||
|
||||
int
|
||||
lumiera_time_hours (gavl_time_t time)
|
||||
lumiera_time_hours (raw_time_64 time)
|
||||
{
|
||||
return time / TIME_SCALE_MS / 1000 / 60 / 60;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_minutes (gavl_time_t time)
|
||||
lumiera_time_minutes (raw_time_64 time)
|
||||
{
|
||||
return (time / TIME_SCALE_MS / 1000 / 60) % 60;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_seconds (gavl_time_t time)
|
||||
lumiera_time_seconds (raw_time_64 time)
|
||||
{
|
||||
return (time / TIME_SCALE_MS / 1000) % 60;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_millis (gavl_time_t time)
|
||||
lumiera_time_millis (raw_time_64 time)
|
||||
{
|
||||
return (time / TIME_SCALE_MS) % 1000;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_frames (gavl_time_t time, uint fps)
|
||||
lumiera_time_frames (raw_time_64 time, uint fps)
|
||||
{
|
||||
REQUIRE (fps < uint(std::numeric_limits<int>::max()));
|
||||
return floordiv<int> (lumiera_time_millis(time) * int(fps), TIME_SCALE_MS);
|
||||
|
|
@ -581,7 +581,7 @@ namespace { // implementation helper
|
|||
* @todo I doubt this works correct for negative times!!
|
||||
*/
|
||||
inline int64_t
|
||||
calculate_drop_frame_number (gavl_time_t time)
|
||||
calculate_drop_frame_number (raw_time_64 time)
|
||||
{
|
||||
int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001);
|
||||
|
||||
|
|
@ -599,30 +599,30 @@ namespace { // implementation helper
|
|||
}
|
||||
|
||||
int
|
||||
lumiera_time_ntsc_drop_frames (gavl_time_t time)
|
||||
lumiera_time_ntsc_drop_frames (raw_time_64 time)
|
||||
{
|
||||
return calculate_drop_frame_number(time) % 30;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_ntsc_drop_seconds (gavl_time_t time)
|
||||
lumiera_time_ntsc_drop_seconds (raw_time_64 time)
|
||||
{
|
||||
return calculate_drop_frame_number(time) / 30 % 60;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_ntsc_drop_minutes (gavl_time_t time)
|
||||
lumiera_time_ntsc_drop_minutes (raw_time_64 time)
|
||||
{
|
||||
return calculate_drop_frame_number(time) / 30 / 60 % 60;
|
||||
}
|
||||
|
||||
int
|
||||
lumiera_time_ntsc_drop_hours (gavl_time_t time)
|
||||
lumiera_time_ntsc_drop_hours (raw_time_64 time)
|
||||
{
|
||||
return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24;
|
||||
}
|
||||
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
|
||||
{
|
||||
uint64_t total_mins = 60 * hours + mins;
|
||||
|
|
@ -631,7 +631,7 @@ lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
|
|||
+ 30 * secs
|
||||
+ frames
|
||||
- 2 * (total_mins - total_mins / 10);
|
||||
gavl_time_t result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC);
|
||||
raw_time_64 result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC);
|
||||
|
||||
if (0 != result) // compensate for truncating down on conversion
|
||||
result += 1; // without this adjustment the frame number
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ namespace time {
|
|||
/** */
|
||||
Secs::Secs (QuTime const& quantisedTime)
|
||||
: TCode(quantisedTime)
|
||||
// : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) //////////////////////////////////////////////////////TICKET #736 implement Seconds format
|
||||
// : sec_(TimeVar(quantisedTime) / TimeValue::SCALE) /////////////////////////////////////////////////////TICKET #736 implement Seconds format
|
||||
{ }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
/** @file timevalue.hpp
|
||||
** a family of time value like entities and their relationships.
|
||||
** This is the foundation for the Lumiera time handling framework. On the implementation
|
||||
** level, time values are represented as 64bit integer values `gavl_time_t`. But for the
|
||||
** level, time values are represented as 64bit integer values `raw_time_64`, similar to
|
||||
** and inspired by `gavl_time_t` from the raw-video handling [Lib-GAVL]. But for the
|
||||
** actual use, we create several kinds of time "values", based on their logical properties.
|
||||
** These time values are considered to be fixed (immutable) values, which may only be
|
||||
** created through some limited construction paths, and any time based calculation
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
** way to retrieve such a value is by formatting it into a time code format.
|
||||
**
|
||||
** The lib::time::TimeValue serves as foundation for all further time calculations;
|
||||
** in fact it is implemented as a single 64bit µ-tick value (`gavl_time_t`). The
|
||||
** in fact it is implemented as a single 64bit µ-tick value (`raw_time_64`). The
|
||||
** further time entities are implemented as value objects (without virtual functions):
|
||||
** - lib::time::Time represents a time instant and is the reference for any usage
|
||||
** - lib::time::TimeVar is a mutable time variable and can be used for calculations
|
||||
|
|
@ -86,7 +87,8 @@
|
|||
** @see time.h basic time calculation library functions
|
||||
** @see timequant.hpp
|
||||
** @see TimeValue_test
|
||||
**
|
||||
**
|
||||
** [Lib-GAVL]: https://github.com/bplaum/gavl
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -100,11 +102,6 @@
|
|||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#include <stdint.h>
|
||||
#include <gavl/gavltime.h>
|
||||
}
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
|
|
@ -118,11 +115,22 @@ namespace time {
|
|||
class Mutation;
|
||||
|
||||
|
||||
/**
|
||||
* Raw µ-tick time representation used in Lumiera.
|
||||
* @remark this representation was inspired by [Lib-GAVL].
|
||||
* @todo 2025 a mere type alias is up to debate -- very likely we'll use a wrapper soon /////////////////TICKET #1258
|
||||
* @warning application logic should avoid handling any raw time value
|
||||
* directly and rather treat time data as an opaque entity.
|
||||
* [Lib-GAVL]: https://github.com/bplaum/gavl
|
||||
*/
|
||||
using raw_time_64 = int64_t;
|
||||
|
||||
|
||||
/**
|
||||
* basic constant internal time value.
|
||||
* These time values provide the implementation base
|
||||
* for all further time types. They can be created by
|
||||
* wrapping up a raw micro tick value (gavl_time_t),
|
||||
* wrapping up a raw micro tick value (raw_time_64),
|
||||
* are totally ordered, but besides that,
|
||||
* they are opaque and non-mutable.
|
||||
* @note clients should prefer to use Time instances,
|
||||
|
|
@ -132,12 +140,12 @@ namespace time {
|
|||
*/
|
||||
class TimeValue
|
||||
: boost::totally_ordered<TimeValue,
|
||||
boost::totally_ordered<TimeValue, gavl_time_t>>
|
||||
boost::totally_ordered<TimeValue, raw_time_64>>
|
||||
{
|
||||
protected:
|
||||
/** the raw (internal) time value
|
||||
* used to implement the time types */
|
||||
gavl_time_t t_;
|
||||
raw_time_64 t_;
|
||||
|
||||
|
||||
/** Assigning of time values is not allowed,
|
||||
|
|
@ -153,9 +161,9 @@ namespace time {
|
|||
friend class Mutation;
|
||||
|
||||
/** explicit limit of allowed time range */
|
||||
static gavl_time_t limitedTime (gavl_time_t raw);
|
||||
static raw_time_64 limitedTime (raw_time_64 raw);
|
||||
/** safe calculation of explicitly limited time offset */
|
||||
static gavl_time_t limitedDelta (gavl_time_t origin, gavl_time_t target);
|
||||
static raw_time_64 limitedDelta (raw_time_64 origin, raw_time_64 target);
|
||||
|
||||
/** @internal for Offset and Duration entities built on top */
|
||||
TimeValue (TimeValue const& origin, TimeValue const& target)
|
||||
|
|
@ -164,11 +172,11 @@ namespace time {
|
|||
|
||||
public:
|
||||
/** Number of micro ticks (µs) per second as basic time scale */
|
||||
static const gavl_time_t SCALE;
|
||||
static const raw_time_64 SCALE;
|
||||
|
||||
|
||||
explicit
|
||||
TimeValue (gavl_time_t val) ///< time given in µ ticks here
|
||||
TimeValue (raw_time_64 val) ///< time given in µ ticks here
|
||||
: t_{limitedTime (val)}
|
||||
{ }
|
||||
|
||||
|
|
@ -178,9 +186,9 @@ namespace time {
|
|||
{ }
|
||||
|
||||
/** @internal to pass Time values to C functions */
|
||||
friend gavl_time_t _raw (TimeValue const& time) { return time.t_; }
|
||||
friend raw_time_64 _raw (TimeValue const& time) { return time.t_; }
|
||||
friend HashVal hash_value (TimeValue const&);
|
||||
static TimeValue buildRaw_(gavl_time_t);
|
||||
static TimeValue buildRaw_(raw_time_64);
|
||||
|
||||
/** @internal diagnostics */
|
||||
operator std::string () const;
|
||||
|
|
@ -190,10 +198,10 @@ namespace time {
|
|||
|
||||
// Supporting totally_ordered
|
||||
friend bool operator< (TimeValue const& t1, TimeValue const& t2) { return t1.t_ < t2.t_; }
|
||||
friend bool operator< (TimeValue const& t1, gavl_time_t t2) { return t1.t_ < t2 ; }
|
||||
friend bool operator> (TimeValue const& t1, gavl_time_t t2) { return t1.t_ > t2 ; }
|
||||
friend bool operator< (TimeValue const& t1, raw_time_64 t2) { return t1.t_ < t2 ; }
|
||||
friend bool operator> (TimeValue const& t1, raw_time_64 t2) { return t1.t_ > t2 ; }
|
||||
friend bool operator== (TimeValue const& t1, TimeValue const& t2) { return t1.t_ == t2.t_; }
|
||||
friend bool operator== (TimeValue const& t1, gavl_time_t t2) { return t1.t_ == t2 ; }
|
||||
friend bool operator== (TimeValue const& t1, raw_time_64 t2) { return t1.t_ == t2 ; }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -207,7 +215,7 @@ namespace time {
|
|||
/** relative framecount or frame number.
|
||||
* Used within the engine at places where the underlying
|
||||
* grid and origin is obvious from the call context.
|
||||
* @warning do not mix up gavl_time_t and FrameCnt.
|
||||
* @warning do not mix up raw_time_64 and FrameCnt.
|
||||
* @warning use 64bit consistently.
|
||||
* beware: `long` is 32bit on i386
|
||||
* @note any conversion to frame numbers should go through
|
||||
|
|
@ -216,7 +224,7 @@ namespace time {
|
|||
using FrameCnt = int64_t;
|
||||
|
||||
/** rational representation of fractional seconds
|
||||
* @warning do not mix up gavl_time_t and FSecs */
|
||||
* @warning do not mix up raw_time_64 and FSecs */
|
||||
using FSecs = boost::rational<int64_t>;
|
||||
|
||||
|
||||
|
|
@ -226,7 +234,7 @@ namespace time {
|
|||
* allowing copy and re-accessing
|
||||
* @note supports scaling by a factor,
|
||||
* which _deliberately_ is chosen
|
||||
* as int, not gavl_time_t, because the
|
||||
* as int, not raw_time_64, because the
|
||||
* multiplying of times is meaningless.
|
||||
*/
|
||||
class TimeVar
|
||||
|
|
@ -258,7 +266,7 @@ namespace time {
|
|||
}
|
||||
|
||||
/// Support mixing with plain 64bit int arithmetics
|
||||
operator gavl_time_t() const { return t_; }
|
||||
operator raw_time_64() const { return t_; }
|
||||
/// Support for micro-tick precise time arithmetics
|
||||
operator FSecs() const { return FSecs{t_, TimeValue::SCALE}; }
|
||||
|
||||
|
|
@ -708,8 +716,8 @@ namespace time {
|
|||
return n;
|
||||
}
|
||||
|
||||
inline gavl_time_t
|
||||
symmetricLimit (gavl_time_t raw, TimeValue lim)
|
||||
inline raw_time_64
|
||||
symmetricLimit (raw_time_64 raw, TimeValue lim)
|
||||
{
|
||||
return raw > lim? _raw(lim)
|
||||
: -raw > lim? -_raw(lim)
|
||||
|
|
@ -744,21 +752,21 @@ namespace time {
|
|||
* While Time entities are \c not a "safeInt"
|
||||
* implementation, we limit new values to
|
||||
* lower the likelihood of wrap-around */
|
||||
inline gavl_time_t
|
||||
TimeValue::limitedTime (gavl_time_t raw)
|
||||
inline raw_time_64
|
||||
TimeValue::limitedTime (raw_time_64 raw)
|
||||
{
|
||||
return symmetricLimit (raw, Time::MAX);
|
||||
}
|
||||
|
||||
inline gavl_time_t
|
||||
TimeValue::limitedDelta (gavl_time_t origin, gavl_time_t target)
|
||||
inline raw_time_64
|
||||
TimeValue::limitedDelta (raw_time_64 origin, raw_time_64 target)
|
||||
{
|
||||
if (0 > (origin^target))
|
||||
{// prevent possible numeric wrap
|
||||
origin = symmetricLimit (origin, Duration::MAX);
|
||||
target = symmetricLimit (target, Duration::MAX);
|
||||
}
|
||||
gavl_time_t res = target - origin;
|
||||
raw_time_64 res = target - origin;
|
||||
return symmetricLimit (res, Duration::MAX);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
|
||||
#include "stage/widget/timecode-widget.hpp"
|
||||
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/time/diagnostics.hpp" ////////////TODO: temporary solution to get H:M:S components. Use TimeCode instead!
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
|
@ -40,9 +40,6 @@
|
|||
#include <locale>
|
||||
#include <string>
|
||||
|
||||
#include <gavl/gavl.h>
|
||||
#include <sigc++/bind.h>
|
||||
|
||||
|
||||
using boost::lexical_cast;
|
||||
using sigc::mem_fun;
|
||||
|
|
@ -433,7 +430,7 @@ namespace widget {
|
|||
// void
|
||||
// TimeCode::smpte_offset_changed()
|
||||
// {
|
||||
// gavl_time_t current;
|
||||
// raw_time_64 current;
|
||||
|
||||
// switch (_mode) {
|
||||
// case SMPTE:
|
||||
|
|
@ -1278,7 +1275,7 @@ namespace widget {
|
|||
Time
|
||||
TimeCode::audio_time_from_display () const
|
||||
{
|
||||
gavl_time_t parsedAudioFrames = lexical_cast<int>(audio_frames_label.get_text());
|
||||
raw_time_64 parsedAudioFrames = lexical_cast<int>(audio_frames_label.get_text());
|
||||
return Time(TimeValue(parsedAudioFrames));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@
|
|||
** the referenced libraries, though. This will be done with adapter implementations,
|
||||
** where the interface MediaImplLip defines those aspects actually used and required
|
||||
** by our internal processing.
|
||||
** @deprecated 2025 -- this is an architecture draft, and shows the direction to take
|
||||
** for the integration with external libraries. Yet in reality, this task
|
||||
** can not conceivably be thus simple; take everything here with a grain
|
||||
** of salt (since this code is not yet actually used in any way).
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
|||
24
src/steam/external/libgavl.hpp
vendored
24
src/steam/external/libgavl.hpp
vendored
|
|
@ -13,11 +13,31 @@
|
|||
|
||||
|
||||
/** @file libgavl.hpp
|
||||
** Concrete implementation of the MediaImplLib facade to work with `libGAVL`
|
||||
** Concrete implementation of the MediaImplLib facade to work with [lib-GAVL],
|
||||
** a library for handling basic raw video data formats.
|
||||
** @todo a draft and placeholder code from 2008, at which time the intention was
|
||||
** to rely on libGAVL for processing of raw media data. This seemed like a
|
||||
** good idea at that time, but we should re-evaluate if libGAVL is maintained
|
||||
** and in actual use, before we start really relying on it
|
||||
** and in actual use, before we start really relying on it.
|
||||
** @todo 2025 this code is a kind of _architecture placeholder_ — it is clear that
|
||||
** a solution can can not be _that simple_, but any viable solution will
|
||||
** rely onto an approach like outlined here: provide a façade interface,
|
||||
** which has to be implemented by any plug-in component to provide access
|
||||
** to the processing capabilities of a specific library. The access point
|
||||
** functions exposed on the facade however must be shaped by the specific
|
||||
** way how the builder and the render engine interacts with and delegates
|
||||
** back to the media-handling library. Such an interface can not be based
|
||||
** on first principles alone.
|
||||
** @todo 2025 I still retain the dependency on lib-GAVL for now, even while we
|
||||
** never got into actually using it. Simply because I still consider this
|
||||
** library exemplary, and would love to use it for real. Yet it remains
|
||||
** to be seen if the project actually gets a chance to do so, simply
|
||||
** because FFmpeg roughly covers the same ground. Presumably it turns
|
||||
** out to be a basic requirement to ship a plug-in with FFmpeg binding.
|
||||
** Furthermore, one especially tricky aspect is how to support displays
|
||||
** with extended dynamic range -- a requirement which has the potential
|
||||
** to blur the notion of »raw video«...
|
||||
** [Lib-GAVL]: https://github.com/bplaum/gavl
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ namespace gear {
|
|||
adjustEpochStep (double factor)
|
||||
{
|
||||
double stretched = _raw(epochStep_) * factor;
|
||||
gavl_time_t microTicks(floor (stretched));
|
||||
raw_time_64 microTicks(floor (stretched));
|
||||
epochStep_ = TimeValue{microTicks};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ union InvocationInstanceID
|
|||
*/
|
||||
struct lumiera_jobParameter_struct
|
||||
{
|
||||
gavl_time_t nominalTime; /////////////////////////////////////////////////////////////////////////TICKET #1295 job invocation parameter: framework to interpret this time
|
||||
raw_time_64 nominalTime; /////////////////////////////////////////////////////////////////////////TICKET #1295 job invocation parameter: framework to interpret this time
|
||||
InvocationInstanceID invoKey;
|
||||
//////////////////////////////////////////////////////////////TODO: place an additional parameter value here, or make the instanceID globally unique?
|
||||
////////////////////////////////////////////////////////////////////////////////////TICKET #1293 job invocation identity
|
||||
|
|
@ -166,7 +166,7 @@ typedef lumiera_jobDefinition* LumieraJobDefinition;
|
|||
*/
|
||||
struct lumiera_jobDescriptor_struct
|
||||
{
|
||||
gavl_time_t deadline; ///< given in real wall clock time
|
||||
raw_time_64 deadline; ///< given in real wall clock time
|
||||
JobState jobState;
|
||||
|
||||
lumiera_jobDefinition jobDefinition; ///< of interest only to Steam-Layer
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ namespace gear {
|
|||
{
|
||||
auto scatter = [&](Duration horizon)
|
||||
{
|
||||
gavl_time_t wrap = hash_value(now) % _raw(horizon);
|
||||
lib::time::raw_time_64 wrap = hash_value(now) % _raw(horizon);
|
||||
ENSURE (0 <= wrap and wrap < _raw(horizon));
|
||||
return TimeValue{wrap};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ namespace test{
|
|||
CHECK (!(var < ref) );
|
||||
CHECK ( (var > ref) );
|
||||
|
||||
gavl_time_t gat(var);
|
||||
raw_time_64 gat(var);
|
||||
CHECK (!(gat == ref) );
|
||||
CHECK ( (gat != ref) );
|
||||
CHECK ( (gat >= ref) );
|
||||
|
|
|
|||
|
|
@ -123,13 +123,13 @@ namespace test{
|
|||
*/
|
||||
class TimeControl_test : public Test
|
||||
{
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
random_or_get (string arg)
|
||||
{
|
||||
if (isnil(arg))
|
||||
return gavl_time_t(1 + rani (100000)) * TimeValue::SCALE;
|
||||
return raw_time_64(1 + rani (100000)) * TimeValue::SCALE;
|
||||
else
|
||||
return lexical_cast<gavl_time_t> (arg);
|
||||
return lexical_cast<raw_time_64> (arg);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ namespace test{
|
|||
*/
|
||||
class TimeMutation_test : public Test
|
||||
{
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
random_or_get (string arg)
|
||||
{
|
||||
if (isnil(arg))
|
||||
return gavl_time_t(1 + rani (100000)) * TimeValue::SCALE;
|
||||
return raw_time_64(1 + rani (100000)) * TimeValue::SCALE;
|
||||
else
|
||||
return lexical_cast<gavl_time_t> (arg);
|
||||
return lexical_cast<raw_time_64> (arg);
|
||||
}
|
||||
|
||||
struct TestValues
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace test{
|
|||
*/
|
||||
class TimeValue_test : public Test
|
||||
{
|
||||
gavl_time_t
|
||||
raw_time_64
|
||||
random_or_get (Arg arg)
|
||||
{
|
||||
if (isnil(arg))
|
||||
|
|
@ -53,7 +53,7 @@ namespace test{
|
|||
return 1 + rani(10000);
|
||||
}
|
||||
else
|
||||
return lexical_cast<gavl_time_t> (arg[1]);
|
||||
return lexical_cast<raw_time_64> (arg[1]);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ namespace test{
|
|||
CHECK (val < max);
|
||||
|
||||
// mixed comparisons with raw numeric time
|
||||
gavl_time_t g2 (-2);
|
||||
raw_time_64 g2 (-2);
|
||||
CHECK (zero > g2);
|
||||
CHECK (one > g2);
|
||||
CHECK (one >= g2);
|
||||
|
|
@ -142,7 +142,7 @@ namespace test{
|
|||
CHECK (var < Time::MAX);
|
||||
CHECK (var > Time::MIN);
|
||||
|
||||
gavl_time_t raw (var);
|
||||
raw_time_64 raw (var);
|
||||
CHECK (raw == org);
|
||||
CHECK (raw > org - two);
|
||||
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ namespace test {
|
|||
{
|
||||
Time frame{pipeline.currPoint}; // can access the embedded PipeFrameTick core to get "currPoint" (nominal time)
|
||||
Job job = pipeline->ticket().createJobFor(frame); // looking always at the second element, which is the current JobTicket
|
||||
TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (gavl_time_t)
|
||||
TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (raw_time_64)
|
||||
int32_t mark = job.parameter.invoKey.part.a; // the MockDispatcher places the given "mark" here
|
||||
return _Fmt{"J(%d|%s)"} % mark % nominalTime;
|
||||
};
|
||||
|
|
@ -309,7 +309,7 @@ namespace test {
|
|||
auto visualise = [](auto& pipeline) -> string
|
||||
{
|
||||
Job job = pipeline.buildJob(); // let the JobPlanning construct the »current job«
|
||||
TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (gavl_time_t)
|
||||
TimeValue nominalTime{job.parameter.nominalTime}; // job parameter holds the microseconds (raw_time_64)
|
||||
int32_t mark = job.parameter.invoKey.part.a; // the MockDispatcher places the given "mark" here
|
||||
TimeValue deadline{pipeline.determineDeadline()};
|
||||
return _Fmt{"J(%d|%s⧐%s)"}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,18 @@
|
|||
|
||||
|
||||
#include "lib/test/test.h"
|
||||
#include "lib/time.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include <nobug.h>
|
||||
|
||||
typedef int64_t raw_time_64; //////////////////////////////////////////////////////////////////////////////TICKET #1259 turn time.h in to a C++ implementation header
|
||||
|
||||
#include "lib/time.h" /////////////////////////////////////////////////////////////////////////////////////TICKET #1259 this should not be a general purpose library, but rather an implementation base
|
||||
|
||||
|
||||
static int
|
||||
calculate_framecount (gavl_time_t t, uint fps)
|
||||
calculate_framecount (raw_time_64 t, uint fps)
|
||||
{
|
||||
return lumiera_quantise_frames_fps (t,0,fps);
|
||||
}
|
||||
|
|
@ -48,9 +53,9 @@ const int FPS = 24;
|
|||
TEST (basic)
|
||||
{
|
||||
// Zero
|
||||
gavl_time_t t = lumiera_build_time (0,0,0,0);
|
||||
raw_time_64 t = lumiera_build_time (0,0,0,0);
|
||||
|
||||
CHECK ((gavl_time_t) t == 0);
|
||||
CHECK ((raw_time_64) t == 0);
|
||||
CHECK (lumiera_time_millis (t) == 0);
|
||||
CHECK (lumiera_time_seconds (t) == 0);
|
||||
CHECK (lumiera_time_minutes (t) == 0);
|
||||
|
|
@ -83,7 +88,7 @@ TEST (basic)
|
|||
|
||||
TEST (fps)
|
||||
{
|
||||
gavl_time_t t = lumiera_build_time_fps (FPS, FRAMES, SECONDS, MINUTES, HOURS);
|
||||
raw_time_64 t = lumiera_build_time_fps (FPS, FRAMES, SECONDS, MINUTES, HOURS);
|
||||
|
||||
CHECK (lumiera_time_millis (t) == FRAMES * 1000 / FPS);
|
||||
CHECK (lumiera_time_seconds (t) == SECONDS);
|
||||
|
|
@ -102,9 +107,9 @@ TEST (fps)
|
|||
TEST (ntsc_drop_frame)
|
||||
{
|
||||
// Make sure frame 0 begins at 0
|
||||
gavl_time_t t = lumiera_build_time_ntsc_drop (0, 0, 0, 0);
|
||||
raw_time_64 t = lumiera_build_time_ntsc_drop (0, 0, 0, 0);
|
||||
|
||||
CHECK ((gavl_time_t) t == 0);
|
||||
CHECK ((raw_time_64) t == 0);
|
||||
CHECK (lumiera_time_millis (t) == 0);
|
||||
CHECK (lumiera_time_seconds (t) == 0);
|
||||
CHECK (lumiera_time_minutes (t) == 0);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ namespace stage{
|
|||
namespace model{
|
||||
namespace test {
|
||||
|
||||
using lib::time::raw_time_64;
|
||||
|
||||
namespace { // simplified notation for expected results...
|
||||
|
||||
|
|
@ -577,7 +578,7 @@ namespace test {
|
|||
CHECK (2_r/3 < poison and poison < 1); // looks innocuous...
|
||||
CHECK (poison + Time::SCALE < 0); // simple calculations fail due to numeric overflow
|
||||
CHECK (poison * Time::SCALE < 0);
|
||||
CHECK (-6 == rational_cast<gavl_time_t>(poison * Time::SCALE)); // naive conversion to µ-ticks would lead to overflow
|
||||
CHECK (-6 == rational_cast<raw_time_64>(poison * Time::SCALE)); // naive conversion to µ-ticks would lead to overflow
|
||||
CHECK (671453 == _raw(Time(FSecs(poison)))); // however the actual conversion routine is safeguarded
|
||||
CHECK (671453.812f == rational_cast<float>(poison)*Time::SCALE);
|
||||
|
||||
|
|
@ -667,7 +668,7 @@ namespace test {
|
|||
CHECK (win.overallSpan().duration() == TimeValue{307445734561825860}); // However, all base values turn out unaffected
|
||||
CHECK (win.visible().duration() == TimeValue{856350691});
|
||||
|
||||
TimeValue targetPos{gavl_time_t(_raw(win.overallSpan().duration()) // based on the overall span...
|
||||
TimeValue targetPos{raw_time_64(_raw(win.overallSpan().duration()) // based on the overall span...
|
||||
* rational_cast<double> (poison))}; // the given toxic factor would point at that target position
|
||||
|
||||
CHECK (targetPos == TimeValue{206435633551724864});
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ namespace test {
|
|||
{
|
||||
auto N = AVERAGE_EPOCHS;
|
||||
auto averageTicks = double(_raw(old))*(N-1)/N + contribution/N;
|
||||
return TimeValue{gavl_time_t (floor (averageTicks))};
|
||||
return TimeValue{raw_time_64 (floor (averageTicks))};
|
||||
};
|
||||
|
||||
TimeVar step = bFlow.getEpochStep();
|
||||
|
|
@ -396,8 +396,8 @@ namespace test {
|
|||
{
|
||||
const size_t FPS = 200;
|
||||
const size_t TICK_P_S = FPS * ACTIVITIES_P_FR; // Simulated throughput 200 frames per second
|
||||
const gavl_time_t STP = Time::SCALE / TICK_P_S; // Simulation stepping (here 2 steps per ms)
|
||||
const gavl_time_t RUN = _raw(Time{0,0,3}); // nominal length of the simulation time axis
|
||||
const raw_time_64 STP = Time::SCALE / TICK_P_S; // Simulation stepping (here 2 steps per ms)
|
||||
const raw_time_64 RUN = _raw(Time{0,0,3}); // nominal length of the simulation time axis
|
||||
Offset BASE_DEADLINE{FSecs{1,2}}; // base pre-roll before deadline
|
||||
Offset SPREAD_DEAD{FSecs{2,100}}; // random spread of deadline around base
|
||||
const uint INVOKE_LAG = _raw(Time{250,0}) /STP; // „invoke“ the Activity after simulated 250ms (≙ 500 steps)
|
||||
|
|
|
|||
|
|
@ -129698,8 +129698,8 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)
|
|||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1746196449003" ID="ID_1070775068" MODIFIED="1746196453932" TEXT="Video-Display">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1746196482500" ID="ID_1819581335" LINK="https://issues.lumiera.org/ticket/1403" MODIFIED="1747342428317" TEXT=" #1403 Invesitgate if XV displayer can be revived ">
|
||||
<linktarget COLOR="#fefeb4" DESTINATION="ID_1819581335" ENDARROW="Default" ENDINCLINATION="-2783;152;" ID="Arrow_ID_1160980366" SOURCE="ID_1219347509" STARTARROW="None" STARTINCLINATION="-4870;297;"/>
|
||||
<linktarget COLOR="#fdebca" DESTINATION="ID_1819581335" ENDARROW="Default" ENDINCLINATION="-2783;152;" ID="Arrow_ID_421873944" SOURCE="ID_150758489" STARTARROW="None" STARTINCLINATION="1417;60;"/>
|
||||
<linktarget COLOR="#fefeb4" DESTINATION="ID_1819581335" ENDARROW="Default" ENDINCLINATION="-2783;152;" ID="Arrow_ID_1160980366" SOURCE="ID_1219347509" STARTARROW="None" STARTINCLINATION="-4870;297;"/>
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node COLOR="#338800" CREATED="1746196518190" ID="ID_411481779" MODIFIED="1747013174739" TEXT="Research-Setup">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
|
|
@ -159479,18 +159479,21 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo
|
|||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1742256149038" FOLDED="true" ID="ID_1347393646" MODIFIED="1742256644046" TEXT="welche Rolle können andere Libraries hier noch spielen?">
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1742256149038" ID="ID_1347393646" MODIFIED="1747523933091" TEXT="welche Rolle können andere Libraries hier noch spielen?">
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1742256164252" ID="ID_798828861" MODIFIED="1742256311817" TEXT="konkret: Gavl und Gmerlin">
|
||||
<node CREATED="1742256164252" ID="ID_798828861" LINK="https://github.com/bplaum/gavl" MODIFIED="1747524023410" TEXT="konkret: Gavl und Gmerlin">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Christian kannte bereits Burkart Pflaum, und hat uns auf dessen Libraries aufmerksam gemacht. Die Libraries sind vorbildlich in iherer Struktur und in dem beschränkten Scope. Wahrscheinlich sind sie problemloser einzubinden als FFmpeg. Aber das Kernproblem bleibt: das ist eine one-Man-Show, und wir werden trotzdem auf FFmpeg nicht verzichten können....
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</html>
|
||||
</richcontent>
|
||||
<node CREATED="1742256340945" ID="ID_457200888" MODIFIED="1742256484596" TEXT="denkbar als Ergänzung, sollte FFmpeg tatsächliche Schwächen zeigen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
|
|
@ -162285,7 +162288,8 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1742168602576" ID="ID_871737890" MODIFIED="1742168698565" TEXT="Gmerlin">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1742249270854" ID="ID_1347801315" MODIFIED="1742249278755" TEXT="falsch! es ist Gavl">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1742249270854" ID="ID_1347801315" MODIFIED="1747524127181" TEXT="falsch! es ist Gavl">
|
||||
<arrowlink COLOR="#c95974" DESTINATION="ID_219954631" ENDARROW="Default" ENDINCLINATION="813;-128;" ID="Arrow_ID_889384701" STARTARROW="None" STARTINCLINATION="1093;150;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1742249279746" ID="ID_1189167261" MODIFIED="1742249293729" TEXT="Vorsicht: Upgrade auf Version-2">
|
||||
|
|
@ -162713,8 +162717,7 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
man hat ein time::Control (was auch immer das in Zukunft sein wird), welches mit einem Change-Signal verbunden wird. GUI-seitig verdrahtet man dieses Signal mit dem Code, der die tatsächliche Änderung des Presentation-State macht
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#7f6a74" DESTINATION="ID_1446567013" ENDARROW="Default" ENDINCLINATION="-1578;239;" ID="Arrow_ID_590507558" STARTARROW="None" STARTINCLINATION="-2460;177;"/>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -162775,8 +162778,7 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
Abgesehen davon ist der Code inzwischen auch technologisch überholt und wird mit GTK-4 nicht mehr funktionieren
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1747355273449" ID="ID_1703747958" MODIFIED="1747355286397" TEXT="ehrlich gesagt: wegwerfen und neuschreiben">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
|
|
@ -162785,10 +162787,11 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1742249461535" ID="ID_1554376708" MODIFIED="1742256553530" TEXT="Rolle der lib-Gavl überprüfen">
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1742249461535" ID="ID_1554376708" MODIFIED="1747524255970" TEXT="Rolle der lib-Gavl überprüfen">
|
||||
<linktarget COLOR="#a41b18" DESTINATION="ID_1554376708" ENDARROW="Default" ENDINCLINATION="440;-36;" ID="Arrow_ID_608156847" SOURCE="ID_1683248748" STARTARROW="None" STARTINCLINATION="193;12;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1742256560103" ID="ID_1579484179" MODIFIED="1742266889152" TEXT="so traurig es ist: Gavl ist nicht erste Wahl">
|
||||
<linktarget COLOR="#4c3b8b" DESTINATION="ID_1554376708" ENDARROW="Default" ENDINCLINATION="219;519;" ID="Arrow_ID_589652743" SOURCE="ID_219954631" STARTARROW="None" STARTINCLINATION="326;-188;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#95060e" CREATED="1742256560103" ID="ID_1579484179" MODIFIED="1747524214221" TEXT="so traurig es ist: Gavl ist nicht erste Wahl">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -162801,8 +162804,9 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#592f6e" DESTINATION="ID_450633851" ENDARROW="Default" ENDINCLINATION="338;12;" ID="Arrow_ID_637755539" STARTARROW="None" STARTINCLINATION="466;0;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1744719728307" ID="ID_1783840760" MODIFIED="1744719819653" TEXT="siehe steam/external/libgavl.hpp">
|
||||
<node COLOR="#435e98" CREATED="1744719728307" ID="ID_1783840760" MODIFIED="1747524201441" TEXT="siehe steam/external/libgavl.hpp">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -162812,6 +162816,98 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1747489068132" ID="ID_491604266" MODIFIED="1747489074061" TEXT="wird derzeit überhaupt nicht verwendet"/>
|
||||
<node CREATED="1747489084369" ID="ID_1779252669" MODIFIED="1747489105751" TEXT="wohl auch gar nicht implementierbar"/>
|
||||
<node CREATED="1747489074724" ID="ID_663261791" MODIFIED="1747489124777" TEXT="ist eine bloße leere Hülle"/>
|
||||
<node CREATED="1747489111527" ID="ID_229720543" MODIFIED="1747489130041" TEXT="zeigt aber die Richtung"/>
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1747489138341" ID="ID_621636330" MODIFIED="1747489143519" TEXT="dokumentieren">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#166d3d" CREATED="1747489147636" ID="ID_1626357015" MODIFIED="1747524236658" TEXT="gavl_time_t ersetzen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1747489190149" ID="ID_395347682" LINK="https://issues.lumiera.org/ticket/1261" MODIFIED="1747489218934" TEXT="siehe Design-Skizze in #1261">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1747489228725" ID="ID_397750077" MODIFIED="1747489369180" TEXT="verwende einen Namen exakt gleicher Länge">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
raw_time_64
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1747489254856" ID="ID_971197875" MODIFIED="1747489345064" TEXT="danach können alle Includes des C-Headers time.h weg">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
schon seit langem will ich von der "C-Time-Library" weg, denn diese ist gradezu eine Einladung, Timecode-Operationen ad-hoc zu implementieren
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1747489277561" ID="ID_1782395866" MODIFIED="1747489284084" TEXT="einzige Ausnahme: Time.cpp"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1747520420088" ID="ID_1585137684" MODIFIED="1747524248767" TEXT="C-Library für Time-Handling auflösen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1747520433501" ID="ID_1627011016" MODIFIED="1747520503864" TEXT="die ist mir schon lange ein »Dorn im Auge«">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
....weil eine solche Library gradezu dazu einläd, sich nicht mit den „mühsamen“ Abstraktionen des Time-Handling-Frameworks herumzuschlagen, sondern stattessen <i>einfach mit Zahlen zu rechnen.</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1747520507617" ID="ID_391130031" MODIFIED="1747520575548" TEXT="sollte die konkreten Verwendungen durchsehen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...möglicherweise lassen sich diese Funktionen nämlich in Gruppen einteilen und dann direkt in einen anonymen namespace in die jeweilige Translation-Unit schieben....
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1747520518579" ID="ID_1232667630" MODIFIED="1747520531129" TEXT="müßte dann auch den C-Test (test-time) auflösen">
|
||||
<node CREATED="1747520690709" ID="ID_920147641" MODIFIED="1747520695437" TEXT="der ist eigentlich nicht lang"/>
|
||||
<node CREATED="1747520696080" ID="ID_1610185152" MODIFIED="1747520880755" TEXT="und bei näherer Betrachtung ziemlich banal">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Der Test täuscht: weil er in C geschrieben ist, sieht er so komplex aus. Dabei besteht die erste Hälfte lediglich draus, eine Konstruktor-Funktion zu testen. Der sinnvolle Kern daran ist die decimator-Sequenz zum Herunterbrechen von Zeiten. Die wird hier aber nur oberflächlich getestet. Da sollte man, wenn schon, wirklich auf die Grenzfälle losgehen ⟹ Test neu schreiben!
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Der zweite Teil besteht im <i>Antesten</i> der drop-Frame-Umwandlung ⟹ auch das in eigenständigen neuen Test packen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1746059853111" ID="ID_975342117" MODIFIED="1746059870062" TEXT="auch altes RenderNode-Framework zurückbauen">
|
||||
|
|
@ -163898,6 +163994,15 @@ Since then others have made contributions, see the log for the history.</font></
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1742313583059" ID="ID_580462484" MODIFIED="1742313592747" TEXT="Dependency-Liste aktualisieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1747524067725" ID="ID_219954631" MODIFIED="1747524193833" TEXT="Abhängigkeit GAVL">
|
||||
<arrowlink COLOR="#4c3b8b" DESTINATION="ID_1554376708" ENDARROW="Default" ENDINCLINATION="219;519;" ID="Arrow_ID_589652743" STARTARROW="None" STARTINCLINATION="326;-188;"/>
|
||||
<linktarget COLOR="#c95974" DESTINATION="ID_219954631" ENDARROW="Default" ENDINCLINATION="813;-128;" ID="Arrow_ID_889384701" SOURCE="ID_1347801315" STARTARROW="None" STARTINCLINATION="1093;150;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1747524076555" ID="ID_1964951178" MODIFIED="1747524084106" TEXT="behalte das vorerst noch bei">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1747524085359" ID="ID_976130232" LINK="https://github.com/bplaum/gavl" MODIFIED="1747524093250" TEXT="Achtung: umgezogen auf Gitub"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1742313577420" ID="ID_527584176" MODIFIED="1742313592746" TEXT="Bau-Anleitung überarbeiten">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue