code: Migrate encoder::ffmpeg to modern handler loader

A different version of the dynamic loader allows us to simply register handlers at load time, instead of requiring custom code. Could also make it so that it loads them when needed, but since they're mostly static code, this won't matter much.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2023-05-14 18:38:28 +02:00 committed by Xaymar
parent a1968b970b
commit c4461e70b9
4 changed files with 125 additions and 156 deletions

View File

@ -10,7 +10,6 @@
#include "strings.hpp"
#include "codecs/hevc.hpp"
#include "ffmpeg/tools.hpp"
#include "handlers/debug_handler.hpp"
#include "obs/gs/gs-helper.hpp"
#include "plugin.hpp"
@ -32,24 +31,6 @@ extern "C" {
#include "warning-enable.hpp"
}
#ifdef ENABLE_ENCODER_FFMPEG_AMF
#include "handlers/amf_h264_handler.hpp"
#include "handlers/amf_hevc_handler.hpp"
#endif
#ifdef ENABLE_ENCODER_FFMPEG_NVENC
#include "handlers/nvenc_h264_handler.hpp"
#include "handlers/nvenc_hevc_handler.hpp"
#endif
#ifdef ENABLE_ENCODER_FFMPEG_PRORES
#include "handlers/prores_aw_handler.hpp"
#endif
#ifdef ENABLE_ENCODER_FFMPEG_DNXHR
#include "handlers/dnxhd_handler.hpp"
#endif
#ifdef WIN32
#include "ffmpeg/hwapi/d3d11.hpp"
#endif
@ -176,7 +157,7 @@ ffmpeg_instance::~ffmpeg_instance()
void ffmpeg_instance::get_properties(obs_properties_t* props)
{
if (_handler)
_handler->get_properties(props, _codec, _context, _handler->is_hardware_encoder(_factory));
_handler->properties(this->_factory, this, props);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_KEYFRAMES_INTERVALTYPE), false);
obs_property_set_enabled(obs_properties_get(props, ST_KEY_KEYFRAMES_INTERVAL_SECONDS), false);
@ -189,7 +170,7 @@ void ffmpeg_instance::get_properties(obs_properties_t* props)
void ffmpeg_instance::migrate(obs_data_t* settings, uint64_t version)
{
if (_handler)
_handler->migrate(settings, version, _codec, _context);
_handler->migrate(this->_factory, this, settings, version);
}
bool ffmpeg_instance::update(obs_data_t* settings)
@ -199,7 +180,7 @@ bool ffmpeg_instance::update(obs_data_t* settings)
bool support_reconfig_gpu = false;
bool support_reconfig_keyframes = false;
if (_handler) {
support_reconfig = _handler->supports_reconfigure(_factory, support_reconfig_threads, support_reconfig_gpu, support_reconfig_keyframes);
support_reconfig = _handler->is_reconfigurable(_factory, support_reconfig_threads, support_reconfig_gpu, support_reconfig_keyframes);
}
if (!_context->internal) {
@ -245,7 +226,7 @@ bool ffmpeg_instance::update(obs_data_t* settings)
if (!_context->internal || (support_reconfig && support_reconfig_keyframes)) {
// Keyframes
if (_handler && _handler->has_keyframe_support(_factory)) {
if (_handler && _handler->has_keyframes(_factory)) {
// Key-Frame Options
obs_video_info ovi;
if (!obs_get_video_info(&ovi)) {
@ -268,7 +249,7 @@ bool ffmpeg_instance::update(obs_data_t* settings)
if (!_context->internal || support_reconfig) {
// Handler Options
if (_handler)
_handler->update(settings, _codec, _context);
_handler->update(this->_factory, this, settings);
{ // FFmpeg Custom Options
const char* opts = obs_data_get_string(settings, ST_KEY_FFMPEG_CUSTOMSETTINGS);
@ -279,7 +260,7 @@ bool ffmpeg_instance::update(obs_data_t* settings)
// Handler Overrides
if (_handler)
_handler->override_update(this, settings);
_handler->override_update(this->_factory, this, settings);
}
// Handler Logging
@ -310,7 +291,7 @@ bool ffmpeg_instance::update(obs_data_t* settings)
}
if (_handler) {
_handler->log_options(settings, _codec, _context);
_handler->log(this->_factory, this, settings);
}
}
@ -438,7 +419,7 @@ void ffmpeg_instance::initialize_sw(obs_data_t* settings)
}
if (_handler) // Allow Handler to override the automatic color format for sanity reasons.
_handler->override_colorformat(pix_fmt_target, settings, _codec, _context);
_handler->override_colorformat(this->_factory, this, settings, pix_fmt_target);
}
// Setup from OBS information.
@ -634,8 +615,9 @@ int ffmpeg_instance::receive_packet(bool* received_packet, struct encoder_packet
}
// Allow Handler Post-Processing
if (_handler)
_handler->process_avpacket(_packet, _codec, _context);
//FIXME! Is this still necessary?
//if (_handler)
// _handler->process_avpacket(_packet, _codec, _context);
// Build packet for use in OBS.
packet->type = OBS_ENCODER_VIDEO;
@ -960,10 +942,10 @@ ffmpeg_factory::ffmpeg_factory(ffmpeg_manager* manager, const AVCodec* codec) :
// Find any available handlers for this codec.
if (_handler = manager->get_handler(_avcodec->name); _handler) {
// Override any found info with the one specified by the handler.
_handler->adjust_info(this, _avcodec, _id, _name, _codec);
_handler->adjust_info(this, _id, _name, _codec);
// Add texture capability for hardware encoders.
if (_handler->is_hardware_encoder(this)) {
if (_handler->is_hardware(this)) {
_info.caps |= OBS_ENCODER_CAP_PASS_TEXTURE;
}
} else {
@ -1007,9 +989,9 @@ const char* ffmpeg_factory::get_name()
void ffmpeg_factory::get_defaults2(obs_data_t* settings)
{
if (_handler) {
_handler->get_defaults(settings, _avcodec, nullptr, _handler->is_hardware_encoder(this));
_handler->defaults(this, settings);
if (_handler->has_keyframe_support(this)) {
if (_handler->has_keyframes(this)) {
obs_data_set_default_int(settings, ST_KEY_KEYFRAMES_INTERVALTYPE, 0);
obs_data_set_default_double(settings, ST_KEY_KEYFRAMES_INTERVAL_SECONDS, 2.0);
obs_data_set_default_int(settings, ST_KEY_KEYFRAMES_INTERVAL_FRAMES, 300);
@ -1027,7 +1009,7 @@ void ffmpeg_factory::get_defaults2(obs_data_t* settings)
void ffmpeg_factory::migrate(obs_data_t* data, uint64_t version)
{
if (_handler)
_handler->migrate(data, version, _avcodec, nullptr);
_handler->migrate(this, nullptr, data, version);
}
static bool modified_keyframes(obs_properties_t* props, obs_property_t*, obs_data_t* settings) noexcept
@ -1061,9 +1043,9 @@ obs_properties_t* ffmpeg_factory::get_properties2(instance_t* data)
}
if (_handler)
_handler->get_properties(props, _avcodec, nullptr, _handler->is_hardware_encoder(this));
_handler->properties(this, data, props);
if (_handler && _handler->has_keyframe_support(this)) {
if (_handler && _handler->has_keyframes(this)) {
// Key-Frame Options
obs_properties_t* grp = props;
if (!streamfx::util::are_property_groups_broken()) {
@ -1099,11 +1081,11 @@ obs_properties_t* ffmpeg_factory::get_properties2(instance_t* data)
auto p = obs_properties_add_text(grp, ST_KEY_FFMPEG_CUSTOMSETTINGS, D_TRANSLATE(ST_I18N_FFMPEG_CUSTOMSETTINGS), obs_text_type::OBS_TEXT_DEFAULT);
}
if (_handler && _handler->is_hardware_encoder(this)) {
if (_handler && _handler->is_hardware(this)) {
auto p = obs_properties_add_int(grp, ST_KEY_FFMPEG_GPU, D_TRANSLATE(ST_I18N_FFMPEG_GPU), -1, std::numeric_limits<uint8_t>::max(), 1);
}
if (_handler && _handler->has_threading_support(this)) {
if (_handler && _handler->has_threading(this)) {
auto p = obs_properties_add_int_slider(grp, ST_KEY_FFMPEG_THREADS, D_TRANSLATE(ST_I18N_FFMPEG_THREADS), 0, static_cast<int64_t>(std::thread::hardware_concurrency()) * 2, 1);
}
@ -1132,7 +1114,7 @@ obs_properties_t* ffmpeg_factory::get_properties2(instance_t* data)
bool ffmpeg_factory::on_manual_open(obs_properties_t* props, obs_property_t* property, void* data)
{
ffmpeg_factory* ptr = static_cast<ffmpeg_factory*>(data);
streamfx::open_url(ptr->_handler->get_help_url(ptr->_avcodec));
streamfx::open_url(ptr->_handler->help(ptr));
return false;
}
#endif
@ -1147,25 +1129,8 @@ obs_encoder_info* streamfx::encoder::ffmpeg::ffmpeg_factory::get_info()
return &_info;
}
ffmpeg_manager::ffmpeg_manager() : _factories(), _handlers(), _debug_handler()
ffmpeg_manager::ffmpeg_manager() : _factories()
{
// Handlers
_debug_handler = ::std::make_shared<handler::debug_handler>();
#ifdef ENABLE_ENCODER_FFMPEG_AMF
register_handler("h264_amf", ::std::make_shared<handler::amf_h264_handler>());
register_handler("hevc_amf", ::std::make_shared<handler::amf_hevc_handler>());
#endif
#ifdef ENABLE_ENCODER_FFMPEG_NVENC
register_handler("h264_nvenc", ::std::make_shared<handler::nvenc_h264_handler>());
register_handler("hevc_nvenc", ::std::make_shared<handler::nvenc_hevc_handler>());
#endif
#ifdef ENABLE_ENCODER_FFMPEG_PRORES
register_handler("prores_aw", ::std::make_shared<handler::prores_aw_handler>());
#endif
#ifdef ENABLE_ENCODER_FFMPEG_DNXHR
register_handler("dnxhd", ::std::make_shared<handler::dnxhd_handler>());
#endif
// Encoders
void* iterator = nullptr;
for (const AVCodec* codec = av_codec_iterate(&iterator); codec != nullptr; codec = av_codec_iterate(&iterator)) {
@ -1188,28 +1153,6 @@ ffmpeg_manager::~ffmpeg_manager()
_factories.clear();
}
void ffmpeg_manager::register_handler(std::string codec, std::shared_ptr<handler::handler> handler)
{
_handlers.emplace(codec, handler);
}
std::shared_ptr<handler::handler> ffmpeg_manager::get_handler(std::string codec)
{
auto fnd = _handlers.find(codec);
if (fnd != _handlers.end())
return fnd->second;
#ifdef _DEBUG
return _debug_handler;
#else
return nullptr;
#endif
}
bool ffmpeg_manager::has_handler(std::string_view codec)
{
return (_handlers.find(codec.data()) != _handlers.end());
}
std::shared_ptr<ffmpeg_manager> ffmpeg_manager::instance()
{
static std::weak_ptr<ffmpeg_manager> winst;
@ -1224,6 +1167,30 @@ std::shared_ptr<ffmpeg_manager> ffmpeg_manager::instance()
return instance;
}
streamfx::encoder::ffmpeg::handler* ffmpeg_manager::find_handler(std::string_view codec)
{
auto handlers = streamfx::encoder::ffmpeg::handler::handlers();
if (auto kv = handlers.find(std::string{codec}); kv != handlers.end()) {
return kv->second;
}
#ifdef _DEBUG
if (auto kv = handlers.find(""); kv != handlers.end()) {
return kv->second;
}
#endif
return nullptr;
}
streamfx::encoder::ffmpeg::handler* ffmpeg_manager::get_handler(std::string_view codec)
{
return find_handler(codec);
}
bool ffmpeg_manager::has_handler(std::string_view codec)
{
return find_handler(codec) != nullptr;
}
static std::shared_ptr<ffmpeg_manager> loader_instance;
static auto loader = streamfx::loader(

View File

@ -5,10 +5,10 @@
#pragma once
#include "common.hpp"
#include "encoders/ffmpeg/handler.hpp"
#include "ffmpeg/avframe-queue.hpp"
#include "ffmpeg/hwapi/base.hpp"
#include "ffmpeg/swscale.hpp"
#include "handlers/handler.hpp"
#include "obs/obs-encoder-factory.hpp"
#include "warning-disable.hpp"
@ -17,16 +17,15 @@
#include <mutex>
#include <queue>
#include <stack>
#include <string>
#include <string_view>
#include <thread>
#include <vector>
#include "warning-enable.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
#include "warning-enable.hpp"
}
#include "warning-enable.hpp"
namespace streamfx::encoder::ffmpeg {
class ffmpeg_instance;
@ -38,7 +37,7 @@ namespace streamfx::encoder::ffmpeg {
const AVCodec* _codec;
AVCodecContext* _context;
std::shared_ptr<handler::handler> _handler;
streamfx::encoder::ffmpeg::handler* _handler;
::streamfx::ffmpeg::swscale _scaler;
std::shared_ptr<AVPacket> _packet;
@ -116,7 +115,7 @@ namespace streamfx::encoder::ffmpeg {
const AVCodec* _avcodec;
std::shared_ptr<handler::handler> _handler;
streamfx::encoder::ffmpeg::handler* _handler;
public:
ffmpeg_factory(ffmpeg_manager* manager, const AVCodec* codec);
@ -142,16 +141,14 @@ namespace streamfx::encoder::ffmpeg {
class ffmpeg_manager {
std::map<const AVCodec*, std::shared_ptr<ffmpeg_factory>> _factories;
std::map<std::string, std::shared_ptr<handler::handler>> _handlers;
std::shared_ptr<handler::handler> _debug_handler;
public:
ffmpeg_manager();
~ffmpeg_manager();
void register_handler(std::string codec, std::shared_ptr<handler::handler> handler);
streamfx::encoder::ffmpeg::handler* find_handler(std::string_view codec);
std::shared_ptr<handler::handler> get_handler(std::string codec);
streamfx::encoder::ffmpeg::handler* get_handler(std::string_view codec);
bool has_handler(std::string_view codec);

View File

@ -1,41 +1,67 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
#include "handler.hpp"
#include "../encoder-ffmpeg.hpp"
using namespace streamfx::encoder::ffmpeg;
streamfx::encoder::ffmpeg::handler::handler_map_t& streamfx::encoder::ffmpeg::handler::handlers()
{
static handler_map_t handlers;
return handlers;
}
bool handler::handler::has_keyframe_support(ffmpeg_factory* instance)
streamfx::encoder::ffmpeg::handler::handler(std::string codec)
{
handlers().emplace(codec, this);
}
bool streamfx::encoder::ffmpeg::handler::has_keyframes(ffmpeg_factory* factory)
{
#if LIBAVCODEC_VERSION_MAJOR > 58
if (auto* desc = avcodec_descriptor_get(instance->get_avcodec()->id); desc) {
if (auto* desc = avcodec_descriptor_get(factory->get_avcodec()->id); desc) {
return (desc->props & AV_CODEC_PROP_INTRA_ONLY) == 0;
} else {
return false;
}
#else
return (instance->get_avcodec()->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0;
return (factory->get_avcodec()->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0;
#endif
}
bool handler::handler::is_hardware_encoder(ffmpeg_factory* instance)
bool streamfx::encoder::ffmpeg::handler::has_threading(ffmpeg_factory* factory)
{
return (factory->get_avcodec()->capabilities & (AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_OTHER_THREADS));
}
bool streamfx::encoder::ffmpeg::handler::is_hardware(ffmpeg_factory* factory)
{
if (factory->get_avcodec()->capabilities & AV_CODEC_CAP_HARDWARE) {
return true;
}
return false;
}
bool handler::handler::has_threading_support(ffmpeg_factory* instance)
{
return (instance->get_avcodec()->capabilities & (AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS));
}
bool handler::handler::has_pixel_format_support(ffmpeg_factory* instance)
{
return (instance->get_avcodec()->pix_fmts != nullptr);
}
bool handler::handler::supports_reconfigure(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes)
bool streamfx::encoder::ffmpeg::handler::is_reconfigurable(ffmpeg_factory* factory, bool& threads, bool& gpu, bool& keyframes)
{
if (factory->get_avcodec()->capabilities & AV_CODEC_CAP_PARAM_CHANGE) {
return true;
}
return false;
}
void streamfx::encoder::ffmpeg::handler::adjust_info(ffmpeg_factory* factory, std::string& id, std::string& name, std::string& codec) {}
std::string streamfx::encoder::ffmpeg::handler::help(ffmpeg_factory* factory) {
return "about:blank";
}
void streamfx::encoder::ffmpeg::handler::defaults(ffmpeg_factory* factory, obs_data_t* settings) {}
void streamfx::encoder::ffmpeg::handler::properties(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props) {}
void streamfx::encoder::ffmpeg::handler::migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version) {}
void streamfx::encoder::ffmpeg::handler::update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) {}
void streamfx::encoder::ffmpeg::handler::override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) {}
void streamfx::encoder::ffmpeg::handler::log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings) {}
void streamfx::encoder::ffmpeg::handler::override_colorformat(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, AVPixelFormat target_format) {}

View File

@ -1,63 +1,42 @@
// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END
#pragma once
#include "common.hpp"
#include "ffmpeg/hwapi/base.hpp"
extern "C" {
#include "warning-disable.hpp"
#include <cstdint>
#include <map>
#include <string>
extern "C" {
#include <obs.h>
#include <libavcodec/avcodec.h>
#include "warning-enable.hpp"
}
#include "warning-enable.hpp"
namespace streamfx::encoder::ffmpeg {
struct ffmpeg_info;
class ffmpeg_factory;
class ffmpeg_instance;
namespace handler {
class handler {
public:
virtual ~handler(){};
struct handler {
handler(std::string codec);
public /*factory*/:
virtual void adjust_info(ffmpeg_factory* factory, const AVCodec* codec, std::string& id, std::string& name, std::string& codec_id){};
virtual bool has_keyframes(ffmpeg_factory* factory);
virtual bool has_threading(ffmpeg_factory* factory);
virtual bool is_hardware(ffmpeg_factory* factory);
virtual bool is_reconfigurable(ffmpeg_factory* factory, bool& threads, bool& gpu, bool& keyframes);
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool hw_encode){};
virtual void adjust_info(ffmpeg_factory* factory, std::string& id, std::string& name, std::string& codec);
virtual std::string_view get_help_url(const AVCodec* codec)
{
return "https://github.com/Xaymar/obs-StreamFX/wiki/Encoder-FFmpeg";
};
virtual std::string help(ffmpeg_factory* factory);
public /*support tests*/:
virtual bool has_keyframe_support(ffmpeg_factory* instance);
virtual void defaults(ffmpeg_factory* factory, obs_data_t* settings);
virtual void properties(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_properties_t* props);
virtual void migrate(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, uint64_t version);
virtual void update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings);
virtual void override_update(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings);
virtual void log(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings);
virtual bool is_hardware_encoder(ffmpeg_factory* instance);
virtual void override_colorformat(ffmpeg_factory* factory, ffmpeg_instance* instance, obs_data_t* settings, AVPixelFormat target_format);
virtual bool has_threading_support(ffmpeg_factory* instance);
public:
typedef std::map<std::string, handler*> handler_map_t;
virtual bool has_pixel_format_support(ffmpeg_factory* instance);
virtual bool supports_reconfigure(ffmpeg_factory* instance, bool& threads, bool& gpu, bool& keyframes);
public /*settings*/:
virtual void get_properties(obs_properties_t* props, const AVCodec* codec, AVCodecContext* context, bool hw_encode){};
virtual void migrate(obs_data_t* settings, uint64_t version, const AVCodec* codec, AVCodecContext* context){};
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context){};
virtual void override_update(ffmpeg_instance* instance, obs_data_t* settings){};
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context){};
public /*instance*/:
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, const AVCodec* codec, AVCodecContext* context){};
virtual void process_avpacket(std::shared_ptr<AVPacket> packet, const AVCodec* codec, AVCodecContext* context){};
};
} // namespace handler
static handler_map_t& handlers();
};
} // namespace streamfx::encoder::ffmpeg