refactor(task): de-Qt oaktask base + project tasks, wire codec task submitter

- Task base: Qt signals become lifecycle listeners (the async-command
  callback exception), cancellation uses oakrender's OakCancelAtom C
  handle; TaskManager runs std::thread workers, no signals
- ConformTask/ProxyTask implement oakcodec's submit-callback contract
  (synchronous interim); register_codec_task_submitter() closes the
  conform/proxy loop - conform of demo.mp4 produces pcm caches in tests
- ProjectImportTask/ProjectLoadTask/ProjectSaveTask over the oaknode
  C ABI; image-sequence confirmation becomes a facade callback
  (default: not a sequence)
- C API additions needed by oaktask: oaknode serializer file-level
  save/load (auto-initializing), footage video-params/cancel-atom/
  as-node, folder add-child command factory; oakcodec decoder
  conform_audio + image-sequence helpers; oakrender cancelatom
  get_native
- fix double-ownership: submitting a task to the manager transfers
  ownership, freeing its handle only releases the wrapper
- C ABI in include/task (task/manager/project, OakTaskTask opaque
  handle + lifecycle subscribe), every function tested (103 cases in
  build-oaktask incl. regressions)
- RenderTask family and OTIO tasks follow in separate commits
This commit is contained in:
2026-08-07 14:01:23 +08:00
parent 66831a870e
commit 6ded1d2d63
54 changed files with 5937 additions and 1 deletions
+33
View File
@@ -25,6 +25,7 @@
#include "error.h"
#include "frame.h"
#include "render/cancelatom.h"
#ifdef __cplusplus
extern "C" {
@@ -199,6 +200,38 @@ OAKCODEC_API int oakcodec_decoder_decode_audio(OakDecoder decoder, int in_num, i
uint64_t channel_layout, float *buf,
int buf_frames);
/**
* @brief Conform the open stream's audio into per-channel pcm cache files
* (Decoder::conform_audio()).
*
* `output_filenames` is an array of `filename_count` final per-channel
* paths. `sample_format` is olive::core::SampleFormat::Format as int.
* `cancelled` may be an empty OakCancelAtom (ctx == NULL).
*
* @return OAKCODEC_OK on success, OAKCODEC_E_STATE when no stream is
* open, OAKCODEC_E_CANCELLED when cancelled, OAKCODEC_E_FAILED
* otherwise.
*/
OAKCODEC_API int oakcodec_decoder_conform_audio(OakDecoder decoder,
const char *const *output_filenames, int filename_count,
int sample_rate, uint64_t channel_layout, int sample_format,
OakCancelAtom cancelled);
/**
* @brief Image-sequence filename heuristics (Decoder::get_image_sequence_*).
*
* digit_count: number of trailing digits in the filename stem (0 = not an
* image sequence filename). index: the numeric value of those digits (-1
* when none). transform: substitute `number` into the digit field,
* two-stage string getter.
*/
OAKCODEC_API int oakcodec_decoder_get_image_sequence_digit_count(
const char *filename);
OAKCODEC_API int64_t oakcodec_decoder_get_image_sequence_index(
const char *filename);
OAKCODEC_API int oakcodec_decoder_transform_image_sequence_file_name(
const char *filename, int64_t number, char *buf, int buf_size);
/**
* @brief Human-readable detail of the last error on this decoder
* (buf/size string getter convention).
+14
View File
@@ -22,6 +22,7 @@
#define OAK_EDITOR_NODE_FOLDER_H
#include "node/error.h"
#include "undo/undocommand.h"
#include "node/project.h"
#ifdef __cplusplus
@@ -73,6 +74,19 @@ OakNodeNode *oaknode_folder_child_at(const OakNodeFolder *folder, int index);
*/
int oaknode_folder_add_child(OakNodeFolder *folder, OakNodeNode *child);
/**
* @brief Borrowed cast from a folder handle to its node handle.
* NULL for NULL.
*/
OakNodeNode *oaknode_folder_as_node(OakNodeFolder *folder);
/**
* @brief Create an undoable FolderAddChild command. Owned; free with
* oakundo_command_free().
*/
OakUndoCommand *oaknode_command_create_folder_add_child(
OakNodeFolder *folder, OakNodeNode *child);
/**
* @brief Remove `child` from `folder` without deleting it (live,
* non-undoable; executes Folder::RemoveElementCommand::redo()).
+41
View File
@@ -23,7 +23,12 @@
#include <stdint.h>
#include "common/videoparams.h"
#include "node/error.h"
// NOTE: quoted-relative to bypass the "render/cancelatom.h" transition
// bridge (oakrender's C++ olive::CancelAtom) that shadows the C ABI
// header on oaknode's include path.
#include "../../include/render/cancelatom.h"
#include "node/project.h"
#ifdef __cplusplus
@@ -60,6 +65,12 @@ typedef struct OakNodeFootage OakNodeFootage;
OakNodeFootage *oaknode_footage_create(OakNodeProject *project,
const char *filename);
/**
* @brief Borrowed cast from a footage handle to its node handle.
* NULL for NULL.
*/
OakNodeNode *oaknode_footage_as_node(OakNodeFootage *footage);
/**
* @brief Current media path (Footage::filename()). Two-stage string getter.
*
@@ -192,6 +203,36 @@ int oaknode_footage_set_proxy(OakNodeFootage *footage, const char *path,
*/
int oaknode_footage_clear_proxy(OakNodeFootage *footage);
/**
* @brief Video stream parameters as an oakcommon video-params handle
* (ViewerOutput::get_video_params()). `out` receives a handle with
* reference count 1 (release with oakcommon_videoparams_free()).
* OAKNODE_E_NOT_FOUND for an out-of-range index.
*/
int oaknode_footage_get_video_params(OakNodeFootage *footage, int index,
OakVideoParams *out);
/**
* @brief Set a video stream's parameters from an oakcommon handle
* (ViewerOutput::set_video_params()).
*/
int oaknode_footage_set_video_params(OakNodeFootage *footage, int index,
const OakVideoParams *params);
/**
* @brief Video length as a rational pair (ViewerOutput::get_video_length()).
*/
int oaknode_footage_get_video_length(OakNodeFootage *footage,
int64_t *out_num, int64_t *out_den);
/**
* @brief Set the footage's cancellation atom used during probing
* (Footage::set_cancel_pointer()). `atom` may be an empty OakCancelAtom
* (ctx == NULL) to clear.
*/
int oaknode_footage_set_cancel_atom(OakNodeFootage *footage,
OakCancelAtom atom);
#ifdef __cplusplus
}
#endif
+41
View File
@@ -242,3 +242,44 @@ int oaknode_serializer_loaddata_connection_at(
#endif
#endif //OAK_EDITOR_NODE_SERIALIZER_H
/**
* @brief Result codes for file-level save/load (mirror
* ProjectSerializer::ResultCode; pinned by test).
*/
enum OakNodeSerializerResultCode {
OAKNODE_SERIALIZER_RESULT_SUCCESS = 0,
OAKNODE_SERIALIZER_RESULT_PROJECT_TOO_OLD = 1,
OAKNODE_SERIALIZER_RESULT_PROJECT_TOO_NEW = 2,
OAKNODE_SERIALIZER_RESULT_UNKNOWN_VERSION = 3,
OAKNODE_SERIALIZER_RESULT_FILE_ERROR = 4,
OAKNODE_SERIALIZER_RESULT_XML_ERROR = 5,
OAKNODE_SERIALIZER_RESULT_OVERWRITE_ERROR = 6,
OAKNODE_SERIALIZER_RESULT_NO_DATA = 7
};
/**
* @brief Save a project to a file (ProjectSerializer::save(), project
* type, optional OVEC compression). Layout data is not serialized
* through this API (app-layer concern, see oakstorage/M10).
*
* @param out_code Receives an OakNodeSerializerResultCode (may be NULL).
* @param details Optional two-stage buffer for the result details
* string (e.g. the fallback filename on overwrite errors).
* @return OAKNODE_OK when the result code is
* OAKNODE_SERIALIZER_RESULT_SUCCESS, OAKNODE_E_FAILED otherwise
* (details in out_code/details), OAKNODE_E_INVALID for NULL args.
*/
int oaknode_serializer_save_to_file(OakNodeProject *project,
const char *filename, int use_compression, int *out_code,
char *details, int details_size);
/**
* @brief Load a project from a file into `project`
* (ProjectSerializer::load(), project type).
*
* Same return/out-param convention as oaknode_serializer_save_to_file().
*/
int oaknode_serializer_load_from_file(OakNodeProject *project,
const char *filename, int *out_code, char *details,
int details_size);
+15 -1
View File
@@ -29,6 +29,14 @@
extern "C" {
#endif
#ifdef __cplusplus
} /* extern "C" */
namespace olive { class CancelAtom; }
extern "C" {
#endif
/**
* @brief Current ABI version stamped into every oakrender handle.
*
@@ -108,8 +116,14 @@ int oakrender_cancelatom_is_cancelled(OakCancelAtom atom, int *cancelled);
* NULL out parameter.
*/
int oakrender_cancelatom_heard_cancel(OakCancelAtom atom, int *heard);
#ifdef __cplusplus
/**
* @brief Borrowed access to the underlying C++ atom (C++ only, for
* adapter layers). Valid while the handle is held. NULL-safe.
*/
olive::CancelAtom *oakrender_cancelatom_get_native(OakCancelAtom atom);
}
#endif
+40
View File
@@ -0,0 +1,40 @@
/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_EDITOR_TASK_ERROR_H
#define OAK_EDITOR_TASK_ERROR_H
/**
* @brief Status and error codes shared by all oaktask C API families.
*
* Return-code convention (mirrors engine/include/oakengine/init.h):
* 0 (OAKTASK_OK) on success, a negative OAKTASK_E_* error code on
* failure. String getters return the required buffer size in bytes
* (including the terminating NUL) as a non-negative value instead.
*/
#define OAKTASK_OK 0 /**< Success. */
#define OAKTASK_E_INVALID (-1) /**< NULL handle or invalid argument. */
#define OAKTASK_E_STATE (-2) /**< Call not valid in the current state. */
#define OAKTASK_E_FAILED (-3) /**< The underlying operation failed. */
#define OAKTASK_E_NOT_FOUND (-4) /**< Index out of range / entry not found. */
#define OAKTASK_E_NOMEM (-5) /**< Allocation failed. */
#define OAKTASK_E_CANCELLED (-6) /**< The operation was cancelled. */
#endif //OAK_EDITOR_TASK_ERROR_H
+54
View File
@@ -0,0 +1,54 @@
/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_EDITOR_TASK_MANAGER_H
#define OAK_EDITOR_TASK_MANAGER_H
#include "task/task.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Task manager singleton lifecycle.
*/
int oaktask_manager_init(void);
void oaktask_manager_shutdown(void);
/**
* @brief Register oaktask as oakcodec's background task submitter
* (olive::register_codec_task_submitter()). Called by
* oaktask_manager_init(); exposed for manual control.
*/
int oaktask_register_codec_submitter(void);
int oaktask_manager_count(void);
/** @brief Borrowed task at index, or NULL when out of range. */
OakTaskTask *oaktask_manager_at(int i);
void oaktask_manager_delete_finished(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TASK_MANAGER_H
+85
View File
@@ -0,0 +1,85 @@
/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_EDITOR_TASK_PROJECT_H
#define OAK_EDITOR_TASK_PROJECT_H
#include "node/footage.h"
#include "node/node.h"
#include "node/project.h"
#include "task/task.h"
#include "undo/undocommand.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Project task factories and result accessors (M8 §2.2).
*/
/** @brief olive::ProjectLoadTask. */
OakTaskTask *oaktask_create_project_load(const char *filename);
/** @brief Take the loaded project (ownership transfer). */
OakNodeProject *oaktask_load_take_project(OakTaskTask *t);
/** @brief olive::ProjectSaveTask. `filename_or_NULL` overrides the
* project's own filename. */
OakTaskTask *oaktask_create_project_save(OakNodeProject *project,
const char *filename_or_NULL,
int use_compression);
/** @brief olive::ProjectImportTask. */
OakTaskTask *oaktask_create_project_import(OakNodeFolder *folder,
OakNodeProject *project,
const char *const *urls,
int url_count);
/** @brief Take the import's undo command (ownership transfer). */
OakUndoCommand *oaktask_import_take_command(OakTaskTask *t);
int oaktask_import_footage_count(OakTaskTask *t);
/** @brief Borrowed footage handle at index, NULL when out of range. */
OakNodeFootage *oaktask_import_footage_at(OakTaskTask *t, int index);
int oaktask_import_invalid_count(OakTaskTask *t);
/** @brief Invalid filename at index (two-stage). */
int oaktask_import_invalid_at(OakTaskTask *t, int index, char *buf,
int buf_size);
/**
* @brief Image-sequence confirmation callback (facade/UI concern;
* olive::ProjectImportTask::set_image_sequence_confirm_callback).
* Return non-zero to treat numbered stills as a sequence.
* Default (no callback): not a sequence.
*/
typedef int (*oaktask_image_sequence_confirm_fn)(const char *filename,
void *userdata);
void oaktask_import_set_image_sequence_confirm_cb(
oaktask_image_sequence_confirm_fn fn, void *userdata);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TASK_PROJECT_H
+97
View File
@@ -0,0 +1,97 @@
/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_EDITOR_TASK_TASK_H
#define OAK_EDITOR_TASK_TASK_H
#include <stdint.h>
#include "task/error.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle to a background task (olive::Task).
*
* Tasks are created through the factories in task/project.h (and future
* family headers) and must be released with oaktask_task_free().
*/
typedef struct OakTaskTask OakTaskTask;
/** @brief Lifecycle event ids for oaktask_task_subscribe(). */
enum OakTaskEvent {
OAKTASK_EVENT_STARTED = 0,
OAKTASK_EVENT_PROGRESS = 1,
OAKTASK_EVENT_FINISHED = 2
};
/**
* @brief Event callback (async command return channel, 01 §4 exception).
*
* For OAKTASK_EVENT_FINISHED, `value` is 1.0 on success / 0.0 on failure;
* for OAKTASK_EVENT_PROGRESS it is 0..1; for OAKTASK_EVENT_STARTED it is
* the start time in milliseconds.
*/
typedef void (*oaktask_event_fn)(int event_id, double value,
void *userdata);
/**
* @brief Free a task. No-op on NULL. The task must not be running on the
* manager (oaktask_task_cancel + wait first if it is).
*/
void oaktask_task_free(OakTaskTask *t);
/** @brief Run synchronously in the calling thread. 1 = succeeded. */
int oaktask_task_start_sync(OakTaskTask *t);
/** @brief Run asynchronously on the task manager. */
int oaktask_task_start(OakTaskTask *t);
int oaktask_task_cancel(OakTaskTask *t);
/** @brief Wait for an asynchronously started task. */
int oaktask_task_wait(OakTaskTask *t);
int oaktask_task_is_finished(const OakTaskTask *t);
int oaktask_task_succeeded(const OakTaskTask *t);
/** @brief Two-stage string getters. */
int oaktask_task_title(OakTaskTask *t, char *buf, int buf_size);
int oaktask_task_error(OakTaskTask *t, char *buf, int buf_size);
/**
* @brief Subscribe to lifecycle events (returns a subscription id >= 0,
* or a negative error code). One-shot per event stream: the
* subscription is dropped after OAKTASK_EVENT_FINISHED.
*/
int64_t oaktask_task_subscribe(OakTaskTask *t, oaktask_event_fn fn,
void *userdata);
/** @brief Alive-count for leak assertions in tests. */
int oaktask_debug_alive_count(void);
#ifdef __cplusplus
}
#endif
#endif //OAK_EDITOR_TASK_TASK_H