refactor: improve error-checking; add abstractions and factories

Signed-off-by: k4yt3x <i@k4yt3x.com>
This commit is contained in:
k4yt3x 2024-12-01 00:00:00 +00:00
parent 72c88244eb
commit a57c7979fa
No known key found for this signature in database
13 changed files with 564 additions and 383 deletions

View File

@ -47,6 +47,15 @@ class FilterLibplacebo : public Filter {
// Returns the filter's type
ProcessorType get_processor_type() const override { return PROCESSOR_LIBPLACEBO; }
// Returns the filter's output dimensions
void get_output_dimensions(
const ProcessorConfig *processor_config,
int in_width,
int in_height,
int &out_width,
int &out_height
) const override;
};
#endif // FILTER_LIBPLACEBO_H

View File

@ -41,6 +41,15 @@ class FilterRealesrgan : public Filter {
// Returns the filter's type
ProcessorType get_processor_type() const override { return PROCESSOR_REALESRGAN; }
// Returns the filter's output dimensions
void get_output_dimensions(
const ProcessorConfig *processor_config,
int in_width,
int in_height,
int &out_width,
int &out_height
) const override;
};
#endif // FILTER_REALESRGAN_H

View File

@ -0,0 +1,19 @@
#ifndef FRAMES_PROCESSOR_H
#define FRAMES_PROCESSOR_H
#include "decoder.h"
#include "encoder.h"
#include "libvideo2x.h"
#include "processor.h"
int process_frames(
const EncoderConfig *encoder_config,
const ProcessorConfig *processor_config,
VideoProcessingContext *proc_ctx,
Decoder &decoder,
Encoder &encoder,
Processor *processor,
bool benchmark = false
);
#endif // FRAMES_PROCESSOR_H

View File

@ -41,15 +41,24 @@ class InterpolatorRIFE : public Interpolator {
// Destructor
virtual ~InterpolatorRIFE() override;
// Initializes the processor with decoder and encoder contexts
// Initializes the interpolator with decoder and encoder contexts
int init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *hw_ctx) override;
// Processes an input frame and returns the processed frame
int interpolate(AVFrame *prev_frame, AVFrame *in_frame, AVFrame **out_frame, float time_step)
override;
// Returns the processor's type
// Returns the interpolator's type
ProcessorType get_processor_type() const override { return PROCESSOR_RIFE; }
// Returns the interpolator's output dimensions
void get_output_dimensions(
const ProcessorConfig *processor_config,
int in_width,
int in_height,
int &out_width,
int &out_height
) const override;
};
#endif // INTERPOLATOR_RIFE_H

View File

@ -18,6 +18,13 @@ class Processor {
virtual int flush(std::vector<AVFrame *> &_) { return 0; }
virtual ProcessingMode get_processing_mode() const = 0;
virtual ProcessorType get_processor_type() const = 0;
virtual void get_output_dimensions(
const ProcessorConfig *processor_config,
int in_width,
int in_height,
int &width,
int &height
) const = 0;
};
// Abstract base class for filters

View File

@ -0,0 +1,36 @@
#ifndef PROCESSOR_FACTORY_H
#define PROCESSOR_FACTORY_H
#include <functional>
#include <memory>
#include <unordered_map>
#include "processor.h"
// Processor Factory Class
class ProcessorFactory {
public:
using Creator = std::function<std::unique_ptr<Processor>(const ProcessorConfig *, uint32_t)>;
// Singleton instance accessor
static ProcessorFactory &instance();
// Register a processor type with its creation function
void register_processor(ProcessorType type, Creator creator);
// Create a processor instance based on configuration
std::unique_ptr<Processor>
create_processor(const ProcessorConfig *processor_config, uint32_t vk_device_index) const;
private:
// Private constructor for Singleton
ProcessorFactory() = default;
// Map of processor types to their creation functions
std::unordered_map<ProcessorType, Creator> creators;
// Static initializer for default processors
static void init_default_processors(ProcessorFactory &factory);
};
#endif // PROCESSOR_FACTORY_H

View File

@ -239,6 +239,13 @@ int Encoder::init(
}
}
// Write the output file header
ret = avformat_write_header(ofmt_ctx_, nullptr);
if (ret < 0) {
spdlog::error("Error writing output file header");
return ret;
}
return 0;
}

View File

