mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-24 04:15:11 +00:00
5a3954ae0e
Fixes several files incorrectly stated a different license from the actual project, as well as the copyright headers included in all files. This change has no effect on the licensing terms, it should clear up a bit of confusion by contributors. Plus the files get a bit smaller, and we have less duplicated information across the entire project. Overall the project is GPLv2 if not built with Qt, and GPLv3 if it is built with Qt. There are no parts licensed under a different license, all have been adapted from other compatible licenses into GPLv2 or GPLv3.
417 lines
13 KiB
C++
417 lines
13 KiB
C++
// AUTOGENERATED COPYRIGHT HEADER START
|
|
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
|
|
|
#pragma once
|
|
#include "common.hpp"
|
|
#include "plugin.hpp"
|
|
|
|
namespace streamfx::obs {
|
|
class encoder_instance {
|
|
protected:
|
|
obs_encoder_t* _self;
|
|
|
|
public:
|
|
encoder_instance(obs_data_t* settings, obs_encoder_t* self, bool is_hw) : _self(self) {}
|
|
virtual ~encoder_instance(){};
|
|
|
|
virtual void migrate(obs_data_t* settings, uint64_t version) {}
|
|
|
|
virtual bool update(obs_data_t* settings)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual bool encode(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet)
|
|
{
|
|
auto type = obs_encoder_get_type(_self);
|
|
if (type == OBS_ENCODER_VIDEO) {
|
|
return encode_video(frame, packet, received_packet);
|
|
} else if (type == OBS_ENCODER_AUDIO) {
|
|
return encode_audio(frame, packet, received_packet);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual bool encode_audio(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet)
|
|
{
|
|
return false;
|
|
};
|
|
|
|
virtual bool encode_video(struct encoder_frame* frame, struct encoder_packet* packet, bool* received_packet)
|
|
{
|
|
return false;
|
|
};
|
|
|
|
virtual bool encode_video(uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
|
struct encoder_packet* packet, bool* received_packet)
|
|
{
|
|
return false;
|
|
};
|
|
|
|
virtual size_t get_frame_size()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual bool get_extra_data(uint8_t** extra_data, size_t* size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual bool get_sei_data(uint8_t** sei_data, size_t* size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual void get_audio_info(struct audio_convert_info* info) {}
|
|
|
|
virtual void get_video_info(struct video_scale_info* info) {}
|
|
|
|
virtual obs_encoder_t* get()
|
|
{
|
|
return _self;
|
|
}
|
|
};
|
|
|
|
template<class _factory, typename _instance>
|
|
class encoder_factory {
|
|
public:
|
|
typedef _factory factory_t;
|
|
typedef _instance instance_t;
|
|
|
|
protected:
|
|
obs_encoder_info _info = {};
|
|
obs_encoder_info _info_fallback = {};
|
|
std::string _info_fallback_id;
|
|
|
|
std::map<std::string, std::shared_ptr<obs_encoder_info>> _proxies;
|
|
std::set<std::string> _proxy_names;
|
|
|
|
public:
|
|
encoder_factory()
|
|
{
|
|
_info.type_data = this;
|
|
|
|
_info.get_name = _get_name;
|
|
_info.create = _create_hw;
|
|
_info.destroy = _destroy;
|
|
_info.get_defaults2 = _get_defaults2;
|
|
_info.get_properties2 = _get_properties2;
|
|
_info.update = _update;
|
|
_info.encode = _encode;
|
|
_info.get_extra_data = _get_extra_data;
|
|
_info.get_sei_data = _get_sei_data;
|
|
}
|
|
virtual ~encoder_factory() = default;
|
|
|
|
void finish_setup()
|
|
{
|
|
if (_info.type == OBS_ENCODER_AUDIO) {
|
|
_info.get_frame_size = _get_frame_size;
|
|
_info.get_audio_info = _get_audio_info;
|
|
} else if (_info.type == OBS_ENCODER_VIDEO) {
|
|
_info.get_video_info = _get_video_info;
|
|
}
|
|
if (_info.caps & OBS_ENCODER_CAP_PASS_TEXTURE) {
|
|
_info.encode_texture = _encode_texture;
|
|
|
|
memcpy(&_info_fallback, &_info, sizeof(obs_encoder_info));
|
|
_info_fallback_id = std::string(_info.id) + "_sw";
|
|
_info_fallback.id = _info_fallback_id.c_str();
|
|
_info_fallback.caps =
|
|
(_info_fallback.caps & ~OBS_ENCODER_CAP_PASS_TEXTURE) | OBS_ENCODER_CAP_DEPRECATED;
|
|
_info_fallback.create = _create;
|
|
_info_fallback.encode_texture = nullptr;
|
|
obs_register_encoder(&_info_fallback);
|
|
} else {
|
|
_info.create = _create;
|
|
}
|
|
|
|
obs_register_encoder(&_info);
|
|
}
|
|
|
|
void register_proxy(std::string_view name)
|
|
{
|
|
auto iter = _proxy_names.emplace(name);
|
|
|
|
// Create proxy.
|
|
std::shared_ptr<obs_encoder_info> proxy = std::make_shared<obs_encoder_info>();
|
|
memcpy(proxy.get(), &_info, sizeof(obs_encoder_info));
|
|
proxy->id = iter.first->c_str();
|
|
proxy->caps |= OBS_ENCODER_CAP_DEPRECATED;
|
|
obs_register_encoder(proxy.get());
|
|
|
|
_proxies.emplace(name, proxy);
|
|
}
|
|
|
|
private:
|
|
void _migrate(obs_data_t* settings, encoder_instance* instance)
|
|
{
|
|
uint64_t version = static_cast<uint64_t>(obs_data_get_int(settings, S_VERSION));
|
|
migrate(settings, version);
|
|
if (instance) {
|
|
instance->migrate(settings, version);
|
|
}
|
|
obs_data_set_int(settings, S_VERSION, static_cast<int64_t>(STREAMFX_VERSION));
|
|
obs_data_set_string(settings, S_COMMIT, STREAMFX_VERSION_BUILD);
|
|
}
|
|
|
|
private /* Factory */:
|
|
static const char* _get_name(void* type_data) noexcept
|
|
{
|
|
try {
|
|
if (type_data)
|
|
return reinterpret_cast<factory_t*>(type_data)->get_name();
|
|
return nullptr;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return nullptr;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static void* _create(obs_data_t* settings, obs_encoder_t* encoder) noexcept
|
|
{
|
|
try {
|
|
auto* fac = reinterpret_cast<factory_t*>(obs_encoder_get_type_data(encoder));
|
|
return fac->create(settings, encoder, false);
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return nullptr;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static void* _create_hw(obs_data_t* settings, obs_encoder_t* encoder) noexcept
|
|
{
|
|
try {
|
|
auto* fac = reinterpret_cast<factory_t*>(obs_encoder_get_type_data(encoder));
|
|
try {
|
|
return fac->create(settings, encoder, true);
|
|
} catch (...) {
|
|
return obs_encoder_create_rerouted(encoder, fac->_info_fallback.id);
|
|
}
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return nullptr;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
static void _get_defaults2(obs_data_t* settings, void* type_data) noexcept
|
|
{
|
|
try {
|
|
if (type_data)
|
|
reinterpret_cast<factory_t*>(type_data)->get_defaults2(settings);
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
}
|
|
}
|
|
|
|
static bool _properties_migrate_settings(void* priv, obs_properties_t*, obs_property_t* p,
|
|
obs_data_t* settings) noexcept
|
|
{
|
|
try {
|
|
obs_property_set_visible(p, false);
|
|
reinterpret_cast<factory_t*>(priv)->_migrate(settings, nullptr);
|
|
return true;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return false;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static obs_properties_t* _get_properties2(void* data, void* type_data) noexcept
|
|
{
|
|
try {
|
|
if (type_data) {
|
|
auto props =
|
|
reinterpret_cast<factory_t*>(type_data)->get_properties2(reinterpret_cast<instance_t*>(data));
|
|
|
|
{ // Support for permanent settings migration.
|
|
auto p = obs_properties_add_int(
|
|
props, S_VERSION, "If you can see this, something went horribly wrong.",
|
|
std::numeric_limits<int32_t>::lowest(), std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_set_modified_callback2(p, _properties_migrate_settings, type_data);
|
|
}
|
|
|
|
return props;
|
|
}
|
|
return nullptr;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return nullptr;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
private /* Instance */:
|
|
static void _destroy(void* data) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
delete reinterpret_cast<instance_t*>(data);
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
}
|
|
}
|
|
|
|
static bool _update(void* data, obs_data_t* settings) noexcept
|
|
{
|
|
try {
|
|
auto priv = reinterpret_cast<encoder_instance*>(data);
|
|
if (priv) {
|
|
reinterpret_cast<factory_t*>(obs_encoder_get_type_data(priv->get()))->_migrate(settings, priv);
|
|
return priv->update(settings);
|
|
}
|
|
return false;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return false;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool _encode(void* data, struct encoder_frame* frame, struct encoder_packet* packet,
|
|
bool* received_packet) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
return reinterpret_cast<encoder_instance*>(data)->encode_video(frame, packet, received_packet);
|
|
return false;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return false;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool _encode_texture(void* data, uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
|
struct encoder_packet* packet, bool* received_packet) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
return reinterpret_cast<encoder_instance*>(data)->encode_video(handle, pts, lock_key, next_key,
|
|
packet, received_packet);
|
|
return false;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return false;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static size_t _get_frame_size(void* data) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
return reinterpret_cast<encoder_instance*>(data)->get_frame_size();
|
|
return 0;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return 0;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool _get_extra_data(void* data, uint8_t** extra_data, size_t* size) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
return reinterpret_cast<encoder_instance*>(data)->get_extra_data(extra_data, size);
|
|
return false;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return false;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool _get_sei_data(void* data, uint8_t** sei_data, size_t* size) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
return reinterpret_cast<encoder_instance*>(data)->get_sei_data(sei_data, size);
|
|
return false;
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
return false;
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void _get_audio_info(void* data, struct audio_convert_info* info) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
reinterpret_cast<encoder_instance*>(data)->get_audio_info(info);
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
}
|
|
}
|
|
|
|
static void _get_video_info(void* data, struct video_scale_info* info) noexcept
|
|
{
|
|
try {
|
|
if (data)
|
|
reinterpret_cast<encoder_instance*>(data)->get_video_info(info);
|
|
} catch (const std::exception& ex) {
|
|
DLOG_ERROR("Unexpected exception in function '%s': %s.", __FUNCTION_NAME__, ex.what());
|
|
} catch (...) {
|
|
DLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
|
}
|
|
}
|
|
|
|
public:
|
|
virtual const char* get_name()
|
|
{
|
|
return "Not Yet Implemented";
|
|
}
|
|
|
|
virtual void* create(obs_data_t* settings, obs_encoder_t* encoder, bool is_hw)
|
|
{
|
|
return reinterpret_cast<void*>(new instance_t(settings, encoder, is_hw));
|
|
}
|
|
|
|
virtual void get_defaults2(obs_data_t* data) {}
|
|
|
|
virtual void migrate(obs_data_t* data, uint64_t version) {}
|
|
|
|
virtual obs_properties_t* get_properties2(instance_t* data)
|
|
{
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
} // namespace streamfx::obs
|