refactor(node): de-Qt oaknode and wrap it in a pure C ABI
- copy engine/node (188 files) to src/node/src, de-Qt in waves: core infra (Node/Param/Value/Variant/mathtypes), project/serializer, block/output, color, effect leaves, generator, gizmo/plugins - strip QObject/signals/slots: notifications move to the facade's oakengine_event channel, ownership becomes explicit (unique_ptr, add_keyframe/add_gizmo), sender() replaced by current_gizmo - QVariant replaced by olive::Variant, Qt math types by POD mathtypes, QXmlStreamReader/Writer by oakcommon's expat-based classes - sink VideoParams/SubtitleParams/LoopMode/ColorTransform to oakcommon (M3.5); polygon/text rasterization behind backend hooks - pure C ABI in include/node + src/node/c_api (oaknode_ prefix, OAKNODE_E_* codes, undoable variants take OakUndoCommand out-params) - fix Project::clear() root_ reset + disconnect assert, Sequence TrackList leak - 96 gtest cases green in standalone build (build-oaknode) - docs: signal/slot handling strategy + M3 implementation status
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
target_sources(oakcommon PRIVATE
|
||||
colortransform.cpp
|
||||
commandlineparser.cpp
|
||||
current.cpp
|
||||
subtitleparams.cpp
|
||||
videoparams.cpp
|
||||
debug.cpp
|
||||
dropworkflowbehavior.cpp
|
||||
ffmpegutils.cpp
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/***
|
||||
|
||||
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/colortransform.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../src/colortransform.h"
|
||||
|
||||
struct OakCommonColorTransform {
|
||||
olive::ColorTransform impl;
|
||||
};
|
||||
|
||||
static int copy_string(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int needed = (int)value.size() + 1;
|
||||
if (buf && buf_size >= needed)
|
||||
memcpy(buf, value.c_str(), needed);
|
||||
return needed;
|
||||
}
|
||||
|
||||
OakCommonColorTransform *oakcommon_colortransform_init_output(
|
||||
const char *output)
|
||||
{
|
||||
if (!output)
|
||||
return nullptr;
|
||||
try {
|
||||
return new OakCommonColorTransform{
|
||||
olive::ColorTransform(std::string(output))};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
OakCommonColorTransform *oakcommon_colortransform_init_display(
|
||||
const char *display, const char *view, const char *look)
|
||||
{
|
||||
if (!display || !view || !look)
|
||||
return nullptr;
|
||||
try {
|
||||
return new OakCommonColorTransform{olive::ColorTransform(
|
||||
std::string(display), std::string(view), std::string(look))};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_colortransform_free(OakCommonColorTransform *transform)
|
||||
{
|
||||
delete transform;
|
||||
}
|
||||
|
||||
int oakcommon_colortransform_is_display(OakCommonColorTransform *transform,
|
||||
int *is_display)
|
||||
{
|
||||
if (!transform || !is_display)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*is_display = transform->impl.is_display() ? 1 : 0;
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_colortransform_get_display(OakCommonColorTransform *transform,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!transform)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(transform->impl.display(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_colortransform_get_output(OakCommonColorTransform *transform,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!transform)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(transform->impl.output(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_colortransform_get_view(OakCommonColorTransform *transform,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!transform)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(transform->impl.view(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_colortransform_get_look(OakCommonColorTransform *transform,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!transform)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(transform->impl.look(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/***
|
||||
|
||||
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/subtitleparams.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../src/subtitleparams.h"
|
||||
|
||||
struct OakCommonSubtitleParams {
|
||||
olive::SubtitleParams impl;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int copy_string(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int needed = (int)value.size() + 1;
|
||||
if (buf && buf_size >= needed)
|
||||
memcpy(buf, value.c_str(), needed);
|
||||
return needed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OakCommonSubtitleParams *oakcommon_subtitleparams_init(void)
|
||||
{
|
||||
try {
|
||||
return new OakCommonSubtitleParams{olive::SubtitleParams()};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_subtitleparams_free(OakCommonSubtitleParams *params)
|
||||
{
|
||||
delete params;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_get_stream_index(
|
||||
OakCommonSubtitleParams *params, int *index)
|
||||
{
|
||||
if (!params || !index)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*index = params->impl.stream_index();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_set_stream_index(
|
||||
OakCommonSubtitleParams *params, int index)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_stream_index(index);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_get_enabled(OakCommonSubtitleParams *params,
|
||||
int *enabled)
|
||||
{
|
||||
if (!params || !enabled)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*enabled = params->impl.enabled() ? 1 : 0;
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_set_enabled(OakCommonSubtitleParams *params,
|
||||
int enabled)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_enabled(enabled != 0);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_is_valid(OakCommonSubtitleParams *params,
|
||||
int *is_valid)
|
||||
{
|
||||
if (!params || !is_valid)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*is_valid = params->impl.is_valid() ? 1 : 0;
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_count(OakCommonSubtitleParams *params, int *count)
|
||||
{
|
||||
if (!params || !count)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*count = (int)params->impl.size();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_duration(OakCommonSubtitleParams *params,
|
||||
int *numerator, int *denominator)
|
||||
{
|
||||
if (!params || !numerator || !denominator)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
olive::core::Rational d = params->impl.duration();
|
||||
*numerator = d.numerator();
|
||||
*denominator = d.denominator();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_add_subtitle(OakCommonSubtitleParams *params,
|
||||
int in_num, int in_den, int out_num,
|
||||
int out_den, const char *text)
|
||||
{
|
||||
if (!params || !text)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
params->impl.push_back(olive::Subtitle(
|
||||
olive::core::TimeRange(olive::core::Rational(in_num, in_den),
|
||||
olive::core::Rational(out_num, out_den)),
|
||||
text));
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_clear(OakCommonSubtitleParams *params)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.clear();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_get_subtitle(OakCommonSubtitleParams *params,
|
||||
int index, int *in_num, int *in_den,
|
||||
int *out_num, int *out_den)
|
||||
{
|
||||
if (!params || !in_num || !in_den || !out_num || !out_den)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
if (index < 0 || index >= (int)params->impl.size())
|
||||
return OAKCOMMON_E_NOT_FOUND;
|
||||
const olive::Subtitle &s = params->impl.at(index);
|
||||
olive::core::Rational in = s.time().in();
|
||||
olive::core::Rational out = s.time().out();
|
||||
*in_num = in.numerator();
|
||||
*in_den = in.denominator();
|
||||
*out_num = out.numerator();
|
||||
*out_den = out.denominator();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_get_subtitle_text(OakCommonSubtitleParams *params,
|
||||
int index, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
if (index < 0 || index >= (int)params->impl.size())
|
||||
return OAKCOMMON_E_NOT_FOUND;
|
||||
try {
|
||||
return copy_string(params->impl.at(index).text(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_generate_ass_header(char *buf, int buf_size)
|
||||
{
|
||||
try {
|
||||
return copy_string(olive::SubtitleParams::generate_ass_header(), buf,
|
||||
buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_load_xml(OakCommonSubtitleParams *params,
|
||||
const char *xml)
|
||||
{
|
||||
if (!params || !xml)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
olive::XmlStreamReader reader(xml);
|
||||
if (reader.has_error())
|
||||
return OAKCOMMON_E_FAILED;
|
||||
// Position on the root element; load() consumes its children.
|
||||
if (!olive::xml_read_next_start_element(&reader))
|
||||
return OAKCOMMON_E_FAILED;
|
||||
params->impl.load(&reader);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_subtitleparams_save_xml(OakCommonSubtitleParams *params,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
olive::XmlStreamWriter writer;
|
||||
writer.write_start_element("subtitleparams");
|
||||
params->impl.save(&writer);
|
||||
writer.write_end_element();
|
||||
return copy_string(writer.output(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/***
|
||||
|
||||
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/videoparams.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "../src/videoparams.h"
|
||||
|
||||
struct OakCommonVideoParams {
|
||||
olive::VideoParams impl;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int copy_string(const std::string &value, char *buf, int buf_size)
|
||||
{
|
||||
int needed = (int)value.size() + 1;
|
||||
if (buf && buf_size >= needed)
|
||||
memcpy(buf, value.c_str(), needed);
|
||||
return needed;
|
||||
}
|
||||
|
||||
int get_rational(const olive::core::Rational &r, int *numerator,
|
||||
int *denominator)
|
||||
{
|
||||
if (!numerator || !denominator)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*numerator = r.numerator();
|
||||
*denominator = r.denominator();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OakCommonVideoParams *oakcommon_videoparams_init(void)
|
||||
{
|
||||
try {
|
||||
return new OakCommonVideoParams{olive::VideoParams()};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
OakCommonVideoParams *oakcommon_videoparams_init_basic(
|
||||
int width, int height, int pixel_format, int nb_channels,
|
||||
int pixel_aspect_num, int pixel_aspect_den, int interlacing, int divider)
|
||||
{
|
||||
try {
|
||||
return new OakCommonVideoParams{olive::VideoParams(
|
||||
width, height,
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format),
|
||||
nb_channels,
|
||||
olive::core::Rational(pixel_aspect_num, pixel_aspect_den),
|
||||
static_cast<olive::VideoParams::Interlacing>(interlacing),
|
||||
divider)};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
OakCommonVideoParams *oakcommon_videoparams_init_with_time_base(
|
||||
int width, int height, int time_base_num, int time_base_den,
|
||||
int pixel_format, int nb_channels, int pixel_aspect_num,
|
||||
int pixel_aspect_den, int interlacing, int divider)
|
||||
{
|
||||
try {
|
||||
return new OakCommonVideoParams{olive::VideoParams(
|
||||
width, height,
|
||||
olive::core::Rational(time_base_num, time_base_den),
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format),
|
||||
nb_channels,
|
||||
olive::core::Rational(pixel_aspect_num, pixel_aspect_den),
|
||||
static_cast<olive::VideoParams::Interlacing>(interlacing),
|
||||
divider)};
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void oakcommon_videoparams_free(OakCommonVideoParams *params)
|
||||
{
|
||||
delete params;
|
||||
}
|
||||
|
||||
#define OAKCOMMON_VIDEOPARAMS_INT_GETTER(name, expr) \
|
||||
int oakcommon_videoparams_get_##name(OakCommonVideoParams *params, \
|
||||
int *out) \
|
||||
{ \
|
||||
if (!params || !out) \
|
||||
return OAKCOMMON_E_INVALID; \
|
||||
*out = (expr); \
|
||||
return OAKCOMMON_OK; \
|
||||
}
|
||||
|
||||
#define OAKCOMMON_VIDEOPARAMS_INT_SETTER(name, stmt) \
|
||||
int oakcommon_videoparams_set_##name(OakCommonVideoParams *params, \
|
||||
int value) \
|
||||
{ \
|
||||
if (!params) \
|
||||
return OAKCOMMON_E_INVALID; \
|
||||
stmt; \
|
||||
return OAKCOMMON_OK; \
|
||||
}
|
||||
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(width, params->impl.width())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(width, params->impl.set_width(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(height, params->impl.height())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(height, params->impl.set_height(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(depth, params->impl.depth())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(depth, params->impl.set_depth(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(is_3d, params->impl.is_3d() ? 1 : 0)
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(format, static_cast<int>(params->impl.format()))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(
|
||||
format, params->impl.set_format(
|
||||
static_cast<olive::core::PixelFormat::Format>(value)))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(channel_count, params->impl.channel_count())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(channel_count,
|
||||
params->impl.set_channel_count(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(interlacing,
|
||||
static_cast<int>(params->impl.interlacing()))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(
|
||||
interlacing,
|
||||
params->impl.set_interlacing(
|
||||
static_cast<olive::VideoParams::Interlacing>(value)))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(divider, params->impl.divider())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(divider, params->impl.set_divider(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(enabled, params->impl.enabled() ? 1 : 0)
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(enabled, params->impl.set_enabled(value != 0))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(stream_index, params->impl.stream_index())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(stream_index,
|
||||
params->impl.set_stream_index(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(video_type,
|
||||
static_cast<int>(params->impl.video_type()))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(
|
||||
video_type,
|
||||
params->impl.set_video_type(static_cast<olive::VideoParams::Type>(value)))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(premultiplied_alpha,
|
||||
params->impl.premultiplied_alpha() ? 1 : 0)
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(premultiplied_alpha,
|
||||
params->impl.set_premultiplied_alpha(
|
||||
value != 0))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(color_range,
|
||||
static_cast<int>(params->impl.color_range()))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(
|
||||
color_range, params->impl.set_color_range(
|
||||
static_cast<olive::VideoParams::ColorRange>(value)))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(color_primaries,
|
||||
params->impl.color_primaries())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(color_primaries,
|
||||
params->impl.set_color_primaries(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(color_transfer, params->impl.color_transfer())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_SETTER(color_transfer,
|
||||
params->impl.set_color_transfer(value))
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(square_pixel_width,
|
||||
params->impl.square_pixel_width())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(effective_width,
|
||||
params->impl.effective_width())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(effective_height,
|
||||
params->impl.effective_height())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(effective_depth,
|
||||
params->impl.effective_depth())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(is_valid, params->impl.is_valid() ? 1 : 0)
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(bytes_per_channel,
|
||||
params->impl.get_bytes_per_channel())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(bytes_per_pixel,
|
||||
params->impl.get_bytes_per_pixel())
|
||||
OAKCOMMON_VIDEOPARAMS_INT_GETTER(buffer_size, params->impl.get_buffer_size())
|
||||
|
||||
int oakcommon_videoparams_get_x(OakCommonVideoParams *params, float *x)
|
||||
{
|
||||
if (!params || !x)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*x = params->impl.x();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_x(OakCommonVideoParams *params, float x)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_x(x);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_y(OakCommonVideoParams *params, float *y)
|
||||
{
|
||||
if (!params || !y)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*y = params->impl.y();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_y(OakCommonVideoParams *params, float y)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_y(y);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_start_time(OakCommonVideoParams *params,
|
||||
int64_t *start_time)
|
||||
{
|
||||
if (!params || !start_time)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*start_time = params->impl.start_time();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_start_time(OakCommonVideoParams *params,
|
||||
int64_t start_time)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_start_time(start_time);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_duration(OakCommonVideoParams *params,
|
||||
int64_t *duration)
|
||||
{
|
||||
if (!params || !duration)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*duration = params->impl.duration();
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_duration(OakCommonVideoParams *params,
|
||||
int64_t duration)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_duration(duration);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_time_base(OakCommonVideoParams *params,
|
||||
int *numerator, int *denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return get_rational(params->impl.time_base(), numerator, denominator);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_time_base(OakCommonVideoParams *params,
|
||||
int numerator, int denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_time_base(olive::core::Rational(numerator, denominator));
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_frame_rate(OakCommonVideoParams *params,
|
||||
int *numerator, int *denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return get_rational(params->impl.frame_rate(), numerator, denominator);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_frame_rate(OakCommonVideoParams *params,
|
||||
int numerator, int denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_frame_rate(olive::core::Rational(numerator, denominator));
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_frame_rate_as_time_base(OakCommonVideoParams *params,
|
||||
int *numerator,
|
||||
int *denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return get_rational(params->impl.frame_rate_as_time_base(), numerator,
|
||||
denominator);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_pixel_aspect_ratio(OakCommonVideoParams *params,
|
||||
int *numerator,
|
||||
int *denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
return get_rational(params->impl.pixel_aspect_ratio(), numerator,
|
||||
denominator);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_pixel_aspect_ratio(OakCommonVideoParams *params,
|
||||
int numerator, int denominator)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_pixel_aspect_ratio(
|
||||
olive::core::Rational(numerator, denominator));
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_colorspace(OakCommonVideoParams *params,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
return copy_string(params->impl.colorspace(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_set_colorspace(OakCommonVideoParams *params,
|
||||
const char *colorspace)
|
||||
{
|
||||
if (!params || !colorspace)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
params->impl.set_colorspace(colorspace);
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_time_in_timebase_units(
|
||||
OakCommonVideoParams *params, int time_num, int time_den,
|
||||
int64_t *timestamp)
|
||||
{
|
||||
if (!params || !timestamp)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*timestamp = params->impl.get_time_in_timebase_units(
|
||||
olive::core::Rational(time_num, time_den));
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_equals(OakCommonVideoParams *params,
|
||||
OakCommonVideoParams *other, int *equal)
|
||||
{
|
||||
if (!params || !other || !equal)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
*equal = (params->impl == other->impl) ? 1 : 0;
|
||||
return OAKCOMMON_OK;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_load_xml(OakCommonVideoParams *params,
|
||||
const char *xml)
|
||||
{
|
||||
if (!params || !xml)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
olive::XmlStreamReader reader(xml);
|
||||
if (reader.has_error())
|
||||
return OAKCOMMON_E_FAILED;
|
||||
// Position on the root element; load() consumes its children.
|
||||
if (!olive::xml_read_next_start_element(&reader))
|
||||
return OAKCOMMON_E_FAILED;
|
||||
params->impl.load(&reader);
|
||||
return OAKCOMMON_OK;
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_save_xml(OakCommonVideoParams *params, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
if (!params)
|
||||
return OAKCOMMON_E_INVALID;
|
||||
try {
|
||||
olive::XmlStreamWriter writer;
|
||||
writer.write_start_element("videoparams");
|
||||
params->impl.save(&writer);
|
||||
writer.write_end_element();
|
||||
return copy_string(writer.output(), buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_bytes_per_channel_for_format(int pixel_format)
|
||||
{
|
||||
return olive::VideoParams::get_bytes_per_channel(
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format));
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_bytes_per_pixel_for_format(int pixel_format,
|
||||
int channels)
|
||||
{
|
||||
return olive::VideoParams::get_bytes_per_pixel(
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format), channels);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_calculate_buffer_size(int width, int height,
|
||||
int pixel_format, int channels)
|
||||
{
|
||||
return olive::VideoParams::get_buffer_size(
|
||||
width, height,
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format), channels);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_format_is_float(int pixel_format)
|
||||
{
|
||||
return olive::VideoParams::format_is_float(
|
||||
static_cast<olive::core::PixelFormat::Format>(pixel_format)) ?
|
||||
1 :
|
||||
0;
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_generate_auto_divider(int64_t width, int64_t height)
|
||||
{
|
||||
return olive::VideoParams::generate_auto_divider(width, height);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_scaled_dimension(int dimension, int divider)
|
||||
{
|
||||
return olive::VideoParams::get_scaled_dimension(dimension, divider);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_divider_for_target_resolution(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height)
|
||||
{
|
||||
return olive::VideoParams::get_divider_for_target_resolution(
|
||||
src_width, src_height, dst_width, dst_height);
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_name_for_divider(int divider, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
try {
|
||||
return copy_string(olive::VideoParams::get_name_for_divider(divider),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_get_format_name(int pixel_format, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
try {
|
||||
return copy_string(olive::VideoParams::get_format_name(
|
||||
static_cast<olive::core::PixelFormat::Format>(
|
||||
pixel_format)),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int oakcommon_videoparams_frame_rate_to_string(int numerator, int denominator,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
try {
|
||||
return copy_string(olive::VideoParams::frame_rate_to_string(
|
||||
olive::core::Rational(numerator, denominator)),
|
||||
buf, buf_size);
|
||||
} catch (...) {
|
||||
return OAKCOMMON_E_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
add_library(oakcommon SHARED
|
||||
commandlineparser.cpp
|
||||
commandlineparser.h
|
||||
colortransform.h
|
||||
current.cpp
|
||||
current.h
|
||||
debug.cpp
|
||||
@@ -25,6 +26,11 @@ add_library(oakcommon SHARED
|
||||
decibel.h
|
||||
define.h
|
||||
dropworkflowbehavior.h
|
||||
loopmode.h
|
||||
subtitleparams.cpp
|
||||
subtitleparams.h
|
||||
videoparams.cpp
|
||||
videoparams.h
|
||||
ffmpegutils.cpp
|
||||
ffmpegutils.h
|
||||
filefunctions.cpp
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/***
|
||||
|
||||
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_COLORTRANSFORM_H
|
||||
#define OAK_COLORTRANSFORM_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Describes a color transform: either a plain output colorspace or a
|
||||
* display/view/look triplet (sunk from engine/render/colortransform.h,
|
||||
* QString replaced with std::string).
|
||||
*/
|
||||
class ColorTransform {
|
||||
public:
|
||||
ColorTransform()
|
||||
{
|
||||
is_display_ = false;
|
||||
}
|
||||
|
||||
ColorTransform(const std::string &output)
|
||||
{
|
||||
is_display_ = false;
|
||||
output_ = output;
|
||||
}
|
||||
|
||||
ColorTransform(const std::string &display, const std::string &view,
|
||||
const std::string &look)
|
||||
{
|
||||
is_display_ = true;
|
||||
output_ = display;
|
||||
view_ = view;
|
||||
look_ = look;
|
||||
}
|
||||
|
||||
bool is_display() const
|
||||
{
|
||||
return is_display_;
|
||||
}
|
||||
|
||||
const std::string &display() const
|
||||
{
|
||||
return output_;
|
||||
}
|
||||
|
||||
const std::string &output() const
|
||||
{
|
||||
return output_;
|
||||
}
|
||||
|
||||
const std::string &view() const
|
||||
{
|
||||
return view_;
|
||||
}
|
||||
|
||||
const std::string &look() const
|
||||
{
|
||||
return look_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string output_;
|
||||
|
||||
bool is_display_;
|
||||
std::string view_;
|
||||
std::string look_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_COLORTRANSFORM_H
|
||||
@@ -0,0 +1,34 @@
|
||||
/***
|
||||
|
||||
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_LOOPMODE_H
|
||||
#define OAK_LOOPMODE_H
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Playback loop mode (sunk from engine/render/loopmode.h).
|
||||
*/
|
||||
enum class LoopMode { k_loop_mode_off, k_loop_mode_loop, k_loop_mode_clamp };
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_LOOPMODE_H
|
||||
@@ -0,0 +1,172 @@
|
||||
/***
|
||||
|
||||
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 "subtitleparams.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
std::string SubtitleParams::generate_ass_header()
|
||||
{
|
||||
// NOTE: We'll probably implement more customization as we support ASS better. Right now, we only
|
||||
// natively support SRT and only make this header because FFmpeg requires it.
|
||||
static const int k_ass_default_play_res_x = 384;
|
||||
static const int k_ass_default_play_res_y = 288;
|
||||
static const char *k_ass_default_font = "Arial";
|
||||
static const int k_ass_default_font_size = 16;
|
||||
static const int k_ass_default_primary_color = 0xFFFFFF; // White
|
||||
static const int k_ass_default_secondary_color = 0xFFFFFF; // White
|
||||
static const int k_ass_default_outline_color = 0x000000; // Black
|
||||
static const int k_ass_default_back_color = 0x000000; // Black
|
||||
static const int k_ass_bold = 0;
|
||||
static const int k_ass_italic = 0;
|
||||
static const int k_ass_underline = 0;
|
||||
static const int k_ass_strike = 0;
|
||||
static const int k_ass_border_style = 1;
|
||||
static const int k_ass_alignment = 2;
|
||||
|
||||
std::string ass_code;
|
||||
char buf[256];
|
||||
|
||||
// Header info
|
||||
// De-Qt note: the original used QCoreApplication::applicationName()/
|
||||
// applicationVersion(); oakcommon has no application object, so a fixed
|
||||
// generator name is emitted instead.
|
||||
ass_code.append("[Script Info]\r\n");
|
||||
ass_code.append("; Script generated by Oak\r\n");
|
||||
ass_code.append("ScriptType: v4.00+\r\n");
|
||||
std::snprintf(buf, sizeof(buf), "PlayResX: %d\r\n", k_ass_default_play_res_x);
|
||||
ass_code.append(buf);
|
||||
std::snprintf(buf, sizeof(buf), "PlayResY: %d\r\n", k_ass_default_play_res_y);
|
||||
ass_code.append(buf);
|
||||
ass_code.append("ScaledBorderAndShadow: yes\r\n");
|
||||
ass_code.append("\r\n");
|
||||
|
||||
// ASSv4 header
|
||||
ass_code.append("[V4+ Styles]\r\n");
|
||||
ass_code.append("Format: Name, ");
|
||||
ass_code.append("Fontname, Fontsize, ");
|
||||
ass_code.append("PrimaryColour, SecondaryColour, OutlineColour, BackColour, ");
|
||||
ass_code.append("Bold, Italic, Underline, StrikeOut, ");
|
||||
ass_code.append("ScaleX, ScaleY, ");
|
||||
ass_code.append("Spacing, Angle, ");
|
||||
ass_code.append("BorderStyle, Outline, Shadow, ");
|
||||
ass_code.append("Alignment, MarginL, MarginR, MarginV, ");
|
||||
ass_code.append("Encoding\r\n");
|
||||
ass_code.append("Style: ");
|
||||
|
||||
// Name
|
||||
ass_code.append("Default,");
|
||||
|
||||
// Font{name,size}
|
||||
std::snprintf(buf, sizeof(buf), "%s,%d,", k_ass_default_font,
|
||||
k_ass_default_font_size);
|
||||
ass_code.append(buf);
|
||||
|
||||
// {Primary,Secondary,Outline,Back}Colour
|
||||
std::snprintf(buf, sizeof(buf), "&H%X,&H%X,&H%X,&H%X,",
|
||||
k_ass_default_primary_color, k_ass_default_secondary_color,
|
||||
k_ass_default_outline_color, k_ass_default_back_color);
|
||||
ass_code.append(buf);
|
||||
|
||||
// Bold, Italic, Underline, StrikeOut
|
||||
std::snprintf(buf, sizeof(buf), "%d,%d,%d,%d,", k_ass_bold, k_ass_italic,
|
||||
k_ass_underline, k_ass_strike);
|
||||
ass_code.append(buf);
|
||||
|
||||
// Scale{X,Y}
|
||||
ass_code.append("100,100,");
|
||||
|
||||
// Spacing, Angle
|
||||
ass_code.append("0,0,");
|
||||
|
||||
// BorderStyle, Outline, Shadow
|
||||
std::snprintf(buf, sizeof(buf), "%d,1,0,", k_ass_border_style);
|
||||
ass_code.append(buf);
|
||||
|
||||
// Alignment, Margin[LRV]
|
||||
std::snprintf(buf, sizeof(buf), "%d,10,10,10,", k_ass_alignment);
|
||||
ass_code.append(buf);
|
||||
|
||||
// Encoding
|
||||
ass_code.append("0\r\n");
|
||||
ass_code.append("\r\n");
|
||||
ass_code.append("[Events]\r\n");
|
||||
ass_code.append(
|
||||
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n");
|
||||
|
||||
return ass_code;
|
||||
}
|
||||
|
||||
void SubtitleParams::load(XmlStreamReader *reader)
|
||||
{
|
||||
this->clear();
|
||||
|
||||
while (xml_read_next_start_element(reader)) {
|
||||
const std::string &name = reader->name();
|
||||
if (name == "streamindex") {
|
||||
set_stream_index(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "enabled") {
|
||||
set_enabled(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "subtitles") {
|
||||
while (xml_read_next_start_element(reader)) {
|
||||
if (reader->name() == "subtitle") {
|
||||
Rational in, out;
|
||||
|
||||
for (const XmlStreamAttribute &attr : reader->attributes()) {
|
||||
if (attr.name == "in") {
|
||||
in = Rational::from_string(attr.value);
|
||||
} else if (attr.name == "out") {
|
||||
out = Rational::from_string(attr.value);
|
||||
}
|
||||
}
|
||||
|
||||
std::string text = reader->read_element_text();
|
||||
|
||||
this->push_back(Subtitle(TimeRange(in, out), text));
|
||||
} else {
|
||||
reader->skip_current_element();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader->skip_current_element();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SubtitleParams::save(XmlStreamWriter *writer) const
|
||||
{
|
||||
writer->write_text_element("streamindex", std::to_string(stream_index_));
|
||||
writer->write_text_element("enabled", std::to_string(enabled_));
|
||||
|
||||
writer->write_start_element("subtitles");
|
||||
for (auto it = this->cbegin(); it != this->cend(); it++) {
|
||||
writer->write_start_element("subtitle");
|
||||
writer->write_attribute("in", it->time().in().to_string());
|
||||
writer->write_attribute("out", it->time().out().to_string());
|
||||
writer->write_characters(it->text());
|
||||
writer->write_end_element(); // subtitle
|
||||
}
|
||||
writer->write_end_element(); // subtitles
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/***
|
||||
|
||||
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_SUBTITLEPARAMS_H
|
||||
#define OAK_SUBTITLEPARAMS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <olive/core/util/timerange.h>
|
||||
|
||||
#include "xmlutils.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A single subtitle entry (sunk from engine/render/subtitleparams.h,
|
||||
* QString replaced with std::string).
|
||||
*/
|
||||
class Subtitle {
|
||||
public:
|
||||
Subtitle() = default;
|
||||
|
||||
Subtitle(const TimeRange &time, const std::string &text)
|
||||
: range_(time)
|
||||
, text_(text)
|
||||
{
|
||||
}
|
||||
|
||||
const TimeRange &time() const
|
||||
{
|
||||
return range_;
|
||||
}
|
||||
void set_time(const TimeRange &t)
|
||||
{
|
||||
range_ = t;
|
||||
}
|
||||
|
||||
const std::string &text() const
|
||||
{
|
||||
return text_;
|
||||
}
|
||||
void set_text(const std::string &t)
|
||||
{
|
||||
text_ = t;
|
||||
}
|
||||
|
||||
// Value equality (time range + text). Required by olive::Variant's
|
||||
// custom-type operator== (SubtitleParams is stored as a Variant value).
|
||||
bool operator==(const Subtitle &rhs) const
|
||||
{
|
||||
return range_ == rhs.range_ && text_ == rhs.text_;
|
||||
}
|
||||
bool operator!=(const Subtitle &rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
TimeRange range_;
|
||||
|
||||
std::string text_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Subtitle track parameter set (sunk from engine/render/subtitleparams.h).
|
||||
*/
|
||||
class SubtitleParams : public std::vector<Subtitle> {
|
||||
public:
|
||||
SubtitleParams()
|
||||
{
|
||||
stream_index_ = 0;
|
||||
enabled_ = true;
|
||||
}
|
||||
|
||||
static std::string generate_ass_header();
|
||||
|
||||
void load(XmlStreamReader *reader);
|
||||
|
||||
void save(XmlStreamWriter *writer) const;
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return !this->empty();
|
||||
}
|
||||
|
||||
Rational duration() const
|
||||
{
|
||||
if (this->empty()) {
|
||||
return 0;
|
||||
} else {
|
||||
return back().time().out();
|
||||
}
|
||||
}
|
||||
|
||||
int stream_index() const
|
||||
{
|
||||
return stream_index_;
|
||||
}
|
||||
void set_stream_index(int i)
|
||||
{
|
||||
stream_index_ = i;
|
||||
}
|
||||
|
||||
bool enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
void set_enabled(bool e)
|
||||
{
|
||||
enabled_ = e;
|
||||
}
|
||||
|
||||
private:
|
||||
int stream_index_;
|
||||
|
||||
bool enabled_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_SUBTITLEPARAMS_H
|
||||
@@ -0,0 +1,466 @@
|
||||
/***
|
||||
|
||||
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 "videoparams.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include <olive/core/util/timecodefunctions.h>
|
||||
|
||||
#include "ofxImageEffect.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
const int VideoParams::k_internal_channel_count = k_rgba_channel_count;
|
||||
|
||||
const Rational VideoParams::k_pixel_aspect_square(1);
|
||||
const Rational VideoParams::k_pixel_aspect_ntsc_standard(8, 9);
|
||||
const Rational VideoParams::k_pixel_aspect_ntsc_widescreen(32, 27);
|
||||
const Rational VideoParams::k_pixel_aspect_pal_standard(16, 15);
|
||||
const Rational VideoParams::k_pixel_aspect_pal_widescreen(64, 45);
|
||||
const Rational VideoParams::k_pixel_aspect1080_anamorphic(4, 3);
|
||||
|
||||
const std::vector<Rational> VideoParams::k_supported_frame_rates = {
|
||||
Rational(10, 1), // 10 FPS
|
||||
Rational(15, 1), // 15 FPS
|
||||
Rational(24000, 1001), // 23.976 FPS
|
||||
Rational(24, 1), // 24 FPS
|
||||
Rational(25, 1), // 25 FPS
|
||||
Rational(30000, 1001), // 29.97 FPS
|
||||
Rational(30, 1), // 30 FPS
|
||||
Rational(48000, 1001), // 47.952 FPS
|
||||
Rational(48, 1), // 48 FPS
|
||||
Rational(50, 1), // 50 FPS
|
||||
Rational(60000, 1001), // 59.94 FPS
|
||||
Rational(60, 1) // 60 FPS
|
||||
};
|
||||
|
||||
const std::vector<int> VideoParams::k_supported_dividers = {
|
||||
1, 2, 3, 4, 6, 8, 12, 16
|
||||
};
|
||||
|
||||
const std::vector<Rational> VideoParams::k_standard_pixel_aspects = {
|
||||
VideoParams::k_pixel_aspect_square,
|
||||
VideoParams::k_pixel_aspect_ntsc_standard,
|
||||
VideoParams::k_pixel_aspect_ntsc_widescreen,
|
||||
VideoParams::k_pixel_aspect_pal_standard,
|
||||
VideoParams::k_pixel_aspect_pal_widescreen,
|
||||
VideoParams::k_pixel_aspect1080_anamorphic
|
||||
};
|
||||
|
||||
VideoParams::VideoParams()
|
||||
: width_(0)
|
||||
, height_(0)
|
||||
, depth_(0)
|
||||
, time_base_(0)
|
||||
, format_(PixelFormat::invalid)
|
||||
, channel_count_(0)
|
||||
, pixel_aspect_ratio_(1)
|
||||
, interlacing_(Interlacing::k_interlace_none)
|
||||
, divider_(1)
|
||||
{
|
||||
calculate_effective_size();
|
||||
validate_pixel_aspect_ratio();
|
||||
set_defaults_for_footage();
|
||||
}
|
||||
|
||||
VideoParams::VideoParams(int width, int height, PixelFormat format,
|
||||
int nb_channels, const Rational &pixel_aspect_ratio,
|
||||
Interlacing interlacing, int divider)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, depth_(1)
|
||||
, format_(format)
|
||||
, channel_count_(nb_channels)
|
||||
, pixel_aspect_ratio_(pixel_aspect_ratio)
|
||||
, interlacing_(interlacing)
|
||||
, divider_(divider)
|
||||
{
|
||||
calculate_effective_size();
|
||||
validate_pixel_aspect_ratio();
|
||||
set_defaults_for_footage();
|
||||
}
|
||||
|
||||
VideoParams::VideoParams(int width, int height, int depth, PixelFormat format,
|
||||
int nb_channels, const Rational &pixel_aspect_ratio,
|
||||
VideoParams::Interlacing interlacing, int divider)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, depth_(depth)
|
||||
, format_(format)
|
||||
, channel_count_(nb_channels)
|
||||
, pixel_aspect_ratio_(pixel_aspect_ratio)
|
||||
, interlacing_(interlacing)
|
||||
, divider_(divider)
|
||||
{
|
||||
calculate_effective_size();
|
||||
validate_pixel_aspect_ratio();
|
||||
set_defaults_for_footage();
|
||||
}
|
||||
|
||||
void VideoParams::set_channel_count(const std::string &ofx_component)
|
||||
{
|
||||
if (ofx_component == kOfxImageComponentAlpha) {
|
||||
channel_count_ = 1;
|
||||
} else if (ofx_component == kOfxImageComponentRGB) {
|
||||
channel_count_ = k_rgb_channel_count;
|
||||
} else if (ofx_component == kOfxImageComponentRGBA) {
|
||||
channel_count_ = k_rgba_channel_count;
|
||||
}
|
||||
}
|
||||
|
||||
VideoParams::VideoParams(int width, int height, const Rational &time_base,
|
||||
PixelFormat format, int nb_channels,
|
||||
const Rational &pixel_aspect_ratio,
|
||||
Interlacing interlacing, int divider)
|
||||
: width_(width)
|
||||
, height_(height)
|
||||
, depth_(1)
|
||||
, time_base_(time_base)
|
||||
, format_(format)
|
||||
, channel_count_(nb_channels)
|
||||
, pixel_aspect_ratio_(pixel_aspect_ratio)
|
||||
, interlacing_(interlacing)
|
||||
, divider_(divider)
|
||||
, frame_rate_(time_base.flipped())
|
||||
{
|
||||
calculate_effective_size();
|
||||
validate_pixel_aspect_ratio();
|
||||
set_defaults_for_footage();
|
||||
}
|
||||
|
||||
int VideoParams::generate_auto_divider(int64_t width, int64_t height)
|
||||
{
|
||||
const int target_res = 1280 * 720;
|
||||
|
||||
int64_t megapixels = width * height;
|
||||
|
||||
double squared_divider = double(megapixels) / double(target_res);
|
||||
double divider = std::sqrt(squared_divider);
|
||||
|
||||
if (divider <= k_supported_dividers.front()) {
|
||||
return k_supported_dividers.front();
|
||||
} else if (divider >= k_supported_dividers.back()) {
|
||||
return k_supported_dividers.back();
|
||||
} else {
|
||||
for (size_t i = 1; i < k_supported_dividers.size(); i++) {
|
||||
int prev_divider = k_supported_dividers.at(i - 1);
|
||||
int next_divider = k_supported_dividers.at(i);
|
||||
|
||||
if (divider >= prev_divider && divider <= next_divider) {
|
||||
double prev_diff = std::fabs(prev_divider - divider);
|
||||
double next_diff = std::fabs(next_divider - divider);
|
||||
|
||||
if (prev_diff < next_diff) {
|
||||
return prev_divider;
|
||||
} else {
|
||||
return next_divider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoParams::operator==(const VideoParams &rhs) const
|
||||
{
|
||||
return width() == rhs.width() && height() == rhs.height() &&
|
||||
depth() == rhs.depth() && interlacing() == rhs.interlacing() &&
|
||||
time_base() == rhs.time_base() && format() == rhs.format() &&
|
||||
pixel_aspect_ratio() == rhs.pixel_aspect_ratio() &&
|
||||
divider() == rhs.divider() && channel_count() == rhs.channel_count();
|
||||
}
|
||||
|
||||
bool VideoParams::operator!=(const VideoParams &rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
int VideoParams::get_bytes_per_channel(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::invalid:
|
||||
case PixelFormat::count:
|
||||
break;
|
||||
case PixelFormat::u8:
|
||||
return 1;
|
||||
case PixelFormat::u10:
|
||||
return 0; // packed format, use get_bytes_per_pixel instead
|
||||
case PixelFormat::u16:
|
||||
case PixelFormat::f16:
|
||||
return 2;
|
||||
case PixelFormat::f32:
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VideoParams::get_bytes_per_pixel(PixelFormat format, int channels)
|
||||
{
|
||||
if (format == PixelFormat::u10) {
|
||||
// Packed 10-bit RGBA10A2: 4 bytes per RGBA pixel regardless of channel count
|
||||
return channels == VideoParams::k_rgba_channel_count ? 4 : 0;
|
||||
}
|
||||
return get_bytes_per_channel(format) * channels;
|
||||
}
|
||||
|
||||
std::string VideoParams::get_name_for_divider(int div)
|
||||
{
|
||||
if (div == 1) {
|
||||
return std::string("Full");
|
||||
} else {
|
||||
return std::string("1/") + std::to_string(div);
|
||||
}
|
||||
}
|
||||
|
||||
std::string VideoParams::get_format_name(PixelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case PixelFormat::u8:
|
||||
return "8-bit";
|
||||
case PixelFormat::u10:
|
||||
return "10-bit Packed";
|
||||
case PixelFormat::u16:
|
||||
return "16-bit Integer";
|
||||
case PixelFormat::f16:
|
||||
return "Half-Float (16-bit)";
|
||||
case PixelFormat::f32:
|
||||
return "Full-Float (32-bit)";
|
||||
case PixelFormat::invalid:
|
||||
case PixelFormat::count:
|
||||
break;
|
||||
}
|
||||
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf), "Unknown (0x%X)",
|
||||
static_cast<int>(format));
|
||||
return buf;
|
||||
}
|
||||
|
||||
int VideoParams::get_divider_for_target_resolution(int src_width, int src_height,
|
||||
int dst_width, int dst_height)
|
||||
{
|
||||
int divider = 0;
|
||||
int test_width, test_height;
|
||||
|
||||
do {
|
||||
divider++;
|
||||
|
||||
test_width = VideoParams::get_scaled_dimension(src_width, divider);
|
||||
test_height = VideoParams::get_scaled_dimension(src_height, divider);
|
||||
} while (test_width > dst_width || test_height > dst_height);
|
||||
|
||||
return divider;
|
||||
}
|
||||
|
||||
void VideoParams::calculate_effective_size()
|
||||
{
|
||||
effective_width_ = get_scaled_dimension(width(), divider_);
|
||||
effective_height_ = get_scaled_dimension(height(), divider_);
|
||||
effective_depth_ = (depth() == 1) ? depth() :
|
||||
get_scaled_dimension(depth(), divider_);
|
||||
calculate_square_pixel_width();
|
||||
}
|
||||
|
||||
void VideoParams::validate_pixel_aspect_ratio()
|
||||
{
|
||||
if (pixel_aspect_ratio_.isNull()) {
|
||||
pixel_aspect_ratio_ = 1;
|
||||
}
|
||||
calculate_square_pixel_width();
|
||||
}
|
||||
|
||||
void VideoParams::set_defaults_for_footage()
|
||||
{
|
||||
enabled_ = true;
|
||||
stream_index_ = 0;
|
||||
video_type_ = k_video_type_video;
|
||||
start_time_ = 0;
|
||||
duration_ = 0;
|
||||
premultiplied_alpha_ = false;
|
||||
x_ = 0;
|
||||
y_ = 0;
|
||||
color_range_ = k_color_range_default;
|
||||
}
|
||||
|
||||
void VideoParams::calculate_square_pixel_width()
|
||||
{
|
||||
if (pixel_aspect_ratio_.denominator() != 0) {
|
||||
par_width_ = int(std::lround(width_ * pixel_aspect_ratio_.to_double()));
|
||||
} else {
|
||||
par_width_ = width_;
|
||||
}
|
||||
}
|
||||
|
||||
bool VideoParams::is_valid() const
|
||||
{
|
||||
return (width() > 0 && height() > 0 && !pixel_aspect_ratio_.isNull() &&
|
||||
format_ > PixelFormat::invalid && format_ < PixelFormat::count &&
|
||||
channel_count_ > 0);
|
||||
}
|
||||
|
||||
std::string VideoParams::frame_rate_to_string(const Rational &frame_rate)
|
||||
{
|
||||
char buf[64];
|
||||
std::snprintf(buf, sizeof(buf), "%g FPS", frame_rate.to_double());
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::vector<std::string> VideoParams::get_standard_pixel_aspect_ratio_names()
|
||||
{
|
||||
std::vector<std::string> strings = {
|
||||
"Square Pixels (%1)",
|
||||
"NTSC Standard (%1)",
|
||||
"NTSC Widescreen (%1)",
|
||||
"PAL Standard (%1)",
|
||||
"PAL Widescreen (%1)",
|
||||
"HD Anamorphic 1080 (%1)"
|
||||
};
|
||||
|
||||
// Format each
|
||||
for (size_t i = 0; i < strings.size(); i++) {
|
||||
strings.at(i) = format_pixel_aspect_ratio_string(
|
||||
strings.at(i), k_standard_pixel_aspects.at(i));
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
std::string VideoParams::format_pixel_aspect_ratio_string(
|
||||
const std::string &format, const Rational &ratio)
|
||||
{
|
||||
char number[64];
|
||||
std::snprintf(number, sizeof(number), "%.4f", ratio.to_double());
|
||||
|
||||
std::string result = format;
|
||||
std::string::size_type pos = result.find("%1");
|
||||
if (pos != std::string::npos) {
|
||||
result.replace(pos, 2, number);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int VideoParams::get_scaled_dimension(int dim, int divider)
|
||||
{
|
||||
return dim / divider;
|
||||
}
|
||||
|
||||
int64_t VideoParams::get_time_in_timebase_units(const Rational &time) const
|
||||
{
|
||||
if (time_base_.isNull()) {
|
||||
return INT64_MIN; // AV_NOPTS_VALUE
|
||||
}
|
||||
|
||||
return Timecode::time_to_timestamp(time, time_base_) + start_time_;
|
||||
}
|
||||
|
||||
void VideoParams::load(XmlStreamReader *reader)
|
||||
{
|
||||
while (xml_read_next_start_element(reader)) {
|
||||
const std::string &name = reader->name();
|
||||
if (name == "width") {
|
||||
set_width(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "height") {
|
||||
set_height(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "depth") {
|
||||
set_depth(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "timebase") {
|
||||
set_time_base(Rational::from_string(reader->read_element_text()));
|
||||
} else if (name == "format") {
|
||||
set_format(static_cast<PixelFormat::Format>(
|
||||
std::stoi(reader->read_element_text())));
|
||||
} else if (name == "channelcount") {
|
||||
set_channel_count(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "pixelaspectratio") {
|
||||
set_pixel_aspect_ratio(
|
||||
Rational::from_string(reader->read_element_text()));
|
||||
} else if (name == "interlacing") {
|
||||
set_interlacing(static_cast<VideoParams::Interlacing>(
|
||||
std::stoi(reader->read_element_text())));
|
||||
} else if (name == "divider") {
|
||||
set_divider(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "enabled") {
|
||||
set_enabled(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "x") {
|
||||
set_x(std::stof(reader->read_element_text()));
|
||||
} else if (name == "y") {
|
||||
set_y(std::stof(reader->read_element_text()));
|
||||
} else if (name == "streamindex") {
|
||||
set_stream_index(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "videotype") {
|
||||
set_video_type(static_cast<VideoParams::Type>(
|
||||
std::stoi(reader->read_element_text())));
|
||||
} else if (name == "framerate") {
|
||||
set_frame_rate(Rational::from_string(reader->read_element_text()));
|
||||
} else if (name == "starttime") {
|
||||
set_start_time(std::stoll(reader->read_element_text()));
|
||||
} else if (name == "duration") {
|
||||
set_duration(std::stoll(reader->read_element_text()));
|
||||
} else if (name == "premultipliedalpha") {
|
||||
set_premultiplied_alpha(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "colorspace") {
|
||||
set_colorspace(reader->read_element_text());
|
||||
} else if (name == "colorrange") {
|
||||
set_color_range(
|
||||
static_cast<ColorRange>(std::stoi(reader->read_element_text())));
|
||||
} else if (name == "colorprimaries") {
|
||||
set_color_primaries(std::stoi(reader->read_element_text()));
|
||||
} else if (name == "colortransfer") {
|
||||
set_color_transfer(std::stoi(reader->read_element_text()));
|
||||
} else {
|
||||
reader->skip_current_element();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VideoParams::save(XmlStreamWriter *writer) const
|
||||
{
|
||||
writer->write_text_element("width", std::to_string(width_));
|
||||
writer->write_text_element("height", std::to_string(height_));
|
||||
writer->write_text_element("depth", std::to_string(depth_));
|
||||
writer->write_text_element("timebase", time_base_.to_string());
|
||||
writer->write_text_element("format", std::to_string(format_));
|
||||
writer->write_text_element("channelcount", std::to_string(channel_count_));
|
||||
writer->write_text_element("pixelaspectratio",
|
||||
pixel_aspect_ratio_.to_string());
|
||||
writer->write_text_element("interlacing", std::to_string(interlacing_));
|
||||
writer->write_text_element("divider", std::to_string(divider_));
|
||||
writer->write_text_element("enabled", std::to_string(enabled_));
|
||||
writer->write_text_element("x", std::to_string(x_));
|
||||
writer->write_text_element("y", std::to_string(y_));
|
||||
writer->write_text_element("streamindex", std::to_string(stream_index_));
|
||||
writer->write_text_element("videotype", std::to_string(video_type_));
|
||||
writer->write_text_element("framerate", frame_rate_.to_string());
|
||||
writer->write_text_element("starttime", std::to_string(start_time_));
|
||||
writer->write_text_element("duration", std::to_string(duration_));
|
||||
writer->write_text_element("premultipliedalpha",
|
||||
std::to_string(premultiplied_alpha_));
|
||||
writer->write_text_element("colorspace", colorspace_);
|
||||
writer->write_text_element("colorrange", std::to_string(color_range_));
|
||||
writer->write_text_element("colorprimaries", std::to_string(color_primaries_));
|
||||
writer->write_text_element("colortransfer", std::to_string(color_transfer_));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
/***
|
||||
|
||||
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_VIDEOPARAMS_H
|
||||
#define OAK_VIDEOPARAMS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <olive/core/render/pixelformat.h>
|
||||
#include <olive/core/util/rational.h>
|
||||
|
||||
#include "xmlutils.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
using namespace core;
|
||||
|
||||
/**
|
||||
* @brief Pure-data video parameter set (sunk from engine/render/videoparams.h).
|
||||
*
|
||||
* De-Qt port: QString -> std::string, QVector -> std::vector,
|
||||
* QXmlStreamReader/Writer -> olive::XmlStreamReader/Writer. The QVector2D
|
||||
* convenience getters (resolution/square_resolution/offset) were dropped;
|
||||
* use width()/height()/x()/y() directly. Translated UI strings are returned
|
||||
* as plain English text.
|
||||
*/
|
||||
class VideoParams {
|
||||
public:
|
||||
enum Interlacing {
|
||||
k_interlace_none,
|
||||
k_interlaced_top_first,
|
||||
k_interlaced_bottom_first
|
||||
};
|
||||
|
||||
enum Type { k_video_type_video, k_video_type_still, k_video_type_image_sequence };
|
||||
enum ColorRange {
|
||||
k_color_range_limited, // 16-235
|
||||
k_color_range_full, // 0-255
|
||||
|
||||
k_color_range_default = k_color_range_limited
|
||||
};
|
||||
|
||||
VideoParams();
|
||||
VideoParams(int width, int height, PixelFormat format, int nb_channels,
|
||||
const Rational &pixel_aspect_ratio = 1,
|
||||
Interlacing interlacing = k_interlace_none, int divider = 1);
|
||||
VideoParams(int width, int height, int depth, PixelFormat format,
|
||||
int nb_channels, const Rational &pixel_aspect_ratio = 1,
|
||||
Interlacing interlacing = k_interlace_none, int divider = 1);
|
||||
VideoParams(int width, int height, const Rational &time_base,
|
||||
PixelFormat format, int nb_channels,
|
||||
const Rational &pixel_aspect_ratio = 1,
|
||||
Interlacing interlacing = k_interlace_none, int divider = 1);
|
||||
|
||||
int width() const
|
||||
{
|
||||
return width_;
|
||||
}
|
||||
|
||||
void set_width(int width)
|
||||
{
|
||||
width_ = width;
|
||||
calculate_effective_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns width multiplied by pixel aspect ratio where applicable
|
||||
*/
|
||||
int square_pixel_width() const
|
||||
{
|
||||
return par_width_;
|
||||
}
|
||||
|
||||
int height() const
|
||||
{
|
||||
return height_;
|
||||
}
|
||||
|
||||
void set_height(int height)
|
||||
{
|
||||
height_ = height;
|
||||
calculate_effective_size();
|
||||
}
|
||||
|
||||
int depth() const
|
||||
{
|
||||
return depth_;
|
||||
}
|
||||
|
||||
void set_depth(int depth)
|
||||
{
|
||||
depth_ = depth;
|
||||
calculate_effective_size();
|
||||
}
|
||||
|
||||
bool is_3d() const
|
||||
{
|
||||
return depth_ > 1;
|
||||
}
|
||||
|
||||
const Rational &time_base() const
|
||||
{
|
||||
return time_base_;
|
||||
}
|
||||
|
||||
void set_time_base(const Rational &r)
|
||||
{
|
||||
time_base_ = r;
|
||||
}
|
||||
|
||||
Rational frame_rate_as_time_base() const
|
||||
{
|
||||
return frame_rate_.flipped();
|
||||
}
|
||||
|
||||
int divider() const
|
||||
{
|
||||
return divider_;
|
||||
}
|
||||
|
||||
void set_divider(int d)
|
||||
{
|
||||
divider_ = d;
|
||||
calculate_effective_size();
|
||||
}
|
||||
|
||||
int effective_width() const
|
||||
{
|
||||
return effective_width_;
|
||||
}
|
||||
|
||||
int effective_height() const
|
||||
{
|
||||
return effective_height_;
|
||||
}
|
||||
|
||||
int effective_depth() const
|
||||
{
|
||||
return effective_depth_;
|
||||
}
|
||||
|
||||
PixelFormat format() const
|
||||
{
|
||||
return format_;
|
||||
}
|
||||
|
||||
void set_format(PixelFormat f)
|
||||
{
|
||||
format_ = f;
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return channel_count_;
|
||||
}
|
||||
|
||||
void set_channel_count(int c)
|
||||
{
|
||||
channel_count_ = c;
|
||||
}
|
||||
void set_channel_count(const std::string &ofx_component);
|
||||
const Rational &pixel_aspect_ratio() const
|
||||
{
|
||||
return pixel_aspect_ratio_;
|
||||
}
|
||||
|
||||
void set_pixel_aspect_ratio(const Rational &r)
|
||||
{
|
||||
pixel_aspect_ratio_ = r;
|
||||
validate_pixel_aspect_ratio();
|
||||
}
|
||||
|
||||
Interlacing interlacing() const
|
||||
{
|
||||
return interlacing_;
|
||||
}
|
||||
|
||||
void set_interlacing(Interlacing i)
|
||||
{
|
||||
interlacing_ = i;
|
||||
}
|
||||
|
||||
static int generate_auto_divider(int64_t width, int64_t height);
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
bool operator==(const VideoParams &rhs) const;
|
||||
bool operator!=(const VideoParams &rhs) const;
|
||||
|
||||
static int get_bytes_per_channel(PixelFormat format);
|
||||
int get_bytes_per_channel() const
|
||||
{
|
||||
return get_bytes_per_channel(format_);
|
||||
}
|
||||
|
||||
static int get_bytes_per_pixel(PixelFormat format, int channels);
|
||||
int get_bytes_per_pixel() const
|
||||
{
|
||||
return get_bytes_per_pixel(format_, channel_count_);
|
||||
}
|
||||
|
||||
static int get_buffer_size(int width, int height, PixelFormat format,
|
||||
int channels)
|
||||
{
|
||||
return width * height * get_bytes_per_pixel(format, channels);
|
||||
}
|
||||
int get_buffer_size() const
|
||||
{
|
||||
return get_buffer_size(width_, height_, format_, channel_count_);
|
||||
}
|
||||
|
||||
static std::string get_name_for_divider(int div);
|
||||
|
||||
static bool format_is_float(PixelFormat format)
|
||||
{
|
||||
return format.is_float();
|
||||
}
|
||||
|
||||
static std::string get_format_name(PixelFormat format);
|
||||
|
||||
static int get_divider_for_target_resolution(int src_width, int src_height,
|
||||
int dst_width, int dst_height);
|
||||
|
||||
static const int k_internal_channel_count;
|
||||
|
||||
static const Rational k_pixel_aspect_square;
|
||||
static const Rational k_pixel_aspect_ntsc_standard;
|
||||
static const Rational k_pixel_aspect_ntsc_widescreen;
|
||||
static const Rational k_pixel_aspect_pal_standard;
|
||||
static const Rational k_pixel_aspect_pal_widescreen;
|
||||
static const Rational k_pixel_aspect1080_anamorphic;
|
||||
|
||||
static const std::vector<Rational> k_supported_frame_rates;
|
||||
static const std::vector<Rational> k_standard_pixel_aspects;
|
||||
static const std::vector<int> k_supported_dividers;
|
||||
|
||||
static const int k_hsv_channel_count = 3;
|
||||
static const int k_rgb_channel_count = 3;
|
||||
static const int k_rgba_channel_count = 4;
|
||||
|
||||
/**
|
||||
* @brief Convert Rational frame rate (i.e. flipped timebase) to a user-friendly string
|
||||
*/
|
||||
static std::string frame_rate_to_string(const Rational &frame_rate);
|
||||
|
||||
static std::vector<std::string> get_standard_pixel_aspect_ratio_names();
|
||||
static std::string format_pixel_aspect_ratio_string(const std::string &format,
|
||||
const Rational &ratio);
|
||||
|
||||
static int get_scaled_dimension(int dim, int divider);
|
||||
|
||||
bool enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void set_enabled(bool e)
|
||||
{
|
||||
enabled_ = e;
|
||||
}
|
||||
|
||||
float x() const
|
||||
{
|
||||
return x_;
|
||||
}
|
||||
void set_x(float x)
|
||||
{
|
||||
x_ = x;
|
||||
}
|
||||
float y() const
|
||||
{
|
||||
return y_;
|
||||
}
|
||||
void set_y(float y)
|
||||
{
|
||||
y_ = y;
|
||||
}
|
||||
|
||||
int stream_index() const
|
||||
{
|
||||
return stream_index_;
|
||||
}
|
||||
|
||||
void set_stream_index(int s)
|
||||
{
|
||||
stream_index_ = s;
|
||||
}
|
||||
|
||||
Type video_type() const
|
||||
{
|
||||
return video_type_;
|
||||
}
|
||||
|
||||
void set_video_type(Type t)
|
||||
{
|
||||
video_type_ = t;
|
||||
}
|
||||
|
||||
const Rational &frame_rate() const
|
||||
{
|
||||
return frame_rate_;
|
||||
}
|
||||
|
||||
void set_frame_rate(const Rational &frame_rate)
|
||||
{
|
||||
frame_rate_ = frame_rate;
|
||||
}
|
||||
|
||||
int64_t start_time() const
|
||||
{
|
||||
return start_time_;
|
||||
}
|
||||
|
||||
void set_start_time(int64_t start_time)
|
||||
{
|
||||
start_time_ = start_time;
|
||||
}
|
||||
|
||||
int64_t duration() const
|
||||
{
|
||||
return duration_;
|
||||
}
|
||||
|
||||
void set_duration(int64_t duration)
|
||||
{
|
||||
duration_ = duration;
|
||||
}
|
||||
|
||||
bool premultiplied_alpha() const
|
||||
{
|
||||
return premultiplied_alpha_;
|
||||
}
|
||||
|
||||
void set_premultiplied_alpha(bool premultiplied_alpha)
|
||||
{
|
||||
premultiplied_alpha_ = premultiplied_alpha;
|
||||
}
|
||||
|
||||
const std::string &colorspace() const
|
||||
{
|
||||
return colorspace_;
|
||||
}
|
||||
|
||||
void set_colorspace(const std::string &c)
|
||||
{
|
||||
colorspace_ = c;
|
||||
}
|
||||
|
||||
const ColorRange &color_range() const
|
||||
{
|
||||
return color_range_;
|
||||
}
|
||||
void set_color_range(const ColorRange &color_range)
|
||||
{
|
||||
color_range_ = color_range;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Color primaries/transfer as reported by the media
|
||||
*
|
||||
* Raw FFmpeg AVColorPrimaries/AVColorTransferCharacteristic values
|
||||
* (0 = unset, 2 = unspecified). Used to auto-detect the input
|
||||
* colorspace when no explicit colorspace has been set.
|
||||
*/
|
||||
int color_primaries() const
|
||||
{
|
||||
return color_primaries_;
|
||||
}
|
||||
|
||||
void set_color_primaries(int p)
|
||||
{
|
||||
color_primaries_ = p;
|
||||
}
|
||||
|
||||
int color_transfer() const
|
||||
{
|
||||
return color_transfer_;
|
||||
}
|
||||
|
||||
void set_color_transfer(int t)
|
||||
{
|
||||
color_transfer_ = t;
|
||||
}
|
||||
|
||||
int64_t get_time_in_timebase_units(const Rational &time) const;
|
||||
|
||||
void load(XmlStreamReader *reader);
|
||||
|
||||
void save(XmlStreamWriter *writer) const;
|
||||
|
||||
private:
|
||||
void calculate_effective_size();
|
||||
|
||||
void validate_pixel_aspect_ratio();
|
||||
|
||||
void set_defaults_for_footage();
|
||||
|
||||
void calculate_square_pixel_width();
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
int depth_;
|
||||
Rational time_base_;
|
||||
|
||||
PixelFormat format_;
|
||||
|
||||
int channel_count_;
|
||||
|
||||
Rational pixel_aspect_ratio_;
|
||||
|
||||
Interlacing interlacing_;
|
||||
|
||||
int divider_;
|
||||
|
||||
// Cached values
|
||||
int effective_width_;
|
||||
int effective_height_;
|
||||
int effective_depth_;
|
||||
int par_width_;
|
||||
|
||||
bool enabled_;
|
||||
int stream_index_;
|
||||
Type video_type_;
|
||||
Rational frame_rate_;
|
||||
int64_t start_time_;
|
||||
int64_t duration_;
|
||||
bool premultiplied_alpha_;
|
||||
std::string colorspace_;
|
||||
float x_;
|
||||
float y_;
|
||||
ColorRange color_range_;
|
||||
int color_primaries_ = 0;
|
||||
int color_transfer_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_VIDEOPARAMS_H
|
||||
@@ -19,6 +19,7 @@ find_package(GTest REQUIRED)
|
||||
include(GoogleTest)
|
||||
|
||||
add_executable(oakcommon-gtest
|
||||
colortransform_test.cpp
|
||||
commandlineparser_test.cpp
|
||||
current_test.cpp
|
||||
debug_test.cpp
|
||||
@@ -31,7 +32,9 @@ add_executable(oakcommon-gtest
|
||||
oiioutils_test.cpp
|
||||
power_test.cpp
|
||||
qtutils_test.cpp
|
||||
subtitleparams_test.cpp
|
||||
threadsafemap_test.cpp
|
||||
videoparams_test.cpp
|
||||
xmlutils_test.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/***
|
||||
|
||||
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/colortransform.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string read_string(int (*fn)(OakCommonColorTransform *, char *, int),
|
||||
OakCommonColorTransform *t)
|
||||
{
|
||||
int needed = fn(t, nullptr, 0);
|
||||
EXPECT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
EXPECT_EQ(fn(t, buf.data(), needed), needed);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(CommonColorTransformCApi, InitOutput)
|
||||
{
|
||||
OakCommonColorTransform *t =
|
||||
oakcommon_colortransform_init_output("sRGB");
|
||||
ASSERT_NE(t, nullptr);
|
||||
|
||||
int is_display = -1;
|
||||
EXPECT_EQ(oakcommon_colortransform_is_display(t, &is_display),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(is_display, 0);
|
||||
EXPECT_EQ(read_string(oakcommon_colortransform_get_output, t), "sRGB");
|
||||
EXPECT_EQ(read_string(oakcommon_colortransform_get_display, t), "sRGB");
|
||||
|
||||
oakcommon_colortransform_free(t);
|
||||
}
|
||||
|
||||
TEST(CommonColorTransformCApi, InitOutputNullString)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_colortransform_init_output(nullptr), nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonColorTransformCApi, InitDisplay)
|
||||
{
|
||||
OakCommonColorTransform *t =
|
||||
oakcommon_colortransform_init_display("sRGB", "Studio", "None");
|
||||
ASSERT_NE(t, nullptr);
|
||||
|
||||
int is_display = 0;
|
||||
EXPECT_EQ(oakcommon_colortransform_is_display(t, &is_display),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(is_display, 1);
|
||||
EXPECT_EQ(read_string(oakcommon_colortransform_get_display, t), "sRGB");
|
||||
EXPECT_EQ(read_string(oakcommon_colortransform_get_view, t), "Studio");
|
||||
EXPECT_EQ(read_string(oakcommon_colortransform_get_look, t), "None");
|
||||
|
||||
oakcommon_colortransform_free(t);
|
||||
}
|
||||
|
||||
TEST(CommonColorTransformCApi, InitDisplayNullString)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_colortransform_init_display(nullptr, "v", "l"),
|
||||
nullptr);
|
||||
EXPECT_EQ(oakcommon_colortransform_init_display("d", nullptr, "l"),
|
||||
nullptr);
|
||||
EXPECT_EQ(oakcommon_colortransform_init_display("d", "v", nullptr),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonColorTransformCApi, FreeNull)
|
||||
{
|
||||
oakcommon_colortransform_free(nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonColorTransformCApi, NullHandleErrors)
|
||||
{
|
||||
int i = 0;
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_colortransform_is_display(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_colortransform_get_display(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_colortransform_get_output(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_colortransform_get_view(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_colortransform_get_look(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonColorTransformCApi, NullOutParam)
|
||||
{
|
||||
OakCommonColorTransform *t =
|
||||
oakcommon_colortransform_init_output("sRGB");
|
||||
ASSERT_NE(t, nullptr);
|
||||
EXPECT_EQ(oakcommon_colortransform_is_display(t, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
oakcommon_colortransform_free(t);
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/***
|
||||
|
||||
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/subtitleparams.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string read_indexed_string(
|
||||
int (*fn)(OakCommonSubtitleParams *, int, char *, int),
|
||||
OakCommonSubtitleParams *p, int index)
|
||||
{
|
||||
int needed = fn(p, index, nullptr, 0);
|
||||
EXPECT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
EXPECT_EQ(fn(p, index, buf.data(), needed), needed);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
||||
std::string read_static_string(int (*fn)(char *, int))
|
||||
{
|
||||
int needed = fn(nullptr, 0);
|
||||
EXPECT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
EXPECT_EQ(fn(buf.data(), needed), needed);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, InitFree)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int index = -1;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_stream_index(p, &index),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(index, 0);
|
||||
|
||||
int enabled = 0;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_enabled(p, &enabled),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(enabled, 1);
|
||||
|
||||
oakcommon_subtitleparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, FreeNull)
|
||||
{
|
||||
oakcommon_subtitleparams_free(nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, SetStreamIndex)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_set_stream_index(p, 3), OAKCOMMON_OK);
|
||||
int index = 0;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_stream_index(p, &index),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(index, 3);
|
||||
oakcommon_subtitleparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, SetStreamIndexNullHandle)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_subtitleparams_set_stream_index(nullptr, 3),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_stream_index(nullptr, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, SetEnabled)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_set_enabled(p, 0), OAKCOMMON_OK);
|
||||
int enabled = 1;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_enabled(p, &enabled),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(enabled, 0);
|
||||
oakcommon_subtitleparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, SetEnabledNullHandle)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_subtitleparams_set_enabled(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_enabled(nullptr, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, AddAndReadSubtitles)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int is_valid = 1;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_is_valid(p, &is_valid), OAKCOMMON_OK);
|
||||
EXPECT_EQ(is_valid, 0);
|
||||
|
||||
EXPECT_EQ(oakcommon_subtitleparams_add_subtitle(p, 1, 2, 3, 2, "Hello"),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_add_subtitle(p, 2, 1, 3, 1, "World"),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
EXPECT_EQ(oakcommon_subtitleparams_is_valid(p, &is_valid), OAKCOMMON_OK);
|
||||
EXPECT_EQ(is_valid, 1);
|
||||
|
||||
int count = 0;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_count(p, &count), OAKCOMMON_OK);
|
||||
EXPECT_EQ(count, 2);
|
||||
|
||||
int in_num, in_den, out_num, out_den;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_subtitle(p, 0, &in_num, &in_den,
|
||||
&out_num, &out_den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(in_num, 1);
|
||||
EXPECT_EQ(in_den, 2);
|
||||
EXPECT_EQ(out_num, 3);
|
||||
EXPECT_EQ(out_den, 2);
|
||||
EXPECT_EQ(read_indexed_string(
|
||||
oakcommon_subtitleparams_get_subtitle_text, p, 0),
|
||||
"Hello");
|
||||
|
||||
int num, den;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_duration(p, &num, &den), OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 3);
|
||||
EXPECT_EQ(den, 1);
|
||||
|
||||
EXPECT_EQ(oakcommon_subtitleparams_clear(p), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_count(p, &count), OAKCOMMON_OK);
|
||||
EXPECT_EQ(count, 0);
|
||||
|
||||
oakcommon_subtitleparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, SubtitleErrorPaths)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int i = 0;
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_subtitleparams_add_subtitle(nullptr, 0, 1, 1, 1, "x"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_add_subtitle(p, 0, 1, 1, 1, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_subtitle(p, 0, &i, &i, &i, &i),
|
||||
OAKCOMMON_E_NOT_FOUND);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_subtitle(nullptr, 0, &i, &i, &i,
|
||||
&i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_subtitle_text(p, 5, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_NOT_FOUND);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_subtitle_text(nullptr, 0, buf,
|
||||
sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_is_valid(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_count(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_duration(nullptr, &i, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_clear(nullptr), OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_subtitleparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, GenerateAssHeader)
|
||||
{
|
||||
std::string header =
|
||||
read_static_string(oakcommon_subtitleparams_generate_ass_header);
|
||||
EXPECT_NE(header.find("[Script Info]"), std::string::npos);
|
||||
EXPECT_NE(header.find("[V4+ Styles]"), std::string::npos);
|
||||
EXPECT_NE(header.find("[Events]"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, XmlRoundTrip)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_set_stream_index(p, 2), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_add_subtitle(p, 1, 25, 2, 25,
|
||||
"First <line>"),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
int needed = oakcommon_subtitleparams_save_xml(p, nullptr, 0);
|
||||
ASSERT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
ASSERT_EQ(oakcommon_subtitleparams_save_xml(p, buf.data(), needed), needed);
|
||||
|
||||
OakCommonSubtitleParams *q = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(q, nullptr);
|
||||
ASSERT_EQ(oakcommon_subtitleparams_load_xml(q, buf.data()), OAKCOMMON_OK);
|
||||
|
||||
int index = 0;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_get_stream_index(q, &index),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(index, 2);
|
||||
int count = 0;
|
||||
EXPECT_EQ(oakcommon_subtitleparams_count(q, &count), OAKCOMMON_OK);
|
||||
EXPECT_EQ(count, 1);
|
||||
EXPECT_EQ(read_indexed_string(
|
||||
oakcommon_subtitleparams_get_subtitle_text, q, 0),
|
||||
"First <line>");
|
||||
|
||||
oakcommon_subtitleparams_free(p);
|
||||
oakcommon_subtitleparams_free(q);
|
||||
}
|
||||
|
||||
TEST(CommonSubtitleParamsCApi, XmlErrorPaths)
|
||||
{
|
||||
OakCommonSubtitleParams *p = oakcommon_subtitleparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_subtitleparams_load_xml(nullptr, "<a/>"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_load_xml(p, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_load_xml(p, "not xml"),
|
||||
OAKCOMMON_E_FAILED);
|
||||
EXPECT_EQ(oakcommon_subtitleparams_save_xml(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
oakcommon_subtitleparams_free(p);
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
/***
|
||||
|
||||
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 <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/videoparams.h"
|
||||
|
||||
TEST(CommonVideoParamsCApi, InitDefaults)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int v = -1;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_width(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 0);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_is_valid(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 0);
|
||||
|
||||
oakcommon_videoparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, FreeNull)
|
||||
{
|
||||
oakcommon_videoparams_free(nullptr);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, InitBasic)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init_basic(
|
||||
1920, 1080, OAKCOMMON_PIXEL_FORMAT_U8,
|
||||
4, 1, 1, OAKCOMMON_VIDEO_INTERLACE_NONE, 2);
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int v;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_width(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1920);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_height(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1080);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_depth(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_is_3d(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 0);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_format(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, OAKCOMMON_PIXEL_FORMAT_U8);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_channel_count(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 4);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_divider(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 2);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_interlacing(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, OAKCOMMON_VIDEO_INTERLACE_NONE);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_is_valid(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1);
|
||||
|
||||
// Effective size is divided by the divider
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_width(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 960);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_height(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 540);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_square_pixel_width(p, &v),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1920);
|
||||
|
||||
// u8 RGBA: 4 bytes per pixel
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_channel(p, &v),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_pixel(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 4);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_buffer_size(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1920 * 1080 * 4);
|
||||
|
||||
oakcommon_videoparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, InitWithTimeBase)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init_with_time_base(
|
||||
1280, 720, 1001, 30000, OAKCOMMON_PIXEL_FORMAT_F32, 4, 1, 1,
|
||||
OAKCOMMON_VIDEO_INTERLACE_NONE, 1);
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int num, den;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_time_base(p, &num, &den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 1001);
|
||||
EXPECT_EQ(den, 30000);
|
||||
|
||||
// Frame rate is the flipped time base
|
||||
EXPECT_EQ(oakcommon_videoparams_get_frame_rate(p, &num, &den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 30000);
|
||||
EXPECT_EQ(den, 1001);
|
||||
EXPECT_EQ(oakcommon_videoparams_frame_rate_as_time_base(p, &num, &den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 1001);
|
||||
EXPECT_EQ(den, 30000);
|
||||
|
||||
oakcommon_videoparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, ScalarSetters)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int v;
|
||||
EXPECT_EQ(oakcommon_videoparams_set_width(p, 640), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_height(p, 480), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_depth(p, 4), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_is_3d(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_format(p, OAKCOMMON_PIXEL_FORMAT_U16),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_channel_count(p, 3), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_interlacing(
|
||||
p, OAKCOMMON_VIDEO_INTERLACED_TOP_FIRST),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_divider(p, 4), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_enabled(p, 0), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_stream_index(p, 5), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_video_type(
|
||||
p, OAKCOMMON_VIDEO_TYPE_IMAGE_SEQUENCE),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_premultiplied_alpha(p, 1),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_range(
|
||||
p, OAKCOMMON_COLOR_RANGE_FULL),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_primaries(p, 9), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_transfer(p, 16), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_time_base(p, 1, 25), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_frame_rate(p, 25, 1), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_pixel_aspect_ratio(p, 4, 3),
|
||||
OAKCOMMON_OK);
|
||||
|
||||
float f;
|
||||
EXPECT_EQ(oakcommon_videoparams_set_x(p, 1.5f), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_y(p, -2.5f), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_x(p, &f), OAKCOMMON_OK);
|
||||
EXPECT_FLOAT_EQ(f, 1.5f);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_y(p, &f), OAKCOMMON_OK);
|
||||
EXPECT_FLOAT_EQ(f, -2.5f);
|
||||
|
||||
int64_t i64;
|
||||
EXPECT_EQ(oakcommon_videoparams_set_start_time(p, 100), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_duration(p, 250), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_start_time(p, &i64), OAKCOMMON_OK);
|
||||
EXPECT_EQ(i64, 100);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_duration(p, &i64), OAKCOMMON_OK);
|
||||
EXPECT_EQ(i64, 250);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_get_width(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 640);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_width(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 160);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_interlacing(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, OAKCOMMON_VIDEO_INTERLACED_TOP_FIRST);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_enabled(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 0);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_stream_index(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 5);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_video_type(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, OAKCOMMON_VIDEO_TYPE_IMAGE_SEQUENCE);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_premultiplied_alpha(p, &v),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_range(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, OAKCOMMON_COLOR_RANGE_FULL);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_primaries(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 9);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_transfer(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 16);
|
||||
|
||||
int num, den;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_pixel_aspect_ratio(p, &num, &den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 4);
|
||||
EXPECT_EQ(den, 3);
|
||||
// Square pixel width = 640 * 4/3 = 853.33 -> 853
|
||||
EXPECT_EQ(oakcommon_videoparams_get_square_pixel_width(p, &v),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 853);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_depth(p, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 1);
|
||||
|
||||
oakcommon_videoparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, NullHandleErrors)
|
||||
{
|
||||
int i;
|
||||
float f;
|
||||
int64_t i64;
|
||||
char buf[16];
|
||||
EXPECT_EQ(oakcommon_videoparams_get_width(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_width(nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_height(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_height(nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_depth(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_depth(nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_is_3d(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_time_base(nullptr, &i, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_time_base(nullptr, 1, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_frame_rate(nullptr, &i, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_frame_rate(nullptr, 1, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_frame_rate_as_time_base(nullptr, &i, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_pixel_aspect_ratio(nullptr, &i, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_pixel_aspect_ratio(nullptr, 1, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_format(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_format(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_channel_count(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_channel_count(nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_interlacing(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_interlacing(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_divider(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_divider(nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_enabled(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_enabled(nullptr, 1),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_x(nullptr, &f), OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_x(nullptr, 0.0f), OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_y(nullptr, &f), OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_y(nullptr, 0.0f), OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_stream_index(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_stream_index(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_video_type(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_video_type(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_start_time(nullptr, &i64),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_start_time(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_duration(nullptr, &i64),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_duration(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_premultiplied_alpha(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_premultiplied_alpha(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_range(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_range(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_primaries(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_primaries(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_transfer(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_transfer(nullptr, 0),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_colorspace(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_colorspace(nullptr, "x"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_square_pixel_width(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_width(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_height(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_effective_depth(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_is_valid(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_channel(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_pixel(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_buffer_size(nullptr, &i),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_time_in_timebase_units(nullptr, 0, 1,
|
||||
&i64),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_load_xml(nullptr, "<a/>"),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_save_xml(nullptr, buf, sizeof(buf)),
|
||||
OAKCOMMON_E_INVALID);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, NullOutParamErrors)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_width(p, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_time_base(p, nullptr, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_colorspace(p, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_load_xml(p, nullptr), OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_load_xml(p, "not xml"),
|
||||
OAKCOMMON_E_FAILED);
|
||||
oakcommon_videoparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, Colorspace)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init();
|
||||
ASSERT_NE(p, nullptr);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_colorspace(p, "rec709"),
|
||||
OAKCOMMON_OK);
|
||||
int needed = oakcommon_videoparams_get_colorspace(p, nullptr, 0);
|
||||
ASSERT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_colorspace(p, buf.data(), needed),
|
||||
needed);
|
||||
EXPECT_STREQ(buf.data(), "rec709");
|
||||
oakcommon_videoparams_free(p);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, TimeInTimebaseUnits)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init_with_time_base(
|
||||
1920, 1080, 1, 25, OAKCOMMON_PIXEL_FORMAT_U8, 4, 1, 1,
|
||||
OAKCOMMON_VIDEO_INTERLACE_NONE, 1);
|
||||
ASSERT_NE(p, nullptr);
|
||||
|
||||
int64_t ts = -1;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_time_in_timebase_units(p, 2, 1, &ts),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(ts, 50); // 2 seconds at 25 fps
|
||||
|
||||
// Without a time base the result is AV_NOPTS_VALUE
|
||||
OakCommonVideoParams *q = oakcommon_videoparams_init();
|
||||
ASSERT_NE(q, nullptr);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_time_in_timebase_units(q, 2, 1, &ts),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(ts, INT64_MIN);
|
||||
|
||||
oakcommon_videoparams_free(p);
|
||||
oakcommon_videoparams_free(q);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, Equals)
|
||||
{
|
||||
OakCommonVideoParams *a = oakcommon_videoparams_init_basic(
|
||||
1920, 1080, OAKCOMMON_PIXEL_FORMAT_U8, 4, 1, 1,
|
||||
OAKCOMMON_VIDEO_INTERLACE_NONE, 1);
|
||||
OakCommonVideoParams *b = oakcommon_videoparams_init_basic(
|
||||
1920, 1080, OAKCOMMON_PIXEL_FORMAT_U8, 4, 1, 1,
|
||||
OAKCOMMON_VIDEO_INTERLACE_NONE, 1);
|
||||
ASSERT_NE(a, nullptr);
|
||||
ASSERT_NE(b, nullptr);
|
||||
|
||||
int equal = 0;
|
||||
EXPECT_EQ(oakcommon_videoparams_equals(a, b, &equal), OAKCOMMON_OK);
|
||||
EXPECT_EQ(equal, 1);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_set_divider(b, 2), OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_equals(a, b, &equal), OAKCOMMON_OK);
|
||||
EXPECT_EQ(equal, 0);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_equals(nullptr, b, &equal),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_equals(a, nullptr, &equal),
|
||||
OAKCOMMON_E_INVALID);
|
||||
EXPECT_EQ(oakcommon_videoparams_equals(a, b, nullptr),
|
||||
OAKCOMMON_E_INVALID);
|
||||
|
||||
oakcommon_videoparams_free(a);
|
||||
oakcommon_videoparams_free(b);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, XmlRoundTrip)
|
||||
{
|
||||
OakCommonVideoParams *p = oakcommon_videoparams_init_with_time_base(
|
||||
1920, 1080, 1, 25, OAKCOMMON_PIXEL_FORMAT_F16, 4, 4, 3,
|
||||
OAKCOMMON_VIDEO_INTERLACED_BOTTOM_FIRST, 2);
|
||||
ASSERT_NE(p, nullptr);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_colorspace(p, "rec709"),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(oakcommon_videoparams_set_color_primaries(p, 9), OAKCOMMON_OK);
|
||||
|
||||
int needed = oakcommon_videoparams_save_xml(p, nullptr, 0);
|
||||
ASSERT_GT(needed, 0);
|
||||
std::vector<char> buf(needed);
|
||||
ASSERT_EQ(oakcommon_videoparams_save_xml(p, buf.data(), needed), needed);
|
||||
|
||||
OakCommonVideoParams *q = oakcommon_videoparams_init();
|
||||
ASSERT_NE(q, nullptr);
|
||||
ASSERT_EQ(oakcommon_videoparams_load_xml(q, buf.data()), OAKCOMMON_OK);
|
||||
|
||||
int equal = 0;
|
||||
EXPECT_EQ(oakcommon_videoparams_equals(p, q, &equal), OAKCOMMON_OK);
|
||||
EXPECT_EQ(equal, 1);
|
||||
|
||||
int num, den;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_frame_rate(q, &num, &den),
|
||||
OAKCOMMON_OK);
|
||||
EXPECT_EQ(num, 25);
|
||||
EXPECT_EQ(den, 1);
|
||||
|
||||
int cneeded = oakcommon_videoparams_get_colorspace(q, nullptr, 0);
|
||||
std::vector<char> cbuf(cneeded);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_colorspace(q, cbuf.data(), cneeded),
|
||||
cneeded);
|
||||
EXPECT_STREQ(cbuf.data(), "rec709");
|
||||
|
||||
int v;
|
||||
EXPECT_EQ(oakcommon_videoparams_get_color_primaries(q, &v), OAKCOMMON_OK);
|
||||
EXPECT_EQ(v, 9);
|
||||
|
||||
oakcommon_videoparams_free(p);
|
||||
oakcommon_videoparams_free(q);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, StaticHelpers)
|
||||
{
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_channel_for_format(
|
||||
OAKCOMMON_PIXEL_FORMAT_U8),
|
||||
1);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_channel_for_format(
|
||||
OAKCOMMON_PIXEL_FORMAT_F32),
|
||||
4);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_channel_for_format(
|
||||
OAKCOMMON_PIXEL_FORMAT_INVALID),
|
||||
0);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_pixel_for_format(
|
||||
OAKCOMMON_PIXEL_FORMAT_U10, 4),
|
||||
4);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_pixel_for_format(
|
||||
OAKCOMMON_PIXEL_FORMAT_U10, 3),
|
||||
0);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_bytes_per_pixel_for_format(
|
||||
OAKCOMMON_PIXEL_FORMAT_U16, 3),
|
||||
6);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_calculate_buffer_size(
|
||||
100, 50, OAKCOMMON_PIXEL_FORMAT_U8, 4),
|
||||
20000);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_format_is_float(
|
||||
OAKCOMMON_PIXEL_FORMAT_F16),
|
||||
1);
|
||||
EXPECT_EQ(oakcommon_videoparams_format_is_float(OAKCOMMON_PIXEL_FORMAT_U8),
|
||||
0);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_generate_auto_divider(1920, 1080), 2);
|
||||
EXPECT_EQ(oakcommon_videoparams_generate_auto_divider(640, 480), 1);
|
||||
EXPECT_EQ(oakcommon_videoparams_generate_auto_divider(16000, 16000), 16);
|
||||
|
||||
EXPECT_EQ(oakcommon_videoparams_get_scaled_dimension(1920, 2), 960);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_divider_for_target_resolution(
|
||||
3840, 2160, 1920, 1080),
|
||||
2);
|
||||
}
|
||||
|
||||
TEST(CommonVideoParamsCApi, StaticStrings)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
int needed = oakcommon_videoparams_get_name_for_divider(1, nullptr, 0);
|
||||
ASSERT_GT(needed, 0);
|
||||
EXPECT_EQ(oakcommon_videoparams_get_name_for_divider(1, buf, sizeof(buf)),
|
||||
needed);
|
||||
EXPECT_STREQ(buf, "Full");
|
||||
EXPECT_GT(oakcommon_videoparams_get_name_for_divider(4, buf, sizeof(buf)),
|
||||
0);
|
||||
EXPECT_STREQ(buf, "1/4");
|
||||
|
||||
EXPECT_GT(oakcommon_videoparams_get_format_name(OAKCOMMON_PIXEL_FORMAT_U8,
|
||||
buf, sizeof(buf)),
|
||||
0);
|
||||
EXPECT_STREQ(buf, "8-bit");
|
||||
EXPECT_GT(oakcommon_videoparams_get_format_name(
|
||||
OAKCOMMON_PIXEL_FORMAT_INVALID, buf, sizeof(buf)),
|
||||
0);
|
||||
EXPECT_NE(std::string(buf).find("Unknown"), std::string::npos);
|
||||
|
||||
EXPECT_GT(oakcommon_videoparams_frame_rate_to_string(25, 1, buf,
|
||||
sizeof(buf)),
|
||||
0);
|
||||
EXPECT_STREQ(buf, "25 FPS");
|
||||
}
|
||||
Reference in New Issue
Block a user