@ -11,16 +11,16 @@
FilterLibplacebo::FilterLibplacebo(
uint32_t vk_device_index,
const std::filesystem::path &shader_path,
int out_width,
int out_height
int width,
int height
)
: filter_graph_(nullptr),
buffersrc_ctx_(nullptr),
buffersink_ctx_(nullptr),
vk_device_index_(vk_device_index),
shader_path_(std::move(shader_path)),
width_(out_width),
height_(out_height) {}
width_(width),
height_(height) {}
FilterLibplacebo::~FilterLibplacebo() {
if (buffersrc_ctx_) {
@ -37,7 +37,7 @@ FilterLibplacebo::~FilterLibplacebo() {
}
}
int FilterLibplacebo::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *_) {
int FilterLibplacebo::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *) {
// Construct the shader path
std::filesystem::path shader_full_path;
if (filepath_is_readable(shader_path_)) {
@ -146,3 +146,14 @@ int FilterLibplacebo::flush(std::vector<AVFrame *> &flushed_frames) {
return 0;
}
void FilterLibplacebo::get_output_dimensions(
const ProcessorConfig *processor_config,
int,
int,
int &out_width,
int &out_height
) const {
out_width = processor_config->width;
out_height = processor_config->height;
}

View File

@ -119,3 +119,14 @@ int FilterRealesrgan::filter(AVFrame *in_frame, AVFrame **out_frame) {
// Return the processed frame to the caller
return ret;
}
void FilterRealesrgan::get_output_dimensions(
const ProcessorConfig *,
int in_width,
int in_height,
int &out_width,
int &out_height
) const {
out_width = in_width * scaling_factor_;
out_height = in_height * scaling_factor_;
}

277
src/frames_processor.cpp Normal file
View File

@ -0,0 +1,277 @@
#include "frames_processor.h"
extern "C" {
#include <libavutil/avutil.h>
}
#include <spdlog/spdlog.h>
#include "avutils.h"
// Process frames using the selected filter.
int process_frames(
const EncoderConfig *encoder_config,
const ProcessorConfig *processor_config,
VideoProcessingContext *proc_ctx,
Decoder &decoder,
Encoder &encoder,
Processor *processor,
bool benchmark
) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
int ret = 0;
// Get required objects
AVFormatContext *ifmt_ctx = decoder.get_format_context();
AVCodecContext *dec_ctx = decoder.get_codec_context();
int in_vstream_idx = decoder.get_video_stream_index();
AVFormatContext *ofmt_ctx = encoder.get_format_context();
int *stream_map = encoder.get_stream_map();
// Get total number of frames
spdlog::debug("Reading total number of frames");
proc_ctx->total_frames = get_video_frame_count(ifmt_ctx, in_vstream_idx);
if (proc_ctx->total_frames <= 0) {
spdlog::warn("Unable to determine the total number of frames");
proc_ctx->total_frames = 0;
} else {
spdlog::debug("{} frames to process", proc_ctx->total_frames);
}
// Set total frames for interpolation
if (processor->get_processing_mode() == PROCESSING_MODE_INTERPOLATE) {
proc_ctx->total_frames *= processor_config->frm_rate_mul;
}
// Allocate AVFrame for the current processed frame
auto av_frame_deleter = [](AVFrame *frame) { av_frame_free(&frame); };
std::unique_ptr<AVFrame, decltype(av_frame_deleter)> frame(av_frame_alloc(), av_frame_deleter);
if (!frame) {
ret = AVERROR(ENOMEM);
return ret;
}
// Allocate AVFrame for the previous processed frame
auto av_frame_deleter_prev = [](AVFrame *prev_frame) {
if (prev_frame != nullptr) {
av_frame_unref(prev_frame);
prev_frame = nullptr;
}
};
std::unique_ptr<AVFrame, decltype(av_frame_deleter_prev)> prev_frame(
nullptr, av_frame_deleter_prev
);
// Allocate AVPacket for reading frames
auto av_packet_deleter = [](AVPacket *packet) {
av_packet_unref(packet);
av_packet_free(&packet);
};
std::unique_ptr<AVPacket, decltype(av_packet_deleter)> packet(
av_packet_alloc(), av_packet_deleter
);
if (!packet) {
spdlog::critical("Could not allocate AVPacket");
return AVERROR(ENOMEM);
}
// Read frames from the input file
while (!proc_ctx->abort) {
ret = av_read_frame(ifmt_ctx, packet.get());
if (ret < 0) {
if (ret == AVERROR_EOF) {
spdlog::debug("Reached end of file");
break;
}
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error reading packet: {}", errbuf);
return ret;
}
if (packet->stream_index == in_vstream_idx) {
ret = avcodec_send_packet(dec_ctx, packet.get());
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error sending packet to decoder: {}", errbuf);
return ret;
}
while (!proc_ctx->abort) {
if (proc_ctx->pause) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
ret = avcodec_receive_frame(dec_ctx, frame.get());
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
spdlog::debug("Frame not ready");
break;
} else if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error decoding video frame: {}", errbuf);
return ret;
}
AVFrame *raw_processed_frame = nullptr;
switch (processor->get_processing_mode()) {
case PROCESSING_MODE_FILTER: {
Filter *filter = static_cast<Filter *>(processor);
ret = filter->filter(frame.get(), &raw_processed_frame);
if (ret < 0 && ret != AVERROR(EAGAIN)) {
av_strerror(ret, errbuf, sizeof(errbuf));
return ret;
} else if (ret == 0 && raw_processed_frame != nullptr) {
auto processed_frame =
std::unique_ptr<AVFrame, decltype(av_frame_deleter)>(
raw_processed_frame, av_frame_deleter
);
if (!benchmark) {
ret = encoder.write_frame(
processed_frame.get(), proc_ctx->processed_frames
);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing frame: {}", errbuf);
return ret;
}
}
}
break;
}
case PROCESSING_MODE_INTERPOLATE: {
Interpolator *interpolator = static_cast<Interpolator *>(processor);
float time_step = 1.0f / static_cast<float>(processor_config->frm_rate_mul);
float current_time_step = time_step;
bool skip_frame = false;
if (prev_frame != nullptr) {
float frame_diff = get_frame_diff(prev_frame.get(), frame.get());
if (frame_diff > processor_config->scn_det_thresh) {
spdlog::debug(
"Scene change detected ({:.2f}%), skipping frame {}",
frame_diff,
proc_ctx->processed_frames
);
skip_frame = true;
}
}
while (prev_frame != nullptr && current_time_step < 1.0f) {
if (!skip_frame) {
ret = interpolator->interpolate(
prev_frame.get(),
frame.get(),
&raw_processed_frame,
current_time_step
);
} else {
ret = 0;
raw_processed_frame = av_frame_clone(prev_frame.get());
}
if (ret < 0 && ret != AVERROR(EAGAIN)) {
av_strerror(ret, errbuf, sizeof(errbuf));
return ret;
} else if (ret == 0 && raw_processed_frame != nullptr) {
auto processed_frame =
std::unique_ptr<AVFrame, decltype(av_frame_deleter)>(
raw_processed_frame, av_frame_deleter
);
if (!benchmark) {
processed_frame->pts = proc_ctx->processed_frames;
ret = encoder.write_frame(
processed_frame.get(), proc_ctx->processed_frames
);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical(
"Error encoding/writing frame: {}", errbuf
);
return ret;
}
}
}
proc_ctx->processed_frames++;
current_time_step += time_step;
}
if (!benchmark) {
frame->pts = proc_ctx->processed_frames;
ret = encoder.write_frame(frame.get(), proc_ctx->processed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing frame: {}", errbuf);
return ret;
}
}
prev_frame.reset(av_frame_clone(frame.get()));
break;
}
default:
spdlog::critical("Unknown processing mode");
return -1;
}
av_frame_unref(frame.get());
proc_ctx->processed_frames++;
spdlog::debug(
"Processed frame {}/{}", proc_ctx->processed_frames, proc_ctx->total_frames
);
}
} else if (encoder_config->copy_streams && stream_map[packet->stream_index] >= 0) {
AVStream *in_stream = ifmt_ctx->streams[packet->stream_index];
int out_stream_index = stream_map[packet->stream_index];
AVStream *out_stream = ofmt_ctx->streams[out_stream_index];
av_packet_rescale_ts(packet.get(), in_stream->time_base, out_stream->time_base);
packet->stream_index = out_stream_index;
ret = av_interleaved_write_frame(ofmt_ctx, packet.get());
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error muxing audio/subtitle packet: {}", errbuf);
return ret;
}
}
}
// Flush the filter
std::vector<AVFrame *> raw_flushed_frames;
ret = processor->flush(raw_flushed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error flushing filter: {}", errbuf);
return ret;
}
// Wrap flushed frames in unique_ptrs
std::vector<std::unique_ptr<AVFrame, decltype(av_frame_deleter)>> flushed_frames;
for (AVFrame *raw_frame : raw_flushed_frames) {
flushed_frames.emplace_back(raw_frame, av_frame_deleter);
}
// Encode and write all flushed frames
for (auto &flushed_frame : flushed_frames) {
ret = encoder.write_frame(flushed_frame.get(), proc_ctx->processed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing flushed frame: {}", errbuf);
return ret;
}
proc_ctx->processed_frames++;
}
// Flush the encoder
ret = encoder.flush();
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error flushing encoder: {}", errbuf);
return ret;
}
return ret;
}

