Physical split: app/{audio,cli,codec,common,config,node,pluginSupport,
render,task,timeline,undo,tool,shaders} plus coreengine, version and
ui/icons+colorcoding move to a new top-level engine/ tree, built as
liboakengine.so (shared). The render backends (oakgl/oakvulkan) move
with it and link the engine library instead of embedding a static
render-core subset (libolive-rendercore is gone).
- oak-render-worker now links liboakengine instead of the whole
libolive-editor object set: 336MB -> 2.9MB, no Qt Widgets UI
- the editor links liboakengine for the engine and keeps only UI
objects in libolive-editor
- install/packaging: GNUInstallDirs libdir on Linux, bundle copy on
macOS, oakengine.dll staged for NSIS, AppImage validation entry
- fix backend lookup for the new layout: DynamicRenderer searched
../app but backends now live in engine/; a stale pre-split liboakgl
in the build tree got dlopened instead, re-initialized and later
destroyed the interposed engine statics (full-suite segfault at
DialogSequenceParameterTab, found via gdb watchpoint)
347 lines
9.6 KiB
C++
347 lines
9.6 KiB
C++
/***
|
|
|
|
Olive - Non-Linear Video Editor
|
|
Copyright (C) 2022 Olive Team
|
|
Modifications Copyright (C) 2025 mikesolar
|
|
|
|
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_DECODER_H
|
|
#define OAK_DECODER_H
|
|
|
|
#include <QFileInfo>
|
|
#include <QMutex>
|
|
#include <QObject>
|
|
#include <QWaitCondition>
|
|
#include <stdint.h>
|
|
|
|
#include "codec/frame.h"
|
|
#include "node/block/block.h"
|
|
#include "node/project/footage/footagedescription.h"
|
|
#include "render/cancelatom.h"
|
|
#include "render/rendermodes.h"
|
|
|
|
namespace olive
|
|
{
|
|
|
|
class Decoder;
|
|
using DecoderPtr = std::shared_ptr<Decoder>;
|
|
|
|
#define DECODER_DEFAULT_DESTRUCTOR(x) \
|
|
virtual ~x() override \
|
|
{ \
|
|
close_internal(); \
|
|
}
|
|
|
|
/**
|
|
* @brief A decoder's is the main class for bringing external media into Olive
|
|
*
|
|
* Its responsibilities are to serve as
|
|
* abstraction from codecs/decoders and provide complete frames. These frames can be video or audio data and are
|
|
* provided as Frame objects in shared pointers to alleviate the responsibility of memory handling.
|
|
*
|
|
* The main function in a decoder is Retrieve() which should return complete image/audio data. A decoder should
|
|
* alleviate all the complexities of codec compression from the rest of the application (i.e. a decoder should never
|
|
* return a partial frame or require other parts of the system to interface directly with the codec). Often this will
|
|
* necessitate pre-emptively caching, indexing, or even fully transcoding media before using it which can be implemented
|
|
* through the Analyze() function.
|
|
*
|
|
* A decoder does NOT perform any pixel/sample format conversion. Frames should pass through the PixelService
|
|
* to be utilized in the rest of the rendering pipeline.
|
|
*/
|
|
class Decoder : public QObject {
|
|
Q_OBJECT
|
|
public:
|
|
enum RetrieveState { k_ready, k_failed_to_open, k_index_unavailable };
|
|
|
|
Decoder();
|
|
|
|
/**
|
|
* @brief Unique decoder ID
|
|
*/
|
|
virtual QString id() const = 0;
|
|
|
|
virtual bool supports_video()
|
|
{
|
|
return false;
|
|
}
|
|
virtual bool supports_audio()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void increment_access_time(qint64 t);
|
|
|
|
class CodecStream {
|
|
public:
|
|
CodecStream()
|
|
: stream_(-1)
|
|
, block_(nullptr)
|
|
{
|
|
}
|
|
|
|
CodecStream(const QString &filename, int stream, Block *block)
|
|
: filename_(filename)
|
|
, stream_(stream)
|
|
, block_(block)
|
|
{
|
|
}
|
|
|
|
bool is_valid() const
|
|
{
|
|
return !filename_.isEmpty() && stream_ >= 0;
|
|
}
|
|
|
|
bool exists() const
|
|
{
|
|
return QFileInfo::exists(filename_);
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
*this = CodecStream();
|
|
}
|
|
|
|
bool operator==(const CodecStream &rhs) const
|
|
{
|
|
return filename_ == rhs.filename_ && stream_ == rhs.stream_;
|
|
}
|
|
|
|
const QString &filename() const
|
|
{
|
|
return filename_;
|
|
}
|
|
|
|
int stream() const
|
|
{
|
|
return stream_;
|
|
}
|
|
|
|
Block *block() const
|
|
{
|
|
return block_;
|
|
}
|
|
|
|
private:
|
|
QString filename_;
|
|
|
|
int stream_;
|
|
|
|
Block *block_;
|
|
};
|
|
|
|
/**
|
|
* @brief Open stream for decoding
|
|
*
|
|
* This function is thread safe.
|
|
*
|
|
* Returns TRUE if stream could be opened successfully. Also returns TRUE if the decoder is
|
|
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
|
|
* be opened OR if already open and the stream is NOT the same.
|
|
*/
|
|
bool open(const CodecStream &stream);
|
|
|
|
static const Rational k_any_timecode;
|
|
|
|
struct RetrieveVideoParams {
|
|
Renderer *renderer = nullptr;
|
|
Rational time;
|
|
int divider = 1;
|
|
PixelFormat maximum_format = PixelFormat::invalid;
|
|
CancelAtom *cancelled = nullptr;
|
|
VideoParams::ColorRange force_range = VideoParams::k_color_range_default;
|
|
VideoParams::Interlacing src_interlacing = VideoParams::k_interlace_none;
|
|
};
|
|
|
|
/**
|
|
* @brief Retrieves a video frame from footage
|
|
*
|
|
* This function will always return a valid frame unless a fatal error occurs (in such case,
|
|
* nullptr will return). If the timecode is before the start of the footage, this function should
|
|
* return the first frame. Likewise, if it is after the timecode, this function should return the
|
|
* last frame.
|
|
*
|
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
|
*/
|
|
TexturePtr retrieve_video(const RetrieveVideoParams &p);
|
|
|
|
/**
|
|
* @brief Retrieves a decoded video frame in CPU memory.
|
|
*
|
|
* Used by render-process isolation to decode media in the main process and pass packed pixel
|
|
* data to workers through shared memory.
|
|
*/
|
|
FramePtr retrieve_video_frame(const RetrieveVideoParams &p);
|
|
|
|
enum RetrieveAudioStatus {
|
|
k_invalid = -1,
|
|
k_ok,
|
|
k_waiting_for_conform,
|
|
k_unknown_error
|
|
};
|
|
|
|
/**
|
|
* @brief Retrieve audio data from footage
|
|
*
|
|
* This function will always return a sample buffer unless a fatal error occurs (in such case,
|
|
* nullptr will return). The SampleBuffer should always have enough audio for the range provided.
|
|
*
|
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
|
*/
|
|
RetrieveAudioStatus
|
|
retrieve_audio(SampleBuffer &dest, const TimeRange &range,
|
|
const AudioParams ¶ms, const QString &cache_path,
|
|
LoopMode loop_mode, RenderMode::Mode mode);
|
|
|
|
/**
|
|
* @brief Determine the last time this decoder instance was used in any way
|
|
*/
|
|
qint64 get_last_accessed_time();
|
|
|
|
/**
|
|
* @brief Generate a Footage object from a file
|
|
*
|
|
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
|
|
* will return nullptr.
|
|
*
|
|
* For sub-classes, this function should be effectively static. We can't do virtual static
|
|
* functions in C++, but it should hold and access no state during its run.
|
|
*
|
|
* This function is re-entrant.
|
|
*/
|
|
virtual FootageDescription probe(const QString &filename,
|
|
CancelAtom *cancelled) const = 0;
|
|
|
|
/**
|
|
* @brief Closes media/deallocates memory
|
|
*
|
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
|
*/
|
|
void close();
|
|
|
|
/**
|
|
* @brief Conform audio stream
|
|
*/
|
|
bool conform_audio(const QVector<QString> &output_filenames,
|
|
const AudioParams ¶ms,
|
|
CancelAtom *cancelled = nullptr);
|
|
|
|
/**
|
|
* @brief Create a Decoder instance using a Decoder ID
|
|
*
|
|
* @return
|
|
*
|
|
* A Decoder instance or nullptr if a Decoder with this ID does not exist
|
|
*/
|
|
static DecoderPtr create_from_id(const QString &id);
|
|
|
|
static QString transform_image_sequence_file_name(const QString &filename,
|
|
const int64_t &number);
|
|
|
|
static int get_image_sequence_digit_count(const QString &filename);
|
|
|
|
static int64_t get_image_sequence_index(const QString &filename);
|
|
|
|
static QVector<DecoderPtr> receive_list_of_all_decoders();
|
|
|
|
protected:
|
|
/**
|
|
* @brief Internal open function
|
|
*
|
|
* Sub-classes must override this function. Function will already be mutexed, so there is no need
|
|
* to worry about thread safety. Also many other sanity checks will be done before this, so
|
|
* sub-classes only need to worry about their own opening functions. It is guaranteed that the
|
|
* decoder is not open yet and that the footage stream was from that sub-classes probe function.
|
|
*
|
|
* Return TRUE if everything opened successfully and the decoder is ready to work. Otherwise,
|
|
* return FALSE. If this function returns false, Decoder will call close_internal to clean any
|
|
* memory allocated during OpenInternal.
|
|
*/
|
|
virtual bool open_internal() = 0;
|
|
|
|
/**
|
|
* @brief Internal close function
|
|
*
|
|
* Sub-classes must override this function. Function should be able to safely clear all allocated
|
|
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
|
|
*/
|
|
virtual void close_internal() = 0;
|
|
|
|
/**
|
|
* @brief Internal frame retrieval function
|
|
*
|
|
* Sub-classes must override this function IF they support video. Function is already mutexed
|
|
* so sub-classes don't need to worry about thread safety.
|
|
*/
|
|
virtual TexturePtr retrieve_video_internal(const RetrieveVideoParams &p);
|
|
|
|
virtual FramePtr retrieve_video_frame_internal(const RetrieveVideoParams &p);
|
|
|
|
virtual bool conform_audio_internal(const QVector<QString> &filenames,
|
|
const AudioParams ¶ms,
|
|
CancelAtom *cancelled);
|
|
|
|
void signal_processing_progress(int64_t ts, int64_t duration);
|
|
|
|
/**
|
|
* @brief Return currently open stream
|
|
*
|
|
* This function is NOT thread safe and should therefore only be called by thread safe functions.
|
|
*/
|
|
const CodecStream &stream() const
|
|
{
|
|
return stream_;
|
|
}
|
|
|
|
virtual Rational get_audio_start_offset() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
signals:
|
|
/**
|
|
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
|
|
* available
|
|
*/
|
|
void index_progress(double);
|
|
|
|
private:
|
|
void update_last_accessed();
|
|
|
|
bool retrieve_audio_from_conform(SampleBuffer &sample_buffer,
|
|
const QVector<QString> &conform_filenames,
|
|
TimeRange range, LoopMode loop_mode,
|
|
const AudioParams ¶ms);
|
|
|
|
CodecStream stream_;
|
|
|
|
QMutex mutex_;
|
|
|
|
std::atomic_int64_t last_accessed_;
|
|
|
|
TexturePtr cached_texture_;
|
|
Rational cached_time_;
|
|
int cached_divider_;
|
|
};
|
|
|
|
uint qHash(Decoder::CodecStream stream, uint seed = 0);
|
|
|
|
}
|
|
|
|
Q_DECLARE_METATYPE(olive::Decoder::RetrieveState)
|
|
|
|
#endif // OAK_DECODER_H
|