mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-30 15:23:01 +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.
1699 lines
64 KiB
C++
1699 lines
64 KiB
C++
// AUTOGENERATED COPYRIGHT HEADER START
|
|
// Copyright (C) 2021-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
|
|
|
//--------------------------------------------------------------------------------//
|
|
// THIS FEATURE IS DEPRECATED. SUBMITTED PATCHES WILL BE REJECTED.
|
|
//--------------------------------------------------------------------------------//
|
|
|
|
#include "encoder-aom-av1.hpp"
|
|
#include "util/util-logging.hpp"
|
|
|
|
#include "warning-disable.hpp"
|
|
#include <filesystem>
|
|
#include <thread>
|
|
#include "warning-enable.hpp"
|
|
|
|
#ifdef _DEBUG
|
|
#define ST_PREFIX "<%s> "
|
|
#define D_LOG_ERROR(x, ...) P_LOG_ERROR(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
|
|
#define D_LOG_WARNING(x, ...) P_LOG_WARN(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
|
|
#define D_LOG_INFO(x, ...) P_LOG_INFO(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
|
|
#define D_LOG_DEBUG(x, ...) P_LOG_DEBUG(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__)
|
|
#else
|
|
#define ST_PREFIX "<encoder::aom::av1> "
|
|
#define D_LOG_ERROR(...) P_LOG_ERROR(ST_PREFIX __VA_ARGS__)
|
|
#define D_LOG_WARNING(...) P_LOG_WARN(ST_PREFIX __VA_ARGS__)
|
|
#define D_LOG_INFO(...) P_LOG_INFO(ST_PREFIX __VA_ARGS__)
|
|
#define D_LOG_DEBUG(...) P_LOG_DEBUG(ST_PREFIX __VA_ARGS__)
|
|
#endif
|
|
|
|
#define ST_I18N "Encoder.AOM.AV1"
|
|
|
|
// Preset
|
|
#define ST_I18N_DEPRECATED ST_I18N ".Deprecated"
|
|
#define ST_I18N_ENCODER ST_I18N ".Encoder"
|
|
#define ST_I18N_ENCODER_USAGE ST_I18N_ENCODER ".Usage"
|
|
#define ST_I18N_ENCODER_USAGE_GOODQUALITY ST_I18N_ENCODER_USAGE ".GoodQuality"
|
|
#define ST_I18N_ENCODER_USAGE_REALTIME ST_I18N_ENCODER_USAGE ".RealTime"
|
|
#define ST_I18N_ENCODER_USAGE_ALLINTRA ST_I18N_ENCODER_USAGE ".AllIntra"
|
|
#define ST_KEY_ENCODER_USAGE "Encoder.Usage"
|
|
#define ST_I18N_ENCODER_CPUUSAGE ST_I18N_ENCODER ".CPUUsage"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_0 ST_I18N_ENCODER ".CPUUsage.0"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_1 ST_I18N_ENCODER ".CPUUsage.1"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_2 ST_I18N_ENCODER ".CPUUsage.2"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_3 ST_I18N_ENCODER ".CPUUsage.3"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_4 ST_I18N_ENCODER ".CPUUsage.4"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_5 ST_I18N_ENCODER ".CPUUsage.5"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_6 ST_I18N_ENCODER ".CPUUsage.6"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_7 ST_I18N_ENCODER ".CPUUsage.7"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_8 ST_I18N_ENCODER ".CPUUsage.8"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_9 ST_I18N_ENCODER ".CPUUsage.9"
|
|
#define ST_I18N_ENCODER_CPUUSAGE_10 ST_I18N_ENCODER ".CPUUsage.10"
|
|
#define ST_KEY_ENCODER_CPUUSAGE "Encoder.CPUUsage"
|
|
#define ST_KEY_ENCODER_PROFILE "Encoder.Profile"
|
|
|
|
// Rate Control
|
|
#define ST_I18N_RATECONTROL ST_I18N ".RateControl"
|
|
#define ST_I18N_RATECONTROL_MODE ST_I18N_RATECONTROL ".Mode"
|
|
#define ST_I18N_RATECONTROL_MODE_CBR ST_I18N_RATECONTROL_MODE ".CBR"
|
|
#define ST_I18N_RATECONTROL_MODE_VBR ST_I18N_RATECONTROL_MODE ".VBR"
|
|
#define ST_I18N_RATECONTROL_MODE_CQ ST_I18N_RATECONTROL_MODE ".CQ"
|
|
#define ST_I18N_RATECONTROL_MODE_Q ST_I18N_RATECONTROL_MODE ".Q"
|
|
#define ST_KEY_RATECONTROL_MODE "RateControl.Mode"
|
|
#define ST_I18N_RATECONTROL_LOOKAHEAD ST_I18N_RATECONTROL ".LookAhead"
|
|
#define ST_KEY_RATECONTROL_LOOKAHEAD "RateControl.LookAhead"
|
|
#define ST_I18N_RATECONTROL_LIMITS ST_I18N_RATECONTROL ".Limits"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BITRATE ST_I18N_RATECONTROL_LIMITS ".Bitrate"
|
|
#define ST_KEY_RATECONTROL_LIMITS_BITRATE "RateControl.Limits.Bitrate"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT ST_I18N_RATECONTROL_LIMITS_BITRATE ".Undershoot"
|
|
#define ST_KEY_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT "RateControl.Limits.Bitrate.Undershoot"
|
|
#define ST_I18N_RATECONTROL_LIMITS_BITRATE_OVERSHOOT ST_I18N_RATECONTROL_LIMITS_BITRATE ".Overshoot"
|
|
#define ST_KEY_RATECONTROL_LIMITS_BITRATE_OVERSHOOT "RateControl.Limits.Bitrate.Overshoot"
|
|
#define ST_I18N_RATECONTROL_LIMITS_QUALITY ST_I18N_RATECONTROL_LIMITS ".Quality"
|
|
#define ST_KEY_RATECONTROL_LIMITS_QUALITY "RateControl.Limits.Quality"
|
|
#define ST_I18N_RATECONTROL_LIMITS_QUANTIZER_MINIMUM ST_I18N_RATECONTROL_LIMITS ".Quantizer.Minimum"
|
|
#define ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MINIMUM "RateControl.Limits.Quantizer.Minimum"
|
|
#define ST_I18N_RATECONTROL_LIMITS_QUANTIZER_MAXIMUM ST_I18N_RATECONTROL_LIMITS ".Quantizer.Maximum"
|
|
#define ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MAXIMUM "RateControl.Limits.Quantizer.Maximum"
|
|
#define ST_I18N_RATECONTROL_BUFFER ST_I18N_RATECONTROL ".Buffer"
|
|
#define ST_I18N_RATECONTROL_BUFFER_SIZE ST_I18N_RATECONTROL_BUFFER ".Size"
|
|
#define ST_KEY_RATECONTROL_BUFFER_SIZE "RateControl.Buffer.Size"
|
|
#define ST_I18N_RATECONTROL_BUFFER_SIZE_INITIAL ST_I18N_RATECONTROL_BUFFER_SIZE ".Initial"
|
|
#define ST_KEY_RATECONTROL_BUFFER_SIZE_INITIAL "RateControl.Buffer.Size.Initial"
|
|
#define ST_I18N_RATECONTROL_BUFFER_SIZE_OPTIMAL ST_I18N_RATECONTROL_BUFFER_SIZE ".Optimal"
|
|
#define ST_KEY_RATECONTROL_BUFFER_SIZE_OPTIMAL "RateControl.Buffer.Size.Optimal"
|
|
|
|
// Key-Frames
|
|
#define ST_I18N_KEYFRAMES ST_I18N ".KeyFrames"
|
|
#define ST_I18N_KEYFRAMES_INTERVALTYPE ST_I18N_KEYFRAMES ".IntervalType"
|
|
#define ST_I18N_KEYFRAMES_INTERVALTYPE_SECONDS ST_I18N_KEYFRAMES_INTERVALTYPE ".Seconds"
|
|
#define ST_I18N_KEYFRAMES_INTERVALTYPE_FRAMES ST_I18N_KEYFRAMES_INTERVALTYPE ".Frames"
|
|
#define ST_KEY_KEYFRAMES_INTERVALTYPE "KeyFrames.IntervalType"
|
|
#define ST_I18N_KEYFRAMES_INTERVAL ST_I18N_KEYFRAMES ".Interval"
|
|
#define ST_KEY_KEYFRAMES_INTERVAL_SECONDS "KeyFrames.Interval.Seconds"
|
|
#define ST_KEY_KEYFRAMES_INTERVAL_FRAMES "KeyFrames.Interval.Frames"
|
|
|
|
// Advanced
|
|
#define ST_I18N_ADVANCED ST_I18N ".Advanced"
|
|
#define ST_I18N_ADVANCED_THREADS ST_I18N_ADVANCED ".Threads"
|
|
#define ST_KEY_ADVANCED_THREADS "Advanced.Threads"
|
|
#define ST_I18N_ADVANCED_ROWMULTITHREADING ST_I18N_ADVANCED ".RowMultiThreading"
|
|
#define ST_KEY_ADVANCED_ROWMULTITHREADING "Advanced.RowMultiThreading"
|
|
#define ST_I18N_ADVANCED_TILE_COLUMNS ST_I18N_ADVANCED ".Tile.Columns"
|
|
#define ST_KEY_ADVANCED_TILE_COLUMNS "Advanced.Tile.Columns"
|
|
#define ST_I18N_ADVANCED_TILE_ROWS ST_I18N_ADVANCED ".Tile.Rows"
|
|
#define ST_KEY_ADVANCED_TILE_ROWS "Advanced.Tile.Rows"
|
|
#define ST_I18N_ADVANCED_TUNE ST_I18N_ADVANCED ".Tune"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC ST_I18N_ADVANCED_TUNE ".Metric"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_PSNR ST_I18N_ADVANCED_TUNE_METRIC ".PSNR"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_SSIM ST_I18N_ADVANCED_TUNE_METRIC ".SSIM"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_VMAFWITHPREPROCESSING ST_I18N_ADVANCED_TUNE_METRIC ".VMAF.WithPreprocessing"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_VMAFWITHOUTPREPROCESSING ST_I18N_ADVANCED_TUNE_METRIC ".VMAF.WithoutPreProcessing"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_VMAFMAXGAIN ST_I18N_ADVANCED_TUNE_METRIC ".VMAF.MaxGain"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_VMAFNEGMAXGAIN ST_I18N_ADVANCED_TUNE_METRIC ".VMAF.NegMaxGain"
|
|
#define ST_I18N_ADVANCED_TUNE_METRIC_BUTTERAUGLI ST_I18N_ADVANCED_TUNE_METRIC ".Butteraugli"
|
|
#define ST_KEY_ADVANCED_TUNE_METRIC "Advanced.Tune.Metric"
|
|
#define ST_I18N_ADVANCED_TUNE_CONTENT ST_I18N_ADVANCED_TUNE ".Content"
|
|
#define ST_I18N_ADVANCED_TUNE_CONTENT_SCREEN ST_I18N_ADVANCED_TUNE_CONTENT ".Screen"
|
|
#define ST_I18N_ADVANCED_TUNE_CONTENT_FILM ST_I18N_ADVANCED_TUNE_CONTENT ".Film"
|
|
#define ST_KEY_ADVANCED_TUNE_CONTENT "Advanced.Tune.Content"
|
|
|
|
using namespace streamfx::encoder::aom::av1;
|
|
|
|
static constexpr std::string_view HELP_URL = "https://github.com/Xaymar/obs-StreamFX/wiki/Encoder-AOM-AV1";
|
|
|
|
const char* obs_video_format_to_string(video_format format)
|
|
{
|
|
switch (format) {
|
|
case VIDEO_FORMAT_I420:
|
|
return "I420";
|
|
case VIDEO_FORMAT_NV12:
|
|
return "NV12";
|
|
case VIDEO_FORMAT_YVYU:
|
|
return "YVYU";
|
|
case VIDEO_FORMAT_YUY2:
|
|
return "YUY";
|
|
case VIDEO_FORMAT_UYVY:
|
|
return "UYVY";
|
|
case VIDEO_FORMAT_RGBA:
|
|
return "RGBA";
|
|
case VIDEO_FORMAT_BGRA:
|
|
return "BGRA";
|
|
case VIDEO_FORMAT_BGRX:
|
|
return "BGRX";
|
|
case VIDEO_FORMAT_Y800:
|
|
return "Y800";
|
|
case VIDEO_FORMAT_I444:
|
|
return "I444";
|
|
case VIDEO_FORMAT_BGR3:
|
|
return "BGR3";
|
|
case VIDEO_FORMAT_I422:
|
|
return "I422";
|
|
case VIDEO_FORMAT_I40A:
|
|
return "I40A";
|
|
case VIDEO_FORMAT_I42A:
|
|
return "I42A";
|
|
case VIDEO_FORMAT_YUVA:
|
|
return "YUVA";
|
|
case VIDEO_FORMAT_AYUV:
|
|
return "AYUV";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
const char* aom_color_format_to_string(aom_img_fmt format)
|
|
{
|
|
switch (format) {
|
|
case AOM_IMG_FMT_AOMYV12:
|
|
return "AOM-YV12";
|
|
case AOM_IMG_FMT_AOMI420:
|
|
return "AOM-I420";
|
|
case AOM_IMG_FMT_YV12:
|
|
return "YV12";
|
|
case AOM_IMG_FMT_I420:
|
|
return "I420";
|
|
case AOM_IMG_FMT_I422:
|
|
return "I422";
|
|
case AOM_IMG_FMT_I444:
|
|
return "I444";
|
|
case AOM_IMG_FMT_YV1216:
|
|
return "YV12-16";
|
|
case AOM_IMG_FMT_I42016:
|
|
return "I420-16";
|
|
case AOM_IMG_FMT_I42216:
|
|
return "I422-16";
|
|
case AOM_IMG_FMT_I44416:
|
|
return "I444-16";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
const char* aom_color_trc_to_string(aom_transfer_characteristics_t trc)
|
|
{
|
|
switch (trc) {
|
|
case AOM_CICP_TC_BT_709:
|
|
return "Bt.709";
|
|
case AOM_CICP_TC_BT_470_M:
|
|
return "Bt.407 M";
|
|
case AOM_CICP_TC_BT_470_B_G:
|
|
return "Bt.407 B/G";
|
|
case AOM_CICP_TC_BT_601:
|
|
return "Bt.601";
|
|
case AOM_CICP_TC_SMPTE_240:
|
|
return "SMPTE 240 M";
|
|
case AOM_CICP_TC_LINEAR:
|
|
return "Linear";
|
|
case AOM_CICP_TC_LOG_100:
|
|
return "Logarithmic (100:1 range)";
|
|
case AOM_CICP_TC_LOG_100_SQRT10:
|
|
return "Logarithmic (100*sqrt(10):1 range)";
|
|
case AOM_CICP_TC_IEC_61966:
|
|
return "IEC 61966-2-4";
|
|
case AOM_CICP_TC_BT_1361:
|
|
return "Bt.1361";
|
|
case AOM_CICP_TC_SRGB:
|
|
return "sRGB";
|
|
case AOM_CICP_TC_BT_2020_10_BIT:
|
|
return "Bt.2020 10b";
|
|
case AOM_CICP_TC_BT_2020_12_BIT:
|
|
return "Bt.2020 12b";
|
|
case AOM_CICP_TC_SMPTE_2084:
|
|
return "Bt.2100 PQ";
|
|
case AOM_CICP_TC_SMPTE_428:
|
|
return "SMPTE ST 428";
|
|
case AOM_CICP_TC_HLG:
|
|
return "Bt.2100 HLG";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
const char* aom_rc_mode_to_string(aom_rc_mode mode)
|
|
{
|
|
switch (mode) {
|
|
case AOM_VBR:
|
|
return "Variable Bitrate (VBR)";
|
|
case AOM_CBR:
|
|
return "Constant Bitrate (CBR)";
|
|
case AOM_CQ:
|
|
return "Constrained Quality (CQ)";
|
|
case AOM_Q:
|
|
return "Constant Quality (Q)";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
const char* aom_kf_mode_to_string(aom_kf_mode mode)
|
|
{
|
|
switch (mode) {
|
|
case AOM_KF_AUTO:
|
|
return "Automatic";
|
|
case AOM_KF_DISABLED:
|
|
return "Disabled";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
const char* aom_tune_metric_to_string(aom_tune_metric tune)
|
|
{
|
|
switch (tune) {
|
|
case AOM_TUNE_PSNR:
|
|
return "PSNR";
|
|
case AOM_TUNE_SSIM:
|
|
return "SSIM";
|
|
case AOM_TUNE_VMAF_WITH_PREPROCESSING:
|
|
return "VMAF w/ pre-processing";
|
|
case AOM_TUNE_VMAF_WITHOUT_PREPROCESSING:
|
|
return "VMAF w/o pre-processing";
|
|
case AOM_TUNE_VMAF_MAX_GAIN:
|
|
return "VMAF max. gain";
|
|
case AOM_TUNE_VMAF_NEG_MAX_GAIN:
|
|
return "VMAF negative max. gain";
|
|
case AOM_TUNE_BUTTERAUGLI:
|
|
return "Butteraugli";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
const char* aom_tune_content_to_string(aom_tune_content tune)
|
|
{
|
|
switch (tune) {
|
|
case AOM_CONTENT_DEFAULT:
|
|
return "Default";
|
|
case AOM_CONTENT_FILM:
|
|
return "Film";
|
|
case AOM_CONTENT_SCREEN:
|
|
return "Screen";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
aom_av1_instance::aom_av1_instance(obs_data_t* settings, obs_encoder_t* self, bool is_hw)
|
|
: obs::encoder_instance(settings, self, is_hw), _factory(aom_av1_factory::get()), _iface(nullptr), _ctx(), _cfg(),
|
|
_image_index(0), _images(), _global_headers(nullptr), _initialized(false), _settings()
|
|
{
|
|
if (is_hw) {
|
|
throw std::runtime_error("Hardware encoding isn't even registered, how did you get here?");
|
|
}
|
|
|
|
// Retrieve encoder interface.
|
|
_iface = _factory->libaom_codec_av1_cx();
|
|
if (!_iface) {
|
|
throw std::runtime_error("AOM library does not provide AV1 encoder.");
|
|
}
|
|
|
|
#ifdef ENABLE_PROFILING
|
|
// Profilers
|
|
_profiler_copy = streamfx::util::profiler::create();
|
|
_profiler_encode = streamfx::util::profiler::create();
|
|
_profiler_packet = streamfx::util::profiler::create();
|
|
#endif
|
|
|
|
{ // Generate Static Configuration
|
|
{ // OBS Information
|
|
video_scale_info ovsi;
|
|
video_t* video = obs_encoder_video(_self);
|
|
const struct video_output_info* video_info = video_output_get_info(video);
|
|
|
|
ovsi.colorspace = video_info->colorspace;
|
|
ovsi.format = video_info->format;
|
|
ovsi.range = video_info->range;
|
|
get_video_info(&ovsi);
|
|
|
|
// Video
|
|
_settings.width = static_cast<uint16_t>(obs_encoder_get_width(_self));
|
|
_settings.height = static_cast<uint16_t>(obs_encoder_get_height(_self));
|
|
_settings.fps.num = static_cast<uint32_t>(video_info->fps_num);
|
|
_settings.fps.den = static_cast<uint32_t>(video_info->fps_den);
|
|
|
|
// Color Format
|
|
switch (ovsi.format) {
|
|
case VIDEO_FORMAT_I420:
|
|
_settings.color_format = AOM_IMG_FMT_I420;
|
|
break;
|
|
case VIDEO_FORMAT_I422:
|
|
_settings.color_format = AOM_IMG_FMT_I422;
|
|
break;
|
|
case VIDEO_FORMAT_I444:
|
|
_settings.color_format = AOM_IMG_FMT_I444;
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Color Format is unknown.");
|
|
}
|
|
|
|
// Color Space
|
|
switch (ovsi.colorspace) {
|
|
case VIDEO_CS_601:
|
|
_settings.color_primaries = AOM_CICP_CP_BT_601;
|
|
_settings.color_trc = AOM_CICP_TC_BT_601;
|
|
_settings.color_matrix = AOM_CICP_MC_BT_601;
|
|
break;
|
|
case VIDEO_CS_709:
|
|
_settings.color_primaries = AOM_CICP_CP_BT_709;
|
|
_settings.color_trc = AOM_CICP_TC_BT_709;
|
|
_settings.color_matrix = AOM_CICP_MC_BT_709;
|
|
break;
|
|
case VIDEO_CS_SRGB:
|
|
_settings.color_primaries = AOM_CICP_CP_BT_709;
|
|
_settings.color_trc = AOM_CICP_TC_SRGB;
|
|
_settings.color_matrix = AOM_CICP_MC_BT_709;
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Color Space is unknown.");
|
|
}
|
|
|
|
// Color Range
|
|
switch (ovsi.range) {
|
|
case VIDEO_RANGE_FULL:
|
|
_settings.color_range = AOM_CR_FULL_RANGE;
|
|
break;
|
|
case VIDEO_RANGE_PARTIAL:
|
|
_settings.color_range = AOM_CR_STUDIO_RANGE;
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Color Range is unknown.");
|
|
}
|
|
|
|
// Monochrome
|
|
_settings.monochrome = (video_info->format == VIDEO_FORMAT_Y800);
|
|
}
|
|
|
|
{ // Encoder
|
|
_settings.profile = static_cast<codec::av1::profile>(obs_data_get_int(settings, ST_KEY_ENCODER_PROFILE));
|
|
if (_settings.profile == codec::av1::profile::UNKNOWN) {
|
|
// Resolve the automatic profile to a proper value.
|
|
bool need_professional = (_settings.color_format == AOM_IMG_FMT_I422);
|
|
bool need_high = (_settings.color_format == AOM_IMG_FMT_I444) || _settings.monochrome;
|
|
|
|
if (need_professional) {
|
|
_settings.profile = codec::av1::profile::PROFESSIONAL;
|
|
} else if (need_high) {
|
|
_settings.profile = codec::av1::profile::HIGH;
|
|
} else {
|
|
_settings.profile = codec::av1::profile::MAIN;
|
|
}
|
|
}
|
|
}
|
|
|
|
{ // Rate Control
|
|
_settings.rc_mode = static_cast<aom_rc_mode>(obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE));
|
|
_settings.rc_lookahead = static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LOOKAHEAD));
|
|
}
|
|
|
|
{ // Threading
|
|
if (auto threads = obs_data_get_int(settings, ST_KEY_ADVANCED_THREADS); threads > 0) {
|
|
_settings.threads = static_cast<int8_t>(threads);
|
|
} else {
|
|
_settings.threads = static_cast<int8_t>(std::thread::hardware_concurrency());
|
|
}
|
|
_settings.rowmultithreading =
|
|
static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_ADVANCED_ROWMULTITHREADING));
|
|
}
|
|
|
|
{ // Tiling
|
|
_settings.tile_columns = static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_ADVANCED_TILE_COLUMNS));
|
|
_settings.tile_rows = static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_ADVANCED_TILE_ROWS));
|
|
}
|
|
|
|
{ // Tuning
|
|
if (auto v = obs_data_get_int(settings, ST_KEY_ADVANCED_TUNE_METRIC); v != -1) {
|
|
_settings.tune_metric = static_cast<aom_tune_metric>(v);
|
|
}
|
|
_settings.tune_content =
|
|
static_cast<aom_tune_content>(obs_data_get_int(settings, ST_KEY_ADVANCED_TUNE_CONTENT));
|
|
}
|
|
}
|
|
|
|
// Apply Settings
|
|
update(settings);
|
|
|
|
// Initialize Encoder
|
|
if (auto error = _factory->libaom_codec_enc_init_ver(&_ctx, _iface, &_cfg, 0, AOM_ENCODER_ABI_VERSION);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
D_LOG_ERROR("Failed to initialize codec, unexpected error: %s (code %" PRIu32 ")", errstr, error);
|
|
throw std::runtime_error(errstr);
|
|
}
|
|
|
|
{ // Apply Static Control Settings
|
|
|
|
{ // Color Information
|
|
#ifdef AOM_CTRL_AV1E_SET_COLOR_PRIMARIES
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_COLOR_PRIMARIES, _settings.color_primaries);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_COLOR_PRIMARIES", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
#else
|
|
D_LOG_ERROR("AOM library was built without AV1E_SET_COLOR_PRIMARIES, behavior is unknown.");
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_TRANSFER_CHARACTERISTICS
|
|
if (auto error =
|
|
_factory->libaom_codec_control(&_ctx, AV1E_SET_TRANSFER_CHARACTERISTICS, _settings.color_trc);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_TRANSFER_CHARACTERISTICS", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
#else
|
|
D_LOG_ERROR("AOM library was built without AV1E_SET_TRANSFER_CHARACTERISTICS, behavior is unknown.");
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_MATRIX_COEFFICIENTS
|
|
if (auto error =
|
|
_factory->libaom_codec_control(&_ctx, AV1E_SET_MATRIX_COEFFICIENTS, _settings.color_matrix);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_MATRIX_COEFFICIENTS", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
#else
|
|
D_LOG_ERROR("AOM library was built without AV1E_SET_MATRIX_COEFFICIENTS, behavior is unknown.");
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_COLOR_RANGE
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_COLOR_RANGE, _settings.color_range);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_COLOR_RANGE", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
#else
|
|
D_LOG_ERROR("AOM library was built without AV1_SET_COLOR_RANGE, behavior is unknown.");
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_CHROMA_SAMPLE_POSITION
|
|
// !TODO: Consider making this user-controlled. At the moment, this follows the H.264 chroma standard.
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_CHROMA_SAMPLE_POSITION, AOM_CSP_VERTICAL);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_CHROMA_SAMPLE_POSITION", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_RENDER_SIZE
|
|
int32_t size[2] = {_settings.width, _settings.height};
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_RENDER_SIZE, &size);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_RENDER_SIZE", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Apply Dynamic Control Settings
|
|
if (!update(settings)) {
|
|
throw std::runtime_error("Unexpected error during configuration.");
|
|
}
|
|
}
|
|
|
|
// Preallocate global headers.
|
|
_global_headers = _factory->libaom_codec_get_global_headers(&_ctx);
|
|
|
|
// Allocate frames.
|
|
_images.resize(_cfg.g_threads);
|
|
for (auto& image : _images) {
|
|
_factory->libaom_img_alloc(&image, _settings.color_format, _settings.width, _settings.height, 8);
|
|
|
|
// Color Information.
|
|
image.fmt = _settings.color_format;
|
|
image.cp = _settings.color_primaries;
|
|
image.tc = _settings.color_trc;
|
|
image.mc = _settings.color_matrix;
|
|
image.range = _settings.color_range;
|
|
image.monochrome = _settings.monochrome ? 1 : 0;
|
|
image.csp = AOM_CSP_VERTICAL; // !TODO: Consider making this user-controlled.
|
|
|
|
// Size
|
|
image.r_w = image.d_w;
|
|
image.r_h = image.d_h;
|
|
image.r_w = image.w;
|
|
image.r_h = image.h;
|
|
}
|
|
|
|
// Log Settings
|
|
log();
|
|
|
|
// Signal to future update() calls that we are fully initialized.
|
|
_initialized = true;
|
|
}
|
|
|
|
aom_av1_instance::~aom_av1_instance()
|
|
{
|
|
#ifdef ENABLE_PROFILING
|
|
// Profiling
|
|
D_LOG_INFO("Timings | Avg. µs | 99.9ile µs | 99.0ile µs | 95.0ile µs | Samples ", "");
|
|
D_LOG_INFO("--------+---------------+---------------+---------------+---------------+----------", "");
|
|
D_LOG_INFO("Copy | %13.1f | %13" PRId64 " | %13" PRId64 " | %13" PRId64 " | %9" PRIu64,
|
|
_profiler_copy->average_duration() / 1000.,
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_copy->percentile(0.999)).count(),
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_copy->percentile(0.990)).count(),
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_copy->percentile(0.950)).count(),
|
|
_profiler_copy->count());
|
|
D_LOG_INFO("Encode | %13.1f | %13" PRId64 " | %13" PRId64 " | %13" PRId64 " | %9" PRIu64,
|
|
_profiler_encode->average_duration() / 1000.,
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_encode->percentile(0.999)).count(),
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_encode->percentile(0.990)).count(),
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_encode->percentile(0.950)).count(),
|
|
_profiler_encode->count());
|
|
D_LOG_INFO("Packet | %13.1f | %13" PRId64 " | %13" PRId64 " | %13" PRId64 " | %9" PRIu64,
|
|
_profiler_packet->average_duration() / 1000.,
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_packet->percentile(0.999)).count(),
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_packet->percentile(0.990)).count(),
|
|
std::chrono::duration_cast<std::chrono::microseconds>(_profiler_packet->percentile(0.950)).count(),
|
|
_profiler_packet->count());
|
|
#endif
|
|
|
|
// Deallocate global buffer.
|
|
if (_global_headers) {
|
|
/* Breaks heap
|
|
if (_global_headers->buf) {
|
|
free(_global_headers->buf);
|
|
_global_headers->buf = nullptr;
|
|
}
|
|
free(_global_headers);
|
|
_global_headers = nullptr;
|
|
*/
|
|
}
|
|
|
|
// Deallocate frames.
|
|
for (auto& image : _images) {
|
|
_factory->libaom_img_free(&image);
|
|
}
|
|
_images.clear();
|
|
|
|
// Destroy encoder.
|
|
_factory->libaom_codec_destroy(&_ctx);
|
|
}
|
|
|
|
void aom_av1_instance::migrate(obs_data_t* settings, uint64_t version) {}
|
|
|
|
bool aom_av1_instance::update(obs_data_t* settings)
|
|
{
|
|
video_t* obsVideo = obs_encoder_video(_self);
|
|
const struct video_output_info* obsVideoInfo = video_output_get_info(obsVideo);
|
|
uint32_t obsFPSnum = obsVideoInfo->fps_num;
|
|
uint32_t obsFPSden = obsVideoInfo->fps_den;
|
|
bool obsMonochrome = (obsVideoInfo->format == VIDEO_FORMAT_Y800);
|
|
|
|
#define SET_IF_NOT_DEFAULT(X, Y) \
|
|
if (X != -1) { \
|
|
Y = static_cast<decltype(Y)>(X); \
|
|
}
|
|
|
|
{ // Generate Dynamic Settings
|
|
|
|
{ // Encoder
|
|
_settings.preset = static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_ENCODER_CPUUSAGE));
|
|
}
|
|
|
|
{ // Rate Control
|
|
_settings.rc_bitrate = static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE));
|
|
_settings.rc_bitrate_overshoot =
|
|
static_cast<int32_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT));
|
|
_settings.rc_bitrate_undershoot =
|
|
static_cast<int32_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_OVERSHOOT));
|
|
_settings.rc_quality = static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY));
|
|
_settings.rc_quantizer_min =
|
|
static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MINIMUM));
|
|
_settings.rc_quantizer_max =
|
|
static_cast<int8_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MAXIMUM));
|
|
_settings.rc_buffer_ms = static_cast<int32_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_BUFFER_SIZE));
|
|
_settings.rc_buffer_initial_ms =
|
|
static_cast<int32_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_BUFFER_SIZE_INITIAL));
|
|
_settings.rc_buffer_optimal_ms =
|
|
static_cast<int32_t>(obs_data_get_int(settings, ST_KEY_RATECONTROL_BUFFER_SIZE_OPTIMAL));
|
|
}
|
|
|
|
{ // Key-Frames
|
|
int64_t kf_type = obs_data_get_int(settings, ST_KEY_KEYFRAMES_INTERVALTYPE);
|
|
bool is_seconds = (kf_type == 0);
|
|
|
|
_settings.kf_mode = AOM_KF_AUTO;
|
|
if (is_seconds) {
|
|
_settings.kf_distance_max = static_cast<int32_t>(
|
|
std::lround(obs_data_get_double(settings, ST_KEY_KEYFRAMES_INTERVAL_SECONDS)
|
|
* static_cast<double>(obsFPSnum) / static_cast<double>(obsFPSden)));
|
|
} else {
|
|
_settings.kf_distance_max =
|
|
static_cast<int32_t>(obs_data_get_int(settings, ST_KEY_KEYFRAMES_INTERVAL_FRAMES));
|
|
}
|
|
_settings.kf_distance_min = _settings.kf_distance_max;
|
|
}
|
|
|
|
// All-Intra requires us to never set Key-Frames.
|
|
if (_cfg.g_usage == AOM_USAGE_ALL_INTRA) {
|
|
_settings.rc_lookahead = 0;
|
|
_settings.kf_mode = AOM_KF_DISABLED;
|
|
_settings.kf_distance_min = 0;
|
|
_settings.kf_distance_max = 0;
|
|
}
|
|
}
|
|
|
|
{ // Configuration.
|
|
|
|
{ // Usage and Defaults
|
|
_cfg.g_usage = static_cast<unsigned int>(obs_data_get_int(settings, ST_KEY_ENCODER_USAGE));
|
|
_factory->libaom_codec_enc_config_default(_iface, &_cfg, _cfg.g_usage);
|
|
}
|
|
|
|
{ // Frame Information
|
|
// Size
|
|
_cfg.g_w = _settings.width;
|
|
_cfg.g_h = _settings.height;
|
|
|
|
// Time Base (Rate is inverted Time Base)
|
|
_cfg.g_timebase.num = static_cast<int>(_settings.fps.den);
|
|
_cfg.g_timebase.den = static_cast<int>(_settings.fps.num);
|
|
|
|
// !INFO: Whenever OBS decides to support anything but 8-bits, let me know.
|
|
_cfg.g_bit_depth = AOM_BITS_8;
|
|
_cfg.g_input_bit_depth = AOM_BITS_8;
|
|
|
|
// Monochrome color
|
|
_cfg.monochrome = _settings.monochrome ? 1u : 0u;
|
|
}
|
|
|
|
{ // Encoder
|
|
|
|
// AV1 Profile
|
|
_cfg.g_profile = static_cast<unsigned int>(_settings.profile);
|
|
}
|
|
|
|
{ // Rate Control
|
|
// Mode
|
|
_cfg.rc_end_usage = static_cast<aom_rc_mode>(obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE));
|
|
|
|
// Look-Ahead
|
|
SET_IF_NOT_DEFAULT(_settings.rc_lookahead, _cfg.g_lag_in_frames);
|
|
|
|
// Limits
|
|
SET_IF_NOT_DEFAULT(_settings.rc_bitrate, _cfg.rc_target_bitrate);
|
|
SET_IF_NOT_DEFAULT(_settings.rc_bitrate_overshoot, _cfg.rc_overshoot_pct);
|
|
SET_IF_NOT_DEFAULT(_settings.rc_bitrate_undershoot, _cfg.rc_undershoot_pct);
|
|
SET_IF_NOT_DEFAULT(_settings.rc_quantizer_min, _cfg.rc_min_quantizer);
|
|
SET_IF_NOT_DEFAULT(_settings.rc_quantizer_max, _cfg.rc_max_quantizer);
|
|
|
|
// Buffer
|
|
SET_IF_NOT_DEFAULT(_settings.rc_buffer_ms, _cfg.rc_buf_sz);
|
|
SET_IF_NOT_DEFAULT(_settings.rc_buffer_initial_ms, _cfg.rc_buf_initial_sz);
|
|
SET_IF_NOT_DEFAULT(_settings.rc_buffer_optimal_ms, _cfg.rc_buf_optimal_sz);
|
|
}
|
|
|
|
{ // Key-Frames
|
|
SET_IF_NOT_DEFAULT(_settings.kf_mode, _cfg.kf_mode);
|
|
SET_IF_NOT_DEFAULT(_settings.kf_distance_min, _cfg.kf_min_dist);
|
|
SET_IF_NOT_DEFAULT(_settings.kf_distance_max, _cfg.kf_max_dist);
|
|
}
|
|
|
|
{ // Advanced
|
|
|
|
// Single-Pass
|
|
_cfg.g_pass = AOM_RC_ONE_PASS;
|
|
|
|
// Threading
|
|
SET_IF_NOT_DEFAULT(_settings.threads, _cfg.g_threads);
|
|
}
|
|
|
|
// TODO: Future
|
|
//_cfg.rc_resize_mode = 0; // "RESIZE_NONE
|
|
//_cfg.rc_resize_denominator = ?;
|
|
//_cfg.rc_resize_kf_denominator = ?;
|
|
//_cfg.rc_superres_mode = AOM_SUPERRES_NONE;
|
|
//_cfg.rc_superres_denominator = ?;
|
|
//_cfg.rc_superres_kf_denominator = ?;
|
|
//_cfg.rc_superres_qthresh = ?;
|
|
//_cfg.rc_superres_kf_qtresh = ?;
|
|
//_cfg.rc_dropframe_thresh = ?;
|
|
//_cfg.fwd_kf_enabled = ?;
|
|
//_cfg.g_forced_max_frame_width = ?;
|
|
//_cfg.g_forced_max_frame_height = ?;
|
|
//_cfg.g_error_resilient = ?;
|
|
//_cfg.g_lag_in_frames = ?;
|
|
//_cfg.sframe_dist = ?;
|
|
//_cfg.sframe_mode = 0;
|
|
//_cfg.large_scale_tile = 0;
|
|
//_cfg.full_still_picture_hdr = ?;
|
|
//_cfg.save_as_annexb = ?;
|
|
//_cfg.encoder_cfg = ?;
|
|
|
|
// Apply configuration
|
|
if (_initialized) {
|
|
if (auto error = _factory->libaom_codec_enc_config_set(&_ctx, &_cfg); error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing configuration: %s (code %" PRIu32 ")%s%s%s%s", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_ctx.iface) { // Control
|
|
|
|
{ // Encoder
|
|
#ifdef AOM_CTRL_AOME_SET_CPUUSED
|
|
if (_settings.preset != -1) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AOME_SET_CPUUSED, _settings.preset);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AOME_SET_CPUUSED", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
{ // Rate Control
|
|
#ifdef AOM_CTRL_AOME_SET_CQ_LEVEL
|
|
if ((_settings.rc_quality != -1) && ((_settings.rc_mode == AOM_CQ) || (_settings.rc_mode == AOM_Q))) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AOME_SET_CQ_LEVEL, _settings.rc_quality);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AOME_SET_CQ_LEVEL", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
{ // Advanced
|
|
#ifdef AOM_CTRL_AV1E_SET_ROW_MT
|
|
if (_settings.rowmultithreading != -1) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_ROW_MT, _settings.rowmultithreading);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_ROW_MT", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_TILE_COLUMNS
|
|
if (_settings.tile_columns != -1) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_TILE_COLUMNS, _settings.tile_columns);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_TILE_COLUMNS", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_TILE_ROWS
|
|
if (_settings.tile_rows != -1) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_TILE_ROWS, _settings.tile_rows);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_TILE_ROWS", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef AOM_CTRL_AOME_SET_TUNING
|
|
if (_settings.tune_metric != -1) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AOME_SET_TUNING, _settings.tune_metric);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AOME_SET_TUNING", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef AOM_CTRL_AV1E_SET_TUNE_CONTENT
|
|
if (_settings.tune_content != AOM_CONTENT_DEFAULT) {
|
|
if (auto error = _factory->libaom_codec_control(&_ctx, AV1E_SET_TUNE_CONTENT, _settings.tune_content);
|
|
error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
const char* err = _factory->libaom_codec_error(&_ctx);
|
|
const char* errdtl = _factory->libaom_codec_error_detail(&_ctx);
|
|
D_LOG_WARNING("Error changing '%s': %s (code %" PRIu32 ")%s%s%s%s", //
|
|
"AV1E_SET_TUNE_CONTENT", //
|
|
(errstr ? errstr : ""), error, //
|
|
(err ? "\n\tMessage: " : ""), (err ? err : ""), //
|
|
(errdtl ? "\n\tDetails: " : ""), (errdtl ? errdtl : "") //
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#undef SET_IF_NOT_DEFAULT
|
|
|
|
// Log the changed settings.
|
|
if (_initialized) {
|
|
log();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void aom_av1_instance::log()
|
|
{
|
|
D_LOG_INFO("AOM AV1:", "");
|
|
D_LOG_INFO(" Video: %" PRIu16 "x%" PRIu16 "@%1.2ffps (%" PRIu32 "/%" PRIu32 ")", _settings.width, _settings.height,
|
|
static_cast<double>(_settings.fps.num) / static_cast<float>(_settings.fps.den), _settings.fps.num,
|
|
_settings.fps.den);
|
|
D_LOG_INFO(" Color: %s/%s/%s%s", aom_color_format_to_string(_settings.color_format),
|
|
aom_color_trc_to_string(_settings.color_trc),
|
|
_settings.color_range == AOM_CR_FULL_RANGE ? "Full" : "Partial",
|
|
_settings.monochrome ? "/Monochrome" : "");
|
|
|
|
// Rate Control
|
|
D_LOG_INFO(" Rate Control: %s", aom_rc_mode_to_string(_settings.rc_mode));
|
|
D_LOG_INFO(" Look-Ahead: %" PRId8, _settings.rc_lookahead);
|
|
D_LOG_INFO(" Buffers: %" PRId32 " ms / %" PRId32 " ms / %" PRId32 " ms", _settings.rc_buffer_ms,
|
|
_settings.rc_buffer_initial_ms, _settings.rc_buffer_optimal_ms);
|
|
D_LOG_INFO(" Bitrate: %" PRId32 " kbit/s (-%" PRId32 "%% - +%" PRId32 "%%)", _settings.rc_bitrate,
|
|
_settings.rc_bitrate_undershoot, _settings.rc_bitrate_overshoot);
|
|
D_LOG_INFO(" Quality: %" PRId8, _settings.rc_quality);
|
|
D_LOG_INFO(" Quantizer: %" PRId8 " - %" PRId8, _settings.rc_quantizer_min, _settings.rc_quantizer_max);
|
|
|
|
// Key-Frames
|
|
D_LOG_INFO(" Key-Frames: %s", aom_kf_mode_to_string(_settings.kf_mode));
|
|
D_LOG_INFO(" Distance: %" PRId32 " - %" PRId32 " frames", _settings.kf_distance_min, _settings.kf_distance_max);
|
|
|
|
// Advanced
|
|
D_LOG_INFO(" Advanced: ", "");
|
|
D_LOG_INFO(" Threads: %" PRId8, _settings.threads);
|
|
D_LOG_INFO(" Row-Multi-Threading: %s", _settings.rowmultithreading == -1 ? "Default"
|
|
: _settings.rowmultithreading == 1 ? "Enabled"
|
|
: "Disabled");
|
|
D_LOG_INFO(" Tiling: %" PRId8 "x%" PRId8, _settings.tile_columns, _settings.tile_rows);
|
|
D_LOG_INFO(" Tune: %s (Metric), %s (Content)", aom_tune_metric_to_string(_settings.tune_metric),
|
|
aom_tune_content_to_string(_settings.tune_content));
|
|
}
|
|
|
|
bool aom_av1_instance::get_extra_data(uint8_t** extra_data, size_t* size)
|
|
{
|
|
if (!_global_headers) {
|
|
return false;
|
|
}
|
|
|
|
*extra_data = static_cast<uint8_t*>(_global_headers->buf);
|
|
*size = _global_headers->sz;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool aom_av1_instance::get_sei_data(uint8_t** sei_data, size_t* size)
|
|
{
|
|
return get_extra_data(sei_data, size);
|
|
}
|
|
|
|
void aom_av1_instance::get_video_info(struct video_scale_info* info)
|
|
{
|
|
// Fix up color format.
|
|
auto format = obs_encoder_get_preferred_video_format(_self);
|
|
if (format == VIDEO_FORMAT_NONE) {
|
|
format = info->format;
|
|
}
|
|
|
|
switch (format) {
|
|
// Perfect matches.
|
|
case VIDEO_FORMAT_I444: // AOM_IMG_I444.
|
|
case VIDEO_FORMAT_I422: // AOM_IMG_I422.
|
|
case VIDEO_FORMAT_I420: // AOM_IMG_I420.
|
|
break;
|
|
|
|
// 4:2:0 formats
|
|
case VIDEO_FORMAT_NV12: // Y, UV Interleaved.
|
|
case VIDEO_FORMAT_I40A:
|
|
D_LOG_WARNING("Color-format '%s' is not supported, forcing 'I420'...", obs_video_format_to_string(format));
|
|
info->format = VIDEO_FORMAT_I420;
|
|
break;
|
|
|
|
// 4:2:2-like formats
|
|
case VIDEO_FORMAT_UYVY:
|
|
case VIDEO_FORMAT_YUY2:
|
|
case VIDEO_FORMAT_YVYU:
|
|
case VIDEO_FORMAT_I42A:
|
|
D_LOG_WARNING("Color-format '%s' is not supported, forcing 'I422'...", obs_video_format_to_string(format));
|
|
info->format = VIDEO_FORMAT_I422;
|
|
break;
|
|
|
|
// 4:4:4
|
|
case VIDEO_FORMAT_BGR3:
|
|
case VIDEO_FORMAT_BGRA:
|
|
case VIDEO_FORMAT_BGRX:
|
|
case VIDEO_FORMAT_RGBA:
|
|
case VIDEO_FORMAT_YUVA:
|
|
case VIDEO_FORMAT_AYUV:
|
|
case VIDEO_FORMAT_Y800: // Grayscale, no exact match.
|
|
D_LOG_WARNING("Color-format '%s' is not supported, forcing 'I444'...", obs_video_format_to_string(format));
|
|
info->format = VIDEO_FORMAT_I444;
|
|
break;
|
|
default:
|
|
throw std::runtime_error("Color Format is unknown.");
|
|
}
|
|
|
|
// Fix up color space.
|
|
if (info->colorspace == VIDEO_CS_DEFAULT) {
|
|
info->colorspace = VIDEO_CS_SRGB;
|
|
}
|
|
|
|
// Fix up color range.
|
|
if (info->range == VIDEO_RANGE_DEFAULT) {
|
|
info->range = VIDEO_RANGE_PARTIAL;
|
|
}
|
|
}
|
|
|
|
bool streamfx::encoder::aom::av1::aom_av1_instance::encode_video(encoder_frame* frame, encoder_packet* packet,
|
|
bool* received_packet)
|
|
{
|
|
// Retrieve current indexed image.
|
|
auto& image = _images.at(_image_index);
|
|
|
|
{ // Copy Image data.
|
|
#ifdef ENABLE_PROFILING
|
|
auto profile = _profiler_copy->track();
|
|
#endif
|
|
std::memcpy(image.planes[AOM_PLANE_Y], frame->data[0], frame->linesize[0] * image.h);
|
|
if (image.fmt == AOM_IMG_FMT_I420) {
|
|
std::memcpy(image.planes[AOM_PLANE_U], frame->data[1], frame->linesize[1] * image.h / 2);
|
|
std::memcpy(image.planes[AOM_PLANE_V], frame->data[2], frame->linesize[2] * image.h / 2);
|
|
} else {
|
|
std::memcpy(image.planes[AOM_PLANE_U], frame->data[1], frame->linesize[1] * image.h);
|
|
std::memcpy(image.planes[AOM_PLANE_V], frame->data[2], frame->linesize[2] * image.h);
|
|
}
|
|
}
|
|
|
|
{ // Try to encode the new image.
|
|
#ifdef ENABLE_PROFILING
|
|
auto profile = _profiler_encode->track();
|
|
#endif
|
|
aom_enc_frame_flags_t flags = 0;
|
|
if (_cfg.g_usage == AOM_USAGE_ALL_INTRA) {
|
|
flags = AOM_EFLAG_FORCE_KF;
|
|
}
|
|
if (auto error = _factory->libaom_codec_encode(&_ctx, &image, frame->pts, 1, flags); error != AOM_CODEC_OK) {
|
|
const char* errstr = _factory->libaom_codec_err_to_string(error);
|
|
D_LOG_ERROR("Encoding frame failed with error: %s (code %" PRIu32 ")\n%s\n%s", errstr, error,
|
|
_factory->libaom_codec_error(&_ctx), _factory->libaom_codec_error_detail(&_ctx));
|
|
return false;
|
|
} else {
|
|
// Increment the image index.
|
|
_image_index = (_image_index++) % _images.size();
|
|
}
|
|
}
|
|
|
|
{ // Get Packet
|
|
#ifdef ENABLE_PROFILING
|
|
auto profile = _profiler_packet->track();
|
|
#endif
|
|
aom_codec_iter_t iter = NULL;
|
|
for (auto* pkt = _factory->libaom_codec_get_cx_data(&_ctx, &iter); pkt != nullptr;
|
|
pkt = _factory->libaom_codec_get_cx_data(&_ctx, &iter)) {
|
|
#ifdef _DEBUG
|
|
{
|
|
const char* kind = "";
|
|
switch (pkt->kind) {
|
|
case AOM_CODEC_CX_FRAME_PKT:
|
|
kind = "Frame";
|
|
break;
|
|
case AOM_CODEC_STATS_PKT:
|
|
kind = "Stats";
|
|
break;
|
|
case AOM_CODEC_FPMB_STATS_PKT:
|
|
kind = "FPMB Stats";
|
|
break;
|
|
case AOM_CODEC_PSNR_PKT:
|
|
kind = "PSNR";
|
|
break;
|
|
case AOM_CODEC_CUSTOM_PKT:
|
|
kind = "Custom";
|
|
break;
|
|
}
|
|
D_LOG_DEBUG("\tPacket: Kind=%s", kind)
|
|
}
|
|
#endif
|
|
|
|
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
|
// Status
|
|
packet->type = OBS_ENCODER_VIDEO;
|
|
packet->keyframe = ((pkt->data.frame.flags & AOM_FRAME_IS_KEY) == AOM_FRAME_IS_KEY)
|
|
|| (_cfg.g_usage == AOM_USAGE_ALL_INTRA);
|
|
if (packet->keyframe) {
|
|
//
|
|
packet->priority = 3; // OBS_NAL_PRIORITY_HIGHEST
|
|
packet->drop_priority = 3; // OBS_NAL_PRIORITY_HIGHEST
|
|
} else if ((pkt->data.frame.flags & AOM_FRAME_IS_DROPPABLE) != AOM_FRAME_IS_DROPPABLE) {
|
|
// Dropping this frame breaks the bitstream.
|
|
packet->priority = 2; // OBS_NAL_PRIORITY_HIGH
|
|
packet->drop_priority = 3; // OBS_NAL_PRIORITY_HIGHEST
|
|
} else {
|
|
// This frame can be dropped at will.
|
|
packet->priority = 0; // OBS_NAL_PRIORITY_DISPOSABLE
|
|
packet->drop_priority = 0; // OBS_NAL_PRIORITY_DISPOSABLE
|
|
}
|
|
|
|
// Data
|
|
packet->data = static_cast<uint8_t*>(pkt->data.frame.buf);
|
|
packet->size = pkt->data.frame.sz;
|
|
|
|
// Timestamps
|
|
//TODO: Temporarily set both to the same until there is a way to figure out actual order.
|
|
packet->pts = pkt->data.frame.pts;
|
|
packet->dts = pkt->data.frame.pts;
|
|
|
|
*received_packet = true;
|
|
}
|
|
|
|
if (*received_packet == true)
|
|
break;
|
|
}
|
|
|
|
if (!*received_packet) {
|
|
packet->type = OBS_ENCODER_VIDEO;
|
|
packet->data = nullptr;
|
|
packet->size = 0;
|
|
packet->pts = -1;
|
|
packet->dts = -1;
|
|
#ifdef _DEBUG
|
|
D_LOG_DEBUG("No Packet", "");
|
|
#endif
|
|
// Not necessarily an error.
|
|
//return false;
|
|
} else {
|
|
#ifdef _DEBUG
|
|
D_LOG_DEBUG("Packet: Type=%s PTS=%06" PRId64 " DTS=%06" PRId64 " Size=%016" PRIuPTR "",
|
|
packet->keyframe ? "I" : "P", packet->pts, packet->dts, packet->size);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
aom_av1_factory::aom_av1_factory()
|
|
{
|
|
// Try and load the AOM library.
|
|
std::vector<std::filesystem::path> libs;
|
|
|
|
#ifdef D_PLATFORM_WINDOWS
|
|
// Try loading from the data directory first.
|
|
libs.push_back(streamfx::data_file_path("aom.dll")); // MSVC (preferred)
|
|
libs.push_back(streamfx::data_file_path("libaom.dll")); // Cross-Compile
|
|
// In any other case, load the system-wide binary.
|
|
libs.push_back("aom.dll");
|
|
libs.push_back("libaom.dll");
|
|
#else
|
|
// Try loading from the data directory first.
|
|
libs.push_back(streamfx::data_file_path("libaom.so"));
|
|
// In any other case, load the system-wide binary.
|
|
libs.push_back("libaom");
|
|
#endif
|
|
|
|
for (auto lib : libs) {
|
|
try {
|
|
_library = streamfx::util::library::load(lib);
|
|
if (_library)
|
|
break;
|
|
} catch (...) {
|
|
D_LOG_WARNING("Loading of '%s' failed.", lib.generic_string().c_str());
|
|
}
|
|
}
|
|
if (!_library) {
|
|
throw std::runtime_error("Unable to load AOM library.");
|
|
}
|
|
|
|
// Load all necessary functions.
|
|
#define _LOAD_SYMBOL(X) \
|
|
{ \
|
|
lib##X = reinterpret_cast<decltype(lib##X)>(_library->load_symbol(std::string(#X))); \
|
|
}
|
|
#define _LOAD_SYMBOL_(X, Y) \
|
|
{ \
|
|
lib##X = reinterpret_cast<decltype(lib##X)>(_library->load_symbol(std::string(Y))); \
|
|
}
|
|
_LOAD_SYMBOL(aom_codec_version);
|
|
_LOAD_SYMBOL(aom_codec_version_str);
|
|
_LOAD_SYMBOL(aom_codec_version_extra_str);
|
|
_LOAD_SYMBOL(aom_codec_build_config);
|
|
_LOAD_SYMBOL(aom_codec_iface_name);
|
|
_LOAD_SYMBOL(aom_codec_err_to_string);
|
|
_LOAD_SYMBOL(aom_codec_error);
|
|
_LOAD_SYMBOL(aom_codec_error_detail);
|
|
_LOAD_SYMBOL(aom_codec_destroy);
|
|
_LOAD_SYMBOL(aom_codec_get_caps);
|
|
_LOAD_SYMBOL(aom_codec_control);
|
|
_LOAD_SYMBOL(aom_codec_set_option);
|
|
_LOAD_SYMBOL(aom_obu_type_to_string);
|
|
_LOAD_SYMBOL(aom_uleb_size_in_bytes);
|
|
_LOAD_SYMBOL(aom_uleb_decode);
|
|
_LOAD_SYMBOL(aom_uleb_encode);
|
|
_LOAD_SYMBOL(aom_uleb_encode_fixed_size);
|
|
_LOAD_SYMBOL(aom_img_alloc);
|
|
_LOAD_SYMBOL(aom_img_wrap);
|
|
_LOAD_SYMBOL(aom_img_alloc_with_border);
|
|
_LOAD_SYMBOL(aom_img_set_rect);
|
|
_LOAD_SYMBOL(aom_img_flip);
|
|
_LOAD_SYMBOL(aom_img_free);
|
|
_LOAD_SYMBOL(aom_img_plane_width);
|
|
_LOAD_SYMBOL(aom_img_plane_height);
|
|
_LOAD_SYMBOL(aom_img_add_metadata);
|
|
_LOAD_SYMBOL(aom_img_get_metadata);
|
|
_LOAD_SYMBOL(aom_img_num_metadata);
|
|
_LOAD_SYMBOL(aom_img_remove_metadata);
|
|
_LOAD_SYMBOL(aom_img_metadata_alloc);
|
|
_LOAD_SYMBOL(aom_img_metadata_free);
|
|
_LOAD_SYMBOL(aom_codec_enc_init_ver);
|
|
_LOAD_SYMBOL(aom_codec_enc_config_default);
|
|
_LOAD_SYMBOL(aom_codec_enc_config_set);
|
|
_LOAD_SYMBOL(aom_codec_get_global_headers);
|
|
_LOAD_SYMBOL(aom_codec_encode);
|
|
_LOAD_SYMBOL(aom_codec_set_cx_data_buf);
|
|
_LOAD_SYMBOL(aom_codec_get_cx_data);
|
|
_LOAD_SYMBOL(aom_codec_get_preview_frame);
|
|
_LOAD_SYMBOL(aom_codec_av1_cx);
|
|
#undef _LOAD_SYMBOL
|
|
|
|
// Register encoder.
|
|
_info.id = S_PREFIX "aom-av1";
|
|
_info.type = obs_encoder_type::OBS_ENCODER_VIDEO;
|
|
_info.codec = "av1";
|
|
_info.caps = OBS_ENCODER_CAP_DYN_BITRATE;
|
|
_info.caps |= OBS_ENCODER_CAP_DEPRECATED;
|
|
|
|
finish_setup();
|
|
}
|
|
|
|
aom_av1_factory::~aom_av1_factory() {}
|
|
|
|
std::shared_ptr<aom_av1_factory> _aom_av1_factory_instance = nullptr;
|
|
|
|
void aom_av1_factory::initialize()
|
|
{
|
|
try {
|
|
if (!_aom_av1_factory_instance) {
|
|
_aom_av1_factory_instance = std::make_shared<aom_av1_factory>();
|
|
}
|
|
} catch (std::exception const& ex) {
|
|
D_LOG_ERROR("Failed to initialize AOM AV1 encoder: %s", ex.what());
|
|
}
|
|
}
|
|
|
|
void aom_av1_factory::finalize()
|
|
{
|
|
_aom_av1_factory_instance.reset();
|
|
}
|
|
|
|
std::shared_ptr<aom_av1_factory> aom_av1_factory::get()
|
|
{
|
|
return _aom_av1_factory_instance;
|
|
}
|
|
|
|
const char* aom_av1_factory::get_name()
|
|
{
|
|
return "AV1 (via AOM)";
|
|
}
|
|
|
|
void* aom_av1_factory::create(obs_data_t* settings, obs_encoder_t* encoder, bool is_hw)
|
|
{
|
|
return new aom_av1_instance(settings, encoder, is_hw);
|
|
}
|
|
|
|
void aom_av1_factory::get_defaults2(obs_data_t* settings)
|
|
{
|
|
{ // Presets
|
|
obs_data_set_default_int(settings, ST_KEY_ENCODER_USAGE, static_cast<long long>(AOM_USAGE_REALTIME));
|
|
obs_data_set_default_int(settings, ST_KEY_ENCODER_CPUUSAGE, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_ENCODER_PROFILE,
|
|
static_cast<long long>(codec::av1::profile::UNKNOWN));
|
|
}
|
|
|
|
{ // Rate-Control
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_MODE, static_cast<long long>(AOM_CBR));
|
|
|
|
// Limits
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE, 6000);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_BITRATE_OVERSHOOT, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_QUALITY, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MINIMUM, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MAXIMUM, -1);
|
|
|
|
// Buffer
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_BUFFER_SIZE, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_BUFFER_SIZE_INITIAL, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_RATECONTROL_BUFFER_SIZE_OPTIMAL, -1);
|
|
}
|
|
|
|
{ // Key-Frame Options
|
|
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);
|
|
}
|
|
|
|
{ // Advanced Options
|
|
obs_data_set_default_int(settings, ST_KEY_ADVANCED_THREADS, 0);
|
|
obs_data_set_default_int(settings, ST_KEY_ADVANCED_ROWMULTITHREADING, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_ADVANCED_TILE_COLUMNS, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_ADVANCED_TILE_ROWS, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_ADVANCED_TUNE_METRIC, -1);
|
|
obs_data_set_default_int(settings, ST_KEY_ADVANCED_TUNE_CONTENT, static_cast<long long>(AOM_CONTENT_DEFAULT));
|
|
}
|
|
}
|
|
|
|
static bool modified_usage(obs_properties_t* props, obs_property_t*, obs_data_t* settings) noexcept
|
|
{
|
|
try {
|
|
bool is_all_intra = false;
|
|
if (obs_data_get_int(settings, ST_KEY_ENCODER_USAGE) == AOM_USAGE_ALL_INTRA) {
|
|
is_all_intra = true;
|
|
}
|
|
|
|
// All-Intra does not support these.
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LOOKAHEAD), !is_all_intra);
|
|
obs_property_set_visible(obs_properties_get(props, ST_I18N_KEYFRAMES), !is_all_intra);
|
|
|
|
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 bool modified_ratecontrol_mode(obs_properties_t* props, obs_property_t*, obs_data_t* settings) noexcept
|
|
{
|
|
try {
|
|
bool is_bitrate_visible = false;
|
|
bool is_overundershoot_visible = false;
|
|
bool is_quality_visible = false;
|
|
|
|
// Fix rate control mode selection if ALL_INTRA is selected.
|
|
if (obs_data_get_int(settings, ST_KEY_ENCODER_USAGE) == AOM_USAGE_ALL_INTRA) {
|
|
obs_data_set_int(settings, ST_KEY_RATECONTROL_MODE, static_cast<long long>(aom_rc_mode::AOM_Q));
|
|
}
|
|
|
|
{ // Based on the Rate Control Mode, show and hide options.
|
|
auto mode = static_cast<aom_rc_mode>(obs_data_get_int(settings, ST_KEY_RATECONTROL_MODE));
|
|
if (mode == AOM_CBR) {
|
|
is_bitrate_visible = true;
|
|
is_overundershoot_visible = true;
|
|
} else if (mode == AOM_VBR) {
|
|
is_bitrate_visible = true;
|
|
is_overundershoot_visible = true;
|
|
} else if (mode == AOM_CQ) {
|
|
is_bitrate_visible = true;
|
|
is_overundershoot_visible = true;
|
|
is_quality_visible = true;
|
|
} else if (mode == AOM_Q) {
|
|
is_quality_visible = true;
|
|
}
|
|
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE), is_bitrate_visible);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT),
|
|
is_overundershoot_visible);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_BITRATE_OVERSHOOT),
|
|
is_overundershoot_visible);
|
|
#ifdef AOM_CTRL_AOME_SET_CQ_LEVEL
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_RATECONTROL_LIMITS_QUALITY), is_quality_visible);
|
|
#endif
|
|
}
|
|
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 bool modified_keyframes(obs_properties_t* props, obs_property_t*, obs_data_t* settings) noexcept
|
|
{
|
|
try {
|
|
bool is_seconds = obs_data_get_int(settings, ST_KEY_KEYFRAMES_INTERVALTYPE) == 0;
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_KEYFRAMES_INTERVAL_FRAMES), !is_seconds);
|
|
obs_property_set_visible(obs_properties_get(props, ST_KEY_KEYFRAMES_INTERVAL_SECONDS), is_seconds);
|
|
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;
|
|
}
|
|
}
|
|
|
|
obs_properties_t* aom_av1_factory::get_properties2(instance_t* data)
|
|
{
|
|
obs_properties_t* props = obs_properties_create();
|
|
|
|
{
|
|
auto p = obs_properties_add_text(props, "[[deprecated]]", D_TRANSLATE(ST_I18N_DEPRECATED), OBS_TEXT_INFO);
|
|
obs_property_text_set_info_type(p, OBS_TEXT_INFO_WARNING);
|
|
obs_property_text_set_info_word_wrap(p, true);
|
|
}
|
|
|
|
#ifdef ENABLE_FRONTEND
|
|
{
|
|
obs_properties_add_button2(props, S_MANUAL_OPEN, D_TRANSLATE(S_MANUAL_OPEN), aom_av1_factory::on_manual_open,
|
|
this);
|
|
}
|
|
#endif
|
|
|
|
{ // Presets
|
|
obs_properties_t* grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_ENCODER, D_TRANSLATE(ST_I18N_ENCODER), OBS_GROUP_NORMAL, grp);
|
|
//obs_properties_add_group(props, S_CODEC_AV1, D_TRANSLATE(S_CODEC_AV1), OBS_GROUP_NORMAL, grp);
|
|
|
|
{ // Usage
|
|
auto p = obs_properties_add_list(grp, ST_KEY_ENCODER_USAGE, D_TRANSLATE(ST_I18N_ENCODER_USAGE),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
obs_property_set_modified_callback(p, modified_usage);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_USAGE_GOODQUALITY),
|
|
static_cast<long long>(AOM_USAGE_GOOD_QUALITY));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_USAGE_REALTIME),
|
|
static_cast<long long>(AOM_USAGE_REALTIME));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_USAGE_ALLINTRA),
|
|
static_cast<long long>(AOM_USAGE_ALL_INTRA));
|
|
}
|
|
|
|
#ifdef AOM_CTRL_AOME_SET_CPUUSED
|
|
{ // CPU Usage
|
|
auto p = obs_properties_add_list(grp, ST_KEY_ENCODER_CPUUSAGE, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_10), 10);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_9), 9);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_8), 8);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_7), 7);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_6), 6);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_5), 5);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_4), 4);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_3), 3);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_2), 2);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_1), 1);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ENCODER_CPUUSAGE_0), 0);
|
|
}
|
|
#endif
|
|
|
|
{ // Profile
|
|
auto p = obs_properties_add_list(grp, ST_KEY_ENCODER_PROFILE, D_TRANSLATE(S_CODEC_AV1_PROFILE),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_AUTOMATIC),
|
|
static_cast<long long>(codec::av1::profile::UNKNOWN));
|
|
obs_property_list_add_int(p, codec::av1::profile_to_string(codec::av1::profile::MAIN),
|
|
static_cast<long long>(codec::av1::profile::MAIN));
|
|
obs_property_list_add_int(p, codec::av1::profile_to_string(codec::av1::profile::HIGH),
|
|
static_cast<long long>(codec::av1::profile::HIGH));
|
|
obs_property_list_add_int(p, codec::av1::profile_to_string(codec::av1::profile::PROFESSIONAL),
|
|
static_cast<long long>(codec::av1::profile::PROFESSIONAL));
|
|
}
|
|
}
|
|
|
|
{ // Rate Control Options
|
|
obs_properties_t* grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_RATECONTROL, D_TRANSLATE(ST_I18N_RATECONTROL), OBS_GROUP_NORMAL, grp);
|
|
|
|
{ // Mode
|
|
auto p = obs_properties_add_list(grp, ST_KEY_RATECONTROL_MODE, D_TRANSLATE(ST_I18N_RATECONTROL_MODE),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
obs_property_set_modified_callback(p, modified_ratecontrol_mode);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_RATECONTROL_MODE_VBR), static_cast<long long>(AOM_VBR));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_RATECONTROL_MODE_CBR), static_cast<long long>(AOM_CBR));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_RATECONTROL_MODE_CQ), static_cast<long long>(AOM_CQ));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_RATECONTROL_MODE_Q), static_cast<long long>(AOM_Q));
|
|
}
|
|
|
|
{ // Look-Ahead
|
|
auto p =
|
|
obs_properties_add_int(grp, ST_KEY_RATECONTROL_LOOKAHEAD, D_TRANSLATE(ST_I18N_RATECONTROL_LOOKAHEAD),
|
|
-1, std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " frames");
|
|
}
|
|
|
|
{ // Limits
|
|
obs_properties_t* grp2 = obs_properties_create();
|
|
obs_properties_add_group(grp, ST_I18N_RATECONTROL_LIMITS, D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS),
|
|
OBS_GROUP_NORMAL, grp2);
|
|
|
|
{ // Bitrate
|
|
auto p = obs_properties_add_int(grp2, ST_KEY_RATECONTROL_LIMITS_BITRATE,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_BITRATE), 0,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " kbit/s");
|
|
}
|
|
|
|
{ // Bitrate Under/Overshoot
|
|
auto p1 = obs_properties_add_int_slider(grp2, ST_KEY_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_BITRATE_UNDERSHOOT), -1,
|
|
100, 1);
|
|
auto p2 = obs_properties_add_int_slider(grp2, ST_KEY_RATECONTROL_LIMITS_BITRATE_OVERSHOOT,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_BITRATE_OVERSHOOT), -1,
|
|
100, 1);
|
|
obs_property_float_set_suffix(p1, " %");
|
|
obs_property_float_set_suffix(p2, " %");
|
|
}
|
|
|
|
#ifdef AOM_CTRL_AOME_SET_CQ_LEVEL
|
|
{ // Quality
|
|
auto p = obs_properties_add_int_slider(grp2, ST_KEY_RATECONTROL_LIMITS_QUALITY,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_QUALITY), -1, 63, 1);
|
|
}
|
|
#endif
|
|
|
|
{ // Quantizer
|
|
auto p1 =
|
|
obs_properties_add_int_slider(grp2, ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MINIMUM,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_QUANTIZER_MINIMUM), -1, 63, 1);
|
|
auto p2 =
|
|
obs_properties_add_int_slider(grp2, ST_KEY_RATECONTROL_LIMITS_QUANTIZER_MAXIMUM,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_LIMITS_QUANTIZER_MAXIMUM), -1, 63, 1);
|
|
}
|
|
}
|
|
|
|
{ // Buffer
|
|
obs_properties_t* grp2 = obs_properties_create();
|
|
obs_properties_add_group(grp, ST_I18N_RATECONTROL_BUFFER, D_TRANSLATE(ST_I18N_RATECONTROL_BUFFER),
|
|
OBS_GROUP_NORMAL, grp2);
|
|
|
|
{ // Buffer Size
|
|
auto p = obs_properties_add_int(grp2, ST_KEY_RATECONTROL_BUFFER_SIZE,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_BUFFER_SIZE), -1,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " ms");
|
|
}
|
|
|
|
{ // Initial Buffer Size
|
|
auto p = obs_properties_add_int(grp2, ST_KEY_RATECONTROL_BUFFER_SIZE_INITIAL,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_BUFFER_SIZE_INITIAL), -1,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " ms");
|
|
}
|
|
|
|
{ // Optimal Buffer Size
|
|
auto p = obs_properties_add_int(grp2, ST_KEY_RATECONTROL_BUFFER_SIZE_OPTIMAL,
|
|
D_TRANSLATE(ST_I18N_RATECONTROL_BUFFER_SIZE_OPTIMAL), -1,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " ms");
|
|
}
|
|
}
|
|
}
|
|
|
|
{ // Key-Frame Options
|
|
obs_properties_t* grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_KEYFRAMES, D_TRANSLATE(ST_I18N_KEYFRAMES), OBS_GROUP_NORMAL, grp);
|
|
|
|
{ // Key-Frame Interval Type
|
|
auto p =
|
|
obs_properties_add_list(grp, ST_KEY_KEYFRAMES_INTERVALTYPE, D_TRANSLATE(ST_I18N_KEYFRAMES_INTERVALTYPE),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
obs_property_set_modified_callback(p, modified_keyframes);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_KEYFRAMES_INTERVALTYPE_SECONDS), 0);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_KEYFRAMES_INTERVALTYPE_FRAMES), 1);
|
|
}
|
|
|
|
{ // Key-Frame Interval Seconds
|
|
auto p = obs_properties_add_float(grp, ST_KEY_KEYFRAMES_INTERVAL_SECONDS,
|
|
D_TRANSLATE(ST_I18N_KEYFRAMES_INTERVAL), 0.00,
|
|
std::numeric_limits<uint16_t>::max(), 0.01);
|
|
obs_property_float_set_suffix(p, " seconds");
|
|
}
|
|
|
|
{ // Key-Frame Interval Frames
|
|
auto p =
|
|
obs_properties_add_int(grp, ST_KEY_KEYFRAMES_INTERVAL_FRAMES, D_TRANSLATE(ST_I18N_KEYFRAMES_INTERVAL),
|
|
0, std::numeric_limits<int32_t>::max(), 1);
|
|
obs_property_int_set_suffix(p, " frames");
|
|
}
|
|
}
|
|
|
|
{ // Advanced Options
|
|
obs_properties_t* grp = obs_properties_create();
|
|
obs_properties_add_group(props, ST_I18N_ADVANCED, D_TRANSLATE(ST_I18N_ADVANCED), OBS_GROUP_NORMAL, grp);
|
|
|
|
{ // Threads
|
|
auto p = obs_properties_add_int(grp, ST_KEY_ADVANCED_THREADS, D_TRANSLATE(ST_I18N_ADVANCED_THREADS), 0,
|
|
std::numeric_limits<int32_t>::max(), 1);
|
|
}
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_ROW_MT
|
|
{ // Row-MT
|
|
auto p = streamfx::util::obs_properties_add_tristate(grp, ST_KEY_ADVANCED_ROWMULTITHREADING,
|
|
D_TRANSLATE(ST_I18N_ADVANCED_ROWMULTITHREADING));
|
|
}
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_TILE_COLUMNS
|
|
{ // Tile Columns
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_ADVANCED_TILE_COLUMNS,
|
|
D_TRANSLATE(ST_I18N_ADVANCED_TILE_COLUMNS), -1, 6, 1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_TILE_ROWS
|
|
{ // Tile Rows
|
|
auto p = obs_properties_add_int_slider(grp, ST_KEY_ADVANCED_TILE_ROWS,
|
|
D_TRANSLATE(ST_I18N_ADVANCED_TILE_ROWS), -1, 6, 1);
|
|
}
|
|
#endif
|
|
{
|
|
obs_properties_t* grp2 = obs_properties_create();
|
|
obs_properties_add_group(grp, ST_I18N_ADVANCED_TUNE, D_TRANSLATE(ST_I18N_ADVANCED_TUNE), OBS_GROUP_NORMAL,
|
|
grp2);
|
|
|
|
#ifdef AOM_CTRL_AOME_SET_TUNING
|
|
{ // Tuning
|
|
auto p = obs_properties_add_list(grp2, ST_KEY_ADVANCED_TUNE_METRIC,
|
|
D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC), OBS_COMBO_TYPE_LIST,
|
|
OBS_COMBO_FORMAT_INT);
|
|
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), -1);
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_PSNR),
|
|
static_cast<long long>(AOM_TUNE_PSNR));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_SSIM),
|
|
static_cast<long long>(AOM_TUNE_SSIM));
|
|
#ifdef _DEBUG
|
|
// These have no actual use outside of debug builds, way too slow!
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_VMAFWITHPREPROCESSING),
|
|
static_cast<long long>(AOM_TUNE_VMAF_WITH_PREPROCESSING));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_VMAFWITHOUTPREPROCESSING),
|
|
static_cast<long long>(AOM_TUNE_VMAF_WITHOUT_PREPROCESSING));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_VMAFMAXGAIN),
|
|
static_cast<long long>(AOM_TUNE_VMAF_MAX_GAIN));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_VMAFNEGMAXGAIN),
|
|
static_cast<long long>(AOM_TUNE_VMAF_NEG_MAX_GAIN));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_METRIC_BUTTERAUGLI),
|
|
static_cast<long long>(AOM_TUNE_BUTTERAUGLI));
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef AOM_CTRL_AV1E_SET_TUNE_CONTENT
|
|
{ // Content Tuning
|
|
auto p = obs_properties_add_list(grp2, ST_KEY_ADVANCED_TUNE_CONTENT,
|
|
D_TRANSLATE(ST_I18N_ADVANCED_TUNE_CONTENT), OBS_COMBO_TYPE_LIST,
|
|
OBS_COMBO_FORMAT_INT);
|
|
obs_property_list_add_int(p, D_TRANSLATE(S_STATE_DEFAULT), static_cast<long long>(AOM_CONTENT_DEFAULT));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_CONTENT_SCREEN),
|
|
static_cast<long long>(AOM_CONTENT_SCREEN));
|
|
obs_property_list_add_int(p, D_TRANSLATE(ST_I18N_ADVANCED_TUNE_CONTENT_FILM),
|
|
static_cast<long long>(AOM_CONTENT_FILM));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
#ifdef ENABLE_FRONTEND
|
|
bool aom_av1_factory::on_manual_open(obs_properties_t* props, obs_property_t* property, void* data)
|
|
{
|
|
streamfx::open_url(HELP_URL);
|
|
return false;
|
|
}
|
|
#endif
|