View File

@ -35,7 +35,7 @@ InterpolatorRIFE::~InterpolatorRIFE() {
}
}
int InterpolatorRIFE::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *_) {
int InterpolatorRIFE::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *) {
// Construct the model directory path using std::filesystem
std::filesystem::path model_param_dir;
@ -108,3 +108,14 @@ int InterpolatorRIFE::interpolate(
// Return the processed frame to the caller
return ret;
}
void InterpolatorRIFE::get_output_dimensions(
const ProcessorConfig *,
int in_width,
int in_height,
int &out_width,
int &out_height
) const {
out_width = in_width;
out_height = in_height;
}

View File

@ -1,10 +1,8 @@
#include "libvideo2x.h"
#include <libavutil/frame.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <thread>
extern "C" {
#include <libavutil/avutil.h>
@ -12,297 +10,13 @@ extern "C" {
#include <spdlog/spdlog.h>
#include "avutils.h"
#include "decoder.h"
#include "encoder.h"
#include "filter_libplacebo.h"
#include "filter_realesrgan.h"
#include "interpolator_rife.h"
#include "frames_processor.h"
#include "processor.h"
#include "processor_factory.h"
// Process frames using the selected filter.
static int process_frames(
const EncoderConfig *encoder_config,
const ProcessorConfig *processor_config,
VideoProcessingContext *proc_ctx,
Decoder &decoder,
Encoder &encoder,
Processor *processor,
bool benchmark = false
) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
int ret = 0;
// Get required objects
AVFormatContext *ifmt_ctx = decoder.get_format_context();
AVCodecContext *dec_ctx = decoder.get_codec_context();
int in_vstream_idx = decoder.get_video_stream_index();
AVFormatContext *ofmt_ctx = encoder.get_format_context();
int *stream_map = encoder.get_stream_map();
// Get total number of frames
spdlog::debug("Reading total number of frames");
proc_ctx->total_frames = get_video_frame_count(ifmt_ctx, in_vstream_idx);
if (proc_ctx->total_frames <= 0) {
spdlog::warn("Unable to determine the total number of frames");
proc_ctx->total_frames = 0;
} else {
spdlog::debug("{} frames to process", proc_ctx->total_frames);
}
// Set total frames for interpolation
if (processor->get_processing_mode() == PROCESSING_MODE_INTERPOLATE) {
proc_ctx->total_frames *= processor_config->frm_rate_mul;
}
// Allocate AVFrame for the current processed frame
auto av_frame_deleter = [](AVFrame *frame) { av_frame_free(&frame); };
std::unique_ptr<AVFrame, decltype(av_frame_deleter)> frame(av_frame_alloc(), av_frame_deleter);
if (!frame) {
ret = AVERROR(ENOMEM);
return ret;
}
// Allocate AVFrame for the previous processed frame
auto av_frame_deleter_prev = [](AVFrame *prev_frame) {
if (prev_frame != nullptr) {
av_frame_unref(prev_frame);
prev_frame = nullptr;
}
};
std::unique_ptr<AVFrame, decltype(av_frame_deleter_prev)> prev_frame(
nullptr, av_frame_deleter_prev
);
// Allocate AVPacket for reading frames
auto av_packet_deleter = [](AVPacket *packet) {
av_packet_unref(packet);
av_packet_free(&packet);
};
std::unique_ptr<AVPacket, decltype(av_packet_deleter)> packet(
av_packet_alloc(), av_packet_deleter
);
if (!packet) {
spdlog::critical("Could not allocate AVPacket");
return AVERROR(ENOMEM);
}
// Read frames from the input file
while (!proc_ctx->abort) {
ret = av_read_frame(ifmt_ctx, packet.get());
if (ret < 0) {
if (ret == AVERROR_EOF) {
spdlog::debug("Reached end of file");
break;
}
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error reading packet: {}", errbuf);
return ret;
}
if (packet->stream_index == in_vstream_idx) {
ret = avcodec_send_packet(dec_ctx, packet.get());
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error sending packet to decoder: {}", errbuf);
return ret;
}
while (!proc_ctx->abort) {
if (proc_ctx->pause) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
ret = avcodec_receive_frame(dec_ctx, frame.get());
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
spdlog::debug("Frame not ready");
break;
} else if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error decoding video frame: {}", errbuf);
return ret;
}
AVFrame *raw_processed_frame = nullptr;
switch (processor->get_processing_mode()) {
case PROCESSING_MODE_FILTER: {
Filter *filter = static_cast<Filter *>(processor);
ret = filter->filter(frame.get(), &raw_processed_frame);
if (ret < 0 && ret != AVERROR(EAGAIN)) {
av_strerror(ret, errbuf, sizeof(errbuf));
return ret;
} else if (ret == 0 && raw_processed_frame != nullptr) {
auto processed_frame =
std::unique_ptr<AVFrame, decltype(av_frame_deleter)>(
raw_processed_frame, av_frame_deleter
);
if (!benchmark) {
ret = encoder.write_frame(
processed_frame.get(), proc_ctx->processed_frames
);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing frame: {}", errbuf);
return ret;
}
}
}
break;
}
case PROCESSING_MODE_INTERPOLATE: {
Interpolator *interpolator = static_cast<Interpolator *>(processor);
float time_step = 1.0f / static_cast<float>(processor_config->frm_rate_mul);
float current_time_step = time_step;
bool skip_frame = false;
if (prev_frame != nullptr) {
float frame_diff = get_frame_diff(prev_frame.get(), frame.get());
if (frame_diff > processor_config->scn_det_thresh) {
spdlog::debug(
"Scene change detected ({:.2f}%), skipping frame {}",
frame_diff,
proc_ctx->processed_frames
);
skip_frame = true;
}
}
while (prev_frame != nullptr && current_time_step < 1.0f) {
if (!skip_frame) {
ret = interpolator->interpolate(
prev_frame.get(),
frame.get(),
&raw_processed_frame,
current_time_step
);
} else {
ret = 0;
raw_processed_frame = av_frame_clone(prev_frame.get());
}
if (ret < 0 && ret != AVERROR(EAGAIN)) {
av_strerror(ret, errbuf, sizeof(errbuf));
return ret;
} else if (ret == 0 && raw_processed_frame != nullptr) {
auto processed_frame =
std::unique_ptr<AVFrame, decltype(av_frame_deleter)>(
raw_processed_frame, av_frame_deleter
);
if (!benchmark) {
processed_frame->pts = proc_ctx->processed_frames;
ret = encoder.write_frame(
processed_frame.get(), proc_ctx->processed_frames
);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical(
"Error encoding/writing frame: {}", errbuf
);
return ret;
}
}
}
proc_ctx->processed_frames++;
current_time_step += time_step;
}
if (!benchmark) {
frame->pts = proc_ctx->processed_frames;
ret = encoder.write_frame(frame.get(), proc_ctx->processed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing frame: {}", errbuf);
return ret;
}
}
prev_frame.reset(av_frame_clone(frame.get()));
break;
}
default:
spdlog::critical("Unknown processing mode");
return -1;
}
av_frame_unref(frame.get());
proc_ctx->processed_frames++;
spdlog::debug(
"Processed frame {}/{}", proc_ctx->processed_frames, proc_ctx->total_frames
);
}
} else if (encoder_config->copy_streams && stream_map[packet->stream_index] >= 0) {
AVStream *in_stream = ifmt_ctx->streams[packet->stream_index];
int out_stream_index = stream_map[packet->stream_index];
AVStream *out_stream = ofmt_ctx->streams[out_stream_index];
av_packet_rescale_ts(packet.get(), in_stream->time_base, out_stream->time_base);
packet->stream_index = out_stream_index;
ret = av_interleaved_write_frame(ofmt_ctx, packet.get());
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error muxing audio/subtitle packet: {}", errbuf);
return ret;
}
}
}
// Flush the filter
std::vector<AVFrame *> raw_flushed_frames;
ret = processor->flush(raw_flushed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error flushing filter: {}", errbuf);
return ret;
}
// Wrap flushed frames in unique_ptrs
std::vector<std::unique_ptr<AVFrame, decltype(av_frame_deleter)>> flushed_frames;
for (AVFrame *raw_frame : raw_flushed_frames) {
flushed_frames.emplace_back(raw_frame, av_frame_deleter);
}
// Encode and write all flushed frames
for (auto &flushed_frame : flushed_frames) {
ret = encoder.write_frame(flushed_frame.get(), proc_ctx->processed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error encoding/writing flushed frame: {}", errbuf);
return ret;
}
proc_ctx->processed_frames++;
}
// Flush the encoder
ret = encoder.flush();
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error flushing encoder: {}", errbuf);
return ret;
}
return ret;
}
extern "C" int process_video(
const CharType *in_fname,
const CharType *out_fname,
Libvideo2xLogLevel log_level,
bool benchmark,
uint32_t vk_device_index,
AVHWDeviceType hw_type,
const ProcessorConfig *processor_config,
EncoderConfig *encoder_config,
VideoProcessingContext *proc_ctx
) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
int ret = 0;
// Set the log level for FFmpeg and spdlog
static void set_log_level(Libvideo2xLogLevel log_level) {
switch (log_level) {
case LIBVIDEO2X_LOG_LEVEL_TRACE:
av_log_set_level(AV_LOG_TRACE);
@ -337,13 +51,32 @@ extern "C" int process_video(
spdlog::set_level(spdlog::level::info);
break;
}
}
extern "C" int process_video(
const CharType *in_fname,
const CharType *out_fname,
Libvideo2xLogLevel log_level,
bool benchmark,
uint32_t vk_device_index,
AVHWDeviceType hw_type,
const ProcessorConfig *processor_config,
EncoderConfig *encoder_config,
VideoProcessingContext *proc_ctx
) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
int ret = 0;
// Set the log level for FFmpeg and spdlog
set_log_level(log_level);
// Convert the file names to std::filesystem::path
std::filesystem::path in_fpath(in_fname);
std::filesystem::path out_fpath(out_fname);
// Create a smart pointer to manage the hardware device context
auto hw_ctx_deleter = [](AVBufferRef *ref) {
if (ref) {
if (ref != nullptr) {
av_buffer_unref(&ref);
}
};
@ -374,26 +107,24 @@ extern "C" int process_video(
AVCodecContext *dec_ctx = decoder.get_codec_context();
int in_vstream_idx = decoder.get_video_stream_index();
// Create and initialize the appropriate filter
std::unique_ptr<Processor> processor(
ProcessorFactory::instance().create_processor(processor_config, vk_device_index)
);
if (processor == nullptr) {
spdlog::critical("Failed to create filter instance");
return -1;
}
// Initialize output dimensions based on filter configuration
int output_width = 0, output_height = 0;
switch (processor_config->processor_type) {
case PROCESSOR_LIBPLACEBO:
output_width = processor_config->width;
output_height = processor_config->height;
break;
case PROCESSOR_REALESRGAN:
output_width = dec_ctx->width * processor_config->scaling_factor;
output_height = dec_ctx->height * processor_config->scaling_factor;
break;
case PROCESSOR_RIFE:
output_width = dec_ctx->width;
output_height = dec_ctx->height;
break;
default:
spdlog::critical("Unknown filter type");
return -1;
processor->get_output_dimensions(
processor_config, dec_ctx->width, dec_ctx->height, output_width, output_height
);
if (output_width <= 0 || output_height <= 0) {
spdlog::critical("Failed to determine the output dimensions");
return -1;
}
spdlog::debug("Output video dimensions: {}x{}", output_width, output_height);
// Update encoder configuration with output dimensions
encoder_config->width = output_width;
@ -410,74 +141,6 @@ extern "C" int process_video(
return ret;
}
// Write the output file header
ret = avformat_write_header(encoder.get_format_context(), NULL);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::critical("Error occurred when opening output file: {}", errbuf);
return ret;
}
// Create and initialize the appropriate filter
std::unique_ptr<Processor> processor;
switch (processor_config->processor_type) {
case PROCESSOR_LIBPLACEBO: {
const auto &config = processor_config->config.libplacebo;
if (!config.shader_path) {
spdlog::critical("Shader path must be provided for the libplacebo filter");
return -1;
}
processor = std::make_unique<FilterLibplacebo>(
vk_device_index,
std::filesystem::path(config.shader_path),
processor_config->width,
processor_config->height
);
break;
}
case PROCESSOR_REALESRGAN: {
const auto &config = processor_config->config.realesrgan;
if (!config.model_name) {
spdlog::critical("Model name must be provided for the RealESRGAN filter");
return -1;
}
processor = std::make_unique<FilterRealesrgan>(
static_cast<int>(vk_device_index),
config.tta_mode,
processor_config->scaling_factor,
config.model_name
);
break;
}
case PROCESSOR_RIFE: {
const auto &config = processor_config->config.rife;
if (!config.model_name) {
spdlog::critical("Model name must be provided for the RIFE filter");
return -1;
}
processor = std::make_unique<InterpolatorRIFE>(
static_cast<int>(vk_device_index),
config.tta_mode,
config.tta_temporal_mode,
config.uhd_mode,
config.num_threads,
config.rife_v2,
config.rife_v4,
config.model_name
);
break;
}
default:
spdlog::critical("Unknown filter type");
return -1;
}
// Check if the filter instance was created successfully
if (processor == nullptr) {
spdlog::critical("Failed to create filter instance");
return -1;
}
// Initialize the filter
ret = processor->init(dec_ctx, encoder.get_encoder_context(), hw_ctx.get());
if (ret < 0) {

112
src/processor_factory.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "processor_factory.h"
#include <spdlog/spdlog.h>
#include <utility>
#include "filter_libplacebo.h"
#include "filter_realesrgan.h"
#include "interpolator_rife.h"
// Access the singleton instance
ProcessorFactory &ProcessorFactory::instance() {
static ProcessorFactory factory;
// Ensure default processors are registered only once
static bool initialized = false;
if (!initialized) {
ProcessorFactory::init_default_processors(factory);
initialized = true;
}
return factory;
}
// Register a processor type and its creator
void ProcessorFactory::register_processor(ProcessorType type, Creator creator) {
creators[type] = std::move(creator);
}
// Create a processor instance
std::unique_ptr<Processor> ProcessorFactory::create_processor(
const ProcessorConfig *processor_config,
uint32_t vk_device_index
) const {
auto it = creators.find(processor_config->processor_type);
if (it == creators.end()) {
spdlog::critical(
"Processor type not registered: {}", static_cast<int>(processor_config->processor_type)
);
return nullptr;
}
// Call the corresponding creator function
return it->second(processor_config, vk_device_index);
}
// Initialize default processors
void ProcessorFactory::init_default_processors(ProcessorFactory &factory) {
factory.register_processor(
PROCESSOR_LIBPLACEBO,
[](const ProcessorConfig *config, uint32_t vk_device_index) -> std::unique_ptr<Processor> {
const auto &cfg = config->config.libplacebo;
if (!cfg.shader_path) {
spdlog::critical("Shader path must be provided for the libplacebo filter");
return nullptr;
}
if (config->width <= 0 || config->height <= 0) {
spdlog::critical(
"Output width and height must be provided for the libplacebo filter"
);
return nullptr;
}
return std::make_unique<FilterLibplacebo>(
vk_device_index,
std::filesystem::path(cfg.shader_path),
config->width,
config->height
);
}
);
factory.register_processor(
PROCESSOR_REALESRGAN,
[](const ProcessorConfig *config, uint32_t vk_device_index) -> std::unique_ptr<Processor> {
const auto &cfg = config->config.realesrgan;
if (config->scaling_factor <= 0) {
spdlog::critical("Scaling factor must be provided for the RealESRGAN filter");
return nullptr;
}
if (!cfg.model_name) {
spdlog::critical("Model name must be provided for the RealESRGAN filter");
return nullptr;
}
return std::make_unique<FilterRealesrgan>(
static_cast<int>(vk_device_index),
cfg.tta_mode,
config->scaling_factor,
cfg.model_name
);
}
);
factory.register_processor(
PROCESSOR_RIFE,
[](const ProcessorConfig *config, uint32_t vk_device_index) -> std::unique_ptr<Processor> {
const auto &cfg = config->config.rife;
if (!cfg.model_name) {
spdlog::critical("Model name must be provided for the RIFE filter");
return nullptr;
}
return std::make_unique<InterpolatorRIFE>(
static_cast<int>(vk_device_index),
cfg.tta_mode,
cfg.tta_temporal_mode,
cfg.uhd_mode,
cfg.num_threads,
cfg.rife_v2,
cfg.rife_v4,
cfg.model_name
);
}
);
}