Files
oak-editor/app/codec/decoder.h
T
Mike-Solar bb40b4923e style: unify identifier naming per updated conventions
Automated with clang-tidy readability-identifier-naming (config added to
.clang-tidy) plus scripted passes, per the updated rules now documented
in CONTRIBUTING.md:

- types (class/struct/enum/alias/template params): PascalCase
- functions, variables, members: snake_case (incl. rational -> Rational)
- private/protected members: trailing underscore; static member
  variables likewise (instance_, available_themes_)
- constants and enum values: snake_case (kLinear -> k_linear,
  F32P -> f32p); ALL_CAPS reserved for macros
- macros: OAK_ prefix (OLIVE_ADD_TEST/OLIVE_ASSERT/OLIVE_CONFIG ->
  OAK_ADD_TEST/OAK_ASSERT/OAK_CONFIG, GL_PREAMBLE -> OAK_GL_PREAMBLE,
  include guards -> OAK_*)
- file names: all lowercase (Current/Plugin/OliveHost/OliveClip/
  OlivePluginInstance -> current/plugin/olivehost/oliveclip/
  oliveplugininstance)
- getters share the member name sans underscore, setters set_foo()
- Qt and third-party (OpenFX) virtual overrides and framework callbacks
  keep their original names (exempt in .clang-tidy)

Manual follow-ups required where automation could not reach:
- string-based QMetaObject/SIGNAL/SLOT references updated to renamed
  methods (AddTask, CreatedFile, DeleteSpecificFile, moveSelectionUp, ...)
- macro bodies referencing renamed methods (OLIVE_CONFIG,
  NODE_DEFAULT_DESTRUCTOR, MANAGEDDISPLAYWIDGET_*)
- self-shadowing locals renamed where signals/methods became same-named
  (size_changed, worker_count, selected_items, import param, filters)
- third_party OFX member/namespace usages restored (OFX::Host::*,
  _created, _clipPrefsDirty, createInstance, clearPersistentMessage)
- STL protocol aliases restored (const_iterator) with .clang-tidy
  ignore rules; qHash overloads restored

Full build and test suite pass: ctest 4/4, ~1960 gtest cases green.
2026-07-19 16:10:54 +08:00

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 &params, 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 &params,
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 &params,
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 &params);
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