refactor(codec): de-Qt oakcodec and wrap it in a pure C ABI; switch common handles to refcounted value structs

- oakcodec: de-Qt all 20 sources (QThread decode loop -> std::thread,
  QObject/signals -> callbacks), pure C ABI in include/codec with
  refcounted neutral handles (OakFrame/OakDecoder/OakEncoder),
  framemanager moved in from render, frame_to_buffer/buffer_to_frame
  moved in from oakcommon oiioutils, codec->task via submit callback
  (M8 will register), all cross-module calls go through the other
  side's C API, -fvisibility=hidden + OAKCODEC_API
- oakcommon: handles become refcounted value structs
  {ctx, addref, release, abi_version} (FFmpeg-style), pass-by-value
  signatures, free() as release wrapper; init_from_native/get_native
  for copyable value objects; OakCommonXxx renamed to OakXxx
- oakcommon: add logging (log_debug/info/warning/critical with level
  filtering and sink injection) + printf-style oakcommon_log C wrapper
- oakrender: add CancelAtom C API family; complete
  oakrender_color_processor_convert_frame; fix get_processor() missing
  definition and OCIO env var lookup
- tests: oakcommon 174, oaknode 96, oakrender 42, oakcodec 18, all
  green in their standalone builds
This commit is contained in:
2026-08-06 18:50:07 +08:00
parent edbd3913af
commit 3d004c081b
127 changed files with 13760 additions and 1222 deletions
+1
View File
@@ -1,6 +1,7 @@
target_sources(oakrender PRIVATE
renderer.cpp
cache.cpp
cancelatom.cpp
color.cpp
manager.cpp
)
+135
View File
@@ -0,0 +1,135 @@
/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "../../../include/render/cancelatom.h"
#include <atomic>
#include <cstdint>
#include "alivecount.h"
#include "cancelatom.h"
namespace
{
/**
* @brief Heap box behind every OakCancelAtom's ctx pointer.
*
* Holds the wrapped CancelAtom plus its atomic reference count. addref
* and release are emitted in this translation unit so the function
* pointers stored in a handle always run code from the DLL that created
* the object.
*/
struct CancelAtomBox {
olive::CancelAtom impl;
std::atomic<uint32_t> refs;
CancelAtomBox()
: refs(1)
{
}
};
CancelAtomBox *box(OakCancelAtom atom)
{
return static_cast<CancelAtomBox *>(atom.ctx);
}
olive::CancelAtom *impl(OakCancelAtom atom)
{
auto *b = box(atom);
return b ? &b->impl : nullptr;
}
/**
* @brief Handle addref thunk: atomically increments the count.
*/
void cancel_atom_addref(void *ctx)
{
auto *b = static_cast<CancelAtomBox *>(ctx);
if (b)
b->refs.fetch_add(1, std::memory_order_relaxed);
}
/**
* @brief Handle release thunk: decrements the count, destroys at zero.
*/
void cancel_atom_release(void *ctx)
{
auto *b = static_cast<CancelAtomBox *>(ctx);
if (b && b->refs.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete b;
oakrender_c_api::alive_dec();
}
}
} // namespace
OakCancelAtom oakrender_cancelatom_init(void)
{
OakCancelAtom h = {};
try {
h.ctx = new CancelAtomBox();
} catch (...) {
h.ctx = nullptr;
}
h.addref = &cancel_atom_addref;
h.release = &cancel_atom_release;
h.abi_version = OAKRENDER_ABI_VERSION;
if (h.ctx)
oakrender_c_api::alive_inc();
return h;
}
void oakrender_cancelatom_free(OakCancelAtom *atom)
{
if (!atom || !atom->ctx || !atom->release)
return;
atom->release(atom->ctx);
atom->ctx = nullptr;
}
int oakrender_cancelatom_cancel(OakCancelAtom atom)
{
auto *c = impl(atom);
if (!c)
return OAKRENDER_E_INVALID;
c->cancel();
return OAKRENDER_OK;
}
int oakrender_cancelatom_is_cancelled(OakCancelAtom atom, int *cancelled)
{
auto *c = impl(atom);
if (!c || !cancelled)
return OAKRENDER_E_INVALID;
*cancelled = c->is_cancelled() ? 1 : 0;
return OAKRENDER_OK;
}
int oakrender_cancelatom_heard_cancel(OakCancelAtom atom, int *heard)
{
auto *c = impl(atom);
if (!c || !heard)
return OAKRENDER_E_INVALID;
*heard = c->heard_cancel() ? 1 : 0;
return OAKRENDER_OK;
}
+30 -10
View File
@@ -30,6 +30,7 @@
#include "color/colormanager/colormanager.h"
#include "filefunctions.h"
#include <OpenColorIO/OpenColorIO.h>
namespace
{
@@ -57,7 +58,7 @@ OakColorProcessor *oakrender_color_processor_create(const char *src_space,
return nullptr;
}
try {
ocio::ConstConfigRcPtr config = olive::ColorManager::get_default_config();
OCIO_NAMESPACE::ConstConfigRcPtr config = olive::ColorManager::get_default_config();
if (!config) {
return nullptr;
}
@@ -69,17 +70,17 @@ OakColorProcessor *oakrender_color_processor_create(const char *src_space,
src = config->getCanonicalName(src_space);
}
// OCIO failures are non-fatal (matching the C++ behavior): the
// OCIO_NAMESPACE failures are non-fatal (matching the C++ behavior): the
// handle is still returned, but holds a null processor and
// conversions pass through.
ocio::ConstProcessorRcPtr processor;
OCIO_NAMESPACE::ConstProcessorRcPtr processor;
try {
if (direction == OAKRENDER_COLOR_DIRECTION_NORMAL) {
processor = config->getProcessor(src.c_str(), dst_transform);
} else {
processor = config->getProcessor(dst_transform, src.c_str());
}
} catch (ocio::Exception &) {
} catch (OCIO_NAMESPACE::Exception &) {
processor = nullptr;
}
@@ -169,7 +170,7 @@ int oakrender_color_manager_display_transform(const char *display,
return OAKRENDER_E_INVALID;
}
try {
ocio::ConstConfigRcPtr config = olive::ColorManager::get_default_config();
OCIO_NAMESPACE::ConstConfigRcPtr config = olive::ColorManager::get_default_config();
if (!config) {
return OAKRENDER_E_STATE;
}
@@ -197,25 +198,44 @@ int oakrender_color_manager_display_transform(const char *display,
}
// Source = the config's reference colorspace (role lookup).
ocio::ConstColorSpaceRcPtr ref_cs =
config->getColorSpace(ocio::ROLE_REFERENCE);
OCIO_NAMESPACE::ConstColorSpaceRcPtr ref_cs =
config->getColorSpace(OCIO_NAMESPACE::ROLE_REFERENCE);
if (!ref_cs) {
return OAKRENDER_E_STATE;
}
auto dvt = ocio::DisplayViewTransform::Create();
auto dvt = OCIO_NAMESPACE::DisplayViewTransform::Create();
dvt->setSrc(ref_cs->getName());
dvt->setDisplay(display);
dvt->setView(view);
ocio::ConstProcessorRcPtr processor = config->getProcessor(dvt);
OCIO_NAMESPACE::ConstProcessorRcPtr processor = config->getProcessor(dvt);
if (!processor) {
return OAKRENDER_E_NOT_FOUND;
}
return write_string(processor->getCacheID(), buf, n);
} catch (ocio::Exception &) {
} catch (OCIO_NAMESPACE::Exception &) {
return OAKRENDER_E_NOT_FOUND;
} catch (...) {
return OAKRENDER_E_FAILED;
}
}
int oakrender_color_processor_convert_frame(OakColorProcessor *processor,
OakCodecFrame *frame)
{
if (!processor || !processor->ptr || !frame || !frame->ptr) {
return OAKRENDER_E_INVALID;
}
try {
// In-place: ColorProcessor::convert_frame() applies the CPU
// processor to the frame's pixel buffer through an
// OCIO::PackedImageDesc view. A processor whose underlying OCIO
// processor is null (creation failure was non-fatal) is a
// pass-through and still reports success, mirroring the C++ API.
processor->ptr->convert_frame(frame->ptr);
return OAKRENDER_OK;
} catch (...) {
return OAKRENDER_E_FAILED;
}
}