core: turn olivecore into liboakcore.so with a pure C ABI

liboakcore is now a shared library that exposes only a C ABI:
- every value class (Rational, TimeRange, Color, Bezier, AudioParams,
  SampleBuffer) and the free-function groups (StringUtils, fraction
  utils, Timecode) is wrapped in an opaque-handle C API under
  core/include/olive/core/oakcore/ (init/copy/free + self-first
  functions), implemented in core/src/capi/
- consumers keep the original C++ API unchanged through same-name
  wrapper classes that hold the handle and forward across the C
  boundary; original implementations moved to core/src/oliveimpl
  (namespace olive::core::internal) and are hidden from export
- TimeRangeList/TimeRangeListFrameIterator are reimplemented inline
  over the wrapper (iterators/containers don't cross C ABI)
- generic Value container stays internal (unused by consumers) and is
  no longer part of the public umbrella header
- hidden visibility + OAKCORE_BUILD export macro; nm shows zero
  olive::* symbols exported
- install into the platform's standard libdir (GNUInstallDirs);
  Windows DLLs next to the executables, macOS into the app bundle
- TimelineWorkArea::in/out/length now return by value: the wrapped
  TimeRange getters return values, and forwarding them through const
  references dangled (found via RenderWorkerFootageTest crash)
- tests: 9 new pure C ABI test executables (oakcore_*_test) covering
  every public C function; 4 stale legacy core tests removed (they
  targeted a long-renamed API and were never built due to a malformed
  option() that also kept OLIVECORE_BUILD_TESTS off)
- CI/CD: oakcore.dll staged for NSIS, liboakcore.so added to the
  AppImage validation list, build-tree DLL copies on Windows
This commit is contained in:
2026-07-19 23:49:40 +08:00
parent 52625f2b98
commit 4fc8b80d7e
69 changed files with 7994 additions and 717 deletions
+217
View File
@@ -0,0 +1,217 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_AUDIOPARAMS_H
#define OAK_LIBOLIVECORE_AUDIOPARAMS_H
#include <cstring>
#include <assert.h>
#include <vector>
#include "render/channellayout.h"
#include "render/sampleformat.h"
#include "../util/rational.h"
namespace olive::core::internal
{
/**
* @brief Audio parameters class managing audio stream configuration
*
* Channel layouts are stored as plain 64-bit masks (see channellayout.h).
* Because the mask is a simple value type, AudioParams has value semantics
* and can be copied freely.
*/
class AudioParams {
public:
/**
* @brief Default constructor creates invalid AudioParams
* sample_rate=0, channel_layout empty, format=INVALID
*/
AudioParams()
: sample_rate_(0)
, channel_layout_mask_(0)
, channel_count_(0)
, format_(SampleFormat::invalid)
{
set_default_footage_parameters();
}
/**
* @brief Constructor from channel layout mask
* @param sample_rate Audio sample rate
* @param channel_layout Channel layout mask (e.g., kChannelLayoutStereo)
* @param format Sample format
*/
AudioParams(const int &sample_rate, uint64_t channel_layout,
const SampleFormat &format)
: sample_rate_(sample_rate)
, channel_layout_mask_(channel_layout)
, channel_count_(0)
, format_(format)
{
set_default_footage_parameters();
timebase_ = sample_rate_as_time_base();
calculate_channel_count();
}
int sample_rate() const
{
return sample_rate_;
}
void set_sample_rate(int sample_rate)
{
sample_rate_ = sample_rate;
}
/**
* @brief Channel layout as a 64-bit mask (0 if unspecified)
*/
const uint64_t &channel_layout() const
{
return channel_layout_mask_;
}
/**
* @brief Set channel layout from mask
* @param mask Channel layout mask (e.g., kChannelLayoutStereo)
*/
void set_channel_layout(uint64_t mask)
{
channel_layout_mask_ = mask;
calculate_channel_count();
}
Rational time_base() const
{
return timebase_;
}
void set_time_base(const Rational &timebase)
{
timebase_ = timebase;
}
Rational sample_rate_as_time_base() const
{
return Rational(1, sample_rate());
}
SampleFormat format() const
{
return format_;
}
void set_format(SampleFormat format)
{
format_ = format;
}
bool enabled() const
{
return enabled_;
}
void set_enabled(bool e)
{
enabled_ = e;
}
int stream_index() const
{
return stream_index_;
}
void set_stream_index(int s)
{
stream_index_ = s;
}
int64_t duration() const
{
return duration_;
}
void set_duration(int64_t duration)
{
duration_ = duration;
}
int64_t time_to_bytes(const double &time) const;
int64_t time_to_bytes(const Rational &time) const;
int64_t time_to_bytes_per_channel(const double &time) const;
int64_t time_to_bytes_per_channel(const Rational &time) const;
int64_t time_to_samples(const double &time) const;
int64_t time_to_samples(const Rational &time) const;
int64_t samples_to_bytes(const int64_t &samples) const;
int64_t samples_to_bytes_per_channel(const int64_t &samples) const;
Rational samples_to_time(const int64_t &samples) const;
int64_t bytes_to_samples(const int64_t &bytes) const;
Rational bytes_to_time(const int64_t &bytes) const;
Rational bytes_per_channel_to_time(const int64_t &bytes) const;
int channel_count() const;
int bytes_per_sample_per_channel() const;
int bits_per_sample() const;
bool is_valid() const;
bool operator==(const AudioParams &other) const;
bool operator!=(const AudioParams &other) const;
static const std::vector<uint64_t> k_supported_channel_layouts;
static const std::vector<int> k_supported_sample_rates;
private:
void set_default_footage_parameters()
{
enabled_ = true;
stream_index_ = 0;
duration_ = 0;
}
/**
* @brief Updates channel_count_ from the current channel_layout_mask_
* Called after any channel layout modification.
*/
void calculate_channel_count();
int sample_rate_; ///< Audio sample rate in Hz (e.g., 48000)
/**
* @brief Channel layout mask (0 if unspecified)
*
* Plain value type mirroring FFmpeg's AV_CH_LAYOUT_* masks; no dynamic
* memory is involved, so copies are trivially safe.
*/
uint64_t channel_layout_mask_;
int channel_count_; ///< Cached channel count from layout
SampleFormat format_; ///< Audio sample format
// Footage-specific parameters (serialized with footage metadata)
int enabled_; // Using int instead of bool fixes GCC 11 stringop-overflow issue (byte alignment)
int stream_index_; ///< Index in the source file's stream list
int64_t duration_; ///< Stream duration in timebase units
Rational timebase_; ///< Timebase for this audio stream
};
}
#endif // OAK_LIBOLIVECORE_AUDIOPARAMS_H
+137
View File
@@ -0,0 +1,137 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_SAMPLEBUFFER_H
#define OAK_LIBOLIVECORE_SAMPLEBUFFER_H
#include <memory>
#include <vector>
#include "audioparams.h"
#include "../util/rational.h"
namespace olive::core::internal
{
/**
* @brief A buffer of audio samples
*
* Audio samples in this structure are always stored in PLANAR (separated by channel). This is done to simplify audio
* rendering code. This replaces the old system of using QByteArrays (containing packed audio) and while SampleBuffer
* replaces many of those in the rendering/processing side of things, QByteArrays are currently still in use for
* playback, including reading to and from the cache.
*/
class SampleBuffer {
public:
SampleBuffer();
SampleBuffer(const AudioParams &audio_params, const Rational &length);
SampleBuffer(const AudioParams &audio_params, size_t samples_per_channel);
SampleBuffer rip_channel(int channel) const;
std::vector<float> rip_channel_vector(int channel) const;
const AudioParams &audio_params() const;
void set_audio_params(const AudioParams &params);
const size_t &sample_count() const
{
return sample_count_per_channel_;
}
void set_sample_count(const size_t &sample_count);
void set_sample_count(const Rational &length)
{
set_sample_count(audio_params_.time_to_samples(length));
}
float *data(int channel)
{
return data_[channel].data();
}
const float *data(int channel) const
{
return data_.at(channel).data();
}
std::vector<float *> to_raw_ptrs()
{
std::vector<float *> r(data_.size());
for (size_t i = 0; i < r.size(); i++) {
r[i] = data_[i].data();
}
return r;
}
int channel_count() const
{
return data_.size();
}
bool is_allocated() const
{
return !data_.empty();
}
void allocate();
void destroy();
void reverse();
void speed(double speed);
void transform_volume(float f);
void transform_volume_for_channel(int channel, float volume);
static void transform_volume(float f, const SampleBuffer *input,
SampleBuffer *output);
static void transform_volume_for_channel(int channel, float volume,
const SampleBuffer *input,
SampleBuffer *output);
void transform_volume_for_sample(size_t sample_index, float volume);
void transform_volume_for_sample_on_channel(size_t sample_index,
int channel, float volume);
void clamp();
void silence();
void silence(size_t start_sample, size_t end_sample);
void silence_bytes(size_t start_byte, size_t end_byte);
void set(int channel, const float *data, size_t sample_offset,
size_t sample_length);
void set(int channel, const float *data, size_t sample_length)
{
set(channel, data, 0, sample_length);
}
void fast_set(const SampleBuffer &other, int to, int from = -1);
private:
void clamp_channel(int channel);
AudioParams audio_params_;
size_t sample_count_per_channel_;
std::vector<std::vector<float>> data_;
};
}
#endif // OAK_LIBOLIVECORE_SAMPLEBUFFER_H
+138
View File
@@ -0,0 +1,138 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_BEZIER_H
#define OAK_LIBOLIVECORE_BEZIER_H
#include <Imath/ImathVec.h>
namespace olive::core::internal
{
class Bezier {
public:
Bezier();
Bezier(double x, double y);
Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x,
double cp2_y);
const double &x() const
{
return x_;
}
const double &y() const
{
return y_;
}
const double &cp1_x() const
{
return cp1_x_;
}
const double &cp1_y() const
{
return cp1_y_;
}
const double &cp2_x() const
{
return cp2_x_;
}
const double &cp2_y() const
{
return cp2_y_;
}
Imath::V2d to_vec() const
{
return Imath::V2d(x_, y_);
}
Imath::V2d control_point_1_to_vec() const
{
return Imath::V2d(cp1_x_, cp1_y_);
}
Imath::V2d control_point_2_to_vec() const
{
return Imath::V2d(cp2_x_, cp2_y_);
}
void set_x(const double &x)
{
x_ = x;
}
void set_y(const double &y)
{
y_ = y;
}
void set_cp1_x(const double &cp1_x)
{
cp1_x_ = cp1_x;
}
void set_cp1_y(const double &cp1_y)
{
cp1_y_ = cp1_y;
}
void set_cp2_x(const double &cp2_x)
{
cp2_x_ = cp2_x;
}
void set_cp2_y(const double &cp2_y)
{
cp2_y_ = cp2_y;
}
static double quadratic_xto_t(double x, double a, double b, double c);
static double quadratic_tto_y(double a, double b, double c, double t);
static double quadratic_xto_y(double x, const Imath::V2d &a,
const Imath::V2d &b, const Imath::V2d &c)
{
return quadratic_tto_y(a.y, b.y, c.y, quadratic_xto_t(x, a.x, b.x, c.x));
}
static double cubic_xto_t(double x, double a, double b, double c, double d);
static double cubic_tto_y(double a, double b, double c, double d, double t);
static double cubic_xto_y(double x, const Imath::V2d &a, const Imath::V2d &b,
const Imath::V2d &c, const Imath::V2d &d)
{
return cubic_tto_y(a.y, b.y, c.y, d.y, cubic_xto_t(x, a.x, b.x, c.x, d.x));
}
private:
static double calculate_t_from_x(bool cubic, double x, double a, double b,
double c, double d);
double x_;
double y_;
double cp1_x_;
double cp1_y_;
double cp2_x_;
double cp2_y_;
};
}
#endif // OAK_LIBOLIVECORE_BEZIER_H
+184
View File
@@ -0,0 +1,184 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_COLOR_H
#define OAK_LIBOLIVECORE_COLOR_H
#include "render/pixelformat.h"
namespace olive::core::internal
{
/**
* @brief High precision 32-bit DataType based RGBA color value
*/
class Color {
public:
using DataType = float;
static constexpr unsigned int rgba = 4;
Color()
{
for (unsigned int i = 0; i < rgba; i++) {
data_[i] = 0.0;
}
}
Color(const DataType &r, const DataType &g, const DataType &b,
const DataType &a = 1.0f)
{
data_[0] = r;
data_[1] = g;
data_[2] = b;
data_[3] = a;
}
Color(const char *data, const PixelFormat &format, int ch_layout);
/**
* @brief Creates a Color struct from hue/saturation/value
*
* Hue expects a value between 0.0 and 360.0. Saturation and Value expect a value between 0.0 and 1.0.
*/
static Color from_hsv(const DataType &h, const DataType &s,
const DataType &v);
const DataType &red() const
{
return data_[0];
}
const DataType &green() const
{
return data_[1];
}
const DataType &blue() const
{
return data_[2];
}
const DataType &alpha() const
{
return data_[3];
}
void to_hsv(DataType *hue, DataType *sat, DataType *val) const;
DataType hsv_hue() const;
DataType hsv_saturation() const;
DataType value() const;
void to_hsl(DataType *hue, DataType *sat, DataType *lightness) const;
DataType hsl_hue() const;
DataType hsl_saturation() const;
DataType lightness() const;
void set_red(const DataType &red)
{
data_[0] = red;
}
void set_green(const DataType &green)
{
data_[1] = green;
}
void set_blue(const DataType &blue)
{
data_[2] = blue;
}
void set_alpha(const DataType &alpha)
{
data_[3] = alpha;
}
DataType *data()
{
return data_;
}
const DataType *data() const
{
return data_;
}
void to_data(char *out, const PixelFormat &format,
unsigned int nb_channels) const;
static Color from_data(const char *in, const PixelFormat &format,
unsigned int nb_channels);
// Suuuuper rough luminance value mostly used for UI (determining whether to overlay with black
// or white text)
DataType get_rough_luminance() const;
// Assignment math operators
Color &operator+=(const Color &rhs);
Color &operator-=(const Color &rhs);
Color &operator+=(const DataType &rhs);
Color &operator-=(const DataType &rhs);
Color &operator*=(const DataType &rhs);
Color &operator/=(const DataType &rhs);
// Binary math operators
Color operator+(const Color &rhs) const
{
Color c(*this);
c += rhs;
return c;
}
Color operator-(const Color &rhs) const
{
Color c(*this);
c -= rhs;
return c;
}
Color operator+(const DataType &rhs) const
{
Color c(*this);
c += rhs;
return c;
}
Color operator-(const DataType &rhs) const
{
Color c(*this);
c -= rhs;
return c;
}
Color operator*(const DataType &rhs) const
{
Color c(*this);
c *= rhs;
return c;
}
Color operator/(const DataType &rhs) const
{
Color c(*this);
c /= rhs;
return c;
}
private:
DataType data_[rgba];
};
}
#endif // OAK_LIBOLIVECORE_COLOR_H
+80
View File
@@ -0,0 +1,80 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_FRACTIONUTILS_H
#define OAK_LIBOLIVECORE_FRACTIONUTILS_H
#include <stdint.h>
namespace olive::core::internal
{
/**
* @brief Rounding modes for RescaleRnd()
*
* Mirrors the FFmpeg AVRounding modes that this codebase used before the
* FFmpeg dependency was removed from core.
*/
enum class FractionRounding {
/**
* Round to the nearest value; halfway cases are rounded away from zero.
* Equivalent to FFmpeg's AV_ROUND_NEAR_INF.
*/
k_near_inf,
/**
* Round toward positive infinity. Equivalent to FFmpeg's AV_ROUND_UP.
*/
k_up
};
/**
* @brief Reduce a fraction so that numerator and denominator fit within `max`
*
* Native re-implementation of FFmpeg's av_reduce(): divides out the greatest
* common divisor and, if the values still do not fit within `max`, finds the
* closest approximation using continued fractions.
*
* A zero denominator is preserved (with the numerator set to zero).
*/
void reduce_fraction(int64_t &num, int64_t &den, int64_t max);
/**
* @brief Compare two fractions
*
* Native re-implementation of FFmpeg's av_cmp_q(): returns -1 if a < b,
* 0 if a == b, 1 if a > b, and INT_MIN when the comparison is meaningless
* (degenerate zero-denominator fractions).
*/
int compare_fractions(int an, int ad, int bn, int bd);
/**
* @brief Rescale `a` by the fraction b/c: returns a * b / c
*
* Native re-implementation of FFmpeg's av_rescale_rnd(). The intermediate
* product is computed with 128-bit arithmetic where available so that no
* precision is lost for large timestamps.
*/
int64_t rescale_rnd(int64_t a, int64_t b, int64_t c, FractionRounding rnd);
}
#endif // OAK_LIBOLIVECORE_FRACTIONUTILS_H
+157
View File
@@ -0,0 +1,157 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_RATIONAL_H
#define OAK_LIBOLIVECORE_RATIONAL_H
#include <climits>
#include <iostream>
#ifdef USE_OTIO
#include <opentime/rationalTime.h>
#endif
namespace olive::core::internal
{
class Rational {
public:
Rational(const int &numerator = 0)
{
num_ = numerator;
den_ = 1;
}
Rational(const int &numerator, const int &denominator)
{
num_ = numerator;
den_ = denominator;
fix_signs();
reduce();
}
Rational(const Rational &rhs) = default;
static Rational from_double(const double &flt, bool *ok = nullptr);
static Rational from_string(const std::string &str, bool *ok = nullptr);
static const Rational na_n;
//Assignment Operators
const Rational &operator=(const Rational &rhs);
const Rational &operator+=(const Rational &rhs);
const Rational &operator-=(const Rational &rhs);
const Rational &operator/=(const Rational &rhs);
const Rational &operator*=(const Rational &rhs);
//Binary math operators
Rational operator+(const Rational &rhs) const;
Rational operator-(const Rational &rhs) const;
Rational operator/(const Rational &rhs) const;
Rational operator*(const Rational &rhs) const;
//Relational and equality operators
bool operator<(const Rational &rhs) const;
bool operator<=(const Rational &rhs) const;
bool operator>(const Rational &rhs) const;
bool operator>=(const Rational &rhs) const;
bool operator==(const Rational &rhs) const;
bool operator!=(const Rational &rhs) const;
//Unary operators
const Rational &operator+() const
{
return *this;
}
Rational operator-() const
{
return Rational(num_, -den_);
}
bool operator!() const
{
return !num_;
}
//Function: convert to double
double to_double() const;
#ifdef USE_OTIO
static Rational fromRationalTime(const opentime::RationalTime &t)
{
// Is this the best way to do this?
return fromDouble(t.to_seconds());
}
// Convert Olive rationals to opentime rationals with the given framerate (defaults to 24)
opentime::RationalTime toRationalTime(double framerate = 24) const;
#endif
// Produce "flipped" version
Rational flipped() const;
void flip();
// Returns whether the Rational is valid but equal to zero or not
//
// A NaN is always a null, but a null is not always a NaN
bool isNull() const
{
return num_ == 0;
}
// Returns whether this Rational is not a valid number (denominator == 0)
bool isNaN() const
{
return den_ == 0;
}
const int &numerator() const
{
return num_;
}
const int &denominator() const
{
return den_;
}
std::string to_string() const;
friend std::ostream &operator<<(std::ostream &out, const Rational &value)
{
out << value.num_ << '/' << value.den_;
return out;
}
private:
void fix_signs();
void reduce();
int num_;
int den_;
};
#define RATIONAL_MIN Rational(INT_MIN)
#define RATIONAL_MAX Rational(INT_MAX)
}
#endif // OAK_LIBOLIVECORE_RATIONAL_H
+211
View File
@@ -0,0 +1,211 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_STRINGUTILS_H
#define OAK_LIBOLIVECORE_STRINGUTILS_H
#include <algorithm>
#include <regex>
#include <vector>
#include <string>
namespace olive::core::internal
{
class StringUtils {
public:
/**
* @brief Split a string into a list of strings using a specific delimiter
*
* @param s
*
* The string to split.
*
* @param separator
*
* The character to split the string on.
*
* @return
*
* A vector of strings split by the specified delimiter.
*/
static std::vector<std::string> split(const std::string &s, char separator);
/**
* @brief Splits a string into a list of strings using regular expressions.
*
* @param s
*
* The string to split.
*
* @param regex
*
* The regular expression to split the string on.
*
* @return
*
* A vector of strings split wherever the regular expression matched.
*/
static std::vector<std::string> split_regex(const std::string &s,
const std::regex &regex);
/**
* @brief Convert a string to int using a bool pointer to determine success rather than an exception
*
* @param s
*
* The string to parse an int from.
*
* @param base
*
* The base of the number in the string (usually 10, or 16 for hex).
*
* @param ok
*
* (Optional) a boolean output parameter specifying whether the conversion was successful or not.
*
* @return
*
* Either the int parsed from the string, or 0 (with *ok set to false) on parser error.
*/
static int to_int(const std::string &s, int base, bool *ok = nullptr);
/**
* @brief Overloaded function
*
* @param s
*
* The string to parse an int from.
*
* @param ok
*
* (Optional) a boolean output parameter specifying whether the conversion was successful or not.
*
* @return
*
* Either the int parsed from the string, or 0 (with *ok set to false) on parser error.
*/
static int to_int(const std::string &s, bool *ok = nullptr)
{
return to_int(s, 10, ok);
}
/**
* @brief Convert a number to a string with left padding
*
* Usually used for converting a number to a string with leading zeroes.
*
* @param val
*
* The number to convert. This is a templated function and will accept any type, e.g.
* int/long/float/double/etc.
*
* @param padding
*
* Total desired length of the string. For example, setting this to `2` will ensure the string
* is at least 2 characters in size, using `c` to pad the left side where necessary.
*
* @param c
*
* The character to pad with. This defaults to `0` assuming you'll be using this function to
* create leading zeroes.
*
* @return
*
* The padded string.
*/
template <typename T>
static std::string to_string_leftpad(T val, size_t padding, char c = '0')
{
std::string s = std::to_string(val);
if (s.size() < padding) {
s.insert(0, padding - s.size(), c);
}
return s;
}
/**
* @brief Format a string
*
* A sprintf wrapper that returns a std::string.
*
* @param fmt
*
* The format to use.
*
* @return
*
* A formatted string in std::string form.
*/
static std::string format(const char *fmt, ...);
// trim from start (in place)
static inline void ltrim(std::string &s)
{
s.erase(s.begin(),
std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string &s)
{
s.erase(std::find_if(s.rbegin(), s.rend(),
[](unsigned char ch) { return !std::isspace(ch); })
.base(),
s.end());
}
// trim from both ends (in place)
static inline void trim(std::string &s)
{
rtrim(s);
ltrim(s);
}
// trim from start (copying)
static inline std::string ltrimmed(std::string s)
{
ltrim(s);
return s;
}
// trim from end (copying)
static inline std::string rtrimmed(std::string s)
{
rtrim(s);
return s;
}
// trim from both ends (copying)
static inline std::string trimmed(std::string s)
{
trim(s);
return s;
}
};
}
#endif // OAK_LIBOLIVECORE_STRINGUTILS_H
@@ -0,0 +1,93 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_TIMECODEFUNCTIONS_H
#define OAK_LIBOLIVECORE_TIMECODEFUNCTIONS_H
#include "rational.h"
#include <cstdint>
namespace olive::core::internal
{
/**
* @brief Functions for converting times/timecodes/timestamps
*
* Olive uses the following terminology through its code:
*
* `time` - time in seconds presented in a Rational form
* `timebase` - the base time unit of an audio/video stream in seconds
* `timestamp` - an integer representation of a time in timebase units (in many cases is used like a frame number)
* `timecode` a user-friendly string representation of a time according to Timecode::Display
*/
class Timecode {
public:
enum Display {
k_timecode_drop_frame,
k_timecode_non_drop_frame,
k_timecode_seconds,
k_frames,
k_milliseconds
};
enum Rounding { k_ceil, k_floor, k_round };
/**
* @brief Convert a timestamp (according to a Rational timebase) to a user-friendly string representation
*/
static std::string time_to_timecode(const Rational &time,
const Rational &timebase,
const Display &display,
bool show_plus_if_positive = false);
static Rational timecode_to_time(std::string timecode,
const Rational &timebase,
const Display &display,
bool *ok = nullptr);
static std::string time_to_string(int64_t ms);
static Rational snap_time_to_timebase(const Rational &time,
const Rational &timebase,
Rounding floor = k_round);
static int64_t time_to_timestamp(const Rational &time,
const Rational &timebase,
Rounding floor = k_round);
static int64_t time_to_timestamp(const double &time,
const Rational &timebase,
Rounding floor = k_round);
static int64_t rescale_timestamp(const int64_t &ts, const Rational &source,
const Rational &dest);
static int64_t rescale_timestamp_ceil(const int64_t &ts,
const Rational &source,
const Rational &dest);
static Rational timestamp_to_time(const int64_t &timestamp,
const Rational &timebase);
static bool timebase_is_drop_frame(const Rational &timebase);
};
}
#endif // OAK_LIBOLIVECORE_TIMECODEFUNCTIONS_H
+315
View File
@@ -0,0 +1,315 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_TIMERANGE_H
#define OAK_LIBOLIVECORE_TIMERANGE_H
#include <list>
#include <vector>
#include "rational.h"
namespace olive::core::internal
{
class TimeRange {
public:
TimeRange() = default;
TimeRange(const Rational &in, const Rational &out);
TimeRange(const TimeRange &r)
: TimeRange(r.in(), r.out())
{
}
TimeRange &operator=(const TimeRange &r)
{
set_range(r.in(), r.out());
return *this;
}
const Rational &in() const;
const Rational &out() const;
const Rational &length() const;
void set_in(const Rational &in);
void set_out(const Rational &out);
void set_range(const Rational &in, const Rational &out);
bool operator==(const TimeRange &r) const;
bool operator!=(const TimeRange &r) const;
bool overlaps_with(const TimeRange &a, bool in_inclusive = true,
bool out_inclusive = true) const;
bool contains(const TimeRange &a, bool in_inclusive = true,
bool out_inclusive = true) const;
bool contains(const Rational &r) const;
TimeRange combined(const TimeRange &a) const;
static TimeRange combine(const TimeRange &a, const TimeRange &b);
TimeRange intersected(const TimeRange &a) const;
static TimeRange intersect(const TimeRange &a, const TimeRange &b);
TimeRange operator+(const Rational &rhs) const;
TimeRange operator-(const Rational &rhs) const;
const TimeRange &operator+=(const Rational &rhs);
const TimeRange &operator-=(const Rational &rhs);
std::list<TimeRange> split(const int &chunk_size) const;
private:
void normalize();
Rational in_;
Rational out_;
Rational length_;
};
class TimeRangeList {
public:
TimeRangeList() = default;
TimeRangeList(std::initializer_list<TimeRange> r)
: array_(r)
{
}
void insert(const TimeRangeList &list_to_add);
void insert(TimeRange range_to_add);
void remove(const TimeRange &remove);
void remove(const TimeRangeList &list);
template <typename T>
static void util_remove(std::vector<T> *list, const TimeRange &remove)
{
std::vector<T> additions;
for (auto it = list->begin(); it != list->end();) {
T &compare = *it;
if (remove.contains(compare)) {
// This element is entirely encompassed in this range, remove it
it = list->erase(it);
} else {
if (compare.contains(remove, false, false)) {
// The remove range is within this element, only choice is to split the element into two
T new_range = compare;
new_range.set_in(remove.out());
compare.set_out(remove.in());
additions.push_back(new_range);
break;
} else {
if (compare.in() < remove.in() &&
compare.out() > remove.in()) {
// This element's out point overlaps the range's in, we'll trim it
compare.set_out(remove.in());
} else if (compare.in() < remove.out() &&
compare.out() > remove.out()) {
// This element's in point overlaps the range's out, we'll trim it
compare.set_in(remove.out());
}
it++;
}
}
}
list->insert(list->end(), additions.begin(), additions.end());
}
bool contains(const TimeRange &range, bool in_inclusive = true,
bool out_inclusive = true) const;
bool contains(const Rational &r) const
{
for (const TimeRange &range : array_) {
if (range.contains(r)) {
return true;
}
}
return false;
}
bool overlaps_with(const TimeRange &r, bool in_inclusive = true,
bool out_inclusive = true) const
{
for (const TimeRange &range : array_) {
if (range.overlaps_with(r, in_inclusive, out_inclusive)) {
return true;
}
}
return false;
}
bool isEmpty() const
{
return array_.empty();
}
void clear()
{
array_.clear();
}
int size() const
{
return array_.size();
}
void shift(const Rational &diff);
void trim_in(const Rational &diff);
void trim_out(const Rational &diff);
TimeRangeList intersects(const TimeRange &range) const;
using const_iterator = std::vector<TimeRange>::const_iterator;
const_iterator begin() const
{
return array_.cbegin();
}
const_iterator end() const
{
return array_.cend();
}
const_iterator cbegin() const
{
return begin();
}
const_iterator cend() const
{
return end();
}
const TimeRange &first() const
{
return array_.front();
}
const TimeRange &last() const
{
return array_.back();
}
const TimeRange &at(int index) const
{
return array_.at(index);
}
const std::vector<TimeRange> &internal_array() const
{
return array_;
}
bool operator==(const TimeRangeList &rhs) const
{
return array_ == rhs.array_;
}
private:
std::vector<TimeRange> array_;
};
class TimeRangeListFrameIterator {
public:
TimeRangeListFrameIterator();
TimeRangeListFrameIterator(const TimeRangeList &list,
const Rational &timebase);
Rational snap(const Rational &r) const;
bool get_next(Rational *out);
bool has_next() const;
std::vector<Rational> to_vector() const
{
TimeRangeListFrameIterator copy(list_, timebase_);
std::vector<Rational> times;
Rational r;
while (copy.get_next(&r)) {
times.push_back(r);
}
return times;
}
int size();
void reset()
{
*this = TimeRangeListFrameIterator();
}
void insert(const TimeRange &range)
{
list_.insert(range);
}
void insert(const TimeRangeList &list)
{
list_.insert(list);
}
bool is_custom_range() const
{
return custom_range_;
}
void set_custom_range(bool e)
{
custom_range_ = e;
}
int frame_index() const
{
return frame_index_;
}
private:
void update_index_if_necessary();
TimeRangeList list_;
Rational timebase_;
Rational current_;
int range_index_;
int size_;
int frame_index_;
bool custom_range_;
};
}
#endif // OAK_LIBOLIVECORE_TIMERANGE_H
+96
View File
@@ -0,0 +1,96 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2023 Olive Studios LLC
Modifications Copyright (C) 2025 mikesolar
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_LIBOLIVECORE_VALUE_H
#define OAK_LIBOLIVECORE_VALUE_H
#include <map>
#include <stdint.h>
#include <string>
#include <string.h>
#include <vector>
namespace olive::core::internal
{
/**
* @brief Generic type container
*/
class Value {
public:
enum Type {
/// Null/no data
none,
/// Signed int64
INT,
/// Double-precision float
FLOAT,
/// UTF-8 string
string
};
Value()
{
type_ = none;
}
Value(int64_t v)
{
data_.resize(sizeof(int64_t));
memcpy(data_.data(), &v, sizeof(int64_t));
type_ = INT;
}
Value(double v)
{
data_.resize(sizeof(double));
memcpy(data_.data(), &v, sizeof(int64_t));
type_ = FLOAT;
}
Value(const char *s)
{
size_t sz = strlen(s);
data_.resize(sz);
memcpy(data_.data(), s, sz);
type_ = string;
}
Value(const std::string &s)
{
data_.resize(s.size());
memcpy(data_.data(), s.data(), data_.size());
type_ = string;
}
private:
std::vector<uint8_t> data_;
Type type_;
};
using ValueMap = std::map<std::string, Value>;
}
#endif // OAK_LIBOLIVECORE_VALUE_H