refactor(common): de-Qt oakcommon and wrap it in a pure C ABI
- de-Qt all classes under src/common (std::string/vector/mutex, std::filesystem, expat-based XmlStreamReader/Writer) - add pure C ABI in include/common + src/common/c_api: opaque handles, init returns NULL on failure, free(NULL) is a no-op, out-params with negative OAKCOMMON_E_* error codes (include/common/error.h) - remove single-consumer classes from oakcommon (html, jobtime, otioutils, playbackaudioclock, tohex, util, avframeptr, crashpadinterface/crashpadutils, autoscroll, digit, range); their destinations are recorded in docs/zh/plans/riir/notes.md - add gtest suites under src/common/tests (127 cases), standalone build driver in src/common/standalone
This commit is contained in:
+5
-2
@@ -48,8 +48,11 @@ set(CMAKE_AUTORCC ON)
|
||||
# root to enrich your IDE with clangd language server protocol functionalities
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
include_directories(include/)
|
||||
# Sanitizers
|
||||
add_library(olive-sanitizers INTERFACE)
|
||||
add_library(olive-sanitizers INTERFACE
|
||||
src/common/c_api/commandlineparser.cpp
|
||||
include/common/commandlineparser.h)
|
||||
include(cmake/Sanitizers.cmake)
|
||||
enable_sanitizers(olive-sanitizers)
|
||||
list(APPEND OLIVE_LIBRARIES olive-sanitizers)
|
||||
@@ -368,7 +371,7 @@ add_subdirectory(cli)
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(worker)
|
||||
|
||||
add_subdirectory(src)
|
||||
if (BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -101,3 +101,67 @@ OAK/OIIO/FFmpeg 类型全部句柄化或拍平字段。)
|
||||
- xml:reader 解析样例串、attr/text 读取、skip、writer 产出解析回读。
|
||||
- files:临时目录建/写/读/尺寸/删除。
|
||||
- 每函数 1 正常 + 1 错误路径;`oakcommon_debug_alive_count()` 泄漏断言。
|
||||
|
||||
## 实施现状(2026-08-05)
|
||||
|
||||
M1 已落地并可独立构建、测试全绿(127 个用例:126 通过,1 个
|
||||
`GTEST_SKIP`)。以下为与上文计划的实际差异。
|
||||
|
||||
### 最终目录结构
|
||||
|
||||
- `src/common/src/` — 去 Qt 化 C++ 实现(`olive::` 命名空间),target
|
||||
`oakcommon`(SHARED)。
|
||||
- `src/common/c_api/` — 纯 C ABI 包装,通过 `target_sources` 合并进
|
||||
`oakcommon`,不单独成库。
|
||||
- `src/common/tests/` — gtest,target `oakcommon-gtest`,
|
||||
`gtest_discover_tests`。
|
||||
- `include/common/`(仓库根)— 公共 C 头:`commandlineparser.h`、
|
||||
`current.h`、`debug.h`、`dropworkflowbehavior.h`、`error.h`、
|
||||
`ffmpegutils.h`、`filefunctions.h`、`miscutils.h`、`ocioutils.h`、
|
||||
`oiioutils.h`、`power.h`、`qtutils.h`、`xmlutils.h`。
|
||||
- `src/common/standalone/CMakeLists.txt` — 独立构建 driver(见下)。
|
||||
|
||||
### 独立构建与测试
|
||||
|
||||
```sh
|
||||
cmake -S src/common/standalone -B build-oakcommon
|
||||
cmake --build build-oakcommon -j
|
||||
ctest --test-dir build-oakcommon --output-on-failure
|
||||
```
|
||||
|
||||
driver 通过 `find_package(... CONFIG)` 使用 Homebrew 的 OCIO/OIIO/GTest
|
||||
(顶层 `cmake/FindOpenColorIO.cmake`/`FindOpenImageIO.cmake` 面向
|
||||
`.so`,macOS 下不适用),并把 config target 映射到 oakcommon
|
||||
CMakeLists 消费的 `${OCIO_LIBRARIES}` 等变量。driver 中额外处理了两点:
|
||||
给 `olivecore` 补 `third_party/openfx/include` 头路径(顶层靠全局
|
||||
include);禁用 OpenTimelineIO(`/opt/otio` 的 dylib 用 `@loader_path`
|
||||
安装名,构建树内无法加载,且 oakcommon 不需要 OTIO)。
|
||||
|
||||
### 实际依赖
|
||||
|
||||
- 第三方:EXPAT(XML 解析)、OpenColorIO、OpenImageIO、FFmpeg(经
|
||||
ffmpeg_bridge 间接)、GTest(仅测试)。
|
||||
- Oak 内部库:**仍链接 `olivecore` 与 `ffmpeg_bridge`**,二者均为真实
|
||||
符号依赖而非纯头文件:
|
||||
- `olive::core::Rational`(`core/include/olive/core/util/rational.h`)
|
||||
是对 `oakcore_rational_*` C ABI 的包装,构造/析构/运算都需要
|
||||
olivecore 的符号;
|
||||
- `FFmpegUtils::get_compatible_bridge_pixel_format` 调用
|
||||
`fb_find_best_pix_fmt_of_list`(ffmpeg_bridge 导出符号)。
|
||||
- `pixelformat.h`/`sampleformat.h` 本身是 header-only,只需头路径
|
||||
(`core/include`、`ffmpeg_bridge/include`,因这两个 target 的
|
||||
include 目录是 PRIVATE,在 oakcommon 里显式补为 PUBLIC)。
|
||||
- 未按计划只链 oakcore 头;OLIVECORE_BUILD_TESTS 在独立构建中关闭。
|
||||
|
||||
### 与计划的主要差异
|
||||
|
||||
- 接口未按 §2 冻结清单逐条实现,而是按实际使用面包装:C ABI 头放在
|
||||
仓库根 `include/common/`,命名 `oakcommon_<模块>_<动词>`,两段式
|
||||
buffer(先查询尺寸再拷入)约定统一。
|
||||
- `xmlutils` 用 expat 实现(事件预先解析成队列);C API 的
|
||||
`oakcommon_xml_reader_read_element_text` 因底层是消费型读取,在
|
||||
handle 内缓存最近一次文本以兼容两段式调用。
|
||||
- 被移除/上移的类(播放钟、autoscroll 等反向 include 涉及项)见
|
||||
`notes.md`。
|
||||
- 计划中提到的 `switch(PixelFormat)` 编译问题实际不存在:
|
||||
`olive::core::PixelFormat` 提供 `operator Format()`,可直接 switch。
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# 记录项
|
||||
|
||||
## common
|
||||
|
||||
CancelableObject只在render和task处有🚰,移入对应模块。
|
||||
|
||||
## 从 oakcommon(src/common)移除的类(2026-08-05)
|
||||
|
||||
判据:不严重依赖 common 其他类,且只有一个非 common 模块使用它
|
||||
(tests/gtest 不计入使用方)。以下类已从 `src/common/` 移除,
|
||||
拆分时应放到对应模块:
|
||||
|
||||
| 类 | 唯一使用方 | 应放到 |
|
||||
|---|---|---|
|
||||
| `Html`(html.h/.cpp) | engine/node | oaknode(M3)。注意它依赖 common 的 xmlutils,迁移时需连同 XML 辅助或改为调用 oakcommon C API |
|
||||
| `JobTime`(jobtime.h/.cpp) | engine/render | oakrender(M7) |
|
||||
| `OTIOUtils`(otioutils.h) | engine/task | oaktask(M8) |
|
||||
| `PlaybackAudioClock`(playbackaudioclock.h) | engine/audio | oakaudio(M6) |
|
||||
| `to_hex`(tohex.h) | engine/node | oaknode(M3) |
|
||||
| `mid()`(util.h) | engine/node | oaknode(M3) |
|
||||
| `AVFramePtr`(avframeptr.h) | engine/render | oakrender(M7) |
|
||||
| `CrashpadInterface`(crashpadinterface.h/.cpp) | app/main.cpp | app(crashhandler 相关) |
|
||||
| crashpadutils.h | app/crashhandler | app/crashhandler |
|
||||
| `AutoScroll`(autoscroll.h,oakutil shim) | app/dialog、app/widget | app |
|
||||
| `digit.h`(oakutil shim) | app/dialog | app |
|
||||
| `range.h`(oakutil shim) | app/widget | app |
|
||||
|
||||
另:`power.h`、`memorypool.h`、`threadsafemap.h` 当前没有任何
|
||||
common 模块外的使用方(零用户),不满足移除判据,暂保留在
|
||||
oakcommon;后续若确认无用途可直接删除。
|
||||
|
||||
## oakcommon 去Qt化的删除与语义变更(2026-08-05)
|
||||
|
||||
去Qt化过程中以下函数被删除或语义变化,迁移调用方时需注意:
|
||||
|
||||
- `CommandLineParser::print_help()`:不再自动读取
|
||||
`QCoreApplication::applicationName()/applicationVersion()`,需先调
|
||||
`set_app_info()`(C API:`oakcommon_commandlineparser_set_app_info`),
|
||||
否则打印默认 `"oak"` + 空版本。
|
||||
- `FileFunctions::get_unique_file_identifier`:哈希由 SHA-1 改为
|
||||
FNV-1a 64-bit,旧缓存 key 全部失效(需一次重建)。
|
||||
`get_configuration_location`/`get_application_path`/
|
||||
`get_temp_file_path`/`get_auto_recovery_root` 的标准路径改为手写
|
||||
平台实现,路径与原 Qt 版不同。`read_file_as_string(":/...")` 的
|
||||
qrc 资源路径不再可用,shader 等资源调用方需改为磁盘路径或内嵌。
|
||||
- xmlutils:`xml_read_next_start_element(reader, CancelAtom*)`
|
||||
重载与 cancel_atom 参数删除(依赖 render/cancelatom.h),取消语义
|
||||
由调用方在循环外自行检查;`XMLAttributeLoop` 宏删除,由
|
||||
`XmlStreamReader::attributes()` + range-for 替代;
|
||||
`QXmlStreamReader/Writer` 由基于 expat 的
|
||||
`olive::XmlStreamReader/Writer` 替代(不支持 XML 命名空间)。
|
||||
- `OIIOUtils::frame_to_buffer/buffer_to_frame`:未进 C API,C++ 侧签名
|
||||
拍平为 `(const void *data, int64_t linesize_bytes, OIIO::ImageBuf *)`,
|
||||
engine/codec/frame.cpp 迁移时传成员即可。`OakCommonPixelFormat` 枚举
|
||||
目前定义在 include/common/ocioutils.h,oiioutils 复用,后续可抽成
|
||||
独立的 include/common/pixelformat.h。
|
||||
- `MemoryPool`:删除 QTimer 每 5 秒自动回收空 arena,改为公有
|
||||
`clear_empty_arenas()` 由调用方周期调用;不再继承 QObject。
|
||||
memorypool/threadsafemap 零使用方且为模板/多态基类,未包 C API。
|
||||
- `debug_handler`:丢失 `QMessageLogContext`(文件/行号)参数(原实现
|
||||
本就未用);`qInstallMessageHandler` 无对应物。
|
||||
- `Current::set_current_video_params/audio_params`:由按值拷贝改为持有
|
||||
`std::shared_ptr<void>`(类型擦除),调用方需自行管理生命周期并用
|
||||
`static_pointer_cast` 还原(调用点:olivehost.cpp、
|
||||
timebasedwidget.cpp、plugin.cpp 等 6 处)。
|
||||
- qtutils:engine 未使用的纯 UI 函数(`q_font_metrics_width`、
|
||||
`create_horizontal/vertical_line`、`set_combo_box_data`、
|
||||
`word_wrap_string`、`flip_control_and_shift_modifiers`、
|
||||
`get_formatted_date_time`、`to_q_color`、`core::qHash`、
|
||||
`Q_DECLARE_METATYPE` 宏组)未迁入 oakcommon,app 层继续用 Qt 版。
|
||||
`ptr_to_value/value_to_ptr` 载体由 QVariant 改为 `uintptr_t`,
|
||||
迁移时调用点需同步改(renderprocessor.cpp、rendermanager.cpp、
|
||||
previewautocacher.cpp、src/capi/worker.cpp)。`get_parent_of_type`
|
||||
依赖 C++ 模板+RTTI,未进 C API。
|
||||
- define.h:删除 `MACRO_NAME_AS_STR`/`MACRO_VAL_AS_STR`/
|
||||
`OLIVE_NS_CONST_ARG`/`OLIVE_NS_ARG`/`OLIVE_NS_RETURN_ARG`
|
||||
(依赖 Qt QArgument,全仓无使用)。
|
||||
- decibel.h:删除 `ALLOW_RETURNING_INFINITY` 编译开关分支(代码库
|
||||
未定义该宏,行为不变)。
|
||||
- `FFmpegUtils`:新增类内常量 `k_rgb_channel_count/k_rgba_channel_count`
|
||||
(3/4)取代对 `VideoParams` 同名常量的引用,消除 common→render
|
||||
反向依赖。
|
||||
@@ -0,0 +1,193 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_COMMANDLINEPARSER_H
|
||||
#define OAK_EDITOR_COMMANDLINEPARSER_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Opaque handle to a command-line parser instance.
|
||||
*/
|
||||
typedef struct OakCommonCommandLineParser OakCommonCommandLineParser;
|
||||
|
||||
/**
|
||||
* @brief Opaque handle to a registered command-line option.
|
||||
*
|
||||
* The handle wrapper is freed with oakcommon_commandlineoption_free();
|
||||
* the underlying option is owned by the parser and stays valid until
|
||||
* the parser is freed.
|
||||
*/
|
||||
typedef struct OakCommonCommandLineOption OakCommonCommandLineOption;
|
||||
|
||||
/**
|
||||
* @brief Opaque handle to a registered positional argument.
|
||||
*
|
||||
* The handle wrapper is freed with
|
||||
* oakcommon_commandlinepositionalargument_free(); the underlying argument
|
||||
* is owned by the parser and stays valid until the parser is freed.
|
||||
*/
|
||||
typedef struct OakCommonCommandLinePositionalArgument
|
||||
OakCommonCommandLinePositionalArgument;
|
||||
|
||||
/**
|
||||
* @brief Create a command-line parser.
|
||||
*
|
||||
* @return Parser handle, or NULL on allocation failure.
|
||||
*/
|
||||
OakCommonCommandLineParser *oakcommon_commandlineparser_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroy a command-line parser.
|
||||
*
|
||||
* Destroys all option and positional-argument handles created from it.
|
||||
* NULL is a no-op.
|
||||
*/
|
||||
void oakcommon_commandlineparser_free(OakCommonCommandLineParser *parser);
|
||||
|
||||
/**
|
||||
* @brief Set the application name/version shown by print_help.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineparser_set_app_info(OakCommonCommandLineParser *parser,
|
||||
const char *name,
|
||||
const char *version);
|
||||
|
||||
/**
|
||||
* @brief Register an option with one or more name strings.
|
||||
*
|
||||
* @param names Array of option name strings (without leading dash).
|
||||
* @param name_count Number of entries in names. Must be > 0.
|
||||
* @param description Help text, may be NULL.
|
||||
* @param takes_arg Non-zero if the option consumes the following argument.
|
||||
* @param arg_placeholder Placeholder shown in help, may be NULL.
|
||||
* @param hidden Non-zero to omit from help output.
|
||||
* @param out_option Receives the option handle. May be NULL if unused.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineparser_add_option(
|
||||
OakCommonCommandLineParser *parser, const char *const *names, int name_count,
|
||||
const char *description, int takes_arg, const char *arg_placeholder,
|
||||
int hidden, OakCommonCommandLineOption **out_option);
|
||||
|
||||
/**
|
||||
* @brief Register a positional argument.
|
||||
*
|
||||
* @param out_argument Receives the argument handle. May be NULL if unused.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineparser_add_positional_argument(
|
||||
OakCommonCommandLineParser *parser, const char *name,
|
||||
const char *description, int required,
|
||||
OakCommonCommandLinePositionalArgument **out_argument);
|
||||
|
||||
/**
|
||||
* @brief Parse an argv-style argument list.
|
||||
*
|
||||
* argv[0] is skipped as the program name, matching C main() convention.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineparser_process(OakCommonCommandLineParser *parser,
|
||||
const char *const *argv, int argc);
|
||||
|
||||
/**
|
||||
* @brief Print usage/help text to stdout.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineparser_print_help(OakCommonCommandLineParser *parser,
|
||||
const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Query whether an option was present on the command line.
|
||||
*
|
||||
* @param is_set Receives the result. Must not be NULL.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineoption_is_set(OakCommonCommandLineOption *option,
|
||||
bool *is_set);
|
||||
|
||||
/**
|
||||
* @brief Free an option handle wrapper.
|
||||
*
|
||||
* Does not unregister the option from the parser. NULL is a no-op.
|
||||
*/
|
||||
void oakcommon_commandlineoption_free(OakCommonCommandLineOption *option);
|
||||
|
||||
/**
|
||||
* @brief Get an option's argument value (two-stage string getter).
|
||||
*
|
||||
* @return Required buffer size in bytes including the terminating NUL
|
||||
* (non-negative), or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineoption_get_setting(OakCommonCommandLineOption *option,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set an option's argument value.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlineoption_set_setting(OakCommonCommandLineOption *option,
|
||||
const char *value);
|
||||
|
||||
/**
|
||||
* @brief Get a positional argument's value (two-stage string getter).
|
||||
*
|
||||
* @return Required buffer size in bytes including the terminating NUL
|
||||
* (non-negative), or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlinepositionalargument_get_setting(
|
||||
OakCommonCommandLinePositionalArgument *argument, char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Set a positional argument's value.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_commandlinepositionalargument_set_setting(
|
||||
OakCommonCommandLinePositionalArgument *argument, const char *value);
|
||||
|
||||
/**
|
||||
* @brief Free a positional argument handle wrapper.
|
||||
*
|
||||
* Does not unregister the argument from the parser. NULL is a no-op.
|
||||
*/
|
||||
void oakcommon_commandlinepositionalargument_free(
|
||||
OakCommonCommandLinePositionalArgument *argument);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_COMMANDLINEPARSER_H
|
||||
@@ -0,0 +1,107 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_CURRENT_H
|
||||
#define OAK_EDITOR_CURRENT_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct OakCommonCurrent OakCommonCurrent;
|
||||
|
||||
/**
|
||||
* @brief Destructor callback for objects handed to Current slots.
|
||||
*
|
||||
* Called when the slot is overwritten or cleared. May be NULL if the
|
||||
* caller keeps ownership of the object.
|
||||
*/
|
||||
typedef void (*OakCommonDestroyFn)(void *obj);
|
||||
|
||||
/**
|
||||
* @brief Return a handle to the process-wide Current singleton.
|
||||
*
|
||||
* The returned handle is borrowed: it is valid for the lifetime of the
|
||||
* process and must not be freed with oakcommon_current_free() more
|
||||
* than out of symmetry (free is a no-op for the singleton).
|
||||
*/
|
||||
OakCommonCurrent *oakcommon_current_instance(void);
|
||||
|
||||
/**
|
||||
* @brief Release a Current handle.
|
||||
*
|
||||
* No-op: the underlying object is a singleton. Safe to call with NULL.
|
||||
*/
|
||||
void oakcommon_current_free(OakCommonCurrent *self);
|
||||
|
||||
/**
|
||||
* @brief Store a pointer in a Current slot, taking over destruction.
|
||||
*
|
||||
* Passing NULL for obj clears the slot (destroy is ignored). If a
|
||||
* previous object with a destroy callback was stored, it is destroyed.
|
||||
*
|
||||
* @param self Handle from oakcommon_current_instance().
|
||||
* @param obj Opaque pointer to the external object (e.g. a
|
||||
* VideoParams), or NULL to clear.
|
||||
* @param destroy Optional destructor invoked when the slot is replaced.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if self is NULL.
|
||||
*/
|
||||
int oakcommon_current_set_video_params(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy);
|
||||
int oakcommon_current_set_audio_params(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy);
|
||||
int oakcommon_current_set_plugin_host(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy);
|
||||
int oakcommon_current_set_plugin_cache(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy);
|
||||
|
||||
/**
|
||||
* @brief Fetch the raw pointer currently stored in a slot.
|
||||
*
|
||||
* The returned pointer is borrowed and remains valid until the slot is
|
||||
* overwritten or cleared. *out is set to NULL when the slot is empty.
|
||||
*
|
||||
* @param self Handle from oakcommon_current_instance().
|
||||
* @param out Receives the stored pointer.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if self or out
|
||||
* is NULL.
|
||||
*/
|
||||
int oakcommon_current_get_video_params(OakCommonCurrent *self, void **out);
|
||||
int oakcommon_current_get_audio_params(OakCommonCurrent *self, void **out);
|
||||
int oakcommon_current_get_plugin_host(OakCommonCurrent *self, void **out);
|
||||
int oakcommon_current_get_plugin_cache(OakCommonCurrent *self, void **out);
|
||||
|
||||
/**
|
||||
* @brief Query whether the session is interactive.
|
||||
*
|
||||
* @param self Handle from oakcommon_current_instance().
|
||||
* @param out Receives 1 for interactive, 0 otherwise.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if self or out
|
||||
* is NULL.
|
||||
*/
|
||||
int oakcommon_current_is_interactive(OakCommonCurrent *self, int *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_CURRENT_H
|
||||
@@ -0,0 +1,75 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_DEBUG_H
|
||||
#define OAK_EDITOR_DEBUG_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Severity levels for oakcommon debug output.
|
||||
*
|
||||
* Mirrors olive::DebugLevel in src/common/src/debug.h.
|
||||
*/
|
||||
enum OakCommonDebugLevel {
|
||||
OAKCOMMON_DEBUG_DEBUG = 0, /**< Verbose debug message. */
|
||||
OAKCOMMON_DEBUG_INFO = 1, /**< Informational message. */
|
||||
OAKCOMMON_DEBUG_WARNING = 2, /**< Warning message. */
|
||||
OAKCOMMON_DEBUG_ERROR = 3, /**< Error message. */
|
||||
OAKCOMMON_DEBUG_FATAL = 4 /**< Fatal error message. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Print a debug message to stderr, prefixed with its level.
|
||||
*
|
||||
* De-Qt replacement for the old Qt message handler. The line is
|
||||
* flushed immediately.
|
||||
*
|
||||
* @param level One of OakCommonDebugLevel; out-of-range values print
|
||||
* as "UNKNOWN".
|
||||
* @param msg NUL-terminated message; NULL is treated as an empty
|
||||
* string.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if msg is NULL.
|
||||
*/
|
||||
int oakcommon_debug_log(int level, const char *msg);
|
||||
|
||||
/**
|
||||
* @brief Copy the printable name of a debug level into buf.
|
||||
*
|
||||
* Two-segment string getter: if buf is NULL or buf_size is too small,
|
||||
* nothing is written.
|
||||
*
|
||||
* @param level One of OakCommonDebugLevel.
|
||||
* @param buf Destination buffer, may be NULL to query the size.
|
||||
* @param buf_size Size of buf in bytes.
|
||||
* @return Required buffer size in bytes including the terminating NUL
|
||||
* (non-negative).
|
||||
*/
|
||||
int oakcommon_debug_level_name(int level, char *buf, int buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_DEBUG_H
|
||||
@@ -0,0 +1,73 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_DROPWORKFLOWBEHAVIOR_H
|
||||
#define OAK_EDITOR_DROPWORKFLOWBEHAVIOR_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Behavior when media is dropped onto a timeline without a
|
||||
* sequence.
|
||||
*
|
||||
* Mirrors olive::DropWithoutSequenceBehavior in
|
||||
* src/common/src/dropworkflowbehavior.h; enumerator order and values
|
||||
* must stay identical because the config layer persists them as ints.
|
||||
*/
|
||||
enum OakCommonDropWorkflowBehavior {
|
||||
OAKCOMMON_DWS_ASK = 0, /**< Ask the user every time. */
|
||||
OAKCOMMON_DWS_AUTO = 1, /**< Automatically create a sequence. */
|
||||
OAKCOMMON_DWS_MANUAL = 2, /**< Never create; import manually. */
|
||||
OAKCOMMON_DWS_DISABLE = 3 /**< Disable dropping entirely. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check whether value is a valid OakCommonDropWorkflowBehavior.
|
||||
*
|
||||
* @param value Integer behavior value (e.g. read from config).
|
||||
* @return 1 if valid, 0 otherwise (this is a predicate, not a status
|
||||
* code).
|
||||
*/
|
||||
int oakcommon_drop_workflow_behavior_is_valid(int value);
|
||||
|
||||
/**
|
||||
* @brief Copy the printable name of a behavior into buf.
|
||||
*
|
||||
* Two-segment string getter: if buf is NULL or buf_size is too small,
|
||||
* nothing is written. Invalid values yield "UNKNOWN".
|
||||
*
|
||||
* @param value One of OakCommonDropWorkflowBehavior.
|
||||
* @param buf Destination buffer, may be NULL to query the size.
|
||||
* @param buf_size Size of buf in bytes.
|
||||
* @return Required buffer size in bytes including the terminating NUL
|
||||
* (non-negative).
|
||||
*/
|
||||
int oakcommon_drop_workflow_behavior_name(int value, char *buf,
|
||||
int buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_DROPWORKFLOWBEHAVIOR_H
|
||||
@@ -0,0 +1,41 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_ERROR_H
|
||||
#define OAK_EDITOR_ERROR_H
|
||||
|
||||
/**
|
||||
* @brief Status and error codes shared by all oakcommon C API families.
|
||||
*
|
||||
* Return-code convention (mirrors engine/include/oakengine/init.h):
|
||||
* 0 (OAKCOMMON_OK) on success, a negative OAKCOMMON_E_* error code on
|
||||
* failure. String getters return the required buffer size in bytes
|
||||
* (including the terminating NUL) as a non-negative value instead.
|
||||
*/
|
||||
#define OAKCOMMON_OK 0 /**< Success. */
|
||||
#define OAKCOMMON_E_INVALID (-1) /**< NULL handle or invalid argument. */
|
||||
#define OAKCOMMON_E_STATE (-2) /**< Call not valid in the current state. */
|
||||
#define OAKCOMMON_E_FAILED (-3) /**< The underlying operation failed. */
|
||||
#define OAKCOMMON_E_NOT_FOUND (-4) /**< Index out of range / entry not found. */
|
||||
#define OAKCOMMON_E_NOMEM (-5) /**< Allocation failed. */
|
||||
|
||||
#define SUCCESS OAKCOMMON_OK /**< @deprecated Use OAKCOMMON_OK. */
|
||||
|
||||
#endif //OAK_EDITOR_ERROR_H
|
||||
@@ -0,0 +1,116 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_FFMPEGUTILS_H
|
||||
#define OAK_EDITOR_FFMPEGUTILS_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Stateless mappings between native pixel/sample formats and the
|
||||
* opaque FBPixelFormat / FBSampleFormat constants of ffmpeg_bridge
|
||||
*
|
||||
* All functions are pure format conversions; there is no handle to create
|
||||
* or free. Native pixel/sample formats are passed as plain ints matching
|
||||
* the olive::core::PixelFormat::Format / SampleFormat::Format enum values
|
||||
* (invalid = -1). Bridge formats are the fb_pix_fmt_* / fb_sample_fmt_*
|
||||
* constants from ffmpeg_bridge/ffmpeg_bridge.h.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief RGB / RGBA channel counts (flattened from VideoParams)
|
||||
*/
|
||||
#define OAKCOMMON_RGB_CHANNEL_COUNT 3
|
||||
#define OAKCOMMON_RGBA_CHANNEL_COUNT 4
|
||||
|
||||
/**
|
||||
* @brief Returns a bridge pixel format that a frame can be converted to
|
||||
* with minimal data loss, clamped to a maximum native precision
|
||||
*
|
||||
* @param pix_fmt bridge pixel format to find a compatible conversion for
|
||||
* @param maximum_pix_fmt maximum native pixel format, or -1 for no limit
|
||||
* @param out receives the chosen bridge pixel format
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
|
||||
*/
|
||||
int oakcommon_ffmpegutils_get_compatible_bridge_pixel_format(
|
||||
int pix_fmt, int maximum_pix_fmt, int *out);
|
||||
|
||||
/**
|
||||
* @brief Returns a native pixel format usable to convert from a native
|
||||
* frame to a bridge frame with minimal data loss
|
||||
*
|
||||
* @param pix_fmt native pixel format
|
||||
* @param out receives the compatible native pixel format (-1 if none)
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
|
||||
*/
|
||||
int oakcommon_ffmpegutils_get_compatible_pixel_format(int pix_fmt,
|
||||
int *out);
|
||||
|
||||
/**
|
||||
* @brief Returns a bridge pixel format for a given native pixel format
|
||||
*
|
||||
* @param pix_fmt native pixel format
|
||||
* @param channel_count OAKCOMMON_RGB_CHANNEL_COUNT or
|
||||
* OAKCOMMON_RGBA_CHANNEL_COUNT
|
||||
* @param out receives the bridge pixel format (fb_pix_fmt_none if none)
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
|
||||
*/
|
||||
int oakcommon_ffmpegutils_get_ffmpeg_pixel_format(int pix_fmt,
|
||||
int channel_count,
|
||||
int *out);
|
||||
|
||||
/**
|
||||
* @brief Returns a native sample format for a given bridge sample format
|
||||
*
|
||||
* @param smp_fmt bridge sample format
|
||||
* @param out receives the native sample format (-1 if unknown)
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
|
||||
*/
|
||||
int oakcommon_ffmpegutils_get_native_sample_format(int smp_fmt, int *out);
|
||||
|
||||
/**
|
||||
* @brief Returns a bridge sample format for a given native sample format
|
||||
*
|
||||
* @param smp_fmt native sample format
|
||||
* @param out receives the bridge sample format (fb_sample_fmt_none if none)
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
|
||||
*/
|
||||
int oakcommon_ffmpegutils_get_ffmpeg_sample_format(int smp_fmt, int *out);
|
||||
|
||||
/**
|
||||
* @brief Converts a "JPEG" full-range bridge pixel format to its regular
|
||||
* counterpart
|
||||
*
|
||||
* @param pix_fmt bridge pixel format
|
||||
* @param out receives the regular-range format (unchanged if not JPEG)
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out is NULL
|
||||
*/
|
||||
int oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(int pix_fmt,
|
||||
int *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_FFMPEGUTILS_H
|
||||
@@ -0,0 +1,153 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_FILEFUNCTIONS_H
|
||||
#define OAK_EDITOR_FILEFUNCTIONS_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Opaque handle for the filefunctions family
|
||||
*
|
||||
* File functions are stateless; the handle only exists to keep the C API
|
||||
* shape uniform across oakcommon families.
|
||||
*/
|
||||
typedef struct OakCommonFileFunctions OakCommonFileFunctions;
|
||||
|
||||
/**
|
||||
* @brief Creates a filefunctions handle
|
||||
*
|
||||
* @return A new handle, or NULL on failure.
|
||||
*/
|
||||
OakCommonFileFunctions *oakcommon_filefunctions_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroys a filefunctions handle (NULL is a no-op)
|
||||
*/
|
||||
void oakcommon_filefunctions_free(OakCommonFileFunctions *self);
|
||||
|
||||
/**
|
||||
* @brief Returns a deterministic identifier string for a file
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code. Returns an empty string (required size 1) if
|
||||
* the file does not exist.
|
||||
*/
|
||||
int oakcommon_filefunctions_get_unique_file_identifier(
|
||||
OakCommonFileFunctions *self, const char *filename, char *buf,
|
||||
int buf_size);
|
||||
|
||||
int oakcommon_filefunctions_get_configuration_location(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size);
|
||||
|
||||
int oakcommon_filefunctions_get_application_path(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size);
|
||||
|
||||
int oakcommon_filefunctions_get_temp_file_path(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size);
|
||||
|
||||
int oakcommon_filefunctions_get_auto_recovery_root(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Checks whether `source` can be copied to `dest` without
|
||||
* overwriting anything
|
||||
*
|
||||
* @param out Receives 1 (safe) or 0 (would overwrite).
|
||||
*/
|
||||
int oakcommon_filefunctions_can_copy_directory_without_overwriting(
|
||||
OakCommonFileFunctions *self, const char *source, const char *dest,
|
||||
int *out);
|
||||
|
||||
/**
|
||||
* @brief Recursively copies a directory
|
||||
*/
|
||||
int oakcommon_filefunctions_copy_directory(OakCommonFileFunctions *self,
|
||||
const char *source,
|
||||
const char *dest, int overwrite);
|
||||
|
||||
/**
|
||||
* @brief Checks whether a directory exists, optionally creating it
|
||||
*
|
||||
* @param out Receives 1 (valid) or 0 (invalid).
|
||||
*/
|
||||
int oakcommon_filefunctions_directory_is_valid(
|
||||
OakCommonFileFunctions *self, const char *dir,
|
||||
int try_to_create_if_not_exists, int *out);
|
||||
|
||||
/**
|
||||
* @brief Ensures a filename ends with the given extension (no dot)
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_filefunctions_ensure_filename_extension(
|
||||
OakCommonFileFunctions *self, const char *filename,
|
||||
const char *extension, char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Reads an entire file into a string
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code. Returns an empty string (required size 1) if
|
||||
* the file cannot be read.
|
||||
*/
|
||||
int oakcommon_filefunctions_read_file_as_string(
|
||||
OakCommonFileFunctions *self, const char *filename, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Returns a non-existing temporary variant of `original`
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_filefunctions_get_safe_temporary_filename(
|
||||
OakCommonFileFunctions *self, const char *original, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Renames `from` to `to`, deleting `to` first if it exists
|
||||
*
|
||||
* @param out Receives 1 (renamed) or 0 (failed).
|
||||
*/
|
||||
int oakcommon_filefunctions_rename_file_allow_overwrite(
|
||||
OakCommonFileFunctions *self, const char *from, const char *to,
|
||||
int *out);
|
||||
|
||||
/**
|
||||
* @brief Appends the platform executable suffix (".exe" on Windows)
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_filefunctions_get_formatted_executable_for_platform(
|
||||
OakCommonFileFunctions *self, const char *unformatted, char *buf,
|
||||
int buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_FILEFUNCTIONS_H
|
||||
@@ -0,0 +1,117 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_MISCUTILS_H
|
||||
#define OAK_EDITOR_MISCUTILS_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Minimum decibel value used by the editor (-200.0 dB).
|
||||
*
|
||||
* In basically all circumstances, this calculates to 0.0 linear.
|
||||
*/
|
||||
#define OAKCOMMON_DECIBEL_MINIMUM (-200.0)
|
||||
|
||||
/**
|
||||
* @brief Convert a linear amplitude to decibels.
|
||||
*
|
||||
* A linear value of 0.0 (or anything yielding an infinite result) returns
|
||||
* OAKCOMMON_DECIBEL_MINIMUM.
|
||||
*
|
||||
* @param linear Linear amplitude.
|
||||
* @param out_db Receives the decibel value. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_db is NULL.
|
||||
*/
|
||||
int oakcommon_decibel_from_linear(double linear, double *out_db);
|
||||
|
||||
/**
|
||||
* @brief Convert decibels to a linear amplitude.
|
||||
*
|
||||
* Results below 1e-6 are clamped to 0.0.
|
||||
*
|
||||
* @param db Decibel value.
|
||||
* @param out_linear Receives the linear amplitude. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_linear is NULL.
|
||||
*/
|
||||
int oakcommon_decibel_to_linear(double db, double *out_linear);
|
||||
|
||||
/**
|
||||
* @brief Convert a logarithmic slider position (0..1) to decibels.
|
||||
*
|
||||
* @param logarithmic Logarithmic position.
|
||||
* @param out_db Receives the decibel value. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_db is NULL.
|
||||
*/
|
||||
int oakcommon_decibel_from_logarithmic(double logarithmic, double *out_db);
|
||||
|
||||
/**
|
||||
* @brief Convert decibels to a logarithmic slider position (0..1).
|
||||
*
|
||||
* @param db Decibel value.
|
||||
* @param out_logarithmic Receives the logarithmic position. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_logarithmic
|
||||
* is NULL.
|
||||
*/
|
||||
int oakcommon_decibel_to_logarithmic(double db, double *out_logarithmic);
|
||||
|
||||
/**
|
||||
* @brief Convert a linear amplitude directly to a logarithmic position.
|
||||
*
|
||||
* @param linear Linear amplitude.
|
||||
* @param out_logarithmic Receives the logarithmic position. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_logarithmic
|
||||
* is NULL.
|
||||
*/
|
||||
int oakcommon_decibel_linear_to_logarithmic(double linear,
|
||||
double *out_logarithmic);
|
||||
|
||||
/**
|
||||
* @brief Convert a logarithmic position directly to a linear amplitude.
|
||||
*
|
||||
* @param logarithmic Logarithmic position.
|
||||
* @param out_linear Receives the linear amplitude. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_linear is NULL.
|
||||
*/
|
||||
int oakcommon_decibel_logarithmic_to_linear(double logarithmic,
|
||||
double *out_linear);
|
||||
|
||||
/**
|
||||
* @brief Linearly interpolate between a and b using t.
|
||||
*
|
||||
* t should be between 0.0 and 1.0: 0.0 returns a, 1.0 returns b.
|
||||
*
|
||||
* @param a Start value.
|
||||
* @param b End value.
|
||||
* @param t Interpolation factor.
|
||||
* @param out_value Receives the interpolated value. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_value is NULL.
|
||||
*/
|
||||
int oakcommon_lerp(double a, double b, double t, double *out_value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_MISCUTILS_H
|
||||
@@ -0,0 +1,88 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_OCIOUTILS_H
|
||||
#define OAK_EDITOR_OCIOUTILS_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Native pixel format codes, mirroring olive::core::PixelFormat
|
||||
*
|
||||
* The numeric values must stay in sync with
|
||||
* olive/core/render/pixelformat.h (Format enum).
|
||||
*/
|
||||
enum OakCommonPixelFormat {
|
||||
OAKCOMMON_PIXEL_FORMAT_INVALID = -1, /**< Invalid/unknown format. */
|
||||
OAKCOMMON_PIXEL_FORMAT_U8 = 0, /**< 8-bit unsigned integer. */
|
||||
OAKCOMMON_PIXEL_FORMAT_U10 = 1, /**< 10-bit unsigned integer. */
|
||||
OAKCOMMON_PIXEL_FORMAT_U16 = 2, /**< 16-bit unsigned integer. */
|
||||
OAKCOMMON_PIXEL_FORMAT_F16 = 3, /**< 16-bit float (half). */
|
||||
OAKCOMMON_PIXEL_FORMAT_F32 = 4, /**< 32-bit float. */
|
||||
OAKCOMMON_PIXEL_FORMAT_COUNT = 5 /**< Sentinel, not a valid format. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief OpenColorIO bit depth codes, matching OCIO::BitDepth
|
||||
*
|
||||
* Returned through the out parameter of
|
||||
* oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format() as a plain
|
||||
* int so that callers never see OCIO types. Values match the OCIO
|
||||
* BitDepth enum: 0 = unknown, 1 = uint8, 2 = uint10, 3 = uint12,
|
||||
* 4 = uint14, 5 = uint16, 6 = uint32, 7 = f16, 8 = f32 (OCIO v2).
|
||||
*/
|
||||
typedef struct OakCommonOCIOUtils OakCommonOCIOUtils;
|
||||
|
||||
/**
|
||||
* @brief Creates an OCIOUtils handle
|
||||
*
|
||||
* The object is stateless; the handle exists only to satisfy the C API
|
||||
* lifetime contract. Returns NULL on failure.
|
||||
*/
|
||||
OakCommonOCIOUtils *oakcommon_ocioutils_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroys an OCIOUtils handle; no-op on NULL
|
||||
*/
|
||||
void oakcommon_ocioutils_free(OakCommonOCIOUtils *self);
|
||||
|
||||
/**
|
||||
* @brief Maps a native pixel format to an OCIO bit depth
|
||||
*
|
||||
* @param self handle from oakcommon_ocioutils_init()
|
||||
* @param pixel_format one of the OakCommonPixelFormat values
|
||||
* @param out_bit_depth receives the OCIO bit depth as an int (see the
|
||||
* OakCommonOCIOUtils typedef documentation); set to 0
|
||||
* (BIT_DEPTH_UNKNOWN) for invalid formats
|
||||
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self or
|
||||
* out_bit_depth is NULL or pixel_format is not a known code
|
||||
*/
|
||||
int oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
OakCommonOCIOUtils *self, int pixel_format, int *out_bit_depth);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_OCIOUTILS_H
|
||||
@@ -0,0 +1,109 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_OIIOUTILS_H
|
||||
#define OAK_EDITOR_OIIOUTILS_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
/* Reuses the OakCommonPixelFormat enum (mirroring
|
||||
* olive::core::PixelFormat) rather than redefining it here. */
|
||||
#include "common/ocioutils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OIIO base type codes, matching OIIO::TypeDesc::BASETYPE
|
||||
*
|
||||
* Passed through the C API as plain ints so callers never see OIIO
|
||||
* types. Values match the OIIO TypeDesc::BASETYPE enum: 0 = UNKNOWN,
|
||||
* 1 = NONE, 2 = UINT8, 3 = INT8, 4 = UINT16, 5 = INT16, 6 = UINT32,
|
||||
* 7 = INT32, 8 = UINT64, 9 = INT64, 10 = HALF, 11 = FLOAT, 12 = DOUBLE,
|
||||
* 13 = STRING, 14 = PTR. OIIO >= 2.5 adds 15 = USTRINGHASH and shifts
|
||||
* LASTBASE, so the exact LASTBASE value is version-dependent.
|
||||
*/
|
||||
typedef struct OakCommonOIIOUtils OakCommonOIIOUtils;
|
||||
|
||||
/**
|
||||
* @brief Creates an OIIOUtils handle
|
||||
*
|
||||
* The object is stateless; the handle exists only to satisfy the C API
|
||||
* lifetime contract. Returns NULL on failure.
|
||||
*/
|
||||
OakCommonOIIOUtils *oakcommon_oiioutils_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroys an OIIOUtils handle; no-op on NULL
|
||||
*/
|
||||
void oakcommon_oiioutils_free(OakCommonOIIOUtils *self);
|
||||
|
||||
/**
|
||||
* @brief Maps a native pixel format to an OIIO base type
|
||||
*
|
||||
* @param self handle from oakcommon_oiioutils_init()
|
||||
* @param pixel_format one of the OakCommonPixelFormat values
|
||||
* @param out_base_type receives the OIIO base type as an int (see the
|
||||
* OakCommonOIIOUtils typedef documentation); set to 0
|
||||
* (TypeDesc::UNKNOWN) for invalid or unmappable formats
|
||||
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self or out_base_type
|
||||
* is NULL or pixel_format is not a known code
|
||||
*/
|
||||
int oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
OakCommonOIIOUtils *self, int pixel_format, int *out_base_type);
|
||||
|
||||
/**
|
||||
* @brief Maps an OIIO base type to a native pixel format
|
||||
*
|
||||
* @param self handle from oakcommon_oiioutils_init()
|
||||
* @param base_type an OIIO TypeDesc::BASETYPE value as an int
|
||||
* @param out_pixel_format receives one of the OakCommonPixelFormat
|
||||
* values; set to OAKCOMMON_PIXEL_FORMAT_INVALID for unknown or
|
||||
* unmappable base types
|
||||
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self or
|
||||
* out_pixel_format is NULL or base_type is negative
|
||||
*/
|
||||
int oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
OakCommonOIIOUtils *self, int base_type, int *out_pixel_format);
|
||||
|
||||
/**
|
||||
* @brief Converts a PixelAspectRatio attribute value to a rational
|
||||
*
|
||||
* Flattened form of the former ImageSpec-based helper: the caller reads
|
||||
* the "PixelAspectRatio" float attribute from the OIIO::ImageSpec
|
||||
* (defaulting to 1.0 when absent) and passes it here.
|
||||
*
|
||||
* @param self handle from oakcommon_oiioutils_init()
|
||||
* @param pixel_aspect_ratio the PixelAspectRatio attribute value
|
||||
* @param out_numerator receives the rational numerator
|
||||
* @param out_denominator receives the rational denominator
|
||||
* @return OAKCOMMON_OK, or OAKCOMMON_E_INVALID if self, out_numerator
|
||||
* or out_denominator is NULL
|
||||
*/
|
||||
int oakcommon_oiioutils_get_pixel_aspect_ratio(
|
||||
OakCommonOIIOUtils *self, double pixel_aspect_ratio, int *out_numerator,
|
||||
int *out_denominator);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_OIIOUTILS_H
|
||||
@@ -0,0 +1,58 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_POWER_H
|
||||
#define OAK_EDITOR_POWER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Round `value` up to the next power of two
|
||||
*
|
||||
* Stateless pure function, no handle required. Writes the result to `out`.
|
||||
*
|
||||
* @param value Input value.
|
||||
* @param out Receives the rounded value. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if `out` is NULL.
|
||||
*/
|
||||
int oakcommon_power_ceil_to_power_of_2(uint32_t value, uint32_t *out);
|
||||
|
||||
/**
|
||||
* @brief Round `value` down to the nearest power of two
|
||||
*
|
||||
* Stateless pure function, no handle required. Writes the result to `out`.
|
||||
*
|
||||
* @param value Input value.
|
||||
* @param out Receives the rounded value. Must not be NULL.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if `out` is NULL.
|
||||
*/
|
||||
int oakcommon_power_floor_to_power_of_2(uint32_t value, uint32_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_POWER_H
|
||||
@@ -0,0 +1,67 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_COMMON_QTUTILS_H
|
||||
#define OAK_COMMON_QTUTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Convert a pointer to an integer value
|
||||
*
|
||||
* @param ptr Pointer to convert (may be NULL, yielding 0).
|
||||
* @param out_value Receives the integer representation of ptr.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_value is NULL.
|
||||
*/
|
||||
int oakcommon_qtutils_ptr_to_value(void *ptr, uint64_t *out_value);
|
||||
|
||||
/**
|
||||
* @brief Convert an integer produced by oakcommon_qtutils_ptr_to_value() back to a pointer
|
||||
*
|
||||
* @param value Integer representation of a pointer.
|
||||
* @param out_ptr Receives the decoded pointer.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID if out_ptr is NULL.
|
||||
*/
|
||||
int oakcommon_qtutils_value_to_ptr(uint64_t value, void **out_ptr);
|
||||
|
||||
/**
|
||||
* @brief Get the creation (birth) time of a file as seconds since the Unix epoch
|
||||
*
|
||||
* Falls back to the last metadata change time when the filesystem does not
|
||||
* record birth times.
|
||||
*
|
||||
* @param path NUL-terminated filesystem path.
|
||||
* @param out_secs Receives the creation time in seconds since the epoch.
|
||||
* @return OAKCOMMON_OK on success, OAKCOMMON_E_INVALID for NULL arguments,
|
||||
* OAKCOMMON_E_NOT_FOUND if the file does not exist or cannot be stat'ed.
|
||||
*/
|
||||
int oakcommon_qtutils_get_creation_date(const char *path, int64_t *out_secs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_COMMON_QTUTILS_H
|
||||
@@ -0,0 +1,156 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_EDITOR_XMLUTILS_H
|
||||
#define OAK_EDITOR_XMLUTILS_H
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct OakCommonXmlReader OakCommonXmlReader;
|
||||
typedef struct OakCommonXmlWriter OakCommonXmlWriter;
|
||||
|
||||
/**
|
||||
* @brief Create a streaming XML reader over a complete document.
|
||||
*
|
||||
* @param data NUL-terminated XML text. Must not be NULL.
|
||||
* @return A new reader, or NULL on failure (NULL data, out of memory).
|
||||
*/
|
||||
OakCommonXmlReader *oakcommon_xml_reader_init(const char *data);
|
||||
|
||||
/**
|
||||
* @brief Destroy a reader. No-op on NULL.
|
||||
*/
|
||||
void oakcommon_xml_reader_free(OakCommonXmlReader *reader);
|
||||
|
||||
/**
|
||||
* @brief Advance until the next start element, an end element, or the end
|
||||
* of the document.
|
||||
*
|
||||
* @param reader Reader handle.
|
||||
* @param found Out: 1 if positioned on a start element, 0 otherwise.
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_reader_read_next_start_element(OakCommonXmlReader *reader,
|
||||
int *found);
|
||||
|
||||
/**
|
||||
* @brief Name of the current element token.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_reader_name(OakCommonXmlReader *reader, char *buf,
|
||||
int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Read the concatenated character data of the current element.
|
||||
*
|
||||
* Must be called on a start element; consumes up to the matching end
|
||||
* element.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_reader_read_element_text(OakCommonXmlReader *reader,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Skip the current element and all of its children.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_reader_skip_current_element(OakCommonXmlReader *reader);
|
||||
|
||||
/**
|
||||
* @brief Number of attributes on the current start element.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_reader_attribute_count(OakCommonXmlReader *reader,
|
||||
int *count);
|
||||
|
||||
/**
|
||||
* @brief Name of the attribute at @p index on the current start element.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), OAKCOMMON_E_NOT_FOUND
|
||||
* if @p index is out of range, or another negative OAKCOMMON_E_* code.
|
||||
*/
|
||||
int oakcommon_xml_reader_attribute_name(OakCommonXmlReader *reader, int index,
|
||||
char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Value of the attribute at @p index on the current start element.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), OAKCOMMON_E_NOT_FOUND
|
||||
* if @p index is out of range, or another negative OAKCOMMON_E_* code.
|
||||
*/
|
||||
int oakcommon_xml_reader_attribute_value(OakCommonXmlReader *reader,
|
||||
int index, char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Whether the document failed to parse.
|
||||
*
|
||||
* @return OAKCOMMON_OK or a negative OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_reader_has_error(OakCommonXmlReader *reader,
|
||||
int *has_error);
|
||||
|
||||
/**
|
||||
* @brief Create a streaming XML writer.
|
||||
*
|
||||
* @return A new writer, or NULL on failure.
|
||||
*/
|
||||
OakCommonXmlWriter *oakcommon_xml_writer_init(void);
|
||||
|
||||
/**
|
||||
* @brief Destroy a writer. No-op on NULL.
|
||||
*/
|
||||
void oakcommon_xml_writer_free(OakCommonXmlWriter *writer);
|
||||
|
||||
int oakcommon_xml_writer_write_start_element(OakCommonXmlWriter *writer,
|
||||
const char *name);
|
||||
int oakcommon_xml_writer_write_attribute(OakCommonXmlWriter *writer,
|
||||
const char *name, const char *value);
|
||||
int oakcommon_xml_writer_write_characters(OakCommonXmlWriter *writer,
|
||||
const char *text);
|
||||
int oakcommon_xml_writer_write_text_element(OakCommonXmlWriter *writer,
|
||||
const char *name,
|
||||
const char *text);
|
||||
int oakcommon_xml_writer_write_end_element(OakCommonXmlWriter *writer);
|
||||
int oakcommon_xml_writer_write_end_document(OakCommonXmlWriter *writer);
|
||||
|
||||
/**
|
||||
* @brief The document written so far.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL), or a negative
|
||||
* OAKCOMMON_E_* error code.
|
||||
*/
|
||||
int oakcommon_xml_writer_output(OakCommonXmlWriter *writer, char *buf,
|
||||
int buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OAK_EDITOR_XMLUTILS_H
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(common)
|
||||
@@ -0,0 +1,6 @@
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(c_api)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
@@ -0,0 +1,14 @@
|
||||
target_sources(oakcommon PRIVATE
|
||||
commandlineparser.cpp
|
||||
current.cpp
|
||||
debug.cpp
|
||||
dropworkflowbehavior.cpp
|
||||
ffmpegutils.cpp
|
||||
filefunctions.cpp
|
||||
miscutils.cpp
|
||||
ocioutils.cpp
|
||||
oiioutils.cpp
|
||||
power.cpp
|
||||
qtutils.cpp
|
||||
xmlutils.cpp
|
||||
)
|
||||
@@ -0,0 +1,282 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/commandlineparser.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../src/commandlineparser.h"
|
||||
|
||||
struct OakCommonCommandLineParser {
|
||||
CommandLineParser impl;
|
||||
};
|
||||
|
||||
struct OakCommonCommandLineOption {
|
||||
CommandLineParser::Option *option;
|
||||
};
|
||||
|
||||
struct OakCommonCommandLinePositionalArgument {
|
||||
CommandLineParser::PositionalArgument *argument;
|
||||
};
|
||||
|
||||
OakCommonCommandLineParser *oakcommon_commandlineparser_init(void)
|
||||
{
|
||||
try {
|
||||
return new (std::nothrow) OakCommonCommandLineParser();
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_commandlineparser_free(OakCommonCommandLineParser *parser)
|
||||
{
|
||||
delete parser;
|
||||
}
|
||||
|
||||
int oakcommon_commandlineparser_set_app_info(OakCommonCommandLineParser *parser,
|
||||
const char *name,
|
||||
const char *version)
|
||||
{
|
||||
if (!parser || !name) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
parser->impl.set_app_info(name, version ? version : "");
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlineparser_add_option(
|
||||
OakCommonCommandLineParser *parser, const char *const *names, int name_count,
|
||||
const char *description, int takes_arg, const char *arg_placeholder,
|
||||
int hidden, OakCommonCommandLineOption **out_option)
|
||||
{
|
||||
if (!parser || !names || name_count <= 0) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
std::vector<std::string> strings;
|
||||
strings.reserve(static_cast<size_t>(name_count));
|
||||
for (int i = 0; i < name_count; i++) {
|
||||
if (!names[i]) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
strings.emplace_back(names[i]);
|
||||
}
|
||||
|
||||
const CommandLineParser::Option *option = parser->impl.add_option(
|
||||
strings, description ? description : "", takes_arg != 0,
|
||||
arg_placeholder ? arg_placeholder : "", hidden != 0);
|
||||
|
||||
if (out_option) {
|
||||
auto *handle =
|
||||
new (std::nothrow) OakCommonCommandLineOption();
|
||||
if (!handle) {
|
||||
return OAKCOMMON_E_NOMEM;
|
||||
}
|
||||
handle->option = const_cast<CommandLineParser::Option *>(option);
|
||||
*out_option = handle;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlineparser_add_positional_argument(
|
||||
OakCommonCommandLineParser *parser, const char *name,
|
||||
const char *description, int required,
|
||||
OakCommonCommandLinePositionalArgument **out_argument)
|
||||
{
|
||||
if (!parser || !name) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
const CommandLineParser::PositionalArgument *argument =
|
||||
parser->impl.add_positional_argument(
|
||||
name, description ? description : "", required != 0);
|
||||
|
||||
if (out_argument) {
|
||||
auto *handle =
|
||||
new (std::nothrow) OakCommonCommandLinePositionalArgument();
|
||||
if (!handle) {
|
||||
return OAKCOMMON_E_NOMEM;
|
||||
}
|
||||
handle->argument =
|
||||
const_cast<CommandLineParser::PositionalArgument *>(argument);
|
||||
*out_argument = handle;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlineparser_process(OakCommonCommandLineParser *parser,
|
||||
const char *const *argv, int argc)
|
||||
{
|
||||
if (!parser || !argv || argc < 0) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
std::vector<std::string> args;
|
||||
args.reserve(static_cast<size_t>(argc));
|
||||
for (int i = 0; i < argc; i++) {
|
||||
args.emplace_back(argv[i] ? argv[i] : "");
|
||||
}
|
||||
|
||||
parser->impl.process(args);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlineparser_print_help(OakCommonCommandLineParser *parser,
|
||||
const char *filename)
|
||||
{
|
||||
if (!parser || !filename) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
parser->impl.print_help(filename);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlineoption_is_set(OakCommonCommandLineOption *option,
|
||||
bool *is_set)
|
||||
{
|
||||
if (!option || !option->option || !is_set) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
*is_set = option->option->is_set();
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shared two-stage string getter.
|
||||
*
|
||||
* Returns the required buffer size in bytes (including the terminating
|
||||
* NUL) as a non-negative value, or a negative error code.
|
||||
*/
|
||||
static int copy_setting(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int required = static_cast<int>(value.size()) + 1;
|
||||
|
||||
if (buf && buf_size > 0) {
|
||||
size_t copy_len = value.size();
|
||||
if (copy_len > static_cast<size_t>(buf_size) - 1) {
|
||||
copy_len = static_cast<size_t>(buf_size) - 1;
|
||||
}
|
||||
memcpy(buf, value.data(), copy_len);
|
||||
buf[copy_len] = '\0';
|
||||
}
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
int oakcommon_commandlineoption_get_setting(OakCommonCommandLineOption *option,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!option || !option->option) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return copy_setting(option->option->get_setting(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlineoption_set_setting(OakCommonCommandLineOption *option,
|
||||
const char *value)
|
||||
{
|
||||
if (!option || !option->option || !value) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
option->option->set_setting(value);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlinepositionalargument_get_setting(
|
||||
OakCommonCommandLinePositionalArgument *argument, char *buf, int buf_size)
|
||||
{
|
||||
if (!argument || !argument->argument) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return copy_setting(argument->argument->get_setting(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_commandlinepositionalargument_set_setting(
|
||||
OakCommonCommandLinePositionalArgument *argument, const char *value)
|
||||
{
|
||||
if (!argument || !argument->argument || !value) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
argument->argument->set_setting(value);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_commandlineoption_free(OakCommonCommandLineOption *option)
|
||||
{
|
||||
delete option;
|
||||
}
|
||||
|
||||
void oakcommon_commandlinepositionalargument_free(
|
||||
OakCommonCommandLinePositionalArgument *argument)
|
||||
{
|
||||
delete argument;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/current.h"
|
||||
|
||||
#include "../src/current.h"
|
||||
|
||||
struct OakCommonCurrent {
|
||||
Current *current;
|
||||
};
|
||||
|
||||
OakCommonCurrent *oakcommon_current_instance(void)
|
||||
{
|
||||
static OakCommonCurrent handle = { &Current::get_instance() };
|
||||
return &handle;
|
||||
}
|
||||
|
||||
void oakcommon_current_free(OakCommonCurrent *self)
|
||||
{
|
||||
// No-op: the handle wraps a process-wide singleton.
|
||||
(void)self;
|
||||
}
|
||||
|
||||
static int current_set(Current *current,
|
||||
void (Current::*set_fn)(std::shared_ptr<void>),
|
||||
void *obj, OakCommonDestroyFn destroy)
|
||||
{
|
||||
try {
|
||||
std::shared_ptr<void> value;
|
||||
if (obj) {
|
||||
if (destroy)
|
||||
value = std::shared_ptr<void>(obj, destroy);
|
||||
else
|
||||
value = std::shared_ptr<void>(
|
||||
obj, [](void *) {});
|
||||
}
|
||||
(current->*set_fn)(value);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
static int current_get(Current *current,
|
||||
std::shared_ptr<void> (Current::*get_fn)() const,
|
||||
void **out)
|
||||
{
|
||||
try {
|
||||
*out = (current->*get_fn)().get();
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_current_set_video_params(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy)
|
||||
{
|
||||
if (!self)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_set(self->current, &Current::set_current_video_params,
|
||||
obj, destroy);
|
||||
}
|
||||
|
||||
int oakcommon_current_set_audio_params(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy)
|
||||
{
|
||||
if (!self)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_set(self->current, &Current::set_current_audio_params,
|
||||
obj, destroy);
|
||||
}
|
||||
|
||||
int oakcommon_current_set_plugin_host(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy)
|
||||
{
|
||||
if (!self)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_set(self->current, &Current::set_plugin_host, obj,
|
||||
destroy);
|
||||
}
|
||||
|
||||
int oakcommon_current_set_plugin_cache(OakCommonCurrent *self, void *obj,
|
||||
OakCommonDestroyFn destroy)
|
||||
{
|
||||
if (!self)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_set(self->current, &Current::set_plugin_cache, obj,
|
||||
destroy);
|
||||
}
|
||||
|
||||
int oakcommon_current_get_video_params(OakCommonCurrent *self, void **out)
|
||||
{
|
||||
if (!self || !out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_get(self->current, &Current::current_video_params,
|
||||
out);
|
||||
}
|
||||
|
||||
int oakcommon_current_get_audio_params(OakCommonCurrent *self, void **out)
|
||||
{
|
||||
if (!self || !out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_get(self->current, &Current::current_audio_params,
|
||||
out);
|
||||
}
|
||||
|
||||
int oakcommon_current_get_plugin_host(OakCommonCurrent *self, void **out)
|
||||
{
|
||||
if (!self || !out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_get(self->current, &Current::plugin_host, out);
|
||||
}
|
||||
|
||||
int oakcommon_current_get_plugin_cache(OakCommonCurrent *self, void **out)
|
||||
{
|
||||
if (!self || !out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return current_get(self->current, &Current::plugin_cache, out);
|
||||
}
|
||||
|
||||
int oakcommon_current_is_interactive(OakCommonCurrent *self, int *out)
|
||||
{
|
||||
if (!self || !out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
*out = self->current->interactive() ? 1 : 0;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "../src/debug.h"
|
||||
|
||||
int oakcommon_debug_log(int level, const char *msg)
|
||||
{
|
||||
if (!msg)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
olive::debug_handler(level, msg);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_debug_level_name(int level, char *buf, int buf_size)
|
||||
{
|
||||
try {
|
||||
return olive::debug_level_name(level, buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/dropworkflowbehavior.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../src/dropworkflowbehavior.h"
|
||||
|
||||
static const char *behavior_name(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case olive::k_dws_ask:
|
||||
return "ASK";
|
||||
case olive::k_dws_auto:
|
||||
return "AUTO";
|
||||
case olive::k_dws_manual:
|
||||
return "MANUAL";
|
||||
case olive::k_dws_disable:
|
||||
return "DISABLE";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
int oakcommon_drop_workflow_behavior_is_valid(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case olive::k_dws_ask:
|
||||
case olive::k_dws_auto:
|
||||
case olive::k_dws_manual:
|
||||
case olive::k_dws_disable:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oakcommon_drop_workflow_behavior_name(int value, char *buf, int buf_size)
|
||||
{
|
||||
try {
|
||||
const char *name = behavior_name(value);
|
||||
int needed = (int)strlen(name) + 1;
|
||||
|
||||
if (buf && buf_size >= needed)
|
||||
memcpy(buf, name, needed);
|
||||
return needed;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
#include "../src/ffmpegutils.h"
|
||||
|
||||
int oakcommon_ffmpegutils_get_compatible_bridge_pixel_format(
|
||||
int pix_fmt, int maximum_pix_fmt, int *out)
|
||||
{
|
||||
if (!out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
*out = olive::FFmpegUtils::get_compatible_bridge_pixel_format(
|
||||
pix_fmt,
|
||||
static_cast<olive::core::PixelFormat::Format>(
|
||||
maximum_pix_fmt));
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_ffmpegutils_get_compatible_pixel_format(int pix_fmt, int *out)
|
||||
{
|
||||
if (!out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
*out = olive::FFmpegUtils::get_compatible_pixel_format(
|
||||
static_cast<olive::core::PixelFormat::Format>(pix_fmt));
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_ffmpegutils_get_ffmpeg_pixel_format(int pix_fmt,
|
||||
int channel_count, int *out)
|
||||
{
|
||||
if (!out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
*out = olive::FFmpegUtils::get_ffmpeg_pixel_format(
|
||||
static_cast<olive::core::PixelFormat::Format>(pix_fmt),
|
||||
channel_count);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_ffmpegutils_get_native_sample_format(int smp_fmt, int *out)
|
||||
{
|
||||
if (!out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
*out = olive::FFmpegUtils::get_native_sample_format(smp_fmt);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_ffmpegutils_get_ffmpeg_sample_format(int smp_fmt, int *out)
|
||||
{
|
||||
if (!out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
*out = olive::FFmpegUtils::get_ffmpeg_sample_format(
|
||||
static_cast<olive::core::SampleFormat::Format>(smp_fmt));
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(int pix_fmt,
|
||||
int *out)
|
||||
{
|
||||
if (!out)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
try {
|
||||
*out = olive::FFmpegUtils::convert_jpeg_space_to_regular_space(
|
||||
pix_fmt);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "../src/filefunctions.h"
|
||||
|
||||
struct OakCommonFileFunctions {
|
||||
int unused; /**< Stateless family; handle kept for API uniformity. */
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Copies `value` into the caller's two-stage buffer
|
||||
*
|
||||
* @return Required buffer size in bytes (including the terminating NUL).
|
||||
* Always non-negative; error codes are handled by the callers.
|
||||
*/
|
||||
int write_string_result(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int required = static_cast<int>(value.size()) + 1;
|
||||
if (buf != nullptr && buf_size >= required) {
|
||||
memcpy(buf, value.c_str(), required);
|
||||
}
|
||||
return required;
|
||||
}
|
||||
|
||||
bool is_valid_string_out(const char *buf, int buf_size)
|
||||
{
|
||||
return buf_size >= 0 && (buf_size == 0 || buf != nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OakCommonFileFunctions *oakcommon_filefunctions_init(void)
|
||||
{
|
||||
try {
|
||||
return new OakCommonFileFunctions{0};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_filefunctions_free(OakCommonFileFunctions *self)
|
||||
{
|
||||
delete self;
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_unique_file_identifier(
|
||||
OakCommonFileFunctions *self, const char *filename, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (self == nullptr || filename == nullptr ||
|
||||
!is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::get_unique_file_identifier(filename), buf,
|
||||
buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_configuration_location(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size)
|
||||
{
|
||||
if (self == nullptr || !is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::get_configuration_location(), buf,
|
||||
buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_application_path(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size)
|
||||
{
|
||||
if (self == nullptr || !is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(FileFunctions::get_application_path(),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_temp_file_path(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size)
|
||||
{
|
||||
if (self == nullptr || !is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(FileFunctions::get_temp_file_path(),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_auto_recovery_root(
|
||||
OakCommonFileFunctions *self, char *buf, int buf_size)
|
||||
{
|
||||
if (self == nullptr || !is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::get_auto_recovery_root(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_can_copy_directory_without_overwriting(
|
||||
OakCommonFileFunctions *self, const char *source, const char *dest,
|
||||
int *out)
|
||||
{
|
||||
if (self == nullptr || source == nullptr || dest == nullptr ||
|
||||
out == nullptr) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
*out = FileFunctions::can_copy_directory_without_overwriting(
|
||||
source, dest)
|
||||
? 1
|
||||
: 0;
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_copy_directory(OakCommonFileFunctions *self,
|
||||
const char *source,
|
||||
const char *dest, int overwrite)
|
||||
{
|
||||
if (self == nullptr || source == nullptr || dest == nullptr) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
FileFunctions::copy_directory(source, dest, overwrite != 0);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_directory_is_valid(
|
||||
OakCommonFileFunctions *self, const char *dir,
|
||||
int try_to_create_if_not_exists, int *out)
|
||||
{
|
||||
if (self == nullptr || dir == nullptr || out == nullptr) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
*out = FileFunctions::directory_is_valid(
|
||||
dir, try_to_create_if_not_exists != 0)
|
||||
? 1
|
||||
: 0;
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_ensure_filename_extension(
|
||||
OakCommonFileFunctions *self, const char *filename,
|
||||
const char *extension, char *buf, int buf_size)
|
||||
{
|
||||
if (self == nullptr || filename == nullptr || extension == nullptr ||
|
||||
!is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::ensure_filename_extension(filename,
|
||||
extension),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_read_file_as_string(
|
||||
OakCommonFileFunctions *self, const char *filename, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (self == nullptr || filename == nullptr ||
|
||||
!is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::read_file_as_string(filename), buf,
|
||||
buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_safe_temporary_filename(
|
||||
OakCommonFileFunctions *self, const char *original, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (self == nullptr || original == nullptr ||
|
||||
!is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::get_safe_temporary_filename(original),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_rename_file_allow_overwrite(
|
||||
OakCommonFileFunctions *self, const char *from, const char *to,
|
||||
int *out)
|
||||
{
|
||||
if (self == nullptr || from == nullptr || to == nullptr ||
|
||||
out == nullptr) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
*out = FileFunctions::rename_file_allow_overwrite(from, to) ? 1
|
||||
: 0;
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_filefunctions_get_formatted_executable_for_platform(
|
||||
OakCommonFileFunctions *self, const char *unformatted, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (self == nullptr || unformatted == nullptr ||
|
||||
!is_valid_string_out(buf, buf_size)) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
return write_string_result(
|
||||
FileFunctions::get_formatted_executable_for_platform(
|
||||
unformatted),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/miscutils.h"
|
||||
|
||||
#include "../src/decibel.h"
|
||||
#include "../src/lerp.h"
|
||||
|
||||
int oakcommon_decibel_from_linear(double linear, double *out_db)
|
||||
{
|
||||
if (!out_db) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_db = olive::Decibel::from_linear(linear);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_decibel_to_linear(double db, double *out_linear)
|
||||
{
|
||||
if (!out_linear) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_linear = olive::Decibel::to_linear(db);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_decibel_from_logarithmic(double logarithmic, double *out_db)
|
||||
{
|
||||
if (!out_db) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_db = olive::Decibel::from_logarithmic(logarithmic);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_decibel_to_logarithmic(double db, double *out_logarithmic)
|
||||
{
|
||||
if (!out_logarithmic) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_logarithmic = olive::Decibel::to_logarithmic(db);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_decibel_linear_to_logarithmic(double linear,
|
||||
double *out_logarithmic)
|
||||
{
|
||||
if (!out_logarithmic) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_logarithmic = olive::Decibel::linear_to_logarithmic(linear);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_decibel_logarithmic_to_linear(double logarithmic,
|
||||
double *out_linear)
|
||||
{
|
||||
if (!out_linear) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_linear = olive::Decibel::logarithmic_to_linear(logarithmic);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_lerp(double a, double b, double t, double *out_value)
|
||||
{
|
||||
if (!out_value) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
try {
|
||||
*out_value = lerp(a, b, t);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "../src/ocioutils.h"
|
||||
|
||||
struct OakCommonOCIOUtils {
|
||||
int unused; /**< Stateless; only the address matters. */
|
||||
};
|
||||
|
||||
OakCommonOCIOUtils *oakcommon_ocioutils_init(void)
|
||||
{
|
||||
try {
|
||||
return new (std::nothrow) OakCommonOCIOUtils{};
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_ocioutils_free(OakCommonOCIOUtils *self)
|
||||
{
|
||||
delete self;
|
||||
}
|
||||
|
||||
int oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
OakCommonOCIOUtils *self, int pixel_format, int *out_bit_depth)
|
||||
{
|
||||
try {
|
||||
if (self == NULL || out_bit_depth == NULL)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
if (pixel_format < OAKCOMMON_PIXEL_FORMAT_INVALID ||
|
||||
pixel_format >= OAKCOMMON_PIXEL_FORMAT_COUNT)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
ocio::BitDepth depth = OCIOUtils::get_ocio_bit_depth_from_pixel_format(
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format));
|
||||
*out_bit_depth = static_cast<int>(depth);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/oiioutils.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "../src/oiioutils.h"
|
||||
|
||||
struct OakCommonOIIOUtils {
|
||||
int unused; /**< Stateless; only the address matters. */
|
||||
};
|
||||
|
||||
OakCommonOIIOUtils *oakcommon_oiioutils_init(void)
|
||||
{
|
||||
try {
|
||||
return new (std::nothrow) OakCommonOIIOUtils{};
|
||||
} catch (...) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_oiioutils_free(OakCommonOIIOUtils *self)
|
||||
{
|
||||
delete self;
|
||||
}
|
||||
|
||||
int oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
OakCommonOIIOUtils *self, int pixel_format, int *out_base_type)
|
||||
{
|
||||
try {
|
||||
if (self == NULL || out_base_type == NULL)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
if (pixel_format < OAKCOMMON_PIXEL_FORMAT_INVALID ||
|
||||
pixel_format >= OAKCOMMON_PIXEL_FORMAT_COUNT)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
OIIO::TypeDesc::BASETYPE base_type =
|
||||
OIIOUtils::get_oiio_base_type_from_format(
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format));
|
||||
*out_base_type = static_cast<int>(base_type);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
OakCommonOIIOUtils *self, int base_type, int *out_pixel_format)
|
||||
{
|
||||
try {
|
||||
if (self == NULL || out_pixel_format == NULL)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
if (base_type < 0 || base_type >= OIIO::TypeDesc::LASTBASE)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
olive::core::PixelFormat format =
|
||||
OIIOUtils::get_format_from_oiio_basetype(
|
||||
static_cast<OIIO::TypeDesc::BASETYPE>(base_type));
|
||||
*out_pixel_format = static_cast<int>(format);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_oiioutils_get_pixel_aspect_ratio(
|
||||
OakCommonOIIOUtils *self, double pixel_aspect_ratio, int *out_numerator,
|
||||
int *out_denominator)
|
||||
{
|
||||
try {
|
||||
if (self == NULL || out_numerator == NULL || out_denominator == NULL)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
|
||||
olive::core::Rational par =
|
||||
olive::core::Rational::from_double(pixel_aspect_ratio);
|
||||
*out_numerator = par.numerator();
|
||||
*out_denominator = par.denominator();
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "../../include/common/power.h"
|
||||
|
||||
#include "../src/power.h"
|
||||
|
||||
int oakcommon_power_ceil_to_power_of_2(uint32_t value, uint32_t *out)
|
||||
{
|
||||
try {
|
||||
if (!out) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
*out = olive::ceil_to_power_of_2(value);
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_power_floor_to_power_of_2(uint32_t value, uint32_t *out)
|
||||
{
|
||||
try {
|
||||
if (!out) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
*out = olive::floor_to_power_of_2(value);
|
||||
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/qtutils.h"
|
||||
|
||||
#include "../src/qtutils.h"
|
||||
|
||||
int oakcommon_qtutils_ptr_to_value(void *ptr, uint64_t *out_value)
|
||||
{
|
||||
if (!out_value) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
*out_value = static_cast<uint64_t>(olive::QtUtils::ptr_to_value(ptr));
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_qtutils_value_to_ptr(uint64_t value, void **out_ptr)
|
||||
{
|
||||
if (!out_ptr) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
*out_ptr =
|
||||
olive::QtUtils::value_to_ptr<void>(static_cast<uintptr_t>(value));
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_qtutils_get_creation_date(const char *path, int64_t *out_secs)
|
||||
{
|
||||
if (!path || !out_secs) {
|
||||
return OAKCOMMON_E_INVALID;
|
||||
}
|
||||
|
||||
try {
|
||||
std::chrono::system_clock::time_point t =
|
||||
olive::QtUtils::get_creation_date(std::filesystem::path(path));
|
||||
|
||||
if (t == std::chrono::system_clock::time_point{}) {
|
||||
return OAKCOMMON_E_NOT_FOUND;
|
||||
}
|
||||
|
||||
*out_secs = static_cast<int64_t>(
|
||||
std::chrono::system_clock::to_time_t(t));
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
#include "../src/xmlutils.h"
|
||||
|
||||
struct OakCommonXmlReader {
|
||||
olive::XmlStreamReader reader;
|
||||
std::string cached_text;
|
||||
bool has_cached_text = false;
|
||||
|
||||
explicit OakCommonXmlReader(const char *data)
|
||||
: reader(data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct OakCommonXmlWriter {
|
||||
olive::XmlStreamWriter writer;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Copy @p value into the two-stage string buffer.
|
||||
*
|
||||
* @return Required buffer size in bytes (including NUL). The buffer is only
|
||||
* written to when it is large enough.
|
||||
*/
|
||||
int copy_string(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int needed = static_cast<int>(value.size()) + 1;
|
||||
if (buf && buf_size >= needed) {
|
||||
memcpy(buf, value.c_str(), needed);
|
||||
}
|
||||
return needed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
OakCommonXmlReader *oakcommon_xml_reader_init(const char *data)
|
||||
{
|
||||
if (!data)
|
||||
return nullptr;
|
||||
try {
|
||||
return new (std::nothrow) OakCommonXmlReader(data);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_xml_reader_free(OakCommonXmlReader *reader)
|
||||
{
|
||||
delete reader;
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_read_next_start_element(OakCommonXmlReader *reader,
|
||||
int *found)
|
||||
{
|
||||
if (!reader || !found)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
reader->has_cached_text = false;
|
||||
*found = olive::xml_read_next_start_element(&reader->reader) ? 1 : 0;
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_name(OakCommonXmlReader *reader, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (!reader)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(reader->reader.name(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_read_element_text(OakCommonXmlReader *reader,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!reader)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
// read_element_text() consumes the stream, so cache the result to
|
||||
// keep the two-stage (size query then copy) buffer convention working.
|
||||
if (!reader->has_cached_text) {
|
||||
reader->cached_text = reader->reader.read_element_text();
|
||||
reader->has_cached_text = true;
|
||||
}
|
||||
return copy_string(reader->cached_text, buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_skip_current_element(OakCommonXmlReader *reader)
|
||||
{
|
||||
if (!reader)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
reader->has_cached_text = false;
|
||||
reader->reader.skip_current_element();
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_attribute_count(OakCommonXmlReader *reader,
|
||||
int *count)
|
||||
{
|
||||
if (!reader || !count)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
*count = static_cast<int>(reader->reader.attributes().size());
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_attribute_name(OakCommonXmlReader *reader, int index,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!reader)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
const auto &attrs = reader->reader.attributes();
|
||||
if (index < 0 || index >= static_cast<int>(attrs.size()))
|
||||
return OAKCOMMON_E_NOT_FOUND;
|
||||
return copy_string(attrs[index].name, buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_attribute_value(OakCommonXmlReader *reader,
|
||||
int index, char *buf, int buf_size)
|
||||
{
|
||||
if (!reader)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
const auto &attrs = reader->reader.attributes();
|
||||
if (index < 0 || index >= static_cast<int>(attrs.size()))
|
||||
return OAKCOMMON_E_NOT_FOUND;
|
||||
return copy_string(attrs[index].value, buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_reader_has_error(OakCommonXmlReader *reader,
|
||||
int *has_error)
|
||||
{
|
||||
if (!reader || !has_error)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
*has_error = reader->reader.has_error() ? 1 : 0;
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
OakCommonXmlWriter *oakcommon_xml_writer_init(void)
|
||||
{
|
||||
try {
|
||||
return new (std::nothrow) OakCommonXmlWriter();
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_xml_writer_free(OakCommonXmlWriter *writer)
|
||||
{
|
||||
delete writer;
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_write_start_element(OakCommonXmlWriter *writer,
|
||||
const char *name)
|
||||
{
|
||||
if (!writer || !name)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
writer->writer.write_start_element(name);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_write_attribute(OakCommonXmlWriter *writer,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
if (!writer || !name || !value)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
writer->writer.write_attribute(name, value);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_write_characters(OakCommonXmlWriter *writer,
|
||||
const char *text)
|
||||
{
|
||||
if (!writer || !text)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
writer->writer.write_characters(text);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_write_text_element(OakCommonXmlWriter *writer,
|
||||
const char *name,
|
||||
const char *text)
|
||||
{
|
||||
if (!writer || !name || !text)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
writer->writer.write_text_element(name, text);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_write_end_element(OakCommonXmlWriter *writer)
|
||||
{
|
||||
if (!writer)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
writer->writer.write_end_element();
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_write_end_document(OakCommonXmlWriter *writer)
|
||||
{
|
||||
if (!writer)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
writer->writer.write_end_document();
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_xml_writer_output(OakCommonXmlWriter *writer, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (!writer)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(writer->writer.output(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,74 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
# 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/>.
|
||||
|
||||
add_library(oakcommon SHARED
|
||||
commandlineparser.cpp
|
||||
commandlineparser.h
|
||||
current.cpp
|
||||
current.h
|
||||
debug.cpp
|
||||
debug.h
|
||||
decibel.h
|
||||
define.h
|
||||
dropworkflowbehavior.h
|
||||
ffmpegutils.cpp
|
||||
ffmpegutils.h
|
||||
filefunctions.cpp
|
||||
filefunctions.h
|
||||
lerp.h
|
||||
memorypool.h
|
||||
ocioutils.cpp
|
||||
ocioutils.h
|
||||
oiioutils.cpp
|
||||
oiioutils.h
|
||||
power.h
|
||||
qtutils.cpp
|
||||
qtutils.h
|
||||
threadsafemap.h
|
||||
xmlutils.cpp
|
||||
xmlutils.h
|
||||
)
|
||||
|
||||
# In a full-tree build the repo root is CMAKE_SOURCE_DIR; a standalone
|
||||
# build (see src/common/standalone) sets OAK_REPO_ROOT explicitly.
|
||||
if(NOT DEFINED OAK_REPO_ROOT)
|
||||
set(OAK_REPO_ROOT ${CMAKE_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
target_include_directories(oakcommon PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${OAK_REPO_ROOT}/include
|
||||
${OAK_REPO_ROOT}/third_party/openfx/include
|
||||
${OCIO_INCLUDE_DIRS}
|
||||
${OIIO_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Public headers of oakcommon include the olive/core wrapper headers
|
||||
# (pixelformat.h, sampleformat.h, rational.h) and the ffmpeg_bridge C ABI
|
||||
# header. Their include dirs are PRIVATE to those targets, so re-export them.
|
||||
target_include_directories(oakcommon PUBLIC
|
||||
${OAK_REPO_ROOT}/core/include
|
||||
${OAK_REPO_ROOT}/ffmpeg_bridge/include
|
||||
)
|
||||
|
||||
target_link_libraries(oakcommon PUBLIC
|
||||
olivecore
|
||||
ffmpeg_bridge
|
||||
EXPAT::EXPAT
|
||||
${OCIO_LIBRARIES}
|
||||
${OIIO_LIBRARIES}
|
||||
)
|
||||
@@ -0,0 +1,197 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "commandlineparser.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
CommandLineParser::~CommandLineParser()
|
||||
{
|
||||
for (const KnownOption &o : options_) {
|
||||
delete o.option;
|
||||
}
|
||||
|
||||
for (const KnownPositionalArgument &a : positional_args_) {
|
||||
delete a.option;
|
||||
}
|
||||
}
|
||||
|
||||
const CommandLineParser::Option *
|
||||
CommandLineParser::add_option(const std::vector<std::string> &strings,
|
||||
const std::string &description, bool takes_arg,
|
||||
const std::string &arg_placeholder, bool hidden)
|
||||
{
|
||||
Option *o = new Option();
|
||||
|
||||
options_.push_back(
|
||||
{ strings, description, o, takes_arg, arg_placeholder, hidden });
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
const CommandLineParser::PositionalArgument *
|
||||
CommandLineParser::add_positional_argument(const std::string &name,
|
||||
const std::string &description,
|
||||
bool required)
|
||||
{
|
||||
PositionalArgument *a = new PositionalArgument();
|
||||
|
||||
positional_args_.push_back({ name, description, a, required });
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static bool string_equals_case_insensitive(const std::string &a,
|
||||
const std::string &b)
|
||||
{
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < a.size(); i++) {
|
||||
if (tolower(static_cast<unsigned char>(a[i])) !=
|
||||
tolower(static_cast<unsigned char>(b[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommandLineParser::process(const std::vector<std::string> &argv)
|
||||
{
|
||||
size_t positional_index = 0;
|
||||
|
||||
for (size_t i = 1; i < argv.size(); i++) {
|
||||
if (!argv[i].empty() && argv[i][0] == '-') {
|
||||
// Must be an option
|
||||
|
||||
// Skip past first dash
|
||||
std::string arg_basename = argv[i].substr(1);
|
||||
|
||||
bool matched_known = false;
|
||||
|
||||
for (KnownOption &o : options_) {
|
||||
for (const std::string &s : o.args) {
|
||||
if (string_equals_case_insensitive(s, arg_basename)) {
|
||||
// Flag discovered!
|
||||
o.option->set();
|
||||
|
||||
if (o.takes_arg && i + 1 < argv.size()) {
|
||||
o.option->set_setting(argv[i + 1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
matched_known = true;
|
||||
goto found_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found_flag:
|
||||
if (!matched_known) {
|
||||
fprintf(stderr, "Unknown parameter: %s\n", argv[i].c_str());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Must be a positional flag
|
||||
if (positional_index < positional_args_.size()) {
|
||||
positional_args_[positional_index].option->set_setting(argv[i]);
|
||||
positional_index++;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown parameter: %s\n", argv[i].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLineParser::print_help(const char *filename)
|
||||
{
|
||||
printf("%s %s\n", app_name_.c_str(), app_version_.c_str());
|
||||
|
||||
printf("Copyright (C) 2018-2022 Oak Video Editor Team\n");
|
||||
|
||||
std::string positional_args;
|
||||
for (size_t i = 0; i < positional_args_.size(); i++) {
|
||||
if (i > 0) {
|
||||
positional_args.append(" ");
|
||||
}
|
||||
|
||||
positional_args.append("[");
|
||||
positional_args.append(positional_args_[i].name);
|
||||
positional_args.append("]");
|
||||
}
|
||||
|
||||
const char *basename;
|
||||
#ifdef _WIN32
|
||||
basename = strrchr(filename, '\\');
|
||||
if (!basename) {
|
||||
basename = strrchr(filename, '/');
|
||||
}
|
||||
#else
|
||||
basename = strrchr(filename, '/');
|
||||
#endif
|
||||
|
||||
if (basename) {
|
||||
// Slash found, increment pointer to avoid showing the slash itself
|
||||
basename++;
|
||||
} else {
|
||||
// If no slashes are found, assume string is already a basename
|
||||
basename = filename;
|
||||
}
|
||||
|
||||
printf("Usage: %s [options] %s\n\n", basename, positional_args.c_str());
|
||||
for (const KnownOption &o : options_) {
|
||||
if (o.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string all_args;
|
||||
|
||||
for (size_t i = 0; i < o.args.size(); i++) {
|
||||
if (i > 0) {
|
||||
all_args.append(", ");
|
||||
}
|
||||
|
||||
all_args.append("-");
|
||||
all_args.append(o.args[i]);
|
||||
}
|
||||
|
||||
if (o.arg_placeholder.empty()) {
|
||||
printf(" %s\n", all_args.c_str());
|
||||
} else {
|
||||
printf(" %s <%s>\n", all_args.c_str(), o.arg_placeholder.c_str());
|
||||
}
|
||||
|
||||
printf(" %s\n\n", o.description.c_str());
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void CommandLineParser::set_app_info(const std::string &name,
|
||||
const std::string &version)
|
||||
{
|
||||
app_name_ = name;
|
||||
app_version_ = version;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_COMMANDLINEPARSER_H
|
||||
#define OAK_COMMANDLINEPARSER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief Command-line argument parser
|
||||
*
|
||||
* You may be wondering why we don't use a library implementation like
|
||||
* QCommandLineParser instead of a custom implementation like this. The
|
||||
* reason why is because QCommandLineParser requires a QApplication object
|
||||
* of some kind to already have been created before it can parse anything,
|
||||
* but we need to be able to control whether a QApplication (GUI-mode) or
|
||||
* a QCoreApplication (CLI-mode) is created which is set by the user as a
|
||||
* command line argument. Therefore we needed a custom implementation that
|
||||
* could parse arguments without the need for any application object to be
|
||||
* present already.
|
||||
*/
|
||||
class CommandLineParser {
|
||||
public:
|
||||
CommandLineParser() = default;
|
||||
~CommandLineParser();
|
||||
|
||||
CommandLineParser(const CommandLineParser &) = delete;
|
||||
CommandLineParser(CommandLineParser &&) = delete;
|
||||
CommandLineParser &operator=(const CommandLineParser &) = delete;
|
||||
CommandLineParser &operator=(CommandLineParser &&) = delete;
|
||||
|
||||
class PositionalArgument {
|
||||
public:
|
||||
PositionalArgument() = default;
|
||||
|
||||
const std::string &get_setting() const
|
||||
{
|
||||
return setting_;
|
||||
}
|
||||
|
||||
void set_setting(const std::string &s)
|
||||
{
|
||||
setting_ = s;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string setting_;
|
||||
};
|
||||
|
||||
class Option : public PositionalArgument {
|
||||
public:
|
||||
Option()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
|
||||
bool is_set() const
|
||||
{
|
||||
return is_set_;
|
||||
}
|
||||
|
||||
void set()
|
||||
{
|
||||
is_set_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set_;
|
||||
};
|
||||
|
||||
const Option *add_option(const std::vector<std::string> &strings,
|
||||
const std::string &description, bool takes_arg = false,
|
||||
const std::string &arg_placeholder = std::string(),
|
||||
bool hidden = false);
|
||||
|
||||
const PositionalArgument *add_positional_argument(const std::string &name,
|
||||
const std::string &description,
|
||||
bool required = false);
|
||||
|
||||
void process(const std::vector<std::string> &argv);
|
||||
|
||||
void print_help(const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Set the application name/version shown by print_help()
|
||||
*
|
||||
* Replaces the former QCoreApplication::applicationName()/applicationVersion()
|
||||
* dependency. Defaults to "oak" with an empty version.
|
||||
*/
|
||||
void set_app_info(const std::string &name, const std::string &version);
|
||||
|
||||
private:
|
||||
struct KnownOption {
|
||||
std::vector<std::string> args;
|
||||
std::string description;
|
||||
Option *option;
|
||||
bool takes_arg;
|
||||
std::string arg_placeholder;
|
||||
bool hidden;
|
||||
};
|
||||
|
||||
struct KnownPositionalArgument {
|
||||
std::string name;
|
||||
std::string description;
|
||||
PositionalArgument *option;
|
||||
bool required;
|
||||
};
|
||||
|
||||
std::vector<KnownOption> options_;
|
||||
|
||||
std::vector<KnownPositionalArgument> positional_args_;
|
||||
|
||||
std::string app_name_ = "oak";
|
||||
|
||||
std::string app_version_;
|
||||
};
|
||||
|
||||
#endif // OAK_COMMANDLINEPARSER_H
|
||||
@@ -0,0 +1,23 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "current.h"
|
||||
|
||||
Current Current::current_;
|
||||
@@ -0,0 +1,95 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_CURRENT_H
|
||||
#define OAK_CURRENT_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @brief Thread-shared holder for the "current" session-wide objects.
|
||||
*
|
||||
* De-Qt version of the old engine/common Current. The external objects
|
||||
* (video/audio params, plugin host, plugin cache) live outside
|
||||
* oakcommon, so they are kept as type-erased pointers
|
||||
* (std::shared_ptr<void>); callers recover the concrete type with
|
||||
* std::static_pointer_cast. Slots may be empty (nullptr).
|
||||
*/
|
||||
class Current {
|
||||
public:
|
||||
static Current &get_instance()
|
||||
{
|
||||
return current_;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> current_video_params() const
|
||||
{
|
||||
return video_params_;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> current_audio_params() const
|
||||
{
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
void set_current_video_params(std::shared_ptr<void> params)
|
||||
{
|
||||
video_params_ = params;
|
||||
}
|
||||
|
||||
void set_current_audio_params(std::shared_ptr<void> params)
|
||||
{
|
||||
audio_params_ = params;
|
||||
}
|
||||
|
||||
bool interactive() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> plugin_host() const
|
||||
{
|
||||
return plugin_host_;
|
||||
}
|
||||
|
||||
void set_plugin_host(std::shared_ptr<void> host)
|
||||
{
|
||||
plugin_host_ = host;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> plugin_cache() const
|
||||
{
|
||||
return plugin_cache_;
|
||||
}
|
||||
|
||||
void set_plugin_cache(std::shared_ptr<void> cache)
|
||||
{
|
||||
plugin_cache_ = cache;
|
||||
}
|
||||
|
||||
private:
|
||||
static Current current_;
|
||||
std::shared_ptr<void> video_params_;
|
||||
std::shared_ptr<void> audio_params_;
|
||||
std::shared_ptr<void> plugin_host_;
|
||||
std::shared_ptr<void> plugin_cache_;
|
||||
};
|
||||
|
||||
#endif // OAK_CURRENT_H
|
||||
@@ -0,0 +1,73 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
int debug_level_name(int level, char *buf, int buf_size)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
switch (level) {
|
||||
case k_debug_debug:
|
||||
name = "DEBUG";
|
||||
break;
|
||||
case k_debug_info:
|
||||
name = "INFO";
|
||||
break;
|
||||
case k_debug_warning:
|
||||
name = "WARNING";
|
||||
break;
|
||||
case k_debug_error:
|
||||
name = "ERROR";
|
||||
break;
|
||||
case k_debug_fatal:
|
||||
name = "FATAL";
|
||||
break;
|
||||
default:
|
||||
name = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
int needed = (int)strlen(name) + 1;
|
||||
|
||||
if (buf && buf_size >= needed)
|
||||
memcpy(buf, name, needed);
|
||||
return needed;
|
||||
}
|
||||
|
||||
void debug_handler(int level, const char *msg)
|
||||
{
|
||||
char level_name[16];
|
||||
|
||||
debug_level_name(level, level_name, sizeof(level_name));
|
||||
fprintf(stderr, "[%s] %s\n", level_name, msg ? msg : "");
|
||||
|
||||
// Always flush so debug messages appear immediately, even on
|
||||
// platforms that buffer stderr.
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_DEBUG_H
|
||||
#define OAK_DEBUG_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Severity levels for debug output.
|
||||
*
|
||||
* Replaces Qt's QtMsgType now that the debug handler no longer depends
|
||||
* on QDebug.
|
||||
*/
|
||||
enum DebugLevel {
|
||||
k_debug_debug,
|
||||
k_debug_info,
|
||||
k_debug_warning,
|
||||
k_debug_error,
|
||||
k_debug_fatal
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Write the printable name of a debug level (e.g. "WARNING") into
|
||||
* a caller-provided buffer.
|
||||
*
|
||||
* Two-stage form: pass `buf == NULL` or a too-small `buf_size` to query
|
||||
* the required size. Writes "UNKNOWN" for levels outside the DebugLevel
|
||||
* range.
|
||||
*
|
||||
* @return Required buffer size in bytes, including the terminating NUL.
|
||||
*/
|
||||
int debug_level_name(int level, char *buf, int buf_size);
|
||||
|
||||
/**
|
||||
* @brief Print a debug message to stderr, prefixed with its level.
|
||||
*
|
||||
* De-Qt replacement for the old Qt message handler: qDebug() output is
|
||||
* replaced by fprintf(stderr). A NULL message is treated as an empty
|
||||
* string. Lines are always flushed so messages appear immediately.
|
||||
*/
|
||||
void debug_handler(int level, const char *msg);
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_DEBUG_H
|
||||
@@ -0,0 +1,96 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_DECIBEL_H
|
||||
#define OAK_DECIBEL_H
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class Decibel {
|
||||
public:
|
||||
// In basically all circumstances, this should calculate to 0.0 linear
|
||||
static constexpr double minimum = -200.0;
|
||||
|
||||
static double from_linear(double linear)
|
||||
{
|
||||
double v = double(20.0) * std::log10(linear);
|
||||
if (std::isinf(v)) {
|
||||
return minimum;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static double to_linear(double decibel)
|
||||
{
|
||||
double to_linear = std::pow(double(10.0), decibel / double(20.0));
|
||||
|
||||
// Minimum threshold that we figure is close enough to 0 that we may as well just return 0
|
||||
if (to_linear < 0.000001) {
|
||||
return 0;
|
||||
} else {
|
||||
return to_linear;
|
||||
}
|
||||
}
|
||||
|
||||
static double from_logarithmic(double logarithmic)
|
||||
{
|
||||
if (logarithmic < 0.001)
|
||||
return minimum;
|
||||
else if (logarithmic > 0.99)
|
||||
return 0;
|
||||
else
|
||||
return 20.0 * std::log10(-std::log(1 - logarithmic) / lo_g100);
|
||||
}
|
||||
|
||||
static double to_logarithmic(double decibel)
|
||||
{
|
||||
if (std::abs(decibel) <= 1e-12) {
|
||||
return 1;
|
||||
} else {
|
||||
return 1 - std::exp(-std::pow(10.0, decibel / 20.0) * lo_g100);
|
||||
}
|
||||
}
|
||||
|
||||
static double linear_to_logarithmic(double linear)
|
||||
{
|
||||
return 1 - std::exp(-linear * lo_g100);
|
||||
}
|
||||
|
||||
static double logarithmic_to_linear(double logarithmic)
|
||||
{
|
||||
if (logarithmic > 0.99) {
|
||||
return 1;
|
||||
} else {
|
||||
return -std::log(1 - logarithmic) / lo_g100;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double lo_g100 = 4.60517018599;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_DECIBEL_H
|
||||
@@ -0,0 +1,58 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_OLIVECOMMONDEFINE_H
|
||||
#define OAK_OLIVECOMMONDEFINE_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/// The minimum size an icon in ProjectExplorer can be
|
||||
const int k_project_icon_size_minimum = 16;
|
||||
|
||||
/// The maximum size an icon in ProjectExplorer can be
|
||||
const int k_project_icon_size_maximum = 256;
|
||||
|
||||
/// The default size an icon in ProjectExplorer can be
|
||||
const int k_project_icon_size_default = 64;
|
||||
|
||||
const int k_bytes_in_gigabyte = 1073741824;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
|
||||
* use our own functions for portability.
|
||||
*/
|
||||
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete; \
|
||||
Class &operator=(const Class &) = delete;
|
||||
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define DISABLE_COPY_MOVE(Class) \
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
|
||||
#endif // OAK_OLIVECOMMONDEFINE_H
|
||||
@@ -0,0 +1,43 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_DROPWORKFLOWBEHAVIOR_H
|
||||
#define OAK_DROPWORKFLOWBEHAVIOR_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Behavior when media is dropped onto a timeline without a sequence
|
||||
*
|
||||
* Shared by the config defaults (engine layer) and the timeline import
|
||||
* tool (UI layer). Enumerator order matches the previous
|
||||
* ImportTool::DropWithoutSequenceBehavior.
|
||||
*/
|
||||
enum DropWithoutSequenceBehavior {
|
||||
k_dws_ask,
|
||||
k_dws_auto,
|
||||
k_dws_manual,
|
||||
k_dws_disable
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_DROPWORKFLOWBEHAVIOR_H
|
||||
@@ -0,0 +1,197 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ffmpegutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
int FFmpegUtils::get_compatible_bridge_pixel_format(int pix_fmt,
|
||||
PixelFormat maximum)
|
||||
{
|
||||
int possible_pix_fmts[4];
|
||||
|
||||
possible_pix_fmts[0] = fb_pix_fmt_rgba;
|
||||
|
||||
if (maximum == PixelFormat::u8) {
|
||||
possible_pix_fmts[1] = fb_pix_fmt_none;
|
||||
} else {
|
||||
possible_pix_fmts[1] = fb_pix_fmt_rgb_a64_le;
|
||||
if (maximum == PixelFormat::f32) {
|
||||
possible_pix_fmts[2] = fb_pix_fmt_rgba_f32_le;
|
||||
possible_pix_fmts[3] = fb_pix_fmt_none;
|
||||
} else {
|
||||
possible_pix_fmts[2] = fb_pix_fmt_none;
|
||||
}
|
||||
}
|
||||
|
||||
return fb_find_best_pix_fmt_of_list(possible_pix_fmts, pix_fmt);
|
||||
}
|
||||
|
||||
SampleFormat FFmpegUtils::get_native_sample_format(int smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case fb_sample_fmt_u8:
|
||||
return SampleFormat::u8;
|
||||
case fb_sample_fmt_s16:
|
||||
return SampleFormat::s16;
|
||||
case fb_sample_fmt_s32:
|
||||
return SampleFormat::s32;
|
||||
case fb_sample_fmt_s64:
|
||||
return SampleFormat::s64;
|
||||
case fb_sample_fmt_flt:
|
||||
return SampleFormat::f32;
|
||||
case fb_sample_fmt_dbl:
|
||||
return SampleFormat::f64;
|
||||
case fb_sample_fmt_u8_p:
|
||||
return SampleFormat::u8_p;
|
||||
case fb_sample_fmt_s16_p:
|
||||
return SampleFormat::s16_p;
|
||||
case fb_sample_fmt_s32_p:
|
||||
return SampleFormat::s32_p;
|
||||
case fb_sample_fmt_s64_p:
|
||||
return SampleFormat::s64_p;
|
||||
case fb_sample_fmt_fltp:
|
||||
return SampleFormat::f32_p;
|
||||
case fb_sample_fmt_dblp:
|
||||
return SampleFormat::f64_p;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SampleFormat::invalid;
|
||||
}
|
||||
|
||||
int FFmpegUtils::get_ffmpeg_sample_format(const SampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case SampleFormat::u8:
|
||||
return fb_sample_fmt_u8;
|
||||
case SampleFormat::s16:
|
||||
return fb_sample_fmt_s16;
|
||||
case SampleFormat::s32:
|
||||
return fb_sample_fmt_s32;
|
||||
case SampleFormat::s64:
|
||||
return fb_sample_fmt_s64;
|
||||
case SampleFormat::f32:
|
||||
return fb_sample_fmt_flt;
|
||||
case SampleFormat::f64:
|
||||
return fb_sample_fmt_dbl;
|
||||
case SampleFormat::u8_p:
|
||||
return fb_sample_fmt_u8_p;
|
||||
case SampleFormat::s16_p:
|
||||
return fb_sample_fmt_s16_p;
|
||||
case SampleFormat::s32_p:
|
||||
return fb_sample_fmt_s32_p;
|
||||
case SampleFormat::s64_p:
|
||||
return fb_sample_fmt_s64_p;
|
||||
case SampleFormat::f32_p:
|
||||
return fb_sample_fmt_fltp;
|
||||
case SampleFormat::f64_p:
|
||||
return fb_sample_fmt_dblp;
|
||||
case SampleFormat::invalid:
|
||||
case SampleFormat::count:
|
||||
break;
|
||||
}
|
||||
|
||||
return fb_sample_fmt_none;
|
||||
}
|
||||
|
||||
int FFmpegUtils::convert_jpeg_space_to_regular_space(int f)
|
||||
{
|
||||
switch (f) {
|
||||
case fb_pix_fmt_yuv_j420_p:
|
||||
return fb_pix_fmt_yu_v420_p;
|
||||
case fb_pix_fmt_yuv_j422_p:
|
||||
return fb_pix_fmt_yu_v422_p;
|
||||
case fb_pix_fmt_yuv_j444_p:
|
||||
return fb_pix_fmt_yu_v444_p;
|
||||
case fb_pix_fmt_yuv_j440_p:
|
||||
return fb_pix_fmt_yu_v440_p;
|
||||
case fb_pix_fmt_yuv_j411_p:
|
||||
return fb_pix_fmt_yu_v411_p;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
int FFmpegUtils::get_ffmpeg_pixel_format(const PixelFormat &pix_fmt,
|
||||
int channel_layout)
|
||||
{
|
||||
if (channel_layout == k_rgb_channel_count) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::u8:
|
||||
return fb_pix_fmt_rg_b24;
|
||||
case PixelFormat::u10:
|
||||
return fb_pix_fmt_none;
|
||||
case PixelFormat::u16:
|
||||
return fb_pix_fmt_rg_b48_le;
|
||||
case PixelFormat::f16:
|
||||
return fb_pix_fmt_rgb_f16_le;
|
||||
case PixelFormat::f32:
|
||||
return fb_pix_fmt_rgb_f32_le;
|
||||
case PixelFormat::invalid:
|
||||
case PixelFormat::count:
|
||||
break;
|
||||
}
|
||||
} else if (channel_layout == k_rgba_channel_count) {
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::u8:
|
||||
return fb_pix_fmt_rgba;
|
||||
case PixelFormat::u10:
|
||||
return fb_pix_fmt_none;
|
||||
case PixelFormat::u16:
|
||||
return fb_pix_fmt_rgb_a64_le;
|
||||
case PixelFormat::f16:
|
||||
return fb_pix_fmt_rgba_f16_le;
|
||||
case PixelFormat::f32:
|
||||
return fb_pix_fmt_rgba_f32_le;
|
||||
case PixelFormat::invalid:
|
||||
case PixelFormat::count:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fb_pix_fmt_none;
|
||||
}
|
||||
|
||||
PixelFormat FFmpegUtils::get_compatible_pixel_format(const PixelFormat &pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::u8:
|
||||
return PixelFormat::u8;
|
||||
case PixelFormat::u10:
|
||||
return PixelFormat::u8;
|
||||
case PixelFormat::u16:
|
||||
case PixelFormat::f16:
|
||||
case PixelFormat::f32:
|
||||
return PixelFormat::u16;
|
||||
case PixelFormat::invalid:
|
||||
case PixelFormat::count:
|
||||
break;
|
||||
}
|
||||
|
||||
return PixelFormat::invalid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_FFMPEGABSTRACTION_H
|
||||
#define OAK_FFMPEGABSTRACTION_H
|
||||
|
||||
#include <ffmpeg_bridge/ffmpeg_bridge.h>
|
||||
|
||||
#include <olive/core/render/pixelformat.h>
|
||||
#include <olive/core/render/sampleformat.h>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
/**
|
||||
* @brief C++ adapter mapping Olive's native enums to bridge pixel/sample
|
||||
* formats
|
||||
*
|
||||
* All "FFmpeg" formats here are actually the opaque FBPixelFormat /
|
||||
* FBSampleFormat constants of the ffmpeg_bridge library; no FFmpeg header
|
||||
* or structure is ever seen by the editor.
|
||||
*/
|
||||
class FFmpegUtils {
|
||||
public:
|
||||
/**
|
||||
* @brief Channel counts formerly taken from VideoParams
|
||||
*
|
||||
* Flattened to plain constants here so that this header no longer
|
||||
* depends on render/videoparams.h (common must not depend on render).
|
||||
*/
|
||||
static constexpr int k_rgb_channel_count = 3;
|
||||
static constexpr int k_rgba_channel_count = 4;
|
||||
|
||||
/**
|
||||
* @brief Returns a bridge pixel format that can be used to convert a frame to a data type Olive supports with minimal data loss
|
||||
*
|
||||
* Named distinctly from the native PixelFormat overload below: with both
|
||||
* taking a single argument, an unscoped enum argument would silently
|
||||
* prefer an int overload over the PixelFormat one.
|
||||
*/
|
||||
static int get_compatible_bridge_pixel_format(
|
||||
int pix_fmt, PixelFormat maximum = PixelFormat::invalid);
|
||||
|
||||
/**
|
||||
* @brief Returns a native pixel format that can be used to convert from a native frame to a bridge frame with minimal data loss
|
||||
*/
|
||||
static PixelFormat get_compatible_pixel_format(const PixelFormat &pix_fmt);
|
||||
|
||||
/**
|
||||
* @brief Returns a bridge pixel format for a given native pixel format
|
||||
*/
|
||||
static int get_ffmpeg_pixel_format(const PixelFormat &pix_fmt,
|
||||
int channel_layout);
|
||||
|
||||
/**
|
||||
* @brief Returns a native sample format type for a given bridge sample format
|
||||
*/
|
||||
static SampleFormat get_native_sample_format(int smp_fmt);
|
||||
|
||||
/**
|
||||
* @brief Returns a bridge sample format type for a given native type
|
||||
*/
|
||||
static int get_ffmpeg_sample_format(const SampleFormat &smp_fmt);
|
||||
|
||||
/**
|
||||
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
|
||||
*
|
||||
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
|
||||
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
|
||||
* aware), we use this function.
|
||||
*/
|
||||
static int convert_jpeg_space_to_regular_space(int f);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_FFMPEGABSTRACTION_H
|
||||
@@ -0,0 +1,375 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "filefunctions.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <system_error>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief FNV-1a hash of a string, returned as lowercase hex
|
||||
*/
|
||||
std::string fnv1a_hex(const std::string &data)
|
||||
{
|
||||
uint64_t hash = 14695981039346656037ULL;
|
||||
for (unsigned char c : data) {
|
||||
hash ^= c;
|
||||
hash *= 1099511628211ULL;
|
||||
}
|
||||
|
||||
char buf[17];
|
||||
snprintf(buf, sizeof(buf), "%016llx",
|
||||
static_cast<unsigned long long>(hash));
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Case-insensitive check whether `s` ends with `suffix`
|
||||
*/
|
||||
bool ends_with_case_insensitive(const std::string &s, const std::string &suffix)
|
||||
{
|
||||
if (suffix.size() > s.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = s.size() - suffix.size();
|
||||
for (size_t i = 0; i < suffix.size(); i++) {
|
||||
char a = s[offset + i];
|
||||
char b = suffix[i];
|
||||
if (a >= 'A' && a <= 'Z') {
|
||||
a += 'a' - 'A';
|
||||
}
|
||||
if (b >= 'A' && b <= 'Z') {
|
||||
b += 'a' - 'A';
|
||||
}
|
||||
if (a != b) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string FileFunctions::get_unique_file_identifier(
|
||||
const std::string &filename)
|
||||
{
|
||||
std::error_code ec;
|
||||
fs::path abs = fs::absolute(filename, ec);
|
||||
if (ec || !fs::exists(abs, ec)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
auto mtime = fs::last_write_time(abs, ec);
|
||||
if (ec) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string hash_input = abs.string();
|
||||
hash_input.append(std::to_string(
|
||||
static_cast<long long>(mtime.time_since_epoch().count())));
|
||||
|
||||
return fnv1a_hex(hash_input);
|
||||
}
|
||||
|
||||
std::string FileFunctions::get_configuration_location()
|
||||
{
|
||||
// Tests and tooling can redirect the configuration (and, since most
|
||||
// locations derive from it, the cache/data) root.
|
||||
const char *override_dir = getenv("OAK_CONFIG_DIR");
|
||||
if (override_dir != nullptr && override_dir[0] != '\0') {
|
||||
std::error_code ec;
|
||||
fs::create_directories(override_dir, ec);
|
||||
return override_dir;
|
||||
}
|
||||
|
||||
if (is_portable()) {
|
||||
return get_application_path();
|
||||
}
|
||||
|
||||
fs::path config_root;
|
||||
#ifdef __APPLE__
|
||||
const char *home = getenv("HOME");
|
||||
if (home != nullptr && home[0] != '\0') {
|
||||
config_root = fs::path(home) / "Library" / "Application Support";
|
||||
}
|
||||
#else
|
||||
const char *xdg_config = getenv("XDG_CONFIG_HOME");
|
||||
if (xdg_config != nullptr && xdg_config[0] != '\0') {
|
||||
config_root = xdg_config;
|
||||
} else {
|
||||
const char *home = getenv("HOME");
|
||||
if (home != nullptr && home[0] != '\0') {
|
||||
config_root = fs::path(home) / ".config";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (config_root.empty()) {
|
||||
// Last resort: temp directory
|
||||
config_root = fs::temp_directory_path();
|
||||
}
|
||||
|
||||
fs::path config_dir = config_root / "oak";
|
||||
std::error_code ec;
|
||||
fs::create_directories(config_dir, ec);
|
||||
return config_dir.string();
|
||||
}
|
||||
|
||||
bool FileFunctions::is_portable()
|
||||
{
|
||||
std::error_code ec;
|
||||
return fs::exists(fs::path(get_application_path()) / "portable", ec);
|
||||
}
|
||||
|
||||
std::string FileFunctions::get_application_path()
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
uint32_t size = 0;
|
||||
_NSGetExecutablePath(nullptr, &size);
|
||||
std::string buf(size, '\0');
|
||||
if (_NSGetExecutablePath(buf.data(), &size) == 0) {
|
||||
std::error_code ec;
|
||||
fs::path p = fs::weakly_canonical(buf, ec);
|
||||
if (!ec) {
|
||||
return p.parent_path().string();
|
||||
}
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
std::error_code ec;
|
||||
fs::path p = fs::read_symlink("/proc/self/exe", ec);
|
||||
if (!ec) {
|
||||
return p.parent_path().string();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Fallback: current working directory
|
||||
std::error_code ec;
|
||||
fs::path cwd = fs::current_path(ec);
|
||||
return ec ? std::string() : cwd.string();
|
||||
}
|
||||
|
||||
std::string FileFunctions::get_temp_file_path()
|
||||
{
|
||||
std::error_code ec;
|
||||
fs::path temp_path = fs::temp_directory_path(ec) / "oak";
|
||||
if (ec) {
|
||||
temp_path = fs::path(".") / "oak-temp";
|
||||
}
|
||||
|
||||
// Ensure it exists
|
||||
fs::create_directories(temp_path, ec);
|
||||
|
||||
return temp_path.string();
|
||||
}
|
||||
|
||||
bool FileFunctions::can_copy_directory_without_overwriting(
|
||||
const std::string &source, const std::string &dest)
|
||||
{
|
||||
std::error_code ec;
|
||||
for (const auto &entry : fs::directory_iterator(source, ec)) {
|
||||
fs::path dest_equivalent = fs::path(dest) / entry.path().filename();
|
||||
|
||||
if (entry.is_directory(ec)) {
|
||||
if (!can_copy_directory_without_overwriting(
|
||||
entry.path().string(),
|
||||
dest_equivalent.string())) {
|
||||
return false;
|
||||
}
|
||||
} else if (fs::exists(dest_equivalent, ec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileFunctions::copy_directory(const std::string &source,
|
||||
const std::string &dest, bool overwrite)
|
||||
{
|
||||
std::error_code ec;
|
||||
if (!fs::is_directory(source, ec)) {
|
||||
fprintf(stderr, "Failed to copy directory, source %s didn't exist\n",
|
||||
source.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fs::create_directories(dest, ec) && ec) {
|
||||
fprintf(stderr, "Failed to create destination directory %s\n",
|
||||
dest.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &entry : fs::directory_iterator(source, ec)) {
|
||||
fs::path dest_file_path =
|
||||
fs::path(dest) / entry.path().filename();
|
||||
|
||||
if (entry.is_directory(ec)) {
|
||||
// Copy dir
|
||||
copy_directory(entry.path().string(),
|
||||
dest_file_path.string(), overwrite);
|
||||
} else {
|
||||
// Copy file
|
||||
if (overwrite) {
|
||||
fs::permissions(dest_file_path,
|
||||
fs::perms::owner_write |
|
||||
fs::perms::group_write |
|
||||
fs::perms::others_write,
|
||||
fs::perm_options::add, ec);
|
||||
ec.clear();
|
||||
fs::remove(dest_file_path, ec);
|
||||
ec.clear();
|
||||
}
|
||||
|
||||
fs::copy_file(entry.path(), dest_file_path,
|
||||
fs::copy_options::none, ec);
|
||||
if (ec) {
|
||||
fprintf(stderr,
|
||||
"Failed to copy file %s to %s: %s\n",
|
||||
entry.path().string().c_str(),
|
||||
dest_file_path.string().c_str(),
|
||||
ec.message().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FileFunctions::directory_is_valid(const std::string &dir,
|
||||
bool try_to_create_if_not_exists)
|
||||
{
|
||||
// Return whether the directory exists, or whether it could be created
|
||||
// if it doesn't
|
||||
std::error_code ec;
|
||||
if (fs::is_directory(dir, ec)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return try_to_create_if_not_exists && fs::create_directories(dir, ec);
|
||||
}
|
||||
|
||||
std::string FileFunctions::ensure_filename_extension(
|
||||
std::string fn, const std::string &extension)
|
||||
{
|
||||
// No-op if either input is empty
|
||||
if (!fn.empty() && !extension.empty()) {
|
||||
std::string extension_with_dot = "." + extension;
|
||||
|
||||
if (!ends_with_case_insensitive(fn, extension_with_dot)) {
|
||||
fn.append(extension_with_dot);
|
||||
}
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
std::string FileFunctions::read_file_as_string(const std::string &filename)
|
||||
{
|
||||
std::ifstream f(filename, std::ios::in | std::ios::binary);
|
||||
if (!f.is_open()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << f.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string FileFunctions::get_safe_temporary_filename(
|
||||
const std::string &original)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
fs::path original_path(original);
|
||||
fs::path dir = original_path.parent_path();
|
||||
std::string filename = original_path.filename().string();
|
||||
|
||||
// Split off the complete suffix (everything from the first dot), like
|
||||
// QFileInfo::completeSuffix()
|
||||
std::string basename = filename;
|
||||
std::string complete_suffix;
|
||||
size_t first_dot = filename.find('.');
|
||||
if (first_dot != std::string::npos) {
|
||||
basename = filename.substr(0, first_dot);
|
||||
complete_suffix = filename.substr(first_dot);
|
||||
}
|
||||
|
||||
fs::path temp_abs_path;
|
||||
do {
|
||||
temp_abs_path =
|
||||
dir / (basename + ".tmp" + std::to_string(counter) +
|
||||
complete_suffix);
|
||||
counter++;
|
||||
} while (fs::exists(temp_abs_path));
|
||||
|
||||
return temp_abs_path.string();
|
||||
}
|
||||
|
||||
bool FileFunctions::rename_file_allow_overwrite(const std::string &from,
|
||||
const std::string &to)
|
||||
{
|
||||
std::error_code ec;
|
||||
if (fs::exists(to, ec) && !fs::remove(to, ec)) {
|
||||
fprintf(stderr, "Couldn't remove existing file %s for overwrite\n",
|
||||
to.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// By this point, we can assume `to` either never existed or has now
|
||||
// been deleted
|
||||
fs::rename(from, to, ec);
|
||||
if (ec) {
|
||||
fprintf(stderr, "Failed to rename file %s to %s: %s\n",
|
||||
from.c_str(), to.c_str(), ec.message().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string FileFunctions::get_formatted_executable_for_platform(
|
||||
std::string unformatted)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
unformatted.append(".exe");
|
||||
#endif
|
||||
|
||||
return unformatted;
|
||||
}
|
||||
|
||||
std::string FileFunctions::get_auto_recovery_root()
|
||||
{
|
||||
return (fs::path(get_configuration_location()) / "autorecovery")
|
||||
.string();
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_FILEFUNCTIONS_H
|
||||
#define OAK_FILEFUNCTIONS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief A collection of static file and directory functions
|
||||
*
|
||||
* Qt-free reimplementation of the former olive::FileFunctions. All paths
|
||||
* are UTF-8 std::strings and all filesystem operations use std::filesystem.
|
||||
*/
|
||||
class FileFunctions {
|
||||
public:
|
||||
/**
|
||||
* @brief Returns true if the application is running in portable mode
|
||||
*
|
||||
* In portable mode, any persistent configuration files should be made in
|
||||
* a path relative to the application rather than in the user's home
|
||||
* folder.
|
||||
*/
|
||||
static bool is_portable();
|
||||
|
||||
/**
|
||||
* @brief Returns a deterministic identifier string for a file
|
||||
*
|
||||
* Derived from the absolute path and the last modification time. Returns
|
||||
* an empty string if the file does not exist. Uses FNV-1a hashing rather
|
||||
* than SHA-1; identifiers are only used as cache keys, not for security.
|
||||
*/
|
||||
static std::string get_unique_file_identifier(const std::string &filename);
|
||||
|
||||
static std::string get_configuration_location();
|
||||
|
||||
static std::string get_application_path();
|
||||
|
||||
static std::string get_temp_file_path();
|
||||
|
||||
static bool can_copy_directory_without_overwriting(
|
||||
const std::string &source, const std::string &dest);
|
||||
|
||||
static void copy_directory(const std::string &source,
|
||||
const std::string &dest,
|
||||
bool overwrite = false);
|
||||
|
||||
static bool directory_is_valid(const std::string &dir,
|
||||
bool try_to_create_if_not_exists = true);
|
||||
|
||||
/**
|
||||
* @brief Ensures a given filename has a certain extension
|
||||
*
|
||||
* Checks if the filename has the extension provided and appends it if
|
||||
* not. The extension is checked case-insensitive. The extension should
|
||||
* be provided with no dot (e.g. "ove" rather than ".ove").
|
||||
*
|
||||
* @return The filename provided either untouched or with the extension
|
||||
* appended to it.
|
||||
*/
|
||||
static std::string ensure_filename_extension(std::string fn,
|
||||
const std::string &extension);
|
||||
|
||||
static std::string read_file_as_string(const std::string &filename);
|
||||
|
||||
/**
|
||||
* @brief Returns a temporary filename that can be used while writing
|
||||
* rather than the original
|
||||
*
|
||||
* If overwriting a file, it's safest to write to a new file first and
|
||||
* then only replace it at the end so that if the program crashes or the
|
||||
* user cancels the save half way through, the original file is still
|
||||
* intact.
|
||||
*
|
||||
* This function returns a slight variant of the filename provided that's
|
||||
* guaranteed to not exist and therefore won't overwrite anything
|
||||
* important.
|
||||
*/
|
||||
static std::string get_safe_temporary_filename(const std::string &original);
|
||||
|
||||
/**
|
||||
* @brief Renames a file from `from` to `to`, deleting `to` if such a
|
||||
* file already exists first
|
||||
*/
|
||||
static bool rename_file_allow_overwrite(const std::string &from,
|
||||
const std::string &to);
|
||||
|
||||
static std::string get_formatted_executable_for_platform(
|
||||
std::string unformatted);
|
||||
|
||||
static std::string get_auto_recovery_root();
|
||||
};
|
||||
|
||||
#endif // OAK_FILEFUNCTIONS_H
|
||||
@@ -0,0 +1,41 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_LERP_H
|
||||
#define OAK_LERP_H
|
||||
|
||||
/**
|
||||
* @brief Linearly interpolate a value between a and b using t
|
||||
*
|
||||
* t should be a number between 0.0 and 1.0. 0.0 will return a, 1.0 will return b, and between will return a value
|
||||
* in between a and b at that point linearly.
|
||||
*/
|
||||
template <typename T> T lerp(T a, T b, double t)
|
||||
{
|
||||
return (a * (1.0 - t)) + (b * t);
|
||||
}
|
||||
|
||||
template <typename T> T lerp(T a, T b, float t)
|
||||
{
|
||||
return (a * (1.0f - t)) + (b * t);
|
||||
}
|
||||
|
||||
#endif // OAK_LERP_H
|
||||
@@ -0,0 +1,440 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_MEMORYPOOL_H
|
||||
#define OAK_MEMORYPOOL_H
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief MemoryPool base class
|
||||
*
|
||||
* A custom memory system that allocates several objects in a large chunk (as opposed to several small
|
||||
* allocations). Improves performance and memory consumption.
|
||||
*
|
||||
* As a class, this base is usable by subclassing and overriding get_element_size() if elements are larger than
|
||||
* one byte. The pool will then allocate `(element_count * get_element_size())` per arena. Arenas are allocated
|
||||
* and destroyed on the fly - when an arena fills up, another is allocated.
|
||||
*
|
||||
* get() will return an ElementPtr. The original desired data can be accessed through ElementPtr::data(). This data
|
||||
* will belong to the caller until ElementPtr goes out of scope and the memory is freed back into the pool.
|
||||
*
|
||||
* Empty arenas are NOT reaped automatically anymore (the Qt version used a QTimer driven by the Qt event loop).
|
||||
* Call clear_empty_arenas() periodically from whatever scheduling mechanism the host application provides.
|
||||
*/
|
||||
class MemoryPool {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param element_count
|
||||
*
|
||||
* Number of elements per arena
|
||||
*/
|
||||
MemoryPool(int element_count)
|
||||
{
|
||||
element_count_ = element_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*
|
||||
* Deletes all arenas.
|
||||
*/
|
||||
virtual ~MemoryPool()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(MemoryPool)
|
||||
|
||||
/**
|
||||
* @brief Clears all arenas, freeing all of their memory
|
||||
*
|
||||
* Note that this function is not safe, any elements that are still out there will be invalid
|
||||
* and accessing them will cause a crash. You'll need to make sure all elements are already
|
||||
* relinquished before then.
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
|
||||
for (Arena *a : arenas_) {
|
||||
delete a;
|
||||
}
|
||||
arenas_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns whether any arenas are successfully allocated
|
||||
*/
|
||||
inline bool is_allocated() const
|
||||
{
|
||||
return !arenas_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns current number of allocated arenas
|
||||
*/
|
||||
inline int get_arena_count() const
|
||||
{
|
||||
return arenas_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deletes arenas that have been empty for at least kMaxEmptyArenaLife milliseconds
|
||||
*
|
||||
* In the Qt version this was a private slot invoked periodically by a QTimer. Without Qt it is now a
|
||||
* public function that the caller is responsible for invoking (e.g. from a std::thread timer).
|
||||
*/
|
||||
void clear_empty_arenas()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
|
||||
const int64_t min_time = now_ms() - kMaxEmptyArenaLife;
|
||||
|
||||
for (auto it = arenas_.begin(); it != arenas_.end();) {
|
||||
Arena *arena = (*it);
|
||||
|
||||
if (arena->get_usage_count() == 0 &&
|
||||
arena->time_arena_was_made_empty() <= min_time) {
|
||||
delete arena;
|
||||
it = arenas_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Arena;
|
||||
|
||||
/**
|
||||
* @brief A handle for a chunk of memory in an arena
|
||||
*
|
||||
* Calling get() on the pool or arena will return a shared pointer to an element which will contain a pointer to
|
||||
* the desired object/data in data(). When Element is destroyed (i.e. when ElementPtr goes out of scope), the
|
||||
* memory is released back into the pool so it can be used by another class.
|
||||
*/
|
||||
class Element {
|
||||
public:
|
||||
/**
|
||||
* @brief Element Constructor
|
||||
*
|
||||
* There is no need to use this outside of the memory pool's internal functions.
|
||||
*/
|
||||
Element(Arena *parent, uint8_t *data)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = data;
|
||||
accessed_ = now_ms();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Element Destructor
|
||||
*
|
||||
* Automatically releases this element's memory back to the arena it was retrieved from.
|
||||
*/
|
||||
~Element()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(Element)
|
||||
|
||||
/**
|
||||
* @brief Access data represented in the pool
|
||||
*/
|
||||
inline uint8_t *data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline const int64_t ×tamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
inline void set_timestamp(const int64_t ×tamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register that this element has been accessed
|
||||
*
|
||||
* \see last_accessed()
|
||||
*/
|
||||
inline void access()
|
||||
{
|
||||
accessed_ = now_ms();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the last time `access()` was called on this function
|
||||
*
|
||||
* Useful for determining the relative age of an element (i.e. if it hasn't been accessed for a certain amount
|
||||
* of time, it can probably be freed back into the pool). This requires all usages to call `access()`.
|
||||
*/
|
||||
inline const int64_t &last_accessed() const
|
||||
{
|
||||
return accessed_;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (data_) {
|
||||
parent_->release(this);
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Arena *parent_;
|
||||
|
||||
uint8_t *data_;
|
||||
|
||||
int64_t timestamp_;
|
||||
|
||||
int64_t accessed_;
|
||||
};
|
||||
|
||||
using ElementPtr = std::shared_ptr<Element>;
|
||||
|
||||
/**
|
||||
* @brief A memory pool arena - a subsection of memory
|
||||
*
|
||||
* The pool itself does not store memory, it stores "arenas". This is so that the pool can handle the situation of
|
||||
* an arena becoming full with no more memory to lend. A pool can automatically allocate another arena and
|
||||
* continue providing memory (and freeing arenas when they're no longer in use).
|
||||
*/
|
||||
class Arena {
|
||||
public:
|
||||
Arena(MemoryPool *parent)
|
||||
{
|
||||
parent_ = parent;
|
||||
data_ = nullptr;
|
||||
allocated_sz_ = 0;
|
||||
empty_time_ = now_ms();
|
||||
}
|
||||
|
||||
~Arena()
|
||||
{
|
||||
std::list<Element *> copy = lent_elements_;
|
||||
for (Element *e : copy) {
|
||||
e->release();
|
||||
}
|
||||
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(Arena)
|
||||
|
||||
/**
|
||||
* @brief Returns an element if there is free memory to do so
|
||||
*/
|
||||
ElementPtr get()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
|
||||
for (size_t i = 0; i < available_.size(); i++) {
|
||||
if (available_.at(i)) {
|
||||
// This buffer is available
|
||||
available_[i] = false;
|
||||
|
||||
ElementPtr e = std::make_shared<Element>(
|
||||
this,
|
||||
reinterpret_cast<uint8_t *>(data_ + i * element_sz_));
|
||||
lent_elements_.push_back(e.get());
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases an element back into the pool for use elsewhere
|
||||
*/
|
||||
void release(Element *e)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
uintptr_t diff = reinterpret_cast<uintptr_t>(e->data()) -
|
||||
reinterpret_cast<uintptr_t>(data_);
|
||||
|
||||
size_t index = diff / element_sz_;
|
||||
|
||||
available_[index] = true;
|
||||
|
||||
lent_elements_.remove(e);
|
||||
|
||||
if (lent_elements_.empty()) {
|
||||
empty_time_ = now_ms();
|
||||
}
|
||||
}
|
||||
|
||||
int get_usage_count()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
return lent_elements_.size();
|
||||
}
|
||||
|
||||
bool allocate(size_t ele_sz, size_t nb_elements)
|
||||
{
|
||||
if (is_allocated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
element_sz_ = ele_sz;
|
||||
|
||||
allocated_sz_ = element_sz_ * nb_elements;
|
||||
|
||||
if ((data_ = new (std::nothrow) uint8_t[allocated_sz_])) {
|
||||
available_.assign(nb_elements, true);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
available_.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline int get_element_count() const
|
||||
{
|
||||
return available_.size();
|
||||
}
|
||||
|
||||
inline bool is_allocated() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline int64_t time_arena_was_made_empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
return empty_time_;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool *parent_;
|
||||
|
||||
uint8_t *data_;
|
||||
|
||||
size_t allocated_sz_;
|
||||
|
||||
std::vector<bool> available_;
|
||||
|
||||
std::mutex lock_;
|
||||
|
||||
size_t element_sz_;
|
||||
|
||||
std::list<Element *> lent_elements_;
|
||||
|
||||
int64_t empty_time_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Retrieves an element from an available arena
|
||||
*/
|
||||
ElementPtr get()
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(lock_);
|
||||
|
||||
// Attempt to get an element from an arena
|
||||
for (Arena *a : arenas_) {
|
||||
ElementPtr e = a->get();
|
||||
|
||||
if (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
// All arenas were empty, we'll need to create a new one
|
||||
size_t ele_sz = get_element_size();
|
||||
|
||||
if (!ele_sz) {
|
||||
fprintf(stderr, "Failed to create arena, element size was 0\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (element_count_ <= 0) {
|
||||
fprintf(stderr, "Failed to create arena, element count was invalid: %d\n",
|
||||
element_count_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Arena *a = new Arena(this);
|
||||
if (!a->allocate(ele_sz, element_count_)) {
|
||||
fprintf(stderr,
|
||||
"Failed to create arena, allocation failed. Out of memory?\n");
|
||||
delete a;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
arenas_.push_back(a);
|
||||
return a->get();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The size of each element
|
||||
*
|
||||
* Override this to use a custom size (e.g. a char array where the element size is > 1)
|
||||
*/
|
||||
virtual size_t get_element_size()
|
||||
{
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Milliseconds since the epoch (replaces QDateTime::currentMSecsSinceEpoch())
|
||||
*/
|
||||
static int64_t now_ms()
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
int element_count_;
|
||||
|
||||
std::list<Arena *> arenas_;
|
||||
|
||||
std::mutex lock_;
|
||||
|
||||
static const int64_t kMaxEmptyArenaLife = 5000;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_MEMORYPOOL_H
|
||||
@@ -0,0 +1,43 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "ocioutils.h"
|
||||
|
||||
ocio::BitDepth OCIOUtils::get_ocio_bit_depth_from_pixel_format(
|
||||
olive::core::PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case olive::core::PixelFormat::u8:
|
||||
return ocio::BIT_DEPTH_UINT8;
|
||||
case olive::core::PixelFormat::u10:
|
||||
return ocio::BIT_DEPTH_UINT10;
|
||||
case olive::core::PixelFormat::u16:
|
||||
return ocio::BIT_DEPTH_UINT16;
|
||||
case olive::core::PixelFormat::f16:
|
||||
return ocio::BIT_DEPTH_F16;
|
||||
case olive::core::PixelFormat::f32:
|
||||
return ocio::BIT_DEPTH_F32;
|
||||
case olive::core::PixelFormat::invalid:
|
||||
case olive::core::PixelFormat::count:
|
||||
break;
|
||||
}
|
||||
|
||||
return ocio::BIT_DEPTH_UNKNOWN;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_OCIOUTILS_H
|
||||
#define OAK_OCIOUTILS_H
|
||||
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace ocio = OCIO_NAMESPACE;
|
||||
|
||||
#include <olive/core/render/pixelformat.h>
|
||||
|
||||
/**
|
||||
* @brief A collection of static OpenColorIO helper functions
|
||||
*
|
||||
* Qt-free reimplementation of the former olive::OCIOUtils. The dependency
|
||||
* on render/videoparams.h (which pulls in Qt) was replaced with the
|
||||
* Qt-free olive/core/render/pixelformat.h, where PixelFormat is defined.
|
||||
*/
|
||||
class OCIOUtils {
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the OCIO bit depth matching a native pixel format
|
||||
*
|
||||
* Returns ocio::BIT_DEPTH_UNKNOWN for invalid or out-of-range formats.
|
||||
*/
|
||||
static ocio::BitDepth get_ocio_bit_depth_from_pixel_format(
|
||||
olive::core::PixelFormat format);
|
||||
};
|
||||
|
||||
#endif // OAK_OCIOUTILS_H
|
||||
@@ -0,0 +1,104 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oiioutils.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
OIIO::TypeDesc::BASETYPE
|
||||
OIIOUtils::get_oiio_base_type_from_format(olive::core::PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case olive::core::PixelFormat::u8:
|
||||
return OIIO::TypeDesc::UINT8;
|
||||
case olive::core::PixelFormat::u10:
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
case olive::core::PixelFormat::u16:
|
||||
return OIIO::TypeDesc::UINT16;
|
||||
case olive::core::PixelFormat::f16:
|
||||
return OIIO::TypeDesc::HALF;
|
||||
case olive::core::PixelFormat::f32:
|
||||
return OIIO::TypeDesc::FLOAT;
|
||||
case olive::core::PixelFormat::invalid:
|
||||
case olive::core::PixelFormat::count:
|
||||
break;
|
||||
}
|
||||
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
}
|
||||
|
||||
olive::core::PixelFormat
|
||||
OIIOUtils::get_format_from_oiio_basetype(OIIO::TypeDesc::BASETYPE type)
|
||||
{
|
||||
switch (type) {
|
||||
case OIIO::TypeDesc::UNKNOWN:
|
||||
case OIIO::TypeDesc::NONE:
|
||||
#if OIIO_VERSION >= 20500
|
||||
case OIIO::TypeDesc::USTRINGHASH:
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::INT8:
|
||||
case OIIO::TypeDesc::INT16:
|
||||
case OIIO::TypeDesc::INT32:
|
||||
case OIIO::TypeDesc::UINT32:
|
||||
case OIIO::TypeDesc::INT64:
|
||||
case OIIO::TypeDesc::UINT64:
|
||||
case OIIO::TypeDesc::STRING:
|
||||
case OIIO::TypeDesc::PTR:
|
||||
case OIIO::TypeDesc::LASTBASE:
|
||||
case OIIO::TypeDesc::DOUBLE:
|
||||
fprintf(stderr, "Tried to use unknown OIIO base type\n");
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::UINT8:
|
||||
return olive::core::PixelFormat::u8;
|
||||
case OIIO::TypeDesc::UINT16:
|
||||
return olive::core::PixelFormat::u16;
|
||||
case OIIO::TypeDesc::HALF:
|
||||
return olive::core::PixelFormat::f16;
|
||||
case OIIO::TypeDesc::FLOAT:
|
||||
return olive::core::PixelFormat::f32;
|
||||
}
|
||||
|
||||
return olive::core::PixelFormat::invalid;
|
||||
}
|
||||
|
||||
olive::core::Rational
|
||||
OIIOUtils::get_pixel_aspect_ratio_from_oiio(const OIIO::ImageSpec &spec)
|
||||
{
|
||||
return olive::core::Rational::from_double(
|
||||
spec.get_float_attribute("PixelAspectRatio", 1));
|
||||
}
|
||||
|
||||
void OIIOUtils::frame_to_buffer(const void *data, int64_t linesize_bytes,
|
||||
OIIO::ImageBuf *buf)
|
||||
{
|
||||
buf->set_pixels(OIIO::ROI(), buf->spec().format, data, OIIO::AutoStride,
|
||||
static_cast<OIIO::stride_t>(linesize_bytes));
|
||||
}
|
||||
|
||||
void OIIOUtils::buffer_to_frame(OIIO::ImageBuf *buf, void *data,
|
||||
int64_t linesize_bytes)
|
||||
{
|
||||
buf->get_pixels(OIIO::ROI(), buf->spec().format, data, OIIO::AutoStride,
|
||||
static_cast<OIIO::stride_t>(linesize_bytes));
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_OIIOUTILS_H
|
||||
#define OAK_OIIOUTILS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/typedesc.h>
|
||||
|
||||
#include <olive/core/render/pixelformat.h>
|
||||
#include <olive/core/util/rational.h>
|
||||
|
||||
/**
|
||||
* @brief A collection of static OpenImageIO helper functions
|
||||
*
|
||||
* Qt-free reimplementation of the former olive::OIIOUtils. The reverse
|
||||
* dependencies on codec/frame.h and render/videoparams.h (which pull in
|
||||
* Qt) were replaced with the Qt-free olive/core/render/pixelformat.h and
|
||||
* olive/core/util/rational.h. The Frame-based helpers were flattened to
|
||||
* raw data pointer + linesize, which is all they ever used from Frame.
|
||||
*/
|
||||
class OIIOUtils {
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the OIIO base type matching a native pixel format
|
||||
*
|
||||
* Returns OIIO::TypeDesc::UNKNOWN for invalid, out-of-range or
|
||||
* unmappable formats (e.g. u10, which OIIO has no base type for).
|
||||
*/
|
||||
static OIIO::TypeDesc::BASETYPE
|
||||
get_oiio_base_type_from_format(olive::core::PixelFormat format);
|
||||
|
||||
/**
|
||||
* @brief Returns the native pixel format matching an OIIO base type
|
||||
*
|
||||
* Returns olive::core::PixelFormat::invalid for unknown or unmappable
|
||||
* base types.
|
||||
*/
|
||||
static olive::core::PixelFormat
|
||||
get_format_from_oiio_basetype(OIIO::TypeDesc::BASETYPE type);
|
||||
|
||||
/**
|
||||
* @brief Reads the PixelAspectRatio attribute of an image spec
|
||||
*
|
||||
* Defaults to 1:1 when the attribute is not present.
|
||||
*/
|
||||
static olive::core::Rational
|
||||
get_pixel_aspect_ratio_from_oiio(const OIIO::ImageSpec &spec);
|
||||
|
||||
/**
|
||||
* @brief Copies raw pixel data into an OIIO image buffer
|
||||
*
|
||||
* Flattened form of the former Frame-based frame_to_buffer(); pass
|
||||
* Frame::const_data() and Frame::linesize_bytes() at the call site.
|
||||
*/
|
||||
static void frame_to_buffer(const void *data, int64_t linesize_bytes,
|
||||
OIIO::ImageBuf *buf);
|
||||
|
||||
/**
|
||||
* @brief Copies an OIIO image buffer's pixels into raw memory
|
||||
*
|
||||
* Flattened form of the former Frame-based buffer_to_frame(); pass
|
||||
* Frame::data() and Frame::linesize_bytes() at the call site.
|
||||
*/
|
||||
static void buffer_to_frame(OIIO::ImageBuf *buf, void *data,
|
||||
int64_t linesize_bytes);
|
||||
};
|
||||
|
||||
#endif // OAK_OIIOUTILS_H
|
||||
@@ -0,0 +1,58 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
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_POWER_H
|
||||
#define OAK_POWER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "define.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
inline uint32_t ceil_to_power_of_2(uint32_t v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
inline uint32_t floor_to_power_of_2(uint32_t x)
|
||||
{
|
||||
x = x | (x >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 16);
|
||||
|
||||
return x - (x >> 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_POWER_H
|
||||
@@ -0,0 +1,52 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "qtutils.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
std::chrono::system_clock::time_point
|
||||
QtUtils::get_creation_date(const std::filesystem::path &path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(path.c_str(), &st) != 0) {
|
||||
return std::chrono::system_clock::time_point{};
|
||||
}
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
// Filesystems that record a birth time expose it as st_birthtimespec;
|
||||
// fall back to the metadata change time when it is not set.
|
||||
time_t t = st.st_birthtimespec.tv_sec;
|
||||
if (t == 0 || t == -1) {
|
||||
t = st.st_ctimespec.tv_sec;
|
||||
}
|
||||
return std::chrono::system_clock::from_time_t(t);
|
||||
#else
|
||||
// Portable fallback: st_ctime (metadata change time), matching the
|
||||
// Qt implementation's birthTime()/metadataChangeTime() fallback.
|
||||
return std::chrono::system_clock::from_time_t(st.st_ctime);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_QTUTILS_H
|
||||
#define OAK_QTUTILS_H
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Qt-free replacement for the former Qt-based QtUtils helper class.
|
||||
*
|
||||
* Only the functions actually used by the engine are kept. The UI-bound
|
||||
* helpers (font metrics, combo boxes, QFrame creation, QColor conversion)
|
||||
* remain available to Qt callers through the original Qt version.
|
||||
*/
|
||||
class QtUtils {
|
||||
public:
|
||||
/**
|
||||
* @brief Convert a pointer to an integer value that can be passed around as data
|
||||
*/
|
||||
static uintptr_t ptr_to_value(void *ptr)
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert an integer produced by ptr_to_value() back to a pointer of any kind
|
||||
*/
|
||||
template <class T> static T *value_to_ptr(uintptr_t value)
|
||||
{
|
||||
return reinterpret_cast<T *>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Walk the parent chain of an object looking for an ancestor of type T
|
||||
*
|
||||
* Works with any object type that provides a parent() member returning a
|
||||
* pointer to the same (or a base) type, e.g. QObject.
|
||||
*/
|
||||
template <typename T, typename Obj>
|
||||
static T *get_parent_of_type(Obj *child)
|
||||
{
|
||||
auto *t = child ? child->parent() : nullptr;
|
||||
|
||||
while (t) {
|
||||
if (T *p = dynamic_cast<T *>(t)) {
|
||||
return p;
|
||||
}
|
||||
t = t->parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the creation (birth) time of a file
|
||||
*
|
||||
* Falls back to the last metadata change / modification time on
|
||||
* filesystems that do not record birth times.
|
||||
*/
|
||||
static std::chrono::system_clock::time_point
|
||||
get_creation_date(const std::filesystem::path &path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_QTUTILS_H
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* 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_THREADSAFEMAP_H
|
||||
#define OAK_THREADSAFEMAP_H
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
/**
|
||||
* @brief A minimal mutex-guarded associative container
|
||||
*
|
||||
* Drop-in stdlib replacement for the former QMap/QMutex based template.
|
||||
*/
|
||||
template <typename K, typename V> class ThreadSafeMap {
|
||||
public:
|
||||
ThreadSafeMap() = default;
|
||||
|
||||
/**
|
||||
* @brief Insert or overwrite the value stored under `key`
|
||||
*/
|
||||
void insert(const K &key, const V &value)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(mutex_);
|
||||
map_.insert_or_assign(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns whether `key` exists in the map
|
||||
*/
|
||||
bool contains(const K &key) const
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(mutex_);
|
||||
return map_.find(key) != map_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy the value stored under `key` into `out`
|
||||
* @return true if the key existed, false otherwise (`out` left untouched)
|
||||
*/
|
||||
bool get(const K &key, V *out) const
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(mutex_);
|
||||
auto it = map_.find(key);
|
||||
if (it == map_.end()) {
|
||||
return false;
|
||||
}
|
||||
*out = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entries in the map
|
||||
*/
|
||||
size_t size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(mutex_);
|
||||
return map_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
std::map<K, V> map_;
|
||||
};
|
||||
|
||||
#endif // OAK_THREADSAFEMAP_H
|
||||
@@ -0,0 +1,294 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "xmlutils.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Expat user-data used while parsing a document into an event list.
|
||||
*/
|
||||
struct ParseContext {
|
||||
std::vector<XmlStreamReader::Event> *events;
|
||||
bool has_error;
|
||||
std::string error_string;
|
||||
};
|
||||
|
||||
void XMLCALL on_start_element(void *user_data, const XML_Char *name,
|
||||
const XML_Char **atts)
|
||||
{
|
||||
ParseContext *ctx = static_cast<ParseContext *>(user_data);
|
||||
XmlStreamReader::Event ev;
|
||||
ev.type = XmlStreamReader::StartElement;
|
||||
ev.name = name;
|
||||
for (int i = 0; atts[i]; i += 2)
|
||||
ev.attributes.push_back({atts[i], atts[i + 1]});
|
||||
ctx->events->push_back(std::move(ev));
|
||||
}
|
||||
|
||||
void XMLCALL on_end_element(void *user_data, const XML_Char *name)
|
||||
{
|
||||
ParseContext *ctx = static_cast<ParseContext *>(user_data);
|
||||
XmlStreamReader::Event ev;
|
||||
ev.type = XmlStreamReader::EndElement;
|
||||
ev.name = name;
|
||||
ctx->events->push_back(std::move(ev));
|
||||
}
|
||||
|
||||
void XMLCALL on_characters(void *user_data, const XML_Char *s, int len)
|
||||
{
|
||||
ParseContext *ctx = static_cast<ParseContext *>(user_data);
|
||||
if (!len)
|
||||
return;
|
||||
if (!ctx->events->empty() &&
|
||||
ctx->events->back().type == XmlStreamReader::Characters) {
|
||||
ctx->events->back().text.append(s, len);
|
||||
return;
|
||||
}
|
||||
XmlStreamReader::Event ev;
|
||||
ev.type = XmlStreamReader::Characters;
|
||||
ev.text.assign(s, len);
|
||||
ctx->events->push_back(std::move(ev));
|
||||
}
|
||||
|
||||
std::string escape_text(const std::string &in)
|
||||
{
|
||||
std::string out;
|
||||
out.reserve(in.size());
|
||||
for (char c : in) {
|
||||
switch (c) {
|
||||
case '&': out += "&"; break;
|
||||
case '<': out += "<"; break;
|
||||
case '>': out += ">"; break;
|
||||
default: out += c; break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string escape_attribute(const std::string &in)
|
||||
{
|
||||
std::string out;
|
||||
out.reserve(in.size());
|
||||
for (char c : in) {
|
||||
switch (c) {
|
||||
case '&': out += "&"; break;
|
||||
case '<': out += "<"; break;
|
||||
case '>': out += ">"; break;
|
||||
case '"': out += """; break;
|
||||
default: out += c; break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
XmlStreamReader::XmlStreamReader(const std::string &data)
|
||||
: pos_(0)
|
||||
, token_(Invalid)
|
||||
, has_error_(false)
|
||||
{
|
||||
ParseContext ctx{&events_, false, std::string()};
|
||||
|
||||
XML_Parser parser = XML_ParserCreate(nullptr);
|
||||
if (!parser) {
|
||||
has_error_ = true;
|
||||
error_string_ = "failed to create XML parser";
|
||||
return;
|
||||
}
|
||||
|
||||
XML_SetUserData(parser, &ctx);
|
||||
XML_SetElementHandler(parser, on_start_element, on_end_element);
|
||||
XML_SetCharacterDataHandler(parser, on_characters);
|
||||
|
||||
if (XML_Parse(parser, data.data(), static_cast<int>(data.size()),
|
||||
XML_TRUE) == XML_STATUS_ERROR) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s at line %lu",
|
||||
XML_ErrorString(XML_GetErrorCode(parser)),
|
||||
static_cast<unsigned long>(
|
||||
XML_GetCurrentLineNumber(parser)));
|
||||
has_error_ = true;
|
||||
error_string_ = buf;
|
||||
}
|
||||
|
||||
XML_ParserFree(parser);
|
||||
}
|
||||
|
||||
XmlStreamReader::TokenType XmlStreamReader::read_next()
|
||||
{
|
||||
attributes_.clear();
|
||||
text_.clear();
|
||||
name_.clear();
|
||||
|
||||
if (has_error_) {
|
||||
token_ = Invalid;
|
||||
return token_;
|
||||
}
|
||||
|
||||
if (pos_ >= events_.size()) {
|
||||
token_ = EndDocument;
|
||||
return token_;
|
||||
}
|
||||
|
||||
const Event &ev = events_[pos_++];
|
||||
token_ = ev.type;
|
||||
name_ = ev.name;
|
||||
text_ = ev.text;
|
||||
attributes_ = ev.attributes;
|
||||
return token_;
|
||||
}
|
||||
|
||||
std::string XmlStreamReader::read_element_text()
|
||||
{
|
||||
if (!is_start_element())
|
||||
return std::string();
|
||||
|
||||
std::string result;
|
||||
int depth = 1;
|
||||
while (true) {
|
||||
TokenType t = read_next();
|
||||
if (t == Invalid || t == EndDocument)
|
||||
break;
|
||||
if (t == StartElement) {
|
||||
depth++;
|
||||
} else if (t == EndElement) {
|
||||
if (--depth == 0)
|
||||
break;
|
||||
} else if (t == Characters && depth == 1) {
|
||||
result += text_;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void XmlStreamReader::skip_current_element()
|
||||
{
|
||||
if (!is_start_element())
|
||||
return;
|
||||
|
||||
int depth = 1;
|
||||
while (true) {
|
||||
TokenType t = read_next();
|
||||
if (t == Invalid || t == EndDocument)
|
||||
break;
|
||||
if (t == StartElement)
|
||||
depth++;
|
||||
else if (t == EndElement && --depth == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XmlStreamWriter::XmlStreamWriter()
|
||||
: open_start_tag_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void XmlStreamWriter::write_start_element(const std::string &name)
|
||||
{
|
||||
if (open_start_tag_) {
|
||||
output_ += '>';
|
||||
open_start_tag_ = false;
|
||||
}
|
||||
output_ += '<';
|
||||
output_ += name;
|
||||
stack_.push_back(name);
|
||||
open_start_tag_ = true;
|
||||
}
|
||||
|
||||
void XmlStreamWriter::write_attribute(const std::string &name,
|
||||
const std::string &value)
|
||||
{
|
||||
if (!open_start_tag_)
|
||||
return;
|
||||
output_ += ' ';
|
||||
output_ += name;
|
||||
output_ += "=\"";
|
||||
output_ += escape_attribute(value);
|
||||
output_ += '"';
|
||||
}
|
||||
|
||||
void XmlStreamWriter::write_characters(const std::string &text)
|
||||
{
|
||||
if (open_start_tag_) {
|
||||
output_ += '>';
|
||||
open_start_tag_ = false;
|
||||
}
|
||||
output_ += escape_text(text);
|
||||
}
|
||||
|
||||
void XmlStreamWriter::write_text_element(const std::string &name,
|
||||
const std::string &text)
|
||||
{
|
||||
write_start_element(name);
|
||||
write_characters(text);
|
||||
write_end_element();
|
||||
}
|
||||
|
||||
void XmlStreamWriter::write_end_element()
|
||||
{
|
||||
if (stack_.empty())
|
||||
return;
|
||||
|
||||
const std::string name = stack_.back();
|
||||
stack_.pop_back();
|
||||
|
||||
if (open_start_tag_) {
|
||||
output_ += "/>";
|
||||
open_start_tag_ = false;
|
||||
} else {
|
||||
output_ += "</";
|
||||
output_ += name;
|
||||
output_ += '>';
|
||||
}
|
||||
}
|
||||
|
||||
void XmlStreamWriter::write_end_document()
|
||||
{
|
||||
while (!stack_.empty())
|
||||
write_end_element();
|
||||
}
|
||||
|
||||
bool xml_read_next_start_element(XmlStreamReader *reader)
|
||||
{
|
||||
XmlStreamReader::TokenType token;
|
||||
|
||||
while ((token = reader->read_next()) != XmlStreamReader::Invalid &&
|
||||
token != XmlStreamReader::EndDocument) {
|
||||
if (reader->is_end_element())
|
||||
return false;
|
||||
if (reader->is_start_element())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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_XMLUTILS_H
|
||||
#define OAK_XMLUTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A single XML attribute (name/value pair).
|
||||
*
|
||||
* De-Qt replacement for QXmlStreamAttribute.
|
||||
*/
|
||||
struct XmlStreamAttribute {
|
||||
std::string name; /**< Attribute name. */
|
||||
std::string value; /**< Attribute value (entity references resolved). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Minimal pull-style streaming XML reader (de-Qt replacement for QXmlStreamReader).
|
||||
*
|
||||
* Backed by expat. The whole document is parsed at construction time into an
|
||||
* internal event list; navigation is done with read_next() and the state
|
||||
* accessors, mirroring the QXmlStreamReader call patterns used by the engine
|
||||
* (read_next_start_element loop, attribute iteration, read_element_text(),
|
||||
* skip_current_element()).
|
||||
*
|
||||
* Only the subset of XML actually used by Oak project/config files is
|
||||
* supported: elements, attributes, character data, comments and processing
|
||||
* instructions (the latter two are skipped transparently by expat handler
|
||||
* omission). Namespace processing is not performed.
|
||||
*/
|
||||
class XmlStreamReader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Token types, mirroring QXmlStreamReader::TokenType subset.
|
||||
*/
|
||||
enum TokenType {
|
||||
Invalid, /**< Parse error occurred. */
|
||||
StartElement,
|
||||
EndElement,
|
||||
Characters,
|
||||
EndDocument
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a reader over a complete XML document.
|
||||
*
|
||||
* @param data The full XML text. Parse errors are reported via
|
||||
* has_error()/error_string() and make read_next() return Invalid.
|
||||
*/
|
||||
explicit XmlStreamReader(const std::string &data);
|
||||
|
||||
/**
|
||||
* @brief Advance to the next token.
|
||||
*
|
||||
* @return The new current token type.
|
||||
*/
|
||||
TokenType read_next();
|
||||
|
||||
TokenType token_type() const { return token_; }
|
||||
bool is_start_element() const { return token_ == StartElement; }
|
||||
bool is_end_element() const { return token_ == EndElement; }
|
||||
bool is_characters() const { return token_ == Characters; }
|
||||
|
||||
/**
|
||||
* @brief Name of the current StartElement/EndElement token.
|
||||
*/
|
||||
const std::string &name() const { return name_; }
|
||||
|
||||
/**
|
||||
* @brief Text of the current Characters token.
|
||||
*/
|
||||
const std::string &text() const { return text_; }
|
||||
|
||||
/**
|
||||
* @brief Attributes of the current StartElement token.
|
||||
*/
|
||||
const std::vector<XmlStreamAttribute> &attributes() const { return attributes_; }
|
||||
|
||||
/**
|
||||
* @brief Read the concatenated character data of the current element.
|
||||
*
|
||||
* Must be called on a StartElement token. Consumes tokens up to and
|
||||
* including the matching EndElement. Mirrors
|
||||
* QXmlStreamReader::readElementText().
|
||||
*
|
||||
* @return The element's text content.
|
||||
*/
|
||||
std::string read_element_text();
|
||||
|
||||
/**
|
||||
* @brief Skip the current element and all of its children.
|
||||
*
|
||||
* Mirrors QXmlStreamReader::skipCurrentElement().
|
||||
*/
|
||||
void skip_current_element();
|
||||
|
||||
bool has_error() const { return has_error_; }
|
||||
const std::string &error_string() const { return error_string_; }
|
||||
|
||||
/**
|
||||
* @brief One parsed SAX event. Internal; exposed for the expat handlers.
|
||||
*/
|
||||
struct Event {
|
||||
TokenType type;
|
||||
std::string name;
|
||||
std::string text;
|
||||
std::vector<XmlStreamAttribute> attributes;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<Event> events_; /**< Parsed event list. */
|
||||
size_t pos_; /**< Index of the next event to consume. */
|
||||
TokenType token_; /**< Current token type. */
|
||||
std::string name_; /**< Current element name. */
|
||||
std::string text_; /**< Current character data. */
|
||||
std::vector<XmlStreamAttribute> attributes_; /**< Current attributes. */
|
||||
bool has_error_;
|
||||
std::string error_string_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Minimal streaming XML writer (de-Qt replacement for QXmlStreamWriter).
|
||||
*
|
||||
* Only the functions actually used by Oak serializers are provided:
|
||||
* write_start_element/write_attribute/write_characters/write_text_element/
|
||||
* write_end_element/write_end_document. Text and attribute values are escaped
|
||||
* for the five predefined XML entities.
|
||||
*/
|
||||
class XmlStreamWriter
|
||||
{
|
||||
public:
|
||||
XmlStreamWriter();
|
||||
|
||||
void write_start_element(const std::string &name);
|
||||
void write_attribute(const std::string &name, const std::string &value);
|
||||
void write_characters(const std::string &text);
|
||||
void write_text_element(const std::string &name, const std::string &text);
|
||||
void write_end_element();
|
||||
void write_end_document();
|
||||
|
||||
/**
|
||||
* @brief The document written so far.
|
||||
*/
|
||||
const std::string &output() const { return output_; }
|
||||
|
||||
private:
|
||||
std::string output_; /**< Accumulated document text. */
|
||||
std::vector<std::string> stack_; /**< Open element name stack. */
|
||||
bool open_start_tag_; /**< A '>' is pending for the open tag. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Workaround for read-next-start-element not detecting the end of a document.
|
||||
*
|
||||
* De-Qt port of the original xml_read_next_start_element(). Reads until a
|
||||
* start element is found (returns true), an end element or the end of the
|
||||
* document is reached (returns false). The old cancel_atom parameter (an
|
||||
* engine/render dependency) has been removed.
|
||||
*
|
||||
* @param reader The reader to advance.
|
||||
* @return true if positioned on a start element, false otherwise.
|
||||
*/
|
||||
bool xml_read_next_start_element(XmlStreamReader *reader);
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_XMLUTILS_H
|
||||
@@ -0,0 +1,70 @@
|
||||
# Oak - Non-Linear Video Editor
|
||||
# Copyright (C) 2026 Oak Team
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# Standalone build driver for the oakcommon module. Builds only oakcommon,
|
||||
# its tests, and the in-repo libraries it links against (olivecore,
|
||||
# ffmpeg_bridge), without configuring the heavy top-level project.
|
||||
#
|
||||
# Usage (macOS/Homebrew):
|
||||
# cmake -S src/common/standalone -B build-oakcommon
|
||||
# cmake --build build-oakcommon -j
|
||||
# ctest --test-dir build-oakcommon
|
||||
|
||||
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
|
||||
|
||||
project(oakcommon-standalone LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
get_filename_component(OAK_REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOLUTE)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${OAK_REPO_ROOT}/cmake")
|
||||
if(EXISTS "/opt/homebrew")
|
||||
list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew")
|
||||
endif()
|
||||
|
||||
# Third-party dependencies. The top-level project resolves OCIO/OIIO through
|
||||
# cmake/FindOpenColorIO.cmake / FindOpenImageIO.cmake (which are .so-centric);
|
||||
# here we use the upstream CMake config packages shipped by Homebrew and map
|
||||
# them onto the variables oakcommon's CMakeLists consumes.
|
||||
find_package(EXPAT REQUIRED)
|
||||
find_package(OpenColorIO CONFIG REQUIRED)
|
||||
find_package(OpenImageIO CONFIG REQUIRED)
|
||||
|
||||
set(OCIO_LIBRARIES OpenColorIO::OpenColorIO)
|
||||
set(OCIO_INCLUDE_DIRS "")
|
||||
set(OIIO_LIBRARIES OpenImageIO::OpenImageIO)
|
||||
set(OIIO_INCLUDE_DIRS "")
|
||||
|
||||
# In-repo libraries oakcommon links against:
|
||||
# - olivecore: olive::core::Rational wraps the oakcore_* C ABI (real symbols)
|
||||
# - ffmpeg_bridge: fb_find_best_pix_fmt_of_list (real symbol)
|
||||
set(OLIVECORE_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
# The /opt/otio OpenTimelineIO dylibs use @loader_path install names that
|
||||
# break when linked from a build tree; oakcommon does not need OTIO.
|
||||
set(CMAKE_DISABLE_FIND_PACKAGE_OpenTimelineIO ON)
|
||||
|
||||
add_subdirectory(${OAK_REPO_ROOT}/core ${CMAKE_BINARY_DIR}/core)
|
||||
# The top-level project puts third_party/openfx/include on the global
|
||||
# include path; replicate it here for olivecore's pixelformat.h (ofxCore.h).
|
||||
target_include_directories(olivecore PUBLIC ${OAK_REPO_ROOT}/third_party/openfx/include)
|
||||
add_subdirectory(${OAK_REPO_ROOT}/ffmpeg_bridge ${CMAKE_BINARY_DIR}/ffmpeg_bridge)
|
||||
|
||||
set(BUILD_TESTING ON)
|
||||
enable_testing()
|
||||
add_subdirectory(${OAK_REPO_ROOT}/src/common ${CMAKE_BINARY_DIR}/common)
|
||||
@@ -0,0 +1,44 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 Olive Team
|
||||
# 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/>.
|
||||
|
||||
find_package(GTest REQUIRED)
|
||||
include(GoogleTest)
|
||||
|
||||
add_executable(oakcommon-gtest
|
||||
commandlineparser_test.cpp
|
||||
current_test.cpp
|
||||
debug_test.cpp
|
||||
dropworkflowbehavior_test.cpp
|
||||
ffmpegutils_test.cpp
|
||||
filefunctions_test.cpp
|
||||
memorypool_test.cpp
|
||||
miscutils_test.cpp
|
||||
ocioutils_test.cpp
|
||||
oiioutils_test.cpp
|
||||
power_test.cpp
|
||||
qtutils_test.cpp
|
||||
threadsafemap_test.cpp
|
||||
xmlutils_test.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(oakcommon-gtest PRIVATE
|
||||
oakcommon
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
gtest_discover_tests(oakcommon-gtest)
|
||||
@@ -0,0 +1,275 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/commandlineparser.h"
|
||||
|
||||
TEST(CommandLineParser, InitFree)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, FreeNull)
|
||||
{
|
||||
oakcommon_commandlineparser_free(nullptr);
|
||||
oakcommon_commandlineoption_free(nullptr);
|
||||
oakcommon_commandlinepositionalargument_free(nullptr);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, AddOptionInvalidArgs)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
const char *names[] = { "h", "-help" };
|
||||
OakCommonCommandLineOption *option = nullptr;
|
||||
|
||||
EXPECT_EQ(oakcommon_commandlineparser_add_option(
|
||||
nullptr, names, 2, "desc", 0, nullptr, 0, &option),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, nullptr, 2, "desc", 0, nullptr, 0, &option),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, names, 0, "desc", 0, nullptr, 0, &option),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, ProcessOptionHitAndMiss)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
const char *names[] = { "h", "-help" };
|
||||
OakCommonCommandLineOption *hit = nullptr;
|
||||
OakCommonCommandLineOption *miss = nullptr;
|
||||
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, names, 2, "Show help", 0, nullptr, 0, &hit),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_NE(hit, nullptr);
|
||||
|
||||
const char *other_names[] = { "v", "-version" };
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, other_names, 2, "Show version", 0, nullptr, 0, &miss),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_NE(miss, nullptr);
|
||||
|
||||
const char *argv[] = { "oak", "-h" };
|
||||
ASSERT_EQ(oakcommon_commandlineparser_process(parser, argv, 2),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
bool is_set = true;
|
||||
ASSERT_EQ(oakcommon_commandlineoption_is_set(hit, &is_set), OAKCOMMON_OK);
|
||||
EXPECT_TRUE(is_set);
|
||||
|
||||
is_set = true;
|
||||
ASSERT_EQ(oakcommon_commandlineoption_is_set(miss, &is_set), OAKCOMMON_OK);
|
||||
EXPECT_FALSE(is_set);
|
||||
|
||||
oakcommon_commandlineoption_free(hit);
|
||||
oakcommon_commandlineoption_free(miss);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, ProcessMatchesSecondAliasCaseInsensitive)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
const char *names[] = { "h", "-help" };
|
||||
OakCommonCommandLineOption *option = nullptr;
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, names, 2, "Show help", 0, nullptr, 0, &option),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
// Only the first dash is stripped; matching is case-insensitive
|
||||
const char *argv[] = { "oak", "--HELP" };
|
||||
ASSERT_EQ(oakcommon_commandlineparser_process(parser, argv, 2),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
bool is_set = false;
|
||||
ASSERT_EQ(oakcommon_commandlineoption_is_set(option, &is_set),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_TRUE(is_set);
|
||||
|
||||
oakcommon_commandlineoption_free(option);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, OptionTakesArg)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
const char *names[] = { "e", "-export" };
|
||||
OakCommonCommandLineOption *option = nullptr;
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, names, 2, "Export", 1, "filename", 0, &option),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
const char *argv[] = { "oak", "-e", "/tmp/out.mp4" };
|
||||
ASSERT_EQ(oakcommon_commandlineparser_process(parser, argv, 3),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
bool is_set = false;
|
||||
ASSERT_EQ(oakcommon_commandlineoption_is_set(option, &is_set),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_TRUE(is_set);
|
||||
|
||||
int required =
|
||||
oakcommon_commandlineoption_get_setting(option, nullptr, 0);
|
||||
ASSERT_GT(required, 0);
|
||||
EXPECT_EQ(required, static_cast<int>(strlen("/tmp/out.mp4")) + 1);
|
||||
|
||||
std::vector<char> buf(static_cast<size_t>(required));
|
||||
ASSERT_EQ(oakcommon_commandlineoption_get_setting(option, buf.data(),
|
||||
required),
|
||||
required);
|
||||
EXPECT_STREQ(buf.data(), "/tmp/out.mp4");
|
||||
|
||||
oakcommon_commandlineoption_free(option);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, PositionalArgument)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
OakCommonCommandLinePositionalArgument *arg = nullptr;
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_positional_argument(
|
||||
parser, "project", "Project file", 0, &arg),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_NE(arg, nullptr);
|
||||
|
||||
const char *argv[] = { "oak", "/tmp/project.ove" };
|
||||
ASSERT_EQ(oakcommon_commandlineparser_process(parser, argv, 2),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
int required =
|
||||
oakcommon_commandlinepositionalargument_get_setting(arg, nullptr, 0);
|
||||
ASSERT_EQ(required, static_cast<int>(strlen("/tmp/project.ove")) + 1);
|
||||
|
||||
std::vector<char> buf(static_cast<size_t>(required));
|
||||
ASSERT_EQ(oakcommon_commandlinepositionalargument_get_setting(
|
||||
arg, buf.data(), required),
|
||||
required);
|
||||
EXPECT_STREQ(buf.data(), "/tmp/project.ove");
|
||||
|
||||
oakcommon_commandlinepositionalargument_free(arg);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, PositionalArgumentSetGetSetting)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
OakCommonCommandLinePositionalArgument *arg = nullptr;
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_positional_argument(
|
||||
parser, "project", "Project file", 0, &arg),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_NE(arg, nullptr);
|
||||
|
||||
ASSERT_EQ(oakcommon_commandlinepositionalargument_set_setting(arg,
|
||||
"hello.ove"),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
char buf[64];
|
||||
int required =
|
||||
oakcommon_commandlinepositionalargument_get_setting(arg, buf,
|
||||
sizeof(buf));
|
||||
EXPECT_EQ(required, static_cast<int>(strlen("hello.ove")) + 1);
|
||||
EXPECT_STREQ(buf, "hello.ove");
|
||||
|
||||
// Error paths
|
||||
EXPECT_EQ(oakcommon_commandlinepositionalargument_set_setting(nullptr,
|
||||
"x"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_commandlinepositionalargument_set_setting(arg,
|
||||
nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_commandlinepositionalargument_get_setting(nullptr,
|
||||
buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_commandlinepositionalargument_free(arg);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, TwoStageStringGetterSmallBuffer)
|
||||
{
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
|
||||
const char *names[] = { "e" };
|
||||
OakCommonCommandLineOption *option = nullptr;
|
||||
ASSERT_EQ(oakcommon_commandlineparser_add_option(
|
||||
parser, names, 1, "Export", 1, "filename", 0, &option),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_EQ(oakcommon_commandlineoption_set_setting(option, "abcdef"),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
// Buffer too small: return value reports the required size (7) and
|
||||
// the written contents are truncated but NUL-terminated
|
||||
char buf[4];
|
||||
int required = oakcommon_commandlineoption_get_setting(option, buf,
|
||||
sizeof(buf));
|
||||
EXPECT_EQ(required, 7);
|
||||
EXPECT_STREQ(buf, "abc");
|
||||
|
||||
// NULL buffer / zero size only reports the required size
|
||||
EXPECT_EQ(oakcommon_commandlineoption_get_setting(option, nullptr, 0), 7);
|
||||
|
||||
// Error paths
|
||||
EXPECT_EQ(oakcommon_commandlineoption_get_setting(nullptr, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_commandlineoption_is_set(option, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
bool dummy = false;
|
||||
EXPECT_EQ(oakcommon_commandlineoption_is_set(nullptr, &dummy),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_commandlineoption_free(option);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
|
||||
TEST(CommandLineParser, ProcessInvalidArgs)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_commandlineparser_process(nullptr, nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
OakCommonCommandLineParser *parser = oakcommon_commandlineparser_init();
|
||||
ASSERT_NE(parser, nullptr);
|
||||
EXPECT_EQ(oakcommon_commandlineparser_process(parser, nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
oakcommon_commandlineparser_free(parser);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/current.h"
|
||||
|
||||
TEST(OakCommonCurrent, InstanceIsSingleton)
|
||||
{
|
||||
OakCommonCurrent *a = oakcommon_current_instance();
|
||||
OakCommonCurrent *b = oakcommon_current_instance();
|
||||
|
||||
ASSERT_NE(a, nullptr);
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
||||
|
||||
TEST(OakCommonCurrent, FreeNullIsNoOp)
|
||||
{
|
||||
oakcommon_current_free(nullptr);
|
||||
oakcommon_current_free(oakcommon_current_instance());
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST(OakCommonCurrent, VideoParamsSetGetRoundTrip)
|
||||
{
|
||||
OakCommonCurrent *c = oakcommon_current_instance();
|
||||
int *params = static_cast<int *>(malloc(sizeof(int)));
|
||||
ASSERT_NE(params, nullptr);
|
||||
*params = 42;
|
||||
|
||||
ASSERT_EQ(oakcommon_current_set_video_params(c, params, free),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
void *out = nullptr;
|
||||
ASSERT_EQ(oakcommon_current_get_video_params(c, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, params);
|
||||
EXPECT_EQ(*static_cast<int *>(out), 42);
|
||||
|
||||
// Clearing the slot must invoke the destroy callback.
|
||||
ASSERT_EQ(oakcommon_current_set_video_params(c, nullptr, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_EQ(oakcommon_current_get_video_params(c, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, nullptr);
|
||||
}
|
||||
|
||||
TEST(OakCommonCurrent, AudioParamsSetGetRoundTrip)
|
||||
{
|
||||
OakCommonCurrent *c = oakcommon_current_instance();
|
||||
int value = 7; // non-owning storage, no destroy callback
|
||||
|
||||
ASSERT_EQ(oakcommon_current_set_audio_params(c, &value, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
void *out = nullptr;
|
||||
ASSERT_EQ(oakcommon_current_get_audio_params(c, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, &value);
|
||||
|
||||
ASSERT_EQ(oakcommon_current_set_audio_params(c, nullptr, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
}
|
||||
|
||||
TEST(OakCommonCurrent, PluginHostAndCacheRoundTrip)
|
||||
{
|
||||
OakCommonCurrent *c = oakcommon_current_instance();
|
||||
int host = 1, cache = 2;
|
||||
|
||||
ASSERT_EQ(oakcommon_current_set_plugin_host(c, &host, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_EQ(oakcommon_current_set_plugin_cache(c, &cache, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
void *out = nullptr;
|
||||
ASSERT_EQ(oakcommon_current_get_plugin_host(c, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, &host);
|
||||
ASSERT_EQ(oakcommon_current_get_plugin_cache(c, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, &cache);
|
||||
|
||||
ASSERT_EQ(oakcommon_current_set_plugin_host(c, nullptr, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_EQ(oakcommon_current_set_plugin_cache(c, nullptr, nullptr),
|
||||
OAKCOMMON_OK);
|
||||
}
|
||||
|
||||
TEST(OakCommonCurrent, NullHandleAndOutArgs)
|
||||
{
|
||||
void *out = nullptr;
|
||||
int flag = 0;
|
||||
|
||||
EXPECT_EQ(oakcommon_current_set_video_params(nullptr, &flag, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_current_get_video_params(nullptr, &out),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_current_get_video_params(
|
||||
oakcommon_current_instance(), nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_current_is_interactive(nullptr, &flag),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_current_is_interactive(
|
||||
oakcommon_current_instance(), nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonCurrent, IsInteractive)
|
||||
{
|
||||
int flag = 0;
|
||||
|
||||
ASSERT_EQ(oakcommon_current_is_interactive(
|
||||
oakcommon_current_instance(), &flag),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(flag, 1);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
TEST(OakCommonDebug, LogValidMessage)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_debug_log(OAKCOMMON_DEBUG_WARNING, "hello"),
|
||||
OAKCOMMON_OK);
|
||||
}
|
||||
|
||||
TEST(OakCommonDebug, LogNullMessage)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_debug_log(OAKCOMMON_DEBUG_WARNING, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonDebug, LogOutOfRangeLevel)
|
||||
{
|
||||
// Out-of-range levels are tolerated and print as UNKNOWN.
|
||||
EXPECT_EQ(oakcommon_debug_log(999, "odd level"), OAKCOMMON_OK);
|
||||
}
|
||||
|
||||
TEST(OakCommonDebug, LevelNameRoundTrip)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
int needed = oakcommon_debug_level_name(OAKCOMMON_DEBUG_WARNING,
|
||||
buf, sizeof(buf));
|
||||
ASSERT_GT(needed, 0);
|
||||
ASSERT_LE(needed, (int)sizeof(buf));
|
||||
EXPECT_STREQ(buf, "WARNING");
|
||||
}
|
||||
|
||||
TEST(OakCommonDebug, LevelNameQuerySize)
|
||||
{
|
||||
int needed = oakcommon_debug_level_name(OAKCOMMON_DEBUG_DEBUG,
|
||||
nullptr, 0);
|
||||
EXPECT_EQ(needed, 6); // "DEBUG" + NUL
|
||||
}
|
||||
|
||||
TEST(OakCommonDebug, LevelNameUnknownLevel)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
int needed = oakcommon_debug_level_name(-1, buf, sizeof(buf));
|
||||
ASSERT_GT(needed, 0);
|
||||
ASSERT_LE(needed, (int)sizeof(buf));
|
||||
EXPECT_STREQ(buf, "UNKNOWN");
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/dropworkflowbehavior.h"
|
||||
|
||||
TEST(OakCommonDropWorkflowBehavior, IsValidAcceptsAllEnumerators)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_drop_workflow_behavior_is_valid(OAKCOMMON_DWS_ASK),
|
||||
1);
|
||||
EXPECT_EQ(oakcommon_drop_workflow_behavior_is_valid(OAKCOMMON_DWS_AUTO),
|
||||
1);
|
||||
EXPECT_EQ(
|
||||
oakcommon_drop_workflow_behavior_is_valid(OAKCOMMON_DWS_MANUAL),
|
||||
1);
|
||||
EXPECT_EQ(
|
||||
oakcommon_drop_workflow_behavior_is_valid(OAKCOMMON_DWS_DISABLE),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST(OakCommonDropWorkflowBehavior, IsValidRejectsOutOfRange)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_drop_workflow_behavior_is_valid(-1), 0);
|
||||
EXPECT_EQ(oakcommon_drop_workflow_behavior_is_valid(4), 0);
|
||||
}
|
||||
|
||||
TEST(OakCommonDropWorkflowBehavior, NameRoundTrip)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
int needed = oakcommon_drop_workflow_behavior_name(OAKCOMMON_DWS_AUTO,
|
||||
buf, sizeof(buf));
|
||||
ASSERT_GT(needed, 0);
|
||||
ASSERT_LE(needed, (int)sizeof(buf));
|
||||
EXPECT_STREQ(buf, "AUTO");
|
||||
}
|
||||
|
||||
TEST(OakCommonDropWorkflowBehavior, NameQuerySizeAndTooSmallBuffer)
|
||||
{
|
||||
int needed = oakcommon_drop_workflow_behavior_name(OAKCOMMON_DWS_DISABLE,
|
||||
nullptr, 0);
|
||||
EXPECT_EQ(needed, 8); // "DISABLE" + NUL
|
||||
|
||||
char buf[4];
|
||||
EXPECT_EQ(oakcommon_drop_workflow_behavior_name(OAKCOMMON_DWS_DISABLE,
|
||||
buf, sizeof(buf)),
|
||||
needed);
|
||||
}
|
||||
|
||||
TEST(OakCommonDropWorkflowBehavior, NameInvalidValue)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
int needed = oakcommon_drop_workflow_behavior_name(99, buf, sizeof(buf));
|
||||
ASSERT_GT(needed, 0);
|
||||
ASSERT_LE(needed, (int)sizeof(buf));
|
||||
EXPECT_STREQ(buf, "UNKNOWN");
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ffmpeg_bridge/ffmpeg_bridge.h>
|
||||
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
#include "../src/ffmpegutils.h"
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetCompatiblePixelFormatMapsCorrectly)
|
||||
{
|
||||
int out = -2;
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_compatible_pixel_format(
|
||||
olive::core::PixelFormat::u8, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::PixelFormat::u8);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_compatible_pixel_format(
|
||||
olive::core::PixelFormat::u10, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::PixelFormat::u8);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_compatible_pixel_format(
|
||||
olive::core::PixelFormat::f32, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::PixelFormat::u16);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_compatible_pixel_format(
|
||||
olive::core::PixelFormat::invalid, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::PixelFormat::invalid);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetCompatiblePixelFormatNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_compatible_pixel_format(
|
||||
olive::core::PixelFormat::u8, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetFFmpegPixelFormatMapsCorrectly)
|
||||
{
|
||||
int out = -2;
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_pixel_format(
|
||||
olive::core::PixelFormat::u8,
|
||||
OAKCOMMON_RGBA_CHANNEL_COUNT, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_rgba);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_pixel_format(
|
||||
olive::core::PixelFormat::u8,
|
||||
OAKCOMMON_RGB_CHANNEL_COUNT, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_rg_b24);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_pixel_format(
|
||||
olive::core::PixelFormat::u16,
|
||||
OAKCOMMON_RGBA_CHANNEL_COUNT, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_rgb_a64_le);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_pixel_format(
|
||||
olive::core::PixelFormat::invalid,
|
||||
OAKCOMMON_RGBA_CHANNEL_COUNT, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_none);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_pixel_format(
|
||||
olive::core::PixelFormat::u8, 2, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_none);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetFFmpegPixelFormatNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_pixel_format(
|
||||
olive::core::PixelFormat::u8,
|
||||
OAKCOMMON_RGBA_CHANNEL_COUNT, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetNativeSampleFormatMapsCorrectly)
|
||||
{
|
||||
int out = -2;
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_native_sample_format(
|
||||
fb_sample_fmt_u8, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::SampleFormat::u8);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_native_sample_format(
|
||||
fb_sample_fmt_fltp, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::SampleFormat::f32_p);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_native_sample_format(
|
||||
fb_sample_fmt_dblp, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::SampleFormat::f64_p);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_native_sample_format(
|
||||
fb_sample_fmt_none, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, olive::core::SampleFormat::invalid);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetNativeSampleFormatNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_native_sample_format(
|
||||
fb_sample_fmt_u8, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetFFmpegSampleFormatMapsCorrectly)
|
||||
{
|
||||
int out = -2;
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_sample_format(
|
||||
olive::core::SampleFormat::u8, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_sample_fmt_u8);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_sample_format(
|
||||
olive::core::SampleFormat::f32_p, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_sample_fmt_fltp);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_sample_format(
|
||||
olive::core::SampleFormat::f64_p, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_sample_fmt_dblp);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_sample_format(
|
||||
olive::core::SampleFormat::invalid, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_sample_fmt_none);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetFFmpegSampleFormatNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_ffmpeg_sample_format(
|
||||
olive::core::SampleFormat::u8, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, ConvertJpegSpaceToRegularSpaceMapsCorrectly)
|
||||
{
|
||||
int out = -2;
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(
|
||||
fb_pix_fmt_yuv_j420_p, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_yu_v420_p);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(
|
||||
fb_pix_fmt_yuv_j444_p, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_yu_v444_p);
|
||||
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(
|
||||
fb_pix_fmt_rgba, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, fb_pix_fmt_rgba);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, ConvertJpegSpaceToRegularSpaceNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_convert_jpeg_space_to_regular_space(
|
||||
fb_pix_fmt_rgba, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetCompatibleBridgePixelFormatNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_ffmpegutils_get_compatible_bridge_pixel_format(
|
||||
fb_pix_fmt_rgba, -1, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OakCommonFFmpegUtils, GetCompatibleBridgePixelFormatMapsCorrectly)
|
||||
{
|
||||
/* Calls fb_find_best_pix_fmt_of_list() in ffmpeg_bridge, which
|
||||
* requires a working FFmpeg runtime environment. */
|
||||
GTEST_SKIP() << "requires FFmpeg runtime environment";
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class FileFunctionsTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
handle_ = oakcommon_filefunctions_init();
|
||||
ASSERT_NE(handle_, nullptr);
|
||||
|
||||
temp_dir_ = fs::temp_directory_path() /
|
||||
fs::path("oakcommon_filefunctions_test_" +
|
||||
std::to_string(::getpid()));
|
||||
fs::remove_all(temp_dir_);
|
||||
fs::create_directories(temp_dir_);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
oakcommon_filefunctions_free(handle_);
|
||||
handle_ = nullptr;
|
||||
|
||||
std::error_code ec;
|
||||
fs::remove_all(temp_dir_, ec);
|
||||
}
|
||||
|
||||
std::string write_file(const std::string &name,
|
||||
const std::string &content)
|
||||
{
|
||||
fs::path p = temp_dir_ / name;
|
||||
fs::create_directories(p.parent_path());
|
||||
std::ofstream f(p, std::ios::binary);
|
||||
f << content;
|
||||
return p.string();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Two-stage string call helper: query size, then fetch
|
||||
*/
|
||||
template <typename Fn>
|
||||
std::string fetch_string(Fn &&fn, int *first_return)
|
||||
{
|
||||
int required = fn(nullptr, 0);
|
||||
if (first_return != nullptr) {
|
||||
*first_return = required;
|
||||
}
|
||||
EXPECT_GT(required, 0);
|
||||
if (required <= 0) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<char> buf(required);
|
||||
int second = fn(buf.data(), required);
|
||||
EXPECT_EQ(second, required);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
||||
OakCommonFileFunctions *handle_ = nullptr;
|
||||
fs::path temp_dir_;
|
||||
};
|
||||
|
||||
TEST_F(FileFunctionsTest, FreeNullIsNoOp)
|
||||
{
|
||||
// Must not crash
|
||||
oakcommon_filefunctions_free(nullptr);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, NullHandleReturnsInvalid)
|
||||
{
|
||||
char buf[16];
|
||||
int out = 0;
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_configuration_location(
|
||||
nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_application_path(nullptr, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_temp_file_path(nullptr, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_auto_recovery_root(nullptr, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_unique_file_identifier(
|
||||
nullptr, "x", buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_ensure_filename_extension(
|
||||
nullptr, "x", "y", buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_read_file_as_string(nullptr, "x",
|
||||
buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_safe_temporary_filename(
|
||||
nullptr, "x", buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_formatted_executable_for_platform(
|
||||
nullptr, "x", buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_directory_is_valid(nullptr, "x", 1,
|
||||
&out),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_can_copy_directory_without_overwriting(
|
||||
nullptr, "a", "b", &out),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_copy_directory(nullptr, "a", "b", 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_rename_file_allow_overwrite(
|
||||
nullptr, "a", "b", &out),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, NullArgumentsReturnInvalid)
|
||||
{
|
||||
char buf[16];
|
||||
int out = 0;
|
||||
EXPECT_EQ(oakcommon_filefunctions_read_file_as_string(handle_, nullptr,
|
||||
buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_directory_is_valid(handle_, nullptr,
|
||||
1, &out),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_directory_is_valid(handle_, "x", 1,
|
||||
nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_get_configuration_location(
|
||||
handle_, nullptr, -1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_filefunctions_rename_file_allow_overwrite(
|
||||
handle_, nullptr, "b", &out),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, ReadFileAsStringRoundTrip)
|
||||
{
|
||||
std::string content = "hello oak\nline two\n";
|
||||
std::string path = write_file("read.txt", content);
|
||||
|
||||
std::string result = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_read_file_as_string(
|
||||
handle_, path.c_str(), buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_EQ(result, content);
|
||||
|
||||
// Buffer too small: required size is reported, nothing written
|
||||
char tiny[2] = {'z', '\0'};
|
||||
int required = oakcommon_filefunctions_read_file_as_string(
|
||||
handle_, path.c_str(), tiny, sizeof(tiny));
|
||||
EXPECT_EQ(required, static_cast<int>(content.size()) + 1);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, ReadFileAsStringMissingFile)
|
||||
{
|
||||
std::string path = (temp_dir_ / "no_such_file.txt").string();
|
||||
char buf[8] = {};
|
||||
int required = oakcommon_filefunctions_read_file_as_string(
|
||||
handle_, path.c_str(), buf, sizeof(buf));
|
||||
// Missing file yields an empty string (required size 1)
|
||||
EXPECT_EQ(required, 1);
|
||||
EXPECT_STREQ(buf, "");
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, EnsureFilenameExtension)
|
||||
{
|
||||
std::string result = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_ensure_filename_extension(
|
||||
handle_, "project", "ove", buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_EQ(result, "project.ove");
|
||||
|
||||
// Already present (case-insensitive): unchanged
|
||||
result = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_ensure_filename_extension(
|
||||
handle_, "project.OVE", "ove", buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_EQ(result, "project.OVE");
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, GetUniqueFileIdentifierStable)
|
||||
{
|
||||
std::string path = write_file("id.txt", "some data");
|
||||
|
||||
std::string id1 = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_unique_file_identifier(
|
||||
handle_, path.c_str(), buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_FALSE(id1.empty());
|
||||
|
||||
std::string id2 = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_unique_file_identifier(
|
||||
handle_, path.c_str(), buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_EQ(id1, id2);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, GetUniqueFileIdentifierMissingFile)
|
||||
{
|
||||
std::string path = (temp_dir_ / "missing.bin").string();
|
||||
char buf[8] = {};
|
||||
int required = oakcommon_filefunctions_get_unique_file_identifier(
|
||||
handle_, path.c_str(), buf, sizeof(buf));
|
||||
EXPECT_EQ(required, 1);
|
||||
EXPECT_STREQ(buf, "");
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, DirectoryIsValidCreateAndCheck)
|
||||
{
|
||||
int out = 0;
|
||||
|
||||
// Existing directory
|
||||
EXPECT_EQ(oakcommon_filefunctions_directory_is_valid(
|
||||
handle_, temp_dir_.string().c_str(), 0, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1);
|
||||
|
||||
// Non-existing directory, allowed to create
|
||||
std::string sub = (temp_dir_ / "a" / "b").string();
|
||||
EXPECT_EQ(oakcommon_filefunctions_directory_is_valid(
|
||||
handle_, sub.c_str(), 1, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1);
|
||||
EXPECT_TRUE(fs::is_directory(sub));
|
||||
|
||||
// Non-existing directory, not allowed to create
|
||||
std::string sub2 = (temp_dir_ / "c").string();
|
||||
EXPECT_EQ(oakcommon_filefunctions_directory_is_valid(
|
||||
handle_, sub2.c_str(), 0, &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 0);
|
||||
EXPECT_FALSE(fs::exists(sub2));
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, GetSafeTemporaryFilenameDoesNotExist)
|
||||
{
|
||||
std::string base = write_file("safe.ove", "data");
|
||||
|
||||
std::string temp = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_safe_temporary_filename(
|
||||
handle_, base.c_str(), buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_FALSE(temp.empty());
|
||||
EXPECT_FALSE(fs::exists(temp));
|
||||
EXPECT_NE(temp, base);
|
||||
EXPECT_TRUE(temp.find(".tmp") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, RenameFileAllowOverwrite)
|
||||
{
|
||||
std::string from = write_file("from.txt", "new content");
|
||||
std::string to = write_file("to.txt", "old content");
|
||||
|
||||
int out = 0;
|
||||
EXPECT_EQ(oakcommon_filefunctions_rename_file_allow_overwrite(
|
||||
handle_, from.c_str(), to.c_str(), &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1);
|
||||
EXPECT_FALSE(fs::exists(from));
|
||||
|
||||
std::ifstream f(to, std::ios::binary);
|
||||
std::string content((std::istreambuf_iterator<char>(f)),
|
||||
std::istreambuf_iterator<char>());
|
||||
EXPECT_EQ(content, "new content");
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, RenameFileAllowOverwriteMissingSource)
|
||||
{
|
||||
std::string from = (temp_dir_ / "no_source.txt").string();
|
||||
std::string to = (temp_dir_ / "target.txt").string();
|
||||
|
||||
int out = 1;
|
||||
EXPECT_EQ(oakcommon_filefunctions_rename_file_allow_overwrite(
|
||||
handle_, from.c_str(), to.c_str(), &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 0);
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, CopyDirectoryAndOverwriteCheck)
|
||||
{
|
||||
write_file("src/one.txt", "one");
|
||||
write_file("src/nested/two.txt", "two");
|
||||
std::string src = (temp_dir_ / "src").string();
|
||||
std::string dst = (temp_dir_ / "dst").string();
|
||||
|
||||
// Nothing at dest: safe to copy
|
||||
int out = 0;
|
||||
EXPECT_EQ(oakcommon_filefunctions_can_copy_directory_without_overwriting(
|
||||
handle_, src.c_str(), dst.c_str(), &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1);
|
||||
|
||||
EXPECT_EQ(oakcommon_filefunctions_copy_directory(handle_, src.c_str(),
|
||||
dst.c_str(), 0),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_TRUE(fs::exists(temp_dir_ / "dst" / "one.txt"));
|
||||
EXPECT_TRUE(fs::exists(temp_dir_ / "dst" / "nested" / "two.txt"));
|
||||
|
||||
// Files exist at dest now: no longer safe
|
||||
EXPECT_EQ(oakcommon_filefunctions_can_copy_directory_without_overwriting(
|
||||
handle_, src.c_str(), dst.c_str(), &out),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 0);
|
||||
|
||||
// Read back copied content through the C API
|
||||
std::string copied = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_read_file_as_string(
|
||||
handle_,
|
||||
(temp_dir_ / "dst" / "nested" / "two.txt")
|
||||
.string()
|
||||
.c_str(),
|
||||
buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_EQ(copied, "two");
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, CopyDirectoryMissingSource)
|
||||
{
|
||||
std::string src = (temp_dir_ / "no_such_dir").string();
|
||||
std::string dst = (temp_dir_ / "dst2").string();
|
||||
|
||||
// Underlying call logs and returns without copying; must not crash
|
||||
EXPECT_EQ(oakcommon_filefunctions_copy_directory(handle_, src.c_str(),
|
||||
dst.c_str(), 0),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_FALSE(fs::exists(temp_dir_ / "dst2" / "anything"));
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, LocationGettersReturnNonEmpty)
|
||||
{
|
||||
int first_return = 0;
|
||||
|
||||
std::string config = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_configuration_location(
|
||||
handle_, buf, size);
|
||||
},
|
||||
&first_return);
|
||||
EXPECT_FALSE(config.empty());
|
||||
EXPECT_EQ(first_return, static_cast<int>(config.size()) + 1);
|
||||
|
||||
std::string temp = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_temp_file_path(
|
||||
handle_, buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_FALSE(temp.empty());
|
||||
EXPECT_TRUE(fs::is_directory(temp));
|
||||
|
||||
std::string app = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_application_path(
|
||||
handle_, buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_FALSE(app.empty());
|
||||
|
||||
std::string recovery = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_auto_recovery_root(
|
||||
handle_, buf, size);
|
||||
},
|
||||
nullptr);
|
||||
EXPECT_FALSE(recovery.empty());
|
||||
}
|
||||
|
||||
TEST_F(FileFunctionsTest, GetFormattedExecutableForPlatform)
|
||||
{
|
||||
std::string result = fetch_string(
|
||||
[&](char *buf, int size) {
|
||||
return oakcommon_filefunctions_get_formatted_executable_for_platform(
|
||||
handle_, "oak", buf, size);
|
||||
},
|
||||
nullptr);
|
||||
#ifdef _WIN32
|
||||
EXPECT_EQ(result, "oak.exe");
|
||||
#else
|
||||
EXPECT_EQ(result, "oak");
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../src/memorypool.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Test pool with a fixed 16-byte element size
|
||||
*/
|
||||
class TestPool : public olive::MemoryPool {
|
||||
public:
|
||||
TestPool(int element_count) : MemoryPool(element_count)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t get_element_size() override
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(MemoryPool, AllocateReturnsWritableElement)
|
||||
{
|
||||
TestPool pool(4);
|
||||
|
||||
auto e = pool.get();
|
||||
ASSERT_NE(e, nullptr);
|
||||
ASSERT_NE(e->data(), nullptr);
|
||||
|
||||
// The full element must be writable
|
||||
memset(e->data(), 0xAB, 16);
|
||||
EXPECT_EQ(e->data()[0], 0xAB);
|
||||
EXPECT_EQ(e->data()[15], 0xAB);
|
||||
|
||||
EXPECT_TRUE(pool.is_allocated());
|
||||
EXPECT_EQ(pool.get_arena_count(), 1);
|
||||
}
|
||||
|
||||
TEST(MemoryPool, ReleasedElementIsReused)
|
||||
{
|
||||
TestPool pool(4);
|
||||
|
||||
uint8_t *first = nullptr;
|
||||
{
|
||||
auto e = pool.get();
|
||||
ASSERT_NE(e, nullptr);
|
||||
first = e->data();
|
||||
}
|
||||
|
||||
// After the ElementPtr went out of scope the chunk must be recycled
|
||||
auto e2 = pool.get();
|
||||
ASSERT_NE(e2, nullptr);
|
||||
EXPECT_EQ(e2->data(), first);
|
||||
EXPECT_EQ(pool.get_arena_count(), 1);
|
||||
}
|
||||
|
||||
TEST(MemoryPool, FullArenaGrowsNewArena)
|
||||
{
|
||||
TestPool pool(2);
|
||||
|
||||
auto e1 = pool.get();
|
||||
auto e2 = pool.get();
|
||||
ASSERT_NE(e1, nullptr);
|
||||
ASSERT_NE(e2, nullptr);
|
||||
EXPECT_EQ(pool.get_arena_count(), 1);
|
||||
|
||||
// Both slots lent out, next get() must allocate a second arena
|
||||
auto e3 = pool.get();
|
||||
ASSERT_NE(e3, nullptr);
|
||||
EXPECT_EQ(pool.get_arena_count(), 2);
|
||||
}
|
||||
|
||||
TEST(MemoryPool, ClearFreesAllArenas)
|
||||
{
|
||||
TestPool pool(4);
|
||||
|
||||
auto e = pool.get();
|
||||
ASSERT_NE(e, nullptr);
|
||||
e->release();
|
||||
e.reset();
|
||||
|
||||
pool.clear();
|
||||
EXPECT_FALSE(pool.is_allocated());
|
||||
EXPECT_EQ(pool.get_arena_count(), 0);
|
||||
}
|
||||
|
||||
TEST(MemoryPool, InvalidElementCountReturnsNull)
|
||||
{
|
||||
TestPool pool(0);
|
||||
EXPECT_EQ(pool.get(), nullptr);
|
||||
|
||||
TestPool negative(-1);
|
||||
EXPECT_EQ(negative.get(), nullptr);
|
||||
}
|
||||
|
||||
TEST(MemoryPool, ClearEmptyArenasKeepsRecentlyEmptiedArena)
|
||||
{
|
||||
TestPool pool(2);
|
||||
|
||||
{
|
||||
auto e = pool.get();
|
||||
ASSERT_NE(e, nullptr);
|
||||
}
|
||||
|
||||
// Arena was emptied just now, well within kMaxEmptyArenaLife, so it must survive
|
||||
pool.clear_empty_arenas();
|
||||
EXPECT_EQ(pool.get_arena_count(), 1);
|
||||
}
|
||||
|
||||
TEST(MemoryPool, ClearEmptyArenasKeepsArenaInUse)
|
||||
{
|
||||
TestPool pool(2);
|
||||
|
||||
auto e = pool.get();
|
||||
ASSERT_NE(e, nullptr);
|
||||
|
||||
pool.clear_empty_arenas();
|
||||
EXPECT_EQ(pool.get_arena_count(), 1);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/miscutils.h"
|
||||
|
||||
TEST(MiscUtilsDecibel, FromLinearZero)
|
||||
{
|
||||
double db;
|
||||
ASSERT_EQ(oakcommon_decibel_from_linear(0.0, &db), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(db, OAKCOMMON_DECIBEL_MINIMUM);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, FromLinearNegative)
|
||||
{
|
||||
double db;
|
||||
ASSERT_EQ(oakcommon_decibel_from_linear(-1.0, &db), OAKCOMMON_OK);
|
||||
// log10 of a negative value is NaN, not clamped to minimum
|
||||
EXPECT_TRUE(std::isnan(db));
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, FromLinearOne)
|
||||
{
|
||||
double db;
|
||||
ASSERT_EQ(oakcommon_decibel_from_linear(1.0, &db), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(db, 0.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, FromLinearNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_decibel_from_linear(1.0, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, ToLinearZeroDb)
|
||||
{
|
||||
double linear;
|
||||
ASSERT_EQ(oakcommon_decibel_to_linear(0.0, &linear), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(linear, 1.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, ToLinearMinimum)
|
||||
{
|
||||
double linear;
|
||||
ASSERT_EQ(oakcommon_decibel_to_linear(OAKCOMMON_DECIBEL_MINIMUM,
|
||||
&linear),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(linear, 0.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, ToLinearNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_decibel_to_linear(0.0, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, LinearRoundTrip)
|
||||
{
|
||||
double db;
|
||||
double linear;
|
||||
ASSERT_EQ(oakcommon_decibel_from_linear(0.5, &db), OAKCOMMON_OK);
|
||||
ASSERT_EQ(oakcommon_decibel_to_linear(db, &linear), OAKCOMMON_OK);
|
||||
EXPECT_NEAR(linear, 0.5, 1e-12);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, FromLogarithmicBounds)
|
||||
{
|
||||
double db;
|
||||
ASSERT_EQ(oakcommon_decibel_from_logarithmic(0.0, &db), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(db, OAKCOMMON_DECIBEL_MINIMUM);
|
||||
|
||||
ASSERT_EQ(oakcommon_decibel_from_logarithmic(1.0, &db), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(db, 0.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, FromLogarithmicNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_decibel_from_logarithmic(0.5, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, ToLogarithmicZeroDb)
|
||||
{
|
||||
double logarithmic;
|
||||
ASSERT_EQ(oakcommon_decibel_to_logarithmic(0.0, &logarithmic),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(logarithmic, 1.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, ToLogarithmicNullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_decibel_to_logarithmic(0.0, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, LogarithmicRoundTrip)
|
||||
{
|
||||
double logarithmic;
|
||||
double linear;
|
||||
ASSERT_EQ(oakcommon_decibel_linear_to_logarithmic(0.5, &logarithmic),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_EQ(oakcommon_decibel_logarithmic_to_linear(logarithmic, &linear),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_NEAR(linear, 0.5, 1e-12);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsDecibel, LogarithmicDirectNullOut)
|
||||
{
|
||||
double out;
|
||||
EXPECT_EQ(oakcommon_decibel_linear_to_logarithmic(0.5, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_decibel_logarithmic_to_linear(0.5, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
(void)out;
|
||||
}
|
||||
|
||||
TEST(MiscUtilsLerp, Endpoints)
|
||||
{
|
||||
double value;
|
||||
ASSERT_EQ(oakcommon_lerp(2.0, 8.0, 0.0, &value), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(value, 2.0);
|
||||
|
||||
ASSERT_EQ(oakcommon_lerp(2.0, 8.0, 1.0, &value), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(value, 8.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsLerp, Midpoint)
|
||||
{
|
||||
double value;
|
||||
ASSERT_EQ(oakcommon_lerp(2.0, 8.0, 0.5, &value), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(value, 5.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsLerp, NegativeValues)
|
||||
{
|
||||
double value;
|
||||
ASSERT_EQ(oakcommon_lerp(-10.0, 10.0, 0.25, &value), OAKCOMMON_OK);
|
||||
EXPECT_DOUBLE_EQ(value, -5.0);
|
||||
}
|
||||
|
||||
TEST(MiscUtilsLerp, NullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_lerp(0.0, 1.0, 0.5, nullptr), OAKCOMMON_E_INVALID);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace ocio = OCIO_NAMESPACE;
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
|
||||
/**
|
||||
* @note The whole oakcommon ocioutils family links against OpenColorIO;
|
||||
* if the test binary is built without OCIO these tests cannot run at
|
||||
* all. The mapping itself is pure logic and needs no OCIO config or GPU,
|
||||
* so no GTEST_SKIP() is required at runtime.
|
||||
*/
|
||||
|
||||
TEST(OCIOUtilsCApi, InitReturnsHandle)
|
||||
{
|
||||
OakCommonOCIOUtils *utils = oakcommon_ocioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
oakcommon_ocioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OCIOUtilsCApi, FreeNullIsNoOp)
|
||||
{
|
||||
oakcommon_ocioutils_free(NULL);
|
||||
}
|
||||
|
||||
TEST(OCIOUtilsCApi, BitDepthMappingMatchesOCIO)
|
||||
{
|
||||
OakCommonOCIOUtils *utils = oakcommon_ocioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
const struct {
|
||||
int pixel_format;
|
||||
ocio::BitDepth expected;
|
||||
} cases[] = {
|
||||
{ OAKCOMMON_PIXEL_FORMAT_U8, ocio::BIT_DEPTH_UINT8 },
|
||||
{ OAKCOMMON_PIXEL_FORMAT_U10, ocio::BIT_DEPTH_UINT10 },
|
||||
{ OAKCOMMON_PIXEL_FORMAT_U16, ocio::BIT_DEPTH_UINT16 },
|
||||
{ OAKCOMMON_PIXEL_FORMAT_F16, ocio::BIT_DEPTH_F16 },
|
||||
{ OAKCOMMON_PIXEL_FORMAT_F32, ocio::BIT_DEPTH_F32 },
|
||||
};
|
||||
|
||||
for (const auto &c : cases) {
|
||||
int depth = -1;
|
||||
EXPECT_EQ(oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
utils, c.pixel_format, &depth),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(depth, static_cast<int>(c.expected));
|
||||
}
|
||||
|
||||
oakcommon_ocioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OCIOUtilsCApi, InvalidFormatYieldsUnknownDepth)
|
||||
{
|
||||
OakCommonOCIOUtils *utils = oakcommon_ocioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
int depth = -1;
|
||||
EXPECT_EQ(oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
utils, OAKCOMMON_PIXEL_FORMAT_INVALID, &depth),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(depth, static_cast<int>(ocio::BIT_DEPTH_UNKNOWN));
|
||||
|
||||
oakcommon_ocioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OCIOUtilsCApi, NullHandleReturnsInvalid)
|
||||
{
|
||||
int depth = 0;
|
||||
EXPECT_EQ(oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
NULL, OAKCOMMON_PIXEL_FORMAT_U8, &depth),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(OCIOUtilsCApi, NullOutParamReturnsInvalid)
|
||||
{
|
||||
OakCommonOCIOUtils *utils = oakcommon_ocioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
EXPECT_EQ(oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
utils, OAKCOMMON_PIXEL_FORMAT_U8, NULL),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_ocioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OCIOUtilsCApi, OutOfRangeFormatReturnsInvalid)
|
||||
{
|
||||
OakCommonOCIOUtils *utils = oakcommon_ocioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
int depth = 0;
|
||||
EXPECT_EQ(oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
utils, OAKCOMMON_PIXEL_FORMAT_COUNT, &depth),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_ocioutils_get_ocio_bit_depth_from_pixel_format(
|
||||
utils, -2, &depth),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_ocioutils_free(utils);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/oiioutils.h"
|
||||
|
||||
/**
|
||||
* @note The whole oakcommon oiioutils family links against OpenImageIO,
|
||||
* but every function exposed through the C API is pure logic (enum
|
||||
* mapping and double-to-rational conversion) that needs no image I/O,
|
||||
* codec or GPU at runtime, so no GTEST_SKIP() is required. The
|
||||
* OIIO::ImageBuf-based helpers (frame_to_buffer / buffer_to_frame) are
|
||||
* intentionally not part of the C API and are not tested here.
|
||||
*
|
||||
* OIIO TypeDesc::BASETYPE values used below: UNKNOWN = 0, UINT8 = 2,
|
||||
* UINT16 = 4, HALF = 10, FLOAT = 11, DOUBLE = 12 (LASTBASE is
|
||||
* version-dependent; 100 is safely out of range).
|
||||
*/
|
||||
|
||||
TEST(OIIOUtilsCApi, InitReturnsHandle)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, FreeNullIsNoOp)
|
||||
{
|
||||
oakcommon_oiioutils_free(NULL);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, BaseTypeFromPixelFormat)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
const struct {
|
||||
int pixel_format;
|
||||
int expected_base_type;
|
||||
} cases[] = {
|
||||
{ OAKCOMMON_PIXEL_FORMAT_U8, 2 }, /* UINT8 */
|
||||
{ OAKCOMMON_PIXEL_FORMAT_U16, 4 }, /* UINT16 */
|
||||
{ OAKCOMMON_PIXEL_FORMAT_F16, 10 }, /* HALF */
|
||||
{ OAKCOMMON_PIXEL_FORMAT_F32, 11 }, /* FLOAT */
|
||||
{ OAKCOMMON_PIXEL_FORMAT_U10, 0 }, /* UNKNOWN: unmappable */
|
||||
{ OAKCOMMON_PIXEL_FORMAT_INVALID, 0 }, /* UNKNOWN */
|
||||
};
|
||||
|
||||
for (const auto &c : cases) {
|
||||
int base_type = -1;
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
utils, c.pixel_format, &base_type),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(base_type, c.expected_base_type);
|
||||
}
|
||||
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, BaseTypeFromPixelFormatRejectsBadArgs)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
int base_type = 0;
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
NULL, OAKCOMMON_PIXEL_FORMAT_U8, &base_type),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
utils, OAKCOMMON_PIXEL_FORMAT_U8, NULL),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
utils, OAKCOMMON_PIXEL_FORMAT_COUNT, &base_type),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_oiio_base_type_from_format(
|
||||
utils, -2, &base_type),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, PixelFormatFromBaseType)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
const struct {
|
||||
int base_type;
|
||||
int expected_pixel_format;
|
||||
} cases[] = {
|
||||
{ 2, OAKCOMMON_PIXEL_FORMAT_U8 }, /* UINT8 */
|
||||
{ 4, OAKCOMMON_PIXEL_FORMAT_U16 }, /* UINT16 */
|
||||
{ 10, OAKCOMMON_PIXEL_FORMAT_F16 }, /* HALF */
|
||||
{ 11, OAKCOMMON_PIXEL_FORMAT_F32 }, /* FLOAT */
|
||||
{ 0, OAKCOMMON_PIXEL_FORMAT_INVALID }, /* UNKNOWN */
|
||||
{ 12, OAKCOMMON_PIXEL_FORMAT_INVALID }, /* DOUBLE: unmappable */
|
||||
{ 13, OAKCOMMON_PIXEL_FORMAT_INVALID }, /* STRING: unmappable */
|
||||
};
|
||||
|
||||
for (const auto &c : cases) {
|
||||
int pixel_format = -2;
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
utils, c.base_type, &pixel_format),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(pixel_format, c.expected_pixel_format);
|
||||
}
|
||||
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, PixelFormatFromBaseTypeRejectsBadArgs)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
int pixel_format = 0;
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
NULL, 2, &pixel_format),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
utils, 2, NULL),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
utils, -1, &pixel_format),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_format_from_oiio_basetype(
|
||||
utils, 100, &pixel_format),
|
||||
OAKCOMMON_E_INVALID); /* >= LASTBASE */
|
||||
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, PixelAspectRatioConvertsToRational)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
int num = 0;
|
||||
int den = 0;
|
||||
ASSERT_EQ(oakcommon_oiioutils_get_pixel_aspect_ratio(utils, 1.0, &num,
|
||||
&den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 1);
|
||||
EXPECT_EQ(den, 1);
|
||||
|
||||
ASSERT_EQ(oakcommon_oiioutils_get_pixel_aspect_ratio(utils, 1.5, &num,
|
||||
&den),
|
||||
OAKCOMMON_OK);
|
||||
ASSERT_NE(den, 0);
|
||||
EXPECT_NEAR(static_cast<double>(num) / den, 1.5, 1e-9);
|
||||
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
|
||||
TEST(OIIOUtilsCApi, PixelAspectRatioRejectsBadArgs)
|
||||
{
|
||||
OakCommonOIIOUtils *utils = oakcommon_oiioutils_init();
|
||||
ASSERT_NE(utils, nullptr);
|
||||
|
||||
int num = 0;
|
||||
int den = 0;
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_pixel_aspect_ratio(NULL, 1.0, &num,
|
||||
&den),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_pixel_aspect_ratio(utils, 1.0, NULL,
|
||||
&den),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_oiioutils_get_pixel_aspect_ratio(utils, 1.0, &num,
|
||||
NULL),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_oiioutils_free(utils);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../../include/common/power.h"
|
||||
|
||||
TEST(Power, CeilToPowerOf2)
|
||||
{
|
||||
uint32_t out = 0;
|
||||
|
||||
ASSERT_EQ(oakcommon_power_ceil_to_power_of_2(1, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1u);
|
||||
|
||||
ASSERT_EQ(oakcommon_power_ceil_to_power_of_2(2, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 2u);
|
||||
|
||||
ASSERT_EQ(oakcommon_power_ceil_to_power_of_2(3, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 4u);
|
||||
|
||||
ASSERT_EQ(oakcommon_power_ceil_to_power_of_2(1000, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1024u);
|
||||
}
|
||||
|
||||
TEST(Power, CeilToPowerOf2NullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_power_ceil_to_power_of_2(3, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(Power, FloorToPowerOf2)
|
||||
{
|
||||
uint32_t out = 0;
|
||||
|
||||
ASSERT_EQ(oakcommon_power_floor_to_power_of_2(1, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1u);
|
||||
|
||||
ASSERT_EQ(oakcommon_power_floor_to_power_of_2(3, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 2u);
|
||||
|
||||
ASSERT_EQ(oakcommon_power_floor_to_power_of_2(1024, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1024u);
|
||||
|
||||
ASSERT_EQ(oakcommon_power_floor_to_power_of_2(1025, &out), OAKCOMMON_OK);
|
||||
EXPECT_EQ(out, 1024u);
|
||||
}
|
||||
|
||||
TEST(Power, FloorToPowerOf2NullOut)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_power_floor_to_power_of_2(3, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
|
||||
TEST(QtUtils, PtrToValueRoundTrip)
|
||||
{
|
||||
int value = 42;
|
||||
uint64_t encoded = 0;
|
||||
|
||||
ASSERT_EQ(oakcommon_qtutils_ptr_to_value(&value, &encoded),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(encoded, static_cast<uint64_t>(
|
||||
reinterpret_cast<uintptr_t>(&value)));
|
||||
|
||||
void *decoded = nullptr;
|
||||
ASSERT_EQ(oakcommon_qtutils_value_to_ptr(encoded, &decoded),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(decoded, static_cast<void *>(&value));
|
||||
}
|
||||
|
||||
TEST(QtUtils, PtrToValueNullOutParam)
|
||||
{
|
||||
int value = 42;
|
||||
|
||||
EXPECT_EQ(oakcommon_qtutils_ptr_to_value(&value, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(QtUtils, ValueToPtrNullPointer)
|
||||
{
|
||||
void *decoded = reinterpret_cast<void *>(0xdeadbeef);
|
||||
|
||||
ASSERT_EQ(oakcommon_qtutils_value_to_ptr(0, &decoded), OAKCOMMON_OK);
|
||||
EXPECT_EQ(decoded, nullptr);
|
||||
}
|
||||
|
||||
TEST(QtUtils, ValueToPtrNullOutParam)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_qtutils_value_to_ptr(0, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(QtUtils, GetCreationDateExistingFile)
|
||||
{
|
||||
char path[] = "/tmp/oakcommon_qtutils_test_XXXXXX";
|
||||
int fd = mkstemp(path);
|
||||
ASSERT_NE(fd, -1);
|
||||
close(fd);
|
||||
|
||||
int64_t secs = 0;
|
||||
ASSERT_EQ(oakcommon_qtutils_get_creation_date(path, &secs),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_GT(secs, 0);
|
||||
|
||||
std::remove(path);
|
||||
}
|
||||
|
||||
TEST(QtUtils, GetCreationDateMissingFile)
|
||||
{
|
||||
int64_t secs = 0;
|
||||
|
||||
EXPECT_EQ(oakcommon_qtutils_get_creation_date(
|
||||
"/tmp/oakcommon_qtutils_no_such_file_12345", &secs),
|
||||
OAKCOMMON_E_NOT_FOUND);
|
||||
}
|
||||
|
||||
TEST(QtUtils, GetCreationDateNullArgs)
|
||||
{
|
||||
int64_t secs = 0;
|
||||
|
||||
EXPECT_EQ(oakcommon_qtutils_get_creation_date(nullptr, &secs),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_qtutils_get_creation_date("/tmp", nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "../src/threadsafemap.h"
|
||||
|
||||
TEST(ThreadSafeMap, InsertAndGet)
|
||||
{
|
||||
ThreadSafeMap<int, int> map;
|
||||
|
||||
map.insert(1, 42);
|
||||
|
||||
int value = 0;
|
||||
ASSERT_TRUE(map.get(1, &value));
|
||||
EXPECT_EQ(value, 42);
|
||||
EXPECT_TRUE(map.contains(1));
|
||||
EXPECT_EQ(map.size(), 1u);
|
||||
}
|
||||
|
||||
TEST(ThreadSafeMap, InsertOverwritesExistingKey)
|
||||
{
|
||||
ThreadSafeMap<int, int> map;
|
||||
|
||||
map.insert(1, 42);
|
||||
map.insert(1, 7);
|
||||
|
||||
int value = 0;
|
||||
ASSERT_TRUE(map.get(1, &value));
|
||||
EXPECT_EQ(value, 7);
|
||||
EXPECT_EQ(map.size(), 1u);
|
||||
}
|
||||
|
||||
TEST(ThreadSafeMap, MissingKeyReturnsFalse)
|
||||
{
|
||||
ThreadSafeMap<int, int> map;
|
||||
|
||||
EXPECT_FALSE(map.contains(99));
|
||||
|
||||
int value = 1234;
|
||||
EXPECT_FALSE(map.get(99, &value));
|
||||
// `out` must be left untouched on failure
|
||||
EXPECT_EQ(value, 1234);
|
||||
}
|
||||
|
||||
TEST(ThreadSafeMap, ConcurrentInserts)
|
||||
{
|
||||
ThreadSafeMap<int, int> map;
|
||||
const int k_threads = 8;
|
||||
const int k_inserts_per_thread = 500;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (int t = 0; t < k_threads; t++) {
|
||||
threads.emplace_back([&map, t, k_inserts_per_thread]() {
|
||||
for (int i = 0; i < k_inserts_per_thread; i++) {
|
||||
map.insert(t * k_inserts_per_thread + i, i);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (std::thread &th : threads) {
|
||||
th.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(map.size(), size_t(k_threads * k_inserts_per_thread));
|
||||
|
||||
int value = 0;
|
||||
ASSERT_TRUE(map.get(3 * k_inserts_per_thread + 123, &value));
|
||||
EXPECT_EQ(value, 123);
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/***
|
||||
|
||||
Oak Video Editor - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string read_string(int (*fn)(OakCommonXmlReader *, char *, int),
|
||||
OakCommonXmlReader *reader)
|
||||
{
|
||||
int needed = fn(reader, nullptr, 0);
|
||||
EXPECT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
EXPECT_EQ(fn(reader, buf.data(), needed), needed);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReaderInitNullData)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_xml_reader_init(nullptr), nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReaderFreeNull)
|
||||
{
|
||||
oakcommon_xml_reader_free(nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReadNextStartElement)
|
||||
{
|
||||
OakCommonXmlReader *r =
|
||||
oakcommon_xml_reader_init("<root><child>value</child></root>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "root");
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "child");
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReadNextStartElementNullHandle)
|
||||
{
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(nullptr, &found),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
OakCommonXmlReader *r = oakcommon_xml_reader_init("<root/>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReadNextStartElementReturnsFalseAtEnd)
|
||||
{
|
||||
OakCommonXmlReader *r = oakcommon_xml_reader_init("<root/>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
|
||||
int found = -1;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 0);
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, NameNullHandle)
|
||||
{
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_xml_reader_name(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReadElementText)
|
||||
{
|
||||
OakCommonXmlReader *r =
|
||||
oakcommon_xml_reader_init("<root><child>a & b</child></root>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_read_element_text, r),
|
||||
"a & b");
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, ReadElementTextNullHandle)
|
||||
{
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_element_text(nullptr, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, SkipCurrentElement)
|
||||
{
|
||||
OakCommonXmlReader *r = oakcommon_xml_reader_init(
|
||||
"<root><unknown><nested/></unknown><known/></root>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "unknown");
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_reader_skip_current_element(r), OAKCOMMON_OK);
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "known");
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, SkipCurrentElementNullHandle)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_xml_reader_skip_current_element(nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, Attributes)
|
||||
{
|
||||
OakCommonXmlReader *r = oakcommon_xml_reader_init(
|
||||
"<root><item id=\"7\" name=\"a"b\"/></root>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
|
||||
int count = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_attribute_count(r, &count), OAKCOMMON_OK);
|
||||
ASSERT_EQ(count, 2);
|
||||
|
||||
char buf[32];
|
||||
EXPECT_GT(oakcommon_xml_reader_attribute_name(r, 0, buf, sizeof(buf)), 0);
|
||||
EXPECT_STREQ(buf, "id");
|
||||
EXPECT_GT(oakcommon_xml_reader_attribute_value(r, 0, buf, sizeof(buf)),
|
||||
0);
|
||||
EXPECT_STREQ(buf, "7");
|
||||
EXPECT_GT(oakcommon_xml_reader_attribute_name(r, 1, buf, sizeof(buf)), 0);
|
||||
EXPECT_STREQ(buf, "name");
|
||||
EXPECT_GT(oakcommon_xml_reader_attribute_value(r, 1, buf, sizeof(buf)),
|
||||
0);
|
||||
EXPECT_STREQ(buf, "a\"b");
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, AttributeErrorPaths)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_xml_reader_attribute_count(nullptr, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
OakCommonXmlReader *r = oakcommon_xml_reader_init("<root a=\"1\"/>");
|
||||
ASSERT_NE(r, nullptr);
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_xml_reader_attribute_name(r, 5, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_NOT_FOUND);
|
||||
EXPECT_EQ(oakcommon_xml_reader_attribute_value(r, -1, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_NOT_FOUND);
|
||||
EXPECT_EQ(oakcommon_xml_reader_attribute_name(nullptr, 0, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, HasError)
|
||||
{
|
||||
OakCommonXmlReader *bad =
|
||||
oakcommon_xml_reader_init("<root><unclosed></root>");
|
||||
ASSERT_NE(bad, nullptr);
|
||||
int has_error = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_has_error(bad, &has_error), OAKCOMMON_OK);
|
||||
EXPECT_EQ(has_error, 1);
|
||||
oakcommon_xml_reader_free(bad);
|
||||
|
||||
OakCommonXmlReader *good = oakcommon_xml_reader_init("<root/>");
|
||||
ASSERT_NE(good, nullptr);
|
||||
EXPECT_EQ(oakcommon_xml_reader_has_error(good, &has_error), OAKCOMMON_OK);
|
||||
EXPECT_EQ(has_error, 0);
|
||||
EXPECT_EQ(oakcommon_xml_reader_has_error(nullptr, &has_error),
|
||||
OAKCOMMON_E_INVALID);
|
||||
oakcommon_xml_reader_free(good);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, WriterFreeNull)
|
||||
{
|
||||
oakcommon_xml_writer_free(nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, WriterNullHandleAndArgs)
|
||||
{
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_start_element(nullptr, "a"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_end_element(nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_end_document(nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_xml_writer_output(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
OakCommonXmlWriter *w = oakcommon_xml_writer_init();
|
||||
ASSERT_NE(w, nullptr);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_start_element(w, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_attribute(w, "a", nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_characters(w, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_text_element(w, nullptr, "x"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
oakcommon_xml_writer_free(w);
|
||||
}
|
||||
|
||||
TEST(CommonXmlUtilsCApi, WriterRoundTrip)
|
||||
{
|
||||
OakCommonXmlWriter *w = oakcommon_xml_writer_init();
|
||||
ASSERT_NE(w, nullptr);
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_start_element(w, "root"),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_attribute(w, "version", "1"),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_text_element(w, "child",
|
||||
"a & b"),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_start_element(w, "empty"),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_end_element(w), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_xml_writer_write_end_document(w), OAKCOMMON_OK);
|
||||
|
||||
int needed = oakcommon_xml_writer_output(w, nullptr, 0);
|
||||
ASSERT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
EXPECT_EQ(oakcommon_xml_writer_output(w, buf.data(), needed), needed);
|
||||
oakcommon_xml_writer_free(w);
|
||||
|
||||
// Read the produced document back.
|
||||
OakCommonXmlReader *r = oakcommon_xml_reader_init(buf.data());
|
||||
ASSERT_NE(r, nullptr);
|
||||
int has_error = 1;
|
||||
EXPECT_EQ(oakcommon_xml_reader_has_error(r, &has_error), OAKCOMMON_OK);
|
||||
EXPECT_EQ(has_error, 0);
|
||||
|
||||
int found = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "root");
|
||||
|
||||
int count = 0;
|
||||
EXPECT_EQ(oakcommon_xml_reader_attribute_count(r, &count), OAKCOMMON_OK);
|
||||
ASSERT_EQ(count, 1);
|
||||
char abuf[32];
|
||||
EXPECT_GT(oakcommon_xml_reader_attribute_value(r, 0, abuf, sizeof(abuf)),
|
||||
0);
|
||||
EXPECT_STREQ(abuf, "1");
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "child");
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_read_element_text, r),
|
||||
"a & b");
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_xml_reader_name, r), "empty");
|
||||
|
||||
EXPECT_EQ(oakcommon_xml_reader_read_next_start_element(r, &found),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(found, 0);
|
||||
|
||||
oakcommon_xml_reader_free(r);
|
||||
}
|
||||
Reference in New Issue
Block a user