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:
@@ -0,0 +1,212 @@
|
||||
/***
|
||||
|
||||
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_CODEC_DECODER_H
|
||||
#define OAK_EDITOR_CODEC_DECODER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "error.h"
|
||||
#include "frame.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file decoder.h
|
||||
* @brief C ABI for oakcodec media decoders (olive::Decoder and its
|
||||
* FFmpeg/OIIO implementations): probing, stream enumeration and
|
||||
* CPU-frame decoding.
|
||||
*
|
||||
* Handles follow the neutral by-value convention documented in frame.h
|
||||
* (and oakcommon's common/handle.h). Two usage patterns share the
|
||||
* OakDecoder handle:
|
||||
*
|
||||
* - Probe: oakcodec_decoder_probe() inspects a file WITHOUT opening a
|
||||
* decode session; the stream getters describe what was found.
|
||||
* - Decode: oakcodec_decoder_init() + oakcodec_decoder_open() attach a
|
||||
* decoder instance to one (filename, stream) pair; the decode
|
||||
* functions then produce frames/audio.
|
||||
*/
|
||||
|
||||
typedef struct OakDecoder {
|
||||
void *ctx; /**< Opaque pointer to the reference-counted object. */
|
||||
void (*addref)(void *ctx); /**< Atomically increments the count. */
|
||||
void (*release)(void *ctx); /**< Decrements the count, destroys at 0. */
|
||||
uint32_t abi_version; /**< OAKCODEC_ABI_VERSION. */
|
||||
} OakDecoder;
|
||||
|
||||
/**
|
||||
* @brief POD description of one probed video stream.
|
||||
*
|
||||
* duration_ts counts units of the stream's time base;
|
||||
* time_base_num/den is seconds per time-base unit. color_primaries and
|
||||
* color_trc carry the ISO/IEC 23001-8 code points the decoder reports
|
||||
* (0 = unknown). interlaced is 1 when the stream is interlaced.
|
||||
* format is an OakPixelFormat value (the decoder's native delivery
|
||||
* format), channel_count its plane channel count.
|
||||
*/
|
||||
typedef struct oakcodec_video_stream_info {
|
||||
int stream_index;
|
||||
int width;
|
||||
int height;
|
||||
int frame_rate_num;
|
||||
int frame_rate_den;
|
||||
int64_t duration_ts;
|
||||
int time_base_num;
|
||||
int time_base_den;
|
||||
int format;
|
||||
int channel_count;
|
||||
int color_primaries;
|
||||
int color_trc;
|
||||
int interlaced;
|
||||
} oakcodec_video_stream_info;
|
||||
|
||||
/**
|
||||
* @brief POD description of one probed audio stream.
|
||||
*
|
||||
* channel_layout is the ffmpeg-style channel mask (e.g. 0x3 = stereo).
|
||||
*/
|
||||
typedef struct oakcodec_audio_stream_info {
|
||||
int stream_index;
|
||||
int sample_rate;
|
||||
uint64_t channel_layout;
|
||||
int channel_count;
|
||||
int64_t duration_ts;
|
||||
int time_base_num;
|
||||
int time_base_den;
|
||||
} oakcodec_audio_stream_info;
|
||||
|
||||
/* ---- Probe (stateless inspection) ---------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Probe a media file: decoder name plus stream inventory.
|
||||
*
|
||||
* Tries each available decoder implementation (FFmpeg, then OIIO) and
|
||||
* wraps the first one that recognizes the file. The returned handle only
|
||||
* carries probe results; it cannot decode (use init + open for that).
|
||||
*
|
||||
* @return Handle with reference count 1, or an empty handle (ctx == NULL)
|
||||
* when no decoder recognizes the file (oakcodec_probe_last_error()
|
||||
* carries the reason).
|
||||
*/
|
||||
OAKCODEC_API OakDecoder oakcodec_decoder_probe(const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Thread-local error detail of the last failed probe on this
|
||||
* thread (buf/size string getter convention).
|
||||
*/
|
||||
OAKCODEC_API int oakcodec_probe_last_error(char *buf, int buf_size);
|
||||
|
||||
/** @brief Probed decoder id ("ffmpeg"/"oiio", buf/size getter). */
|
||||
OAKCODEC_API int oakcodec_decoder_probe_decoder_name(OakDecoder probe, char *buf,
|
||||
int buf_size);
|
||||
|
||||
OAKCODEC_API int oakcodec_decoder_probe_video_stream_count(OakDecoder probe);
|
||||
OAKCODEC_API int oakcodec_decoder_probe_audio_stream_count(OakDecoder probe);
|
||||
OAKCODEC_API int oakcodec_decoder_probe_subtitle_stream_count(OakDecoder probe);
|
||||
|
||||
/**
|
||||
* @brief Fill `out` with the video stream at `index` (0-based within the
|
||||
* video stream list).
|
||||
*
|
||||
* @return OAKCODEC_OK, OAKCODEC_E_INVALID, or OAKCODEC_E_NOT_FOUND when
|
||||
* index is out of range.
|
||||
*/
|
||||
OAKCODEC_API int oakcodec_decoder_probe_get_video_stream(OakDecoder probe, int index,
|
||||
oakcodec_video_stream_info *out);
|
||||
OAKCODEC_API int oakcodec_decoder_probe_get_audio_stream(OakDecoder probe, int index,
|
||||
oakcodec_audio_stream_info *out);
|
||||
|
||||
/* ---- Decode session ------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Create a closed decoder handle (count 1).
|
||||
*/
|
||||
OAKCODEC_API OakDecoder oakcodec_decoder_init(void);
|
||||
|
||||
/**
|
||||
* @brief Release one reference to a decoder. No-op on NULL/empty.
|
||||
*/
|
||||
OAKCODEC_API void oakcodec_decoder_free(OakDecoder *decoder);
|
||||
|
||||
/**
|
||||
* @brief Open `filename`'s stream `stream_index` for decoding.
|
||||
*
|
||||
* The decoder implementation is chosen automatically from the probe
|
||||
* results. Opening an already-open decoder on the same stream is a
|
||||
* successful no-op.
|
||||
*
|
||||
* @return OAKCODEC_OK on success, OAKCODEC_E_NOT_FOUND when the file
|
||||
* does not exist, OAKCODEC_E_FAILED otherwise (see
|
||||
* oakcodec_decoder_last_error()).
|
||||
*/
|
||||
OAKCODEC_API int oakcodec_decoder_open(OakDecoder decoder, const char *filename,
|
||||
int stream_index);
|
||||
|
||||
/** @brief Close the current stream (safe when closed). */
|
||||
OAKCODEC_API int oakcodec_decoder_close(OakDecoder decoder);
|
||||
|
||||
/** @brief 1 when a stream is open, 0 otherwise. */
|
||||
OAKCODEC_API int oakcodec_decoder_is_open(OakDecoder decoder);
|
||||
|
||||
/**
|
||||
* @brief Decode the video frame at `numerator/denominator` seconds.
|
||||
*
|
||||
* Before the start of the footage the first frame is returned, after the
|
||||
* end the last frame.
|
||||
*
|
||||
* @return A frame handle with reference count 1 (caller releases), or an
|
||||
* empty handle (ctx == NULL) on error/EOF — check
|
||||
* oakcodec_decoder_last_error().
|
||||
*/
|
||||
OAKCODEC_API OakFrame oakcodec_decoder_decode_video(OakDecoder decoder, int numerator,
|
||||
int denominator);
|
||||
|
||||
/**
|
||||
* @brief Decode audio into a float buffer.
|
||||
*
|
||||
* Decodes the interleaved audio covering [in, out) seconds (rational
|
||||
* pairs), resampled/laid out to `sample_rate`/`channel_layout`.
|
||||
* `buf` must hold at least `buf_frames` frames worth of interleaved
|
||||
* floats.
|
||||
*
|
||||
* @return The number of frames written (>= 0), or a negative
|
||||
* OAKCODEC_E_* code. Conform generation is NOT triggered by this
|
||||
* family in the current intermediate state (no task registrar);
|
||||
* media requiring a conform yields OAKCODEC_E_STATE.
|
||||
*/
|
||||
OAKCODEC_API int oakcodec_decoder_decode_audio(OakDecoder decoder, int in_num, int in_den,
|
||||
int out_num, int out_den, int sample_rate,
|
||||
uint64_t channel_layout, float *buf,
|
||||
int buf_frames);
|
||||
|
||||
/**
|
||||
* @brief Human-readable detail of the last error on this decoder
|
||||
* (buf/size string getter convention).
|
||||
*/
|
||||
OAKCODEC_API int oakcodec_decoder_last_error(OakDecoder decoder, char *buf, int buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //OAK_EDITOR_CODEC_DECODER_H
|
||||
Reference in New Issue
Block a user