refactor(node): switch oaknode to refcounted value handles, migrate consumers

- all 15 OakNode* handle types become neutral by-value structs
  {ctx, addref, release, abi_version}; shared box in
  src/node/c_api/nodehandle.h with owns flag (borrowed accessors
  return non-owning boxes; graph insertion flips owns off)
- oaktimeline/oaktask/oakrender call sites and their own public
  headers migrated to value handles; identity comparisons in
  timeline/task now compare native pointers
- regressions green: oaknode 96, oaktimeline 117, oaktask 106,
  oakrender 44, oakcommon 193, oakcodec 18, oakaudio 36
This commit is contained in:
2026-08-07 17:20:42 +08:00
parent 295ea1bfe5
commit 67176281d9
87 changed files with 4499 additions and 3431 deletions
+19 -13
View File
@@ -22,6 +22,7 @@
#include <new>
#include "../../node/c_api/nodehandle.h"
#include "../src/projectcopier.h"
namespace
@@ -50,36 +51,41 @@ void oakrender_project_copier_free(OakRenderProjectCopier *copier)
}
int oakrender_project_copier_set_project(OakRenderProjectCopier *copier,
OakNodeProject *project)
OakNodeProject project)
{
if (!copier || !project) {
if (!copier || !project.ctx) {
return OAKRENDER_E_INVALID;
}
try {
impl(copier)->set_project(
reinterpret_cast<olive::Project *>(project));
oaknode_c_api::to_native<olive::Project>(project));
return OAKRENDER_OK;
} catch (...) {
return OAKRENDER_E_FAILED;
}
}
OakNodeNode *oakrender_project_copier_get_copy(
OakRenderProjectCopier *copier, OakNodeNode *original)
OakNodeNode oakrender_project_copier_get_copy(
OakRenderProjectCopier *copier, OakNodeNode original)
{
if (!copier || !original) {
return NULL;
if (!copier || !original.ctx) {
return OakNodeNode{};
}
return reinterpret_cast<OakNodeNode *>(
impl(copier)->get_copy(reinterpret_cast<olive::Node *>(original)));
// Borrowed handle: releasing it only destroys the handle box, never
// the copied node (owned by the copier's copied project).
return oaknode_c_api::make_handle<OakNodeNode>(
impl(copier)->get_copy(oaknode_c_api::to_native<olive::Node>(original)),
false, &oaknode_c_api::delete_as<olive::Node>);
}
OakNodeProject *oakrender_project_copier_get_copied_project(
OakNodeProject oakrender_project_copier_get_copied_project(
OakRenderProjectCopier *copier)
{
if (!copier) {
return NULL;
return OakNodeProject{};
}
return reinterpret_cast<OakNodeProject *>(
impl(copier)->get_copied_project());
// Borrowed handle: the copied project is owned by the copier.
return oaknode_c_api::make_handle<OakNodeProject>(
impl(copier)->get_copied_project(), false,
&oaknode_c_api::delete_as<olive::Project>);
}
+9 -5
View File
@@ -30,6 +30,8 @@
#include "alivecount.h"
#include "internalhandles.h"
#include "../../node/c_api/nodehandle.h"
#include "diskmanager.h"
#include "output/viewer/viewer.h"
#include "previewautocacher.h"
@@ -75,10 +77,10 @@ void oakrender_manager_shutdown(void)
}
}
int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts,
int64_t oakrender_request_frame(OakNodeNode viewer, int64_t ts,
oakrender_frame_ready_fn cb, void *userdata)
{
if (!viewer || !cb) {
if (!viewer.ctx || !cb) {
return OAKRENDER_E_INVALID;
}
olive::RenderManager *manager = olive::RenderManager::instance();
@@ -86,7 +88,7 @@ int64_t oakrender_request_frame(OakNodeNode *viewer, int64_t ts,
return OAKRENDER_E_STATE;
}
auto *v = dynamic_cast<olive::ViewerOutput *>(
reinterpret_cast<olive::Node *>(viewer));
oaknode_c_api::to_native<olive::Node>(viewer));
if (!v) {
return OAKRENDER_E_INVALID;
}
@@ -157,14 +159,16 @@ int oakrender_cancel_request(int64_t request_id)
return OAKRENDER_OK;
}
int oakrender_set_cacher_multicam(OakNodeNode *multicam_or_NULL)
int oakrender_set_cacher_multicam(OakNodeNode multicam_or_NULL)
{
olive::RenderManager *manager = olive::RenderManager::instance();
if (!manager) {
return OAKRENDER_E_STATE;
}
manager->get_cacher()->set_multicam_node(
reinterpret_cast<olive::MultiCamNode *>(multicam_or_NULL));
multicam_or_NULL.ctx ?
oaknode_c_api::to_native<olive::MultiCamNode>(multicam_or_NULL) :
nullptr);
return OAKRENDER_OK;
}
+10 -6
View File
@@ -26,6 +26,7 @@
#include "../src/framehashcache.h"
#include "../src/rendermanager.h"
#include "../src/renderticket.h"
#include "../../node/c_api/nodehandle.h"
#include "internalhandles.h"
namespace
@@ -59,9 +60,9 @@ OakRenderTicket *wrap(olive::RenderTicketWatcher *w,
return reinterpret_cast<OakRenderTicket *>(h);
}
olive::Node *to_node(OakNodeNode *n)
olive::Node *to_node(OakNodeNode n)
{
return reinterpret_cast<olive::Node *>(n);
return oaknode_c_api::to_native<olive::Node>(n);
}
} // namespace
@@ -70,7 +71,7 @@ OakRenderTicket *oakrender_ticket_render_frame(
const oakrender_video_ticket_params *params,
oakrender_ticket_finished_fn cb, void *userdata)
{
if (!params || !params->output_node) {
if (!params || !params->output_node.ctx) {
return NULL;
}
@@ -96,7 +97,10 @@ OakRenderTicket *oakrender_ticket_render_frame(
: olive::AudioParams(),
olive::core::Rational(int(params->time_num),
int(params->time_den)),
reinterpret_cast<olive::ColorManager *>(params->color_manager),
params->color_manager.ctx ?
oaknode_c_api::to_native<olive::ColorManager>(
params->color_manager) :
nullptr,
static_cast<olive::RenderMode::Mode>(params->mode));
rvp.force_size =
@@ -161,11 +165,11 @@ OakRenderTicket *oakrender_ticket_render_frame(
}
OakRenderTicket *oakrender_ticket_render_audio(
OakNodeNode *output_node, int64_t in_num, int64_t in_den,
OakNodeNode output_node, int64_t in_num, int64_t in_den,
int64_t out_num, int64_t out_den, const OakAudioParams *params,
int mode, oakrender_ticket_finished_fn cb, void *userdata)
{
if (!output_node || !params) {
if (!output_node.ctx || !params) {
return NULL;
}
+9 -8
View File
@@ -57,30 +57,31 @@ TEST(OakRenderManagerTest, InitShutdown)
TEST(OakRenderManagerTest, RequestFrameRequiresManager)
{
ASSERT_EQ(oaknode_factory_initialize(), OAKNODE_OK);
OakNodeNode *viewer = oaknode_factory_create_from_id(
OakNodeNode viewer = oaknode_factory_create_from_id(
"org.olivevideoeditor.Olive.vieweroutput");
ASSERT_NE(viewer, nullptr);
ASSERT_NE(viewer.ctx, nullptr);
// No oakrender_manager_init() in this process: E_STATE
EXPECT_EQ(oakrender_request_frame(viewer, 0, noop_frame_ready, nullptr),
int64_t(OAKRENDER_E_STATE));
oaknode_node_free(viewer);
oaknode_node_free(&viewer);
oaknode_factory_destroy();
}
TEST(OakRenderManagerTest, RequestFrameInvalidArgs)
{
EXPECT_EQ(oakrender_request_frame(nullptr, 0, noop_frame_ready, nullptr),
EXPECT_EQ(oakrender_request_frame(OakNodeNode{}, 0, noop_frame_ready,
nullptr),
int64_t(OAKRENDER_E_INVALID));
ASSERT_EQ(oaknode_factory_initialize(), OAKNODE_OK);
OakNodeNode *viewer = oaknode_factory_create_from_id(
OakNodeNode viewer = oaknode_factory_create_from_id(
"org.olivevideoeditor.Olive.vieweroutput");
ASSERT_NE(viewer, nullptr);
ASSERT_NE(viewer.ctx, nullptr);
EXPECT_EQ(oakrender_request_frame(viewer, 0, nullptr, nullptr),
int64_t(OAKRENDER_E_INVALID));
oaknode_node_free(viewer);
oaknode_node_free(&viewer);
oaknode_factory_destroy();
}
@@ -98,7 +99,7 @@ TEST(OakRenderManagerTest, CancelUnknownRequest)
TEST(OakRenderManagerTest, CacherSettersRequireManager)
{
EXPECT_EQ(oakrender_set_cacher_multicam(nullptr), OAKRENDER_E_STATE);
EXPECT_EQ(oakrender_set_cacher_multicam(OakNodeNode{}), OAKRENDER_E_STATE);
EXPECT_EQ(oakrender_set_display_color_processor(nullptr),
OAKRENDER_E_STATE);
}