Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a75b9f963 | |||
| 815152d024 | |||
| e4474c29c4 | |||
| 2a75d5fabd | |||
| a0bfac9319 | |||
| 65dddb5aa8 | |||
| 59e33a132b | |||
| be7b2737b4 | |||
| 8f20a45ee8 | |||
| 300531ad8a | |||
| c775ae93b7 | |||
| 18c50b3cbb | |||
| 04a22a13dc | |||
| d8eb38037b | |||
| 81164d0f43 | |||
| 05d8e74088 | |||
| d233b52e1f | |||
| e89ef57bda | |||
| 2759153b0e | |||
| 629afe3661 | |||
| 21629f1e13 | |||
| 80d104bdeb | |||
| ffb348b6c8 | |||
| b67f51df76 | |||
| 24d92e4b92 | |||
| 412c9fc18b | |||
| 2184e5bc71 | |||
| ad761834ee | |||
| a0b667c26f |
+3
-3
@@ -25,8 +25,8 @@ Include("cmake/util.cmake")
|
||||
|
||||
# Automatic Versioning
|
||||
set(VERSION_MAJOR 0)
|
||||
set(VERSION_MINOR 3)
|
||||
set(VERSION_PATCH 1)
|
||||
set(VERSION_MINOR 4)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_TWEAK 0)
|
||||
set(PROJECT_COMMIT "N/A")
|
||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
|
||||
@@ -164,7 +164,7 @@ mark_as_advanced(FORCE OBS_NATIVE OBS_PACKAGE OBS_REFERENCE OBS_DOWNLOAD)
|
||||
|
||||
if(NOT TARGET libobs)
|
||||
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.0-rc2-ci" CACHE STRING "OBS Studio Version to download")
|
||||
set(${PropertyPrefix}OBS_DOWNLOAD_VERSION "24.0.3-ci" CACHE STRING "OBS Studio Version to download")
|
||||
endif()
|
||||
|
||||
if(NOT ${PropertyPrefix}OBS_NATIVE)
|
||||
|
||||
+26
-10
@@ -8,7 +8,7 @@ State.Manual="Manual"
|
||||
# FFmpeg
|
||||
FFmpeg="FFmpeg Options"
|
||||
FFmpeg.CustomSettings="Custom Settings"
|
||||
FFmpeg.CustomSettings.Description="Custom settings that override any detected options above, use with caution.\nThe input should be in the format 'key=value;key=value;...'."
|
||||
FFmpeg.CustomSettings.Description="Override any options shown (or not shown) above with your own.\nThe format is similar to that of the FFmpeg command line:\n -key=value -key2=value2 -key3='quoted value'"
|
||||
FFmpeg.Threads="Number of Threads"
|
||||
FFmpeg.Threads.Description="The number of threads to use for encoding, if supported by the encoder.\nA value of 0 is equal to 'auto-detect' and may result in excessive CPU usage."
|
||||
FFmpeg.ColorFormat="Override Color Format"
|
||||
@@ -20,6 +20,9 @@ FFmpeg.StandardCompliance.Strict="Strict"
|
||||
FFmpeg.StandardCompliance.Normal="Normal"
|
||||
FFmpeg.StandardCompliance.Unofficial="Unofficial"
|
||||
FFmpeg.StandardCompliance.Experimental="Experimental"
|
||||
FFmpeg.GPU="GPU"
|
||||
FFmpeg.GPU.Description="For multiple GPU systems, selects which GPU to use as the main encoder"
|
||||
|
||||
|
||||
# Rate Control
|
||||
RateControl="Rate Control"
|
||||
@@ -46,6 +49,8 @@ KeyFrames="Key Frames"
|
||||
KeyFrames.IntervalType="Interval Type"
|
||||
KeyFrames.IntervalType.Frames="Frames"
|
||||
KeyFrames.IntervalType.Seconds="Seconds"
|
||||
KeyFrames.IntervalType.Description="Keyframe interval type"
|
||||
KeyFrames.Interval.Description="Distance between key frames, in frames or seconds."
|
||||
KeyFrames.Interval="Interval"
|
||||
|
||||
# Codec: H264
|
||||
@@ -55,7 +60,9 @@ Codec.H264.Profile.baseline="Baseline"
|
||||
Codec.H264.Profile.main="Main"
|
||||
Codec.H264.Profile.high="High"
|
||||
Codec.H264.Profile.high444p="High 4:4:4 Predictive"
|
||||
Codec.H264.Profile.Description="H.264 profile determines which features of the codec can be used.\nHigh 4:4:4 Predictive is required for YUV 4:4:4 color space."
|
||||
Codec.H264.Level="Level"
|
||||
Codec.H264.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video."
|
||||
|
||||
# Codec: HEVC
|
||||
Codec.HEVC="HEVC"
|
||||
@@ -67,6 +74,7 @@ Codec.HEVC.Tier="Tier"
|
||||
Codec.HEVC.Tier.main="Main"
|
||||
Codec.HEVC.Tier.high="High"
|
||||
Codec.HEVC.Level="Level"
|
||||
Codec.HEVC.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video."
|
||||
|
||||
# Codec: Apple ProRes
|
||||
Codec.ProRes.Profile="Profile"
|
||||
@@ -79,6 +87,7 @@ Codec.ProRes.Profile.AP4X="4444 Extra Quality/XQ (AP4X)"
|
||||
|
||||
# NVENC
|
||||
NVENC.Preset="Preset"
|
||||
NVENC.Preset.Description="Presets are NVIDIA's preconfigured default settings."
|
||||
NVENC.Preset.Default="Default"
|
||||
NVENC.Preset.Slow="Slow"
|
||||
NVENC.Preset.Medium="Medium"
|
||||
@@ -93,19 +102,26 @@ NVENC.Preset.Lossless="Lossless"
|
||||
NVENC.Preset.LosslessHighPerformance="Lossless High Performance"
|
||||
NVENC.RateControl="Rate Control Options"
|
||||
NVENC.RateControl.Mode="Mode"
|
||||
NVENC.RateControl.Mode.Description="Rate control mode selection"
|
||||
NVENC.RateControl.Mode.CQP="Constant Quantization Parameter"
|
||||
NVENC.RateControl.Mode.CQP.Description="A flat compression ratio with no regard for bit rates."
|
||||
NVENC.RateControl.Mode.VBR="Variable Bitrate"
|
||||
NVENC.RateControl.Mode.VBR.Description="Sacrifices quality to stay below the upper bitrate limit,\nor saves bitrate where possible."
|
||||
NVENC.RateControl.Mode.VBR_HQ="High Quality Variable Bitrate"
|
||||
NVENC.RateControl.Mode.VBR_HQ.Description="Variable Bitrate with two-pass encoding enabled by default."
|
||||
NVENC.RateControl.Mode.CBR="Constant Bitrate"
|
||||
NVENC.RateControl.Mode.CBR.Description="Compresses footage so that it matches the target bitrate over the duration of\none second. This comes at a cost in quality during high motion scenes or\nscenes with flickering brightness like often seen in RPGs."
|
||||
NVENC.RateControl.Mode.CBR_HQ="High Quality Constant Bitrate"
|
||||
NVENC.RateControl.Mode.CBR_HQ.Description="Constant Bitrate with two-pass encoding enabled by default."
|
||||
NVENC.RateControl.Mode.CBR_LD_HQ="Low Delay High Quality Constant Bitrate"
|
||||
NVENC.RateControl.Mode.CBR_LD_HQ.Description="Constant Bitrate optimized for lowest encoding latency."
|
||||
NVENC.RateControl.LookAhead="Look Ahead"
|
||||
NVENC.RateControl.LookAhead.Description="Look ahead this many frames while encoding to better distribute bitrate.\nImproves quality slightly at the cost of some GPU time.\nSet to 0 to disable."
|
||||
NVENC.RateControl.AdaptiveI="Enable adaptive I-Frame insertion"
|
||||
NVENC.RateControl.AdaptiveI="Adaptive I-Frames"
|
||||
NVENC.RateControl.AdaptiveI.Description="Enables adaptive I-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0."
|
||||
NVENC.RateControl.AdaptiveB="Enable adaptive B-Frame insertion"
|
||||
NVENC.RateControl.AdaptiveB="Adaptive B-Frames"
|
||||
NVENC.RateControl.AdaptiveB.Description="Enables adaptive B-Frame insertion.\nOnly has an effect when look ahead is set to a value other than 0."
|
||||
NVENC.RateControl.TwoPass="Enable Two Pass"
|
||||
NVENC.RateControl.TwoPass="Two Pass"
|
||||
NVENC.RateControl.TwoPass.Description="Enable a secondary pass for encoding, which can help with quality and bitrate stability.\nImproves quality slightly at the cost of some GPU time.\nNvidia Turing hardware might actually see a quality degrade from this."
|
||||
NVENC.RateControl.Bitrate="Bitrate Limits"
|
||||
NVENC.RateControl.Bitrate.Target="Target Bitrate"
|
||||
@@ -131,21 +147,21 @@ NVENC.RateControl.QP.B.Description="Quantization parameter for B-Frames.\nSmalle
|
||||
NVENC.RateControl.QP.B.Initial="Initial B-Frame QP"
|
||||
NVENC.RateControl.QP.B.Initial.Description="Initial B-Frame quantization parameter.\nSet to -1 to use the automatically detected value instead."
|
||||
NVENC.AQ="Adaptive Quantization"
|
||||
NVENC.AQ.Spatial="Enable Spatial Adaptive Quantization"
|
||||
NVENC.AQ.Spatial="Spatial Adaptive Quantization"
|
||||
NVENC.AQ.Spatial.Description="Enable spatial adaptive quantization, also sometimes referred to as Psychovisual Adaptive Quantization."
|
||||
NVENC.AQ.Strength="Spatial AQ Strength"
|
||||
NVENC.AQ.Strength.Description="Strength of the spatial adaptive quantization.\nValues closer to 15 mean more aggressive, while values closer to 1 mean more relaxed."
|
||||
NVENC.AQ.Temporal="Enable Temporal Adaptive Quantization"
|
||||
NVENC.AQ.Temporal="Temporal Adaptive Quantization"
|
||||
NVENC.AQ.Temporal.Description="Enable temporal adaptive quantization."
|
||||
NVENC.Other="Other Options"
|
||||
NVENC.Other.BFrames="Maximum B-Frames"
|
||||
NVENC.Other.BFrames.Description="Maximum number of B-Frames to insert into the encoded bitstream.\nActual number of B-Frames may be lower depending on content and lookahead settings."
|
||||
NVENC.Other.BFrames.Description="Maximum number of B-Frames to insert into the encoded bitstream.\nActual number of B-Frames may be lower depending on content and lookahead settings.\nOnly Turing NVENC supports B-Frames for HEVC."
|
||||
NVENC.Other.BFrameReferenceMode="B-Frame Reference Mode"
|
||||
NVENC.Other.BFrameReferenceMode.Each="Each B-Frame will be used for references"
|
||||
NVENC.Other.BFrameReferenceMode.Middle="Only (# of B-Frames)/2 will be used for references"
|
||||
NVENC.Other.ZeroLatency="Enable Zero Latency"
|
||||
NVENC.Other.ZeroLatency="Zero Latency"
|
||||
NVENC.Other.ZeroLatency.Description="Enable zero latency operation, which ensures that there is no reordering delay."
|
||||
NVENC.Other.WeightedPrediction="Enable Weighted Prediction"
|
||||
NVENC.Other.WeightedPrediction="Weighted Prediction"
|
||||
NVENC.Other.WeightedPrediction.Description="Enable weighted prediction for encoding.\nCan't be used with B-Frames."
|
||||
NVENC.Other.NonReferencePFrames="Enable non-reference P-Frames"
|
||||
NVENC.Other.NonReferencePFrames="Non-reference P-Frames"
|
||||
NVENC.Other.NonReferencePFrames.Description="Enable the automatic insertion of non-reference P-Frames."
|
||||
|
||||
@@ -197,8 +197,8 @@ void progress_parse(uint8_t*& ptr, uint8_t* end, size_t& sz)
|
||||
sz = get_nal_size(ptr, end);
|
||||
}
|
||||
|
||||
void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data,
|
||||
std::vector<uint8_t>& header, std::vector<uint8_t>& sei)
|
||||
void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
|
||||
std::vector<uint8_t>& sei)
|
||||
{
|
||||
uint8_t* ptr = data;
|
||||
uint8_t* end = data + sz_data;
|
||||
|
||||
@@ -61,8 +61,8 @@ namespace obsffmpeg {
|
||||
UNKNOWN = -1,
|
||||
};
|
||||
|
||||
void extract_header_sei(uint8_t* data, size_t sz_data,
|
||||
std::vector<uint8_t>& header, std::vector<uint8_t>& sei);
|
||||
void extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
|
||||
std::vector<uint8_t>& sei);
|
||||
|
||||
} // namespace hevc
|
||||
} // namespace codecs
|
||||
|
||||
+293
-92
@@ -23,6 +23,7 @@
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <thread>
|
||||
#include <util/profiler.hpp>
|
||||
#include <vector>
|
||||
@@ -63,10 +64,12 @@ extern "C" {
|
||||
#define ST_FFMPEG_THREADS "FFmpeg.Threads"
|
||||
#define ST_FFMPEG_COLORFORMAT "FFmpeg.ColorFormat"
|
||||
#define ST_FFMPEG_STANDARDCOMPLIANCE "FFmpeg.StandardCompliance"
|
||||
#define ST_FFMPEG_GPU "FFmpeg.GPU"
|
||||
|
||||
enum class keyframe_type { SECONDS, FRAMES };
|
||||
|
||||
static void* _create(obs_data_t* settings, obs_encoder_t* encoder) noexcept try {
|
||||
static void* _create(obs_data_t* settings, obs_encoder_t* encoder) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, encoder);
|
||||
#endif
|
||||
@@ -79,7 +82,8 @@ static void* _create(obs_data_t* settings, obs_encoder_t* encoder) noexcept try
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void* _create_texture(obs_data_t* settings, obs_encoder_t* encoder) noexcept try {
|
||||
static void* _create_texture(obs_data_t* settings, obs_encoder_t* encoder) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, encoder);
|
||||
#endif
|
||||
@@ -98,7 +102,8 @@ static void* _create_texture(obs_data_t* settings, obs_encoder_t* encoder) noexc
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void _destroy(void* ptr) noexcept try {
|
||||
static void _destroy(void* ptr) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX", __FUNCTION_NAME__, ptr);
|
||||
#endif
|
||||
@@ -110,7 +115,8 @@ static void _destroy(void* ptr) noexcept try {
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
}
|
||||
|
||||
static const char* _get_name(void* type_data) noexcept try {
|
||||
static const char* _get_name(void* type_data) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX", __FUNCTION_NAME__, type_data);
|
||||
#endif
|
||||
@@ -123,7 +129,8 @@ static const char* _get_name(void* type_data) noexcept try {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char* _get_name_fallback(void* type_data) noexcept try {
|
||||
static const char* _get_name_fallback(void* type_data) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX", __FUNCTION_NAME__, type_data);
|
||||
#endif
|
||||
@@ -136,7 +143,8 @@ static const char* _get_name_fallback(void* type_data) noexcept try {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void _get_defaults(obs_data_t* settings, void* type_data) noexcept try {
|
||||
static void _get_defaults(obs_data_t* settings, void* type_data) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data);
|
||||
#endif
|
||||
@@ -147,7 +155,8 @@ static void _get_defaults(obs_data_t* settings, void* type_data) noexcept try {
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
};
|
||||
|
||||
static void _get_defaults_texture(obs_data_t* settings, void* type_data) noexcept try {
|
||||
static void _get_defaults_texture(obs_data_t* settings, void* type_data) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data);
|
||||
#endif
|
||||
@@ -158,7 +167,8 @@ static void _get_defaults_texture(obs_data_t* settings, void* type_data) noexcep
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
};
|
||||
|
||||
static obs_properties_t* _get_properties(void* ptr, void* type_data) noexcept try {
|
||||
static obs_properties_t* _get_properties(void* ptr, void* type_data) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data);
|
||||
#endif
|
||||
@@ -178,7 +188,8 @@ static obs_properties_t* _get_properties(void* ptr, void* type_data) noexcept tr
|
||||
return reinterpret_cast<obs_properties_t*>(0);
|
||||
}
|
||||
|
||||
static obs_properties_t* _get_properties_texture(void* ptr, void* type_data) noexcept try {
|
||||
static obs_properties_t* _get_properties_texture(void* ptr, void* type_data) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data);
|
||||
#endif
|
||||
@@ -198,7 +209,8 @@ static obs_properties_t* _get_properties_texture(void* ptr, void* type_data) noe
|
||||
return reinterpret_cast<obs_properties_t*>(0);
|
||||
}
|
||||
|
||||
static bool _update(void* ptr, obs_data_t* settings) noexcept try {
|
||||
static bool _update(void* ptr, obs_data_t* settings) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, settings);
|
||||
#endif
|
||||
@@ -211,7 +223,8 @@ static bool _update(void* ptr, obs_data_t* settings) noexcept try {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _get_sei_data(void* ptr, uint8_t** sei_data, size_t* size) noexcept try {
|
||||
static bool _get_sei_data(void* ptr, uint8_t** sei_data, size_t* size) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX %llX", __FUNCTION_NAME__, ptr, sei_data, size);
|
||||
#endif
|
||||
@@ -224,7 +237,8 @@ static bool _get_sei_data(void* ptr, uint8_t** sei_data, size_t* size) noexcept
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _get_extra_data(void* ptr, uint8_t** extra_data, size_t* size) noexcept try {
|
||||
static bool _get_extra_data(void* ptr, uint8_t** extra_data, size_t* size) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX %llX", __FUNCTION_NAME__, ptr, extra_data, size);
|
||||
#endif
|
||||
@@ -237,7 +251,8 @@ static bool _get_extra_data(void* ptr, uint8_t** extra_data, size_t* size) noexc
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _get_video_info(void* ptr, struct video_scale_info* info) noexcept try {
|
||||
static void _get_video_info(void* ptr, struct video_scale_info* info) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, info);
|
||||
#endif
|
||||
@@ -249,7 +264,8 @@ static void _get_video_info(void* ptr, struct video_scale_info* info) noexcept t
|
||||
}
|
||||
|
||||
static bool _encode(void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
|
||||
bool* received_packet) noexcept try {
|
||||
bool* received_packet) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX %llX %llX", __FUNCTION_NAME__, ptr, frame, packet, received_packet);
|
||||
#endif
|
||||
@@ -263,7 +279,8 @@ static bool _encode(void* ptr, struct encoder_frame* frame, struct encoder_packe
|
||||
}
|
||||
|
||||
static bool _encode_texture(void* ptr, uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t* next_key,
|
||||
struct encoder_packet* packet, bool* received_packet) noexcept try {
|
||||
struct encoder_packet* packet, bool* received_packet) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %lI %llI %llU %llX %llX %llX", __FUNCTION_NAME__, ptr, handle, pts, lock_key, next_key, packet,
|
||||
received_packet);
|
||||
@@ -278,7 +295,8 @@ static bool _encode_texture(void* ptr, uint32_t handle, int64_t pts, uint64_t lo
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _get_audio_info(void* ptr, struct audio_convert_info* info) noexcept try {
|
||||
static void _get_audio_info(void* ptr, struct audio_convert_info* info) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, info);
|
||||
#endif
|
||||
@@ -289,7 +307,8 @@ static void _get_audio_info(void* ptr, struct audio_convert_info* info) noexcept
|
||||
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__);
|
||||
}
|
||||
|
||||
static size_t _get_frame_size(void* ptr) noexcept try {
|
||||
static size_t _get_frame_size(void* ptr) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX", __FUNCTION_NAME__, ptr);
|
||||
#endif
|
||||
@@ -303,7 +322,8 @@ static size_t _get_frame_size(void* ptr) noexcept try {
|
||||
}
|
||||
|
||||
static bool _encode_audio(void* ptr, struct encoder_frame* frame, struct encoder_packet* packet,
|
||||
bool* received_packet) noexcept try {
|
||||
bool* received_packet) noexcept
|
||||
try {
|
||||
#ifdef DEBUG_CALL_ORDER
|
||||
PLOG_INFO("%s %llX %llX %llX %llX", __FUNCTION_NAME__, ptr, frame, packet, received_packet);
|
||||
#endif
|
||||
@@ -322,7 +342,7 @@ obsffmpeg::encoder_factory::encoder_factory(const AVCodec* codec) : avcodec_ptr(
|
||||
_handler = obsffmpeg::find_codec_handler(avcodec_ptr->name);
|
||||
|
||||
// Unique Id is FFmpeg name.
|
||||
info.uid = avcodec_ptr->name;
|
||||
info.uid = std::string("obs-ffmpeg-encoder_") + avcodec_ptr->name;
|
||||
|
||||
// Also generate a human readable name while we're at it.
|
||||
{
|
||||
@@ -335,10 +355,6 @@ obsffmpeg::encoder_factory::encoder_factory(const AVCodec* codec) : avcodec_ptr(
|
||||
sstr << " (" << avcodec_ptr->name << ")";
|
||||
}
|
||||
info.readable_name = sstr.str();
|
||||
|
||||
// Allow UI Handler to replace visible name.
|
||||
if (_handler)
|
||||
_handler->override_visible_name(avcodec_ptr, info.readable_name);
|
||||
}
|
||||
|
||||
// Assign Ids.
|
||||
@@ -433,17 +449,22 @@ void obsffmpeg::encoder_factory::register_encoder()
|
||||
info_fallback.oei.encode = _encode;
|
||||
info_fallback.oei.type_data = this;
|
||||
|
||||
obs_register_encoder(&info_fallback.oei);
|
||||
PLOG_DEBUG("Registered software fallback for encoder #%llX", avcodec_ptr);
|
||||
} else {
|
||||
// Is not a GPU Encoder, don't implement fallback.
|
||||
info.oei.create = _create;
|
||||
info.oei.encode = _encode;
|
||||
}
|
||||
|
||||
if (_handler)
|
||||
_handler->adjust_encoder_info(this, &info, &info_fallback);
|
||||
|
||||
obs_register_encoder(&info.oei);
|
||||
PLOG_DEBUG("Registered encoder #%llX with name '%s' and long name '%s' and caps %llX", avcodec_ptr,
|
||||
avcodec_ptr->name, avcodec_ptr->long_name, avcodec_ptr->capabilities);
|
||||
if (info_fallback.uid.size() > 0) {
|
||||
obs_register_encoder(&info_fallback.oei);
|
||||
PLOG_DEBUG("Registered software fallback for encoder #%llX", avcodec_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings, bool hw_encode)
|
||||
@@ -464,12 +485,14 @@ void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings, bool hw_enco
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_COLORFORMAT,
|
||||
static_cast<int64_t>(AV_PIX_FMT_NONE));
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_THREADS, 0);
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_GPU, 0);
|
||||
}
|
||||
obs_data_set_default_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE, FF_COMPLIANCE_STRICT);
|
||||
}
|
||||
}
|
||||
|
||||
static bool modified_keyframes(obs_properties_t* props, obs_property_t*, obs_data_t* settings) try {
|
||||
static bool modified_keyframes(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||
try {
|
||||
bool is_seconds = obs_data_get_int(settings, S_KEYFRAMES_INTERVALTYPE) == 0;
|
||||
obs_property_set_visible(obs_properties_get(props, S_KEYFRAMES_INTERVAL_FRAMES), !is_seconds);
|
||||
obs_property_set_visible(obs_properties_get(props, S_KEYFRAMES_INTERVAL_SECONDS), is_seconds);
|
||||
@@ -535,6 +558,11 @@ void obsffmpeg::encoder_factory::get_properties(obs_properties_t* props, bool hw
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_FFMPEG_CUSTOMSETTINGS)));
|
||||
}
|
||||
if (!hw_encode) {
|
||||
{
|
||||
auto p = obs_properties_add_int(grp, ST_FFMPEG_GPU, TRANSLATE(ST_FFMPEG_GPU), 0,
|
||||
std::numeric_limits<uint8_t>::max(), 1);
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_FFMPEG_GPU)));
|
||||
}
|
||||
if (avcodec_ptr->pix_fmts) {
|
||||
auto p = obs_properties_add_list(grp, ST_FFMPEG_COLORFORMAT,
|
||||
TRANSLATE(ST_FFMPEG_COLORFORMAT), OBS_COMBO_TYPE_LIST,
|
||||
@@ -745,25 +773,22 @@ std::shared_ptr<AVFrame> obsffmpeg::encoder::pop_used_frame()
|
||||
}
|
||||
|
||||
obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder, bool is_texture_encode)
|
||||
: _self(encoder), _lag_in_frames(0), _count_send_frames(0), _have_first_frame(false)
|
||||
: _self(encoder), _factory(reinterpret_cast<encoder_factory*>(obs_encoder_get_type_data(_self))),
|
||||
_codec(_factory->get_avcodec()), _context(nullptr), _lag_in_frames(0), _count_send_frames(0),
|
||||
_have_first_frame(false)
|
||||
{
|
||||
// Initial set up.
|
||||
_factory = reinterpret_cast<encoder_factory*>(obs_encoder_get_type_data(_self));
|
||||
_codec = _factory->get_avcodec();
|
||||
// Find a handler
|
||||
_handler = obsffmpeg::find_codec_handler(_codec->name);
|
||||
|
||||
// Initialize GPU Stuff
|
||||
if (is_texture_encode) {
|
||||
#ifdef WIN32
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
if (gs_get_device_type() == GS_DEVICE_DIRECT3D_11) {
|
||||
_hwapi = std::make_shared<obsffmpeg::hwapi::d3d11>();
|
||||
#endif
|
||||
obsffmpeg::hwapi::device dev;
|
||||
if (_handler)
|
||||
dev = _handler->find_hw_device(_hwapi, _codec, _context);
|
||||
try {
|
||||
_hwinst = _hwapi->create(dev);
|
||||
} catch (...) {
|
||||
throw obsffmpeg::unsupported_gpu_exception("Creating GPU context failed.");
|
||||
}
|
||||
#endif
|
||||
_hwinst = _hwapi->create_from_obs();
|
||||
}
|
||||
|
||||
// Initialize context.
|
||||
@@ -777,47 +802,17 @@ obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder, bool i
|
||||
av_init_packet(&_current_packet);
|
||||
av_new_packet(&_current_packet, 8 * 1024 * 1024); // 8 MB precached Packet size.
|
||||
|
||||
if (!is_texture_encode) {
|
||||
initialize_sw(settings);
|
||||
} else {
|
||||
try {
|
||||
if (is_texture_encode) {
|
||||
initialize_hw(settings);
|
||||
} catch (...) {
|
||||
throw obsffmpeg::unsupported_gpu_exception("Initializing hardware context failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Log Encoder info
|
||||
PLOG_INFO("[%s] Initializing...", _codec->name);
|
||||
if (_hwinst) {
|
||||
PLOG_INFO("[%s] Video Input: %ldx%ld %s %s %s", _codec->name, _context->width, _context->height,
|
||||
ffmpeg::tools::get_pixel_format_name(_context->sw_pix_fmt),
|
||||
ffmpeg::tools::get_color_space_name(_context->colorspace),
|
||||
_swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
PLOG_INFO("[%s] Video Output: %ldx%ld %s %s %s", _codec->name, _context->width, _context->height,
|
||||
ffmpeg::tools::get_pixel_format_name(_context->sw_pix_fmt),
|
||||
ffmpeg::tools::get_color_space_name(_context->colorspace),
|
||||
_swscale.is_target_full_range() ? "Full" : "Partial");
|
||||
} else {
|
||||
PLOG_INFO("[%s] Video Input: %ldx%ld %s %s %s", _codec->name, _swscale.get_source_width(),
|
||||
_swscale.get_source_height(),
|
||||
ffmpeg::tools::get_pixel_format_name(_swscale.get_source_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_source_colorspace()),
|
||||
_swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
PLOG_INFO("[%s] Video Output: %ldx%ld %s %s %s", _codec->name, _swscale.get_target_width(),
|
||||
_swscale.get_target_height(),
|
||||
ffmpeg::tools::get_pixel_format_name(_swscale.get_target_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_target_colorspace()),
|
||||
_swscale.is_target_full_range() ? "Full" : "Partial");
|
||||
initialize_sw(settings);
|
||||
}
|
||||
PLOG_INFO("[%s] Framerate: %ld/%ld (%f FPS)", _codec->name, _context->time_base.den, _context->time_base.num,
|
||||
static_cast<double_t>(_context->time_base.den) / static_cast<double_t>(_context->time_base.num));
|
||||
PLOG_INFO("[%s] Custom Settings: %s", _codec->name, obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS));
|
||||
|
||||
// Update settings
|
||||
update(settings);
|
||||
|
||||
// Initialize Encoder
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
int res = avcodec_open2(_context, _codec, NULL);
|
||||
if (res < 0) {
|
||||
std::stringstream sstr;
|
||||
@@ -829,6 +824,7 @@ obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder, bool i
|
||||
|
||||
obsffmpeg::encoder::~encoder()
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
if (_context) {
|
||||
// Flush encoders that require it.
|
||||
if ((_codec->capabilities & AV_CODEC_CAP_DELAY) != 0) {
|
||||
@@ -862,45 +858,52 @@ void obsffmpeg::encoder::get_properties(obs_properties_t* props, bool hw_encode)
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_FFMPEG_COLORFORMAT), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_FFMPEG_THREADS), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_FFMPEG_STANDARDCOMPLIANCE), false);
|
||||
obs_property_set_enabled(obs_properties_get(props, ST_FFMPEG_GPU), false);
|
||||
}
|
||||
|
||||
bool obsffmpeg::encoder::update(obs_data_t* settings)
|
||||
{
|
||||
// Settings
|
||||
/// Rate Control
|
||||
_context->strict_std_compliance = static_cast<int>(obs_data_get_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE));
|
||||
// FFmpeg Options
|
||||
_context->debug = 0;
|
||||
_context->strict_std_compliance = static_cast<int>(obs_data_get_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE));
|
||||
|
||||
/// Threading
|
||||
if (_codec->capabilities & (AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS)
|
||||
&& !_hwinst) {
|
||||
if (!_hwinst) {
|
||||
_context->thread_type = 0;
|
||||
if (_codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
|
||||
_context->thread_type |= FF_THREAD_FRAME;
|
||||
}
|
||||
if (_codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
|
||||
_context->thread_type |= FF_THREAD_SLICE;
|
||||
}
|
||||
if (_context->thread_type != 0) {
|
||||
int64_t threads = obs_data_get_int(settings, ST_FFMPEG_THREADS);
|
||||
if (threads > 0) {
|
||||
_context->thread_count = static_cast<int>(threads);
|
||||
_lag_in_frames = _context->thread_count;
|
||||
} else {
|
||||
_context->thread_count = std::thread::hardware_concurrency();
|
||||
_lag_in_frames = _context->thread_count;
|
||||
}
|
||||
} else {
|
||||
_context->thread_count = 1;
|
||||
_context->thread_type = 0;
|
||||
_lag_in_frames = 1;
|
||||
}
|
||||
// Frame Delay (Lag In Frames)
|
||||
_context->delay = _context->thread_count;
|
||||
} else {
|
||||
_context->delay = 0;
|
||||
}
|
||||
|
||||
if (_handler)
|
||||
_handler->update(settings, _codec, _context);
|
||||
// Apply GPU Selection
|
||||
if (!_hwinst && ffmpeg::tools::can_hardware_encode(_codec)) {
|
||||
av_opt_set_int(_context, "gpu", (int)obs_data_get_int(settings, ST_FFMPEG_GPU), AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
|
||||
if ((_codec->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0) {
|
||||
// Keyframes
|
||||
if (_handler && _handler->has_keyframe_support(this)) {
|
||||
// Key-Frame Options
|
||||
obs_video_info ovi;
|
||||
if (!obs_get_video_info(&ovi)) {
|
||||
throw std::runtime_error("no video info");
|
||||
throw std::runtime_error(
|
||||
"obs_get_video_info failed, restart OBS Studio to fix it (hopefully).");
|
||||
}
|
||||
|
||||
int64_t kf_type = obs_data_get_int(settings, S_KEYFRAMES_INTERVALTYPE);
|
||||
@@ -915,18 +918,67 @@ bool obsffmpeg::encoder::update(obs_data_t* settings)
|
||||
_context->keyint_min = _context->gop_size;
|
||||
}
|
||||
|
||||
{ // FFmpeg
|
||||
// Apply custom options.
|
||||
av_opt_set_from_string(_context->priv_data, obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS),
|
||||
nullptr, "=", ";");
|
||||
// Handler Options
|
||||
if (_handler)
|
||||
_handler->update(settings, _codec, _context);
|
||||
|
||||
{ // FFmpeg Custom Options
|
||||
const char* opts = obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS);
|
||||
size_t opts_len = strnlen(opts, 65535);
|
||||
|
||||
parse_ffmpeg_commandline(std::string{opts, opts + opts_len});
|
||||
}
|
||||
|
||||
// Handler Overrides
|
||||
if (_handler)
|
||||
_handler->override_lag_in_frames(_lag_in_frames, settings, _codec, _context);
|
||||
_handler->override_update(this, settings);
|
||||
|
||||
// Handler Logging
|
||||
if (_handler)
|
||||
if (_handler) {
|
||||
PLOG_INFO("[%s] Initializing...", _codec->name);
|
||||
PLOG_INFO("[%s] FFmpeg:", _codec->name);
|
||||
PLOG_INFO("[%s] Custom Settings: %s", _codec->name,
|
||||
obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS));
|
||||
PLOG_INFO("[%s] Standard Compliance: %s", _codec->name,
|
||||
ffmpeg::tools::get_std_compliance_name(_context->strict_std_compliance));
|
||||
PLOG_INFO("[%s] Threading: %s (with %i threads)", _codec->name,
|
||||
ffmpeg::tools::get_thread_type_name(_context->thread_type), _context->thread_count);
|
||||
|
||||
PLOG_INFO("[%s] Video:", _codec->name);
|
||||
if (_hwinst) {
|
||||
PLOG_INFO("[%s] Texture: %ldx%ld %s %s %s", _codec->name, _context->width, _context->height,
|
||||
ffmpeg::tools::get_pixel_format_name(_context->sw_pix_fmt),
|
||||
ffmpeg::tools::get_color_space_name(_context->colorspace),
|
||||
av_color_range_name(_context->color_range));
|
||||
} else {
|
||||
PLOG_INFO("[%s] Input: %ldx%ld %s %s %s", _codec->name, _swscale.get_source_width(),
|
||||
_swscale.get_source_height(),
|
||||
ffmpeg::tools::get_pixel_format_name(_swscale.get_source_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_source_colorspace()),
|
||||
_swscale.is_source_full_range() ? "Full" : "Partial");
|
||||
PLOG_INFO("[%s] Output: %ldx%ld %s %s %s", _codec->name, _swscale.get_target_width(),
|
||||
_swscale.get_target_height(),
|
||||
ffmpeg::tools::get_pixel_format_name(_swscale.get_target_format()),
|
||||
ffmpeg::tools::get_color_space_name(_swscale.get_target_colorspace()),
|
||||
_swscale.is_target_full_range() ? "Full" : "Partial");
|
||||
if (!_hwinst)
|
||||
PLOG_INFO("[%s] On GPU Index: %lli", _codec->name,
|
||||
obs_data_get_int(settings, ST_FFMPEG_GPU));
|
||||
}
|
||||
PLOG_INFO("[%s] Framerate: %ld/%ld (%f FPS)", _codec->name, _context->time_base.den,
|
||||
_context->time_base.num,
|
||||
static_cast<double_t>(_context->time_base.den)
|
||||
/ static_cast<double_t>(_context->time_base.num));
|
||||
|
||||
PLOG_INFO("[%s] Keyframes: ", _codec->name);
|
||||
if (_context->keyint_min != _context->gop_size) {
|
||||
PLOG_INFO("[%s] Minimum: %i frames", _codec->name, _context->keyint_min);
|
||||
PLOG_INFO("[%s] Maximum: %i frames", _codec->name, _context->gop_size);
|
||||
} else {
|
||||
PLOG_INFO("[%s] Distance: %i frames", _codec->name, _context->gop_size);
|
||||
}
|
||||
_handler->log_options(settings, _codec, _context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1068,9 +1120,14 @@ bool obsffmpeg::encoder::video_encode_texture(uint32_t handle, int64_t pts, uint
|
||||
|
||||
int obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_packet* packet)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
av_packet_unref(&_current_packet);
|
||||
|
||||
int res = avcodec_receive_packet(_context, &_current_packet);
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
res = avcodec_receive_packet(_context, &_current_packet);
|
||||
}
|
||||
if (res != 0) {
|
||||
return res;
|
||||
}
|
||||
@@ -1132,7 +1189,11 @@ int obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_pac
|
||||
|
||||
int obsffmpeg::encoder::send_frame(std::shared_ptr<AVFrame> const frame)
|
||||
{
|
||||
int res = avcodec_send_frame(_context, frame.get());
|
||||
int res = 0;
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
res = avcodec_send_frame(_context, frame.get());
|
||||
}
|
||||
if (res == 0) {
|
||||
push_used_frame(frame);
|
||||
}
|
||||
@@ -1226,3 +1287,143 @@ bool obsffmpeg::encoder::encode_avframe(std::shared_ptr<AVFrame> frame, encoder_
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obsffmpeg::encoder::is_hardware_encode()
|
||||
{
|
||||
return _hwinst != nullptr;
|
||||
}
|
||||
|
||||
const AVCodec* obsffmpeg::encoder::get_avcodec()
|
||||
{
|
||||
return _codec;
|
||||
}
|
||||
|
||||
const AVCodecContext* obsffmpeg::encoder::get_avcodeccontext()
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
|
||||
void obsffmpeg::encoder::parse_ffmpeg_commandline(std::string text)
|
||||
{
|
||||
// Steps to properly parse a command line:
|
||||
// 1. Split by space and package by quotes.
|
||||
// 2. Parse each resulting option individually.
|
||||
|
||||
// First, we split by space and of course respect quotes while doing so.
|
||||
// That means that "-foo= bar" is stored as std::string("-foo= bar"),
|
||||
// and things like -foo="bar" is stored as std::string("-foo=\"bar\"").
|
||||
// However "-foo"=bar" -foo2=bar" is stored as std::string("-foo=bar -foo2=bar")
|
||||
// because the quote was not escaped.
|
||||
std::list<std::string> opts;
|
||||
std::stringstream opt_stream{std::ios_base::in | std::ios_base::out | std::ios_base::binary};
|
||||
std::stack<char> quote_stack;
|
||||
for (size_t p = 0; p <= text.size(); p++) {
|
||||
char here = p < text.size() ? text.at(p) : 0;
|
||||
|
||||
if (here == '\\') {
|
||||
size_t p2 = p + 1;
|
||||
if (p2 < text.size()) {
|
||||
char here2 = text.at(p2);
|
||||
if (isdigit(here2)) { // Octal
|
||||
// Not supported yet.
|
||||
p++;
|
||||
} else if (here2 == 'x') { // Hexadecimal
|
||||
// Not supported yet.
|
||||
p += 3;
|
||||
} else if (here2 == 'u') { // 4 or 8 wide Unicode.
|
||||
// Not supported yet.
|
||||
} else if (here2 == 'a') {
|
||||
opt_stream << '\a';
|
||||
p++;
|
||||
} else if (here2 == 'b') {
|
||||
opt_stream << '\b';
|
||||
p++;
|
||||
} else if (here2 == 'f') {
|
||||
opt_stream << '\f';
|
||||
p++;
|
||||
} else if (here2 == 'n') {
|
||||
opt_stream << '\n';
|
||||
p++;
|
||||
} else if (here2 == 'r') {
|
||||
opt_stream << '\r';
|
||||
p++;
|
||||
} else if (here2 == 't') {
|
||||
opt_stream << '\t';
|
||||
p++;
|
||||
} else if (here2 == 'v') {
|
||||
opt_stream << '\v';
|
||||
p++;
|
||||
} else if (here2 == '\\') {
|
||||
opt_stream << '\\';
|
||||
p++;
|
||||
} else if (here2 == '\'') {
|
||||
opt_stream << '\'';
|
||||
p++;
|
||||
} else if (here2 == '"') {
|
||||
opt_stream << '"';
|
||||
p++;
|
||||
} else if (here2 == '?') {
|
||||
opt_stream << '\?';
|
||||
p++;
|
||||
}
|
||||
}
|
||||
} else if ((here == '\'') || (here == '"')) {
|
||||
if (quote_stack.size() > 1) {
|
||||
opt_stream << here;
|
||||
}
|
||||
if (quote_stack.size() == 0) {
|
||||
quote_stack.push(here);
|
||||
} else if (quote_stack.top() == here) {
|
||||
quote_stack.pop();
|
||||
} else {
|
||||
quote_stack.push(here);
|
||||
}
|
||||
} else if ((here == 0) || ((here == ' ') && (quote_stack.size() == 0))) {
|
||||
std::string ropt = opt_stream.str();
|
||||
if (ropt.size() > 0) {
|
||||
opts.push_back(ropt);
|
||||
opt_stream.str(std::string());
|
||||
opt_stream.clear();
|
||||
}
|
||||
} else {
|
||||
opt_stream << here;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have a list of parameters as neatly grouped strings, and
|
||||
// have also dealt with escaping for the most part. We want to parse
|
||||
// an FFmpeg commandline option set here, so the first character in
|
||||
// the string must be a '-'.
|
||||
for (std::string& opt : opts) {
|
||||
// Skip empty options.
|
||||
if (opt.size() == 0)
|
||||
continue;
|
||||
|
||||
// Skip options that don't start with a '-'.
|
||||
if (opt.at(0) != '-') {
|
||||
PLOG_WARNING("Option '%s' is malformed, must start with a '-'.", opt.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip options that don't contain a '='.
|
||||
const char* cstr = opt.c_str();
|
||||
const char* eq_at = strchr(cstr, '=');
|
||||
if (eq_at == nullptr) {
|
||||
PLOG_WARNING("Option '%s' is malformed, must contain a '='.", opt.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
std::string key = opt.substr(1, eq_at - cstr - 1);
|
||||
std::string value = opt.substr(eq_at - cstr + 1);
|
||||
|
||||
int res = av_opt_set(_context, key.c_str(), value.c_str(), AV_OPT_SEARCH_CHILDREN);
|
||||
if (res < 0) {
|
||||
PLOG_WARNING("Option '%s' (key: '%s', value: '%s') encountered error: %s", opt.c_str(),
|
||||
key.c_str(), value.c_str(), ffmpeg::tools::get_error_description(res));
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
PLOG_ERROR("Option '%s' encountered exception: %s", opt.c_str(), ex.what())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,5 +151,14 @@ namespace obsffmpeg {
|
||||
|
||||
bool encode_avframe(std::shared_ptr<AVFrame> frame, struct encoder_packet* packet,
|
||||
bool* received_packet);
|
||||
|
||||
public: // Handler API
|
||||
bool is_hardware_encode();
|
||||
|
||||
const AVCodec* get_avcodec();
|
||||
|
||||
const AVCodecContext* get_avcodeccontext();
|
||||
|
||||
void parse_ffmpeg_commandline(std::string text);
|
||||
};
|
||||
} // namespace obsffmpeg
|
||||
|
||||
+85
-45
@@ -24,12 +24,15 @@
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include "plugin.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
@@ -113,52 +116,11 @@ const char* ffmpeg::tools::get_color_space_name(AVColorSpace v)
|
||||
|
||||
const char* ffmpeg::tools::get_error_description(int error)
|
||||
{
|
||||
switch (error) {
|
||||
case AVERROR(EPERM):
|
||||
return "Permission Denied";
|
||||
// case AVERROR(ENOENT):
|
||||
// case AVERROR(ESRCH):
|
||||
// case AVERROR(EINTR):
|
||||
// case AVERROR(EIO):
|
||||
// case AVERROR(ENXIO):
|
||||
// case AVERROR(E2BIG):
|
||||
// case AVERROR(ENOEXEC):
|
||||
// case AVERROR(EBADF):
|
||||
// case AVERROR(ECHILD):
|
||||
// case AVERROR(EAGAIN):
|
||||
case AVERROR(ENOMEM):
|
||||
return "Out Of Memory";
|
||||
// case AVERROR(EACCES):
|
||||
// case AVERROR(EFAULT):
|
||||
// case AVERROR(EBUSY):
|
||||
// case AVERROR(EEXIST):
|
||||
// case AVERROR(EXDEV):
|
||||
// case AVERROR(ENODEV):
|
||||
// case AVERROR(ENOTDIR):
|
||||
// case AVERROR(EISDIR):
|
||||
// case AVERROR(ENFILE):
|
||||
// case AVERROR(EMFILE):
|
||||
// case AVERROR(ENOTTY):
|
||||
// case AVERROR(EFBIG):
|
||||
// case AVERROR(ENOSPC):
|
||||
// case AVERROR(ESPIPE):
|
||||
// case AVERROR(EROFS):
|
||||
// case AVERROR(EMLINK):
|
||||
// case AVERROR(EPIPE):
|
||||
// case AVERROR(EDOM):
|
||||
// case AVERROR(EDEADLK):
|
||||
// case AVERROR(ENAMETOOLONG):
|
||||
// case AVERROR(ENOLCK):
|
||||
// case AVERROR(ENOSYS):
|
||||
// case AVERROR(ENOTEMPTY):
|
||||
case AVERROR(EINVAL):
|
||||
return "Invalid Value(s)";
|
||||
case AVERROR(ERANGE):
|
||||
return "Out of Range";
|
||||
// case AVERROR(EILSEQ):
|
||||
// case AVERROR(STRUNCATE):
|
||||
thread_local char error_buf[AV_ERROR_MAX_STRING_SIZE + 1];
|
||||
if (av_strerror(error, error_buf, AV_ERROR_MAX_STRING_SIZE) < 0) {
|
||||
snprintf(error_buf, AV_ERROR_MAX_STRING_SIZE, "Unknown Error (%i)", error);
|
||||
}
|
||||
return "Not Translated Yet";
|
||||
return error_buf;
|
||||
}
|
||||
|
||||
static std::map<video_format, AVPixelFormat> obs_to_av_format_map = {
|
||||
@@ -311,3 +273,81 @@ void ffmpeg::tools::setup_obs_color(video_colorspace colorspace, video_range_typ
|
||||
// Downscaling should result in downscaling, not pixelation
|
||||
context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_std_compliance_name(int compliance)
|
||||
{
|
||||
switch (compliance) {
|
||||
case FF_COMPLIANCE_VERY_STRICT:
|
||||
return "Very Strict";
|
||||
case FF_COMPLIANCE_STRICT:
|
||||
return "Strict";
|
||||
case FF_COMPLIANCE_NORMAL:
|
||||
return "Normal";
|
||||
case FF_COMPLIANCE_UNOFFICIAL:
|
||||
return "Unofficial";
|
||||
case FF_COMPLIANCE_EXPERIMENTAL:
|
||||
return "Experimental";
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
const char* ffmpeg::tools::get_thread_type_name(int thread_type)
|
||||
{
|
||||
switch (thread_type) {
|
||||
case FF_THREAD_FRAME | FF_THREAD_SLICE:
|
||||
return "Slice & Frame";
|
||||
case FF_THREAD_FRAME:
|
||||
return "Frame";
|
||||
case FF_THREAD_SLICE:
|
||||
return "Slice";
|
||||
default:
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::tools::print_av_option_bool(AVCodecContext* context, const char* option, std::string text)
|
||||
{
|
||||
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||
int64_t v = 0;
|
||||
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||
PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), v == 0 ? "Disabled" : "Enabled");
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||
}
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::tools::print_av_option_int(AVCodecContext* context, const char* option, std::string text,
|
||||
std::string suffix)
|
||||
{
|
||||
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||
int64_t v = 0;
|
||||
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||
PLOG_INFO("[%s] %s: %lld %s", context->codec->name, text.c_str(), v, suffix.c_str());
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||
}
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ffmpeg::tools::print_av_option_string(AVCodecContext* context, const char* option, std::string text,
|
||||
std::function<std::string(int64_t)> decoder)
|
||||
{
|
||||
if (av_opt_is_set_to_default_by_name(context, option, AV_OPT_SEARCH_CHILDREN) == 0) {
|
||||
int64_t v = 0;
|
||||
if (av_opt_get_int(context, option, AV_OPT_SEARCH_CHILDREN, &v) == 0) {
|
||||
std::string name = "<Unknown>";
|
||||
if (decoder)
|
||||
name = decoder(v);
|
||||
PLOG_INFO("[%s] %s: %s", context->codec->name, text.c_str(), name.c_str());
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Error>", context->codec->name, text.c_str());
|
||||
}
|
||||
} else {
|
||||
PLOG_INFO("[%s] %s: <Default>", context->codec->name, text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define OBS_FFMPEG_FFMPEG_UTILITY
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <obs.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -57,6 +58,19 @@ namespace ffmpeg {
|
||||
std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list);
|
||||
|
||||
void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context);
|
||||
|
||||
const char* get_std_compliance_name(int compliance);
|
||||
|
||||
const char* get_thread_type_name(int thread_type);
|
||||
|
||||
void print_av_option_bool(AVCodecContext* context, const char* option, std::string text);
|
||||
|
||||
void print_av_option_int(AVCodecContext* context, const char* option, std::string text,
|
||||
std::string suffix);
|
||||
|
||||
void print_av_option_string(AVCodecContext* context, const char* option, std::string text,
|
||||
std::function<std::string(int64_t)> decoder);
|
||||
|
||||
} // namespace tools
|
||||
} // namespace ffmpeg
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace obsffmpeg {
|
||||
virtual std::list<obsffmpeg::hwapi::device> enumerate_adapters() = 0;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create(obsffmpeg::hwapi::device target) = 0;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() = 0;
|
||||
};
|
||||
|
||||
class instance {
|
||||
|
||||
+29
-1
@@ -22,11 +22,14 @@
|
||||
#include "d3d11.hpp"
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "utility.hpp"
|
||||
|
||||
extern "C" {
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244)
|
||||
#include <graphics/graphics.h>
|
||||
#include <libavutil/hwcontext_d3d11va.h>
|
||||
#include <obs.h>
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
@@ -123,6 +126,22 @@ std::shared_ptr<obsffmpeg::hwapi::instance> obsffmpeg::hwapi::d3d11::create(obsf
|
||||
return std::make_shared<d3d11_instance>(device, context);
|
||||
}
|
||||
|
||||
std::shared_ptr<obsffmpeg::hwapi::instance> obsffmpeg::hwapi::d3d11::create_from_obs()
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
if (GS_DEVICE_DIRECT3D_11 != gs_get_device_type()) {
|
||||
throw std::runtime_error("OBS Device is not a D3D11 Device.");
|
||||
}
|
||||
|
||||
ATL::CComPtr<ID3D11Device> device =
|
||||
ATL::CComPtr<ID3D11Device>(reinterpret_cast<ID3D11Device*>(gs_get_device_obj()));
|
||||
ATL::CComPtr<ID3D11DeviceContext> context;
|
||||
device->GetImmediateContext(&context);
|
||||
|
||||
return std::make_shared<d3d11_instance>(device, context);
|
||||
}
|
||||
|
||||
struct D3D11AVFrame {
|
||||
ATL::CComPtr<ID3D11Texture2D> handle;
|
||||
};
|
||||
@@ -150,8 +169,11 @@ AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
|
||||
d3d11va->device->AddRef();
|
||||
d3d11va->device_context = _context;
|
||||
d3d11va->device_context->AddRef();
|
||||
d3d11va->lock = [](void*) { obs_enter_graphics(); };
|
||||
d3d11va->unlock = [](void*) { obs_leave_graphics(); };
|
||||
|
||||
if (av_hwdevice_ctx_init(dctx_ref) < 0)
|
||||
int ret = av_hwdevice_ctx_init(dctx_ref);
|
||||
if (ret < 0)
|
||||
throw std::runtime_error("Failed to initialize AVHWDeviceContext.");
|
||||
|
||||
return dctx_ref;
|
||||
@@ -159,6 +181,8 @@ AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
|
||||
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::allocate_frame(AVBufferRef* frames)
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
auto frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
|
||||
av_frame_unref(frame);
|
||||
av_frame_free(&frame);
|
||||
@@ -174,6 +198,8 @@ std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::allocate_frame(AVBuff
|
||||
void obsffmpeg::hwapi::d3d11_instance::copy_from_obs(AVBufferRef*, uint32_t handle, uint64_t lock_key,
|
||||
uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame)
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
ATL::CComPtr<IDXGIKeyedMutex> mutex;
|
||||
ATL::CComPtr<ID3D11Texture2D> input;
|
||||
|
||||
@@ -211,6 +237,8 @@ void obsffmpeg::hwapi::d3d11_instance::copy_from_obs(AVBufferRef*, uint32_t hand
|
||||
std::shared_ptr<AVFrame> obsffmpeg::hwapi::d3d11_instance::avframe_from_obs(AVBufferRef* frames, uint32_t handle,
|
||||
uint64_t lock_key, uint64_t* next_lock_key)
|
||||
{
|
||||
auto gctx = obsffmpeg::obs_graphics();
|
||||
|
||||
auto frame = this->allocate_frame(frames);
|
||||
this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame);
|
||||
return frame;
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace obsffmpeg {
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance>
|
||||
create(obsffmpeg::hwapi::device target) override;
|
||||
|
||||
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() override;
|
||||
};
|
||||
|
||||
class d3d11_instance : public ::obsffmpeg::hwapi::instance {
|
||||
|
||||
+11
-18
@@ -20,34 +20,27 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "handler.hpp"
|
||||
#include "encoder.hpp"
|
||||
|
||||
void obsffmpeg::ui::handler::override_visible_name(const AVCodec*, std::string&) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_info(obs_encoder_info*, obs_encoder_info*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_lag_in_frames(size_t&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
void obsffmpeg::ui::handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info*,
|
||||
obsffmpeg::encoder_info*)
|
||||
{}
|
||||
|
||||
void obsffmpeg::ui::handler::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
void obsffmpeg::ui::handler::get_properties(obs_properties_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
obsffmpeg::hwapi::device obsffmpeg::ui::handler::find_hw_device(std::shared_ptr<obsffmpeg::hwapi::base>, const AVCodec*,
|
||||
AVCodecContext*)
|
||||
bool obsffmpeg::ui::handler::has_keyframe_support(obsffmpeg::encoder* instance)
|
||||
{
|
||||
return obsffmpeg::hwapi::device();
|
||||
return (instance->get_avcodec()->capabilities & AV_CODEC_CAP_INTRA_ONLY) == 0;
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::handler::get_properties(obs_properties_t*, const AVCodec*, AVCodecContext*, bool) {}
|
||||
|
||||
void obsffmpeg::ui::handler::update(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::override_update(obsffmpeg::encoder*, obs_data_t*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::log_options(obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::import_from_ffmpeg(const std::string, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
std::string obsffmpeg::ui::handler::export_for_ffmpeg(obs_data_t*, const AVCodec*, AVCodecContext*)
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, obs_data_t*, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
void obsffmpeg::ui::handler::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {}
|
||||
|
||||
+17
-17
@@ -37,36 +37,36 @@ extern "C" {
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
struct encoder_info;
|
||||
class encoder_factory;
|
||||
class encoder;
|
||||
|
||||
namespace ui {
|
||||
class handler {
|
||||
public:
|
||||
virtual void override_visible_name(const AVCodec* codec, std::string& name);
|
||||
public /*factory*/:
|
||||
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||
obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback);
|
||||
|
||||
virtual void override_info(obs_encoder_info* main, obs_encoder_info* fallback);
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode);
|
||||
|
||||
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context, bool hw_encode);
|
||||
public /*settings*/:
|
||||
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual obsffmpeg::hwapi::device find_hw_device(std::shared_ptr<obsffmpeg::hwapi::base> api,
|
||||
const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void import_from_ffmpeg(const std::string ffmpeg, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context);
|
||||
public /*instance*/:
|
||||
|
||||
virtual std::string export_for_ffmpeg(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context);
|
||||
};
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "nvenc_h264_handler.hpp"
|
||||
#include "codecs/h264.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "nvenc_shared.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
@@ -34,30 +36,7 @@ extern "C" {
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
/* Missing Options:
|
||||
|
||||
Seem to be covered by initQP_* instead.
|
||||
- [obs-ffmpeg-encoder] Option 'cq' with help 'Set target quality level (0 to 51, 0 means automatic) for constant quality mode in VBR rate control' of type 'Float' with default value '0.000000', minimum '0.000000' and maximum '51.000000'.
|
||||
- [obs-ffmpeg-encoder] Option 'qp' with help 'Constant quantization parameter rate control method' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '51.000000'.
|
||||
|
||||
Not sure what there are useful for.
|
||||
[obs-ffmpeg-encoder] Option 'aud' with help 'Use access unit delimiters' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'surfaces' with help 'Number of concurrent surfaces' of type 'Int' with default value '0', minimum '0.000000' and maximum '64.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'delay' with help 'Delay frame output by the given amount of frames' of type 'Int' with default value '2147483647', minimum '0.000000' and maximum '2147483647.000000'.
|
||||
|
||||
Should probably add this.
|
||||
[obs-ffmpeg-encoder] Option 'gpu' with unit (gpu) with help 'Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.' of type 'Int' with default value '-1', minimum '-2.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] [gpu] Constant 'any' and help text 'Pick the first device available' with value '-1'.
|
||||
[obs-ffmpeg-encoder] [gpu] Constant 'list' and help text 'List the available devices' with value '-2'.
|
||||
|
||||
Useless except for strict_gop maybe?
|
||||
[obs-ffmpeg-encoder] Option 'forced-idr' with help 'If forcing keyframes, force them as IDR frames.' of type 'Bool' with default value 'false', minimum '-1.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'strict_gop' with help 'Set 1 to minimize GOP-to-GOP rate fluctuations' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'bluray-compat' with help 'Bluray compatibility workarounds' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
||||
*/
|
||||
|
||||
using namespace obsffmpeg::codecs::h264;
|
||||
using namespace obsffmpeg::nvenc;
|
||||
|
||||
std::map<profile, std::string> profiles{
|
||||
{profile::BASELINE, "baseline"},
|
||||
@@ -80,15 +59,11 @@ INITIALIZER(nvenc_h264_handler_init)
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::override_visible_name(const AVCodec*, std::string& name)
|
||||
void obsffmpeg::ui::nvenc_h264_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback)
|
||||
{
|
||||
name = "H.264/AVC Encoder (NVidia NVENC)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
nvenc::override_lag_in_frames(lag, settings, codec, context);
|
||||
main->readable_name = "H.264/AVC NVidia NVENC (Hardware)";
|
||||
fallback->readable_name = "H.264/AVC NVidia NVENC (Software)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
@@ -100,6 +75,11 @@ void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const
|
||||
obs_data_set_default_int(settings, P_H264_LEVEL, static_cast<int64_t>(codecs::h264::level::UNKNOWN));
|
||||
}
|
||||
|
||||
bool obsffmpeg::ui::nvenc_h264_handler::has_keyframe_support(obsffmpeg::encoder*)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
@@ -132,20 +112,30 @@ void obsffmpeg::ui::nvenc_h264_handler::update(obs_data_t* settings, const AVCod
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings)
|
||||
{
|
||||
nvenc::override_update(instance, settings);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
nvenc::log_options(settings, codec, context);
|
||||
|
||||
profile cfg_profile = static_cast<profile>(obs_data_get_int(settings, P_H264_PROFILE));
|
||||
level cfg_level = static_cast<level>(obs_data_get_int(settings, P_H264_LEVEL));
|
||||
|
||||
auto found1 = profiles.find(cfg_profile);
|
||||
if (found1 != profiles.end())
|
||||
PLOG_INFO("[%s] H.264 Profile: %s", codec->name, found1->second.c_str());
|
||||
|
||||
auto found2 = levels.find(cfg_level);
|
||||
if (found2 != levels.end())
|
||||
PLOG_INFO("[%s] H.264 Level: %s", codec->name, found2->second.c_str());
|
||||
PLOG_INFO("[%s] H.265/HEVC:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
|
||||
profile val = static_cast<profile>(v);
|
||||
auto index = profiles.find(val);
|
||||
if (index != profiles.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
|
||||
level val = static_cast<level>(v);
|
||||
auto index = levels.find(val);
|
||||
if (index != levels.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||
|
||||
@@ -33,23 +33,28 @@ extern "C" {
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class nvenc_h264_handler : public handler {
|
||||
public:
|
||||
virtual void override_visible_name(const AVCodec* codec, std::string& name) override;
|
||||
|
||||
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
public /*factory*/:
|
||||
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||
obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
bool hw_encode);
|
||||
|
||||
public /*settings*/:
|
||||
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void override_update(obsffmpeg::encoder* 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);
|
||||
|
||||
private:
|
||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "nvenc_hevc_handler.hpp"
|
||||
#include "codecs/hevc.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "nvenc_shared.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
@@ -34,28 +36,6 @@ extern "C" {
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
/* Missing Options:
|
||||
|
||||
Seem to be covered by initQP_* instead.
|
||||
- [obs-ffmpeg-encoder] Option 'cq' with help 'Set target quality level (0 to 51, 0 means automatic) for constant quality mode in VBR rate control' of type 'Float' with default value '0.000000', minimum '0.000000' and maximum '51.000000'.
|
||||
- [obs-ffmpeg-encoder] Option 'qp' with help 'Constant quantization parameter rate control method' of type 'Int' with default value '-1', minimum '-1.000000' and maximum '51.000000'.
|
||||
|
||||
Not sure what there are useful for.
|
||||
[obs-ffmpeg-encoder] Option 'aud' with help 'Use access unit delimiters' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'surfaces' with help 'Number of concurrent surfaces' of type 'Int' with default value '0', minimum '0.000000' and maximum '64.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'delay' with help 'Delay frame output by the given amount of frames' of type 'Int' with default value '2147483647', minimum '0.000000' and maximum '2147483647.000000'.
|
||||
|
||||
Should probably add this.
|
||||
[obs-ffmpeg-encoder] Option 'gpu' with unit (gpu) with help 'Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.' of type 'Int' with default value '-1', minimum '-2.000000' and maximum '2147483647.000000'.
|
||||
[obs-ffmpeg-encoder] [gpu] Constant 'any' and help text 'Pick the first device available' with value '-1'.
|
||||
[obs-ffmpeg-encoder] [gpu] Constant 'list' and help text 'List the available devices' with value '-2'.
|
||||
|
||||
Useless except for strict_gop maybe?
|
||||
[obs-ffmpeg-encoder] Option 'forced-idr' with help 'If forcing keyframes, force them as IDR frames.' of type 'Bool' with default value 'false', minimum '-1.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'strict_gop' with help 'Set 1 to minimize GOP-to-GOP rate fluctuations' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
||||
[obs-ffmpeg-encoder] Option 'bluray-compat' with help 'Bluray compatibility workarounds' of type 'Bool' with default value 'false', minimum '0.000000' and maximum '1.000000'.
|
||||
*/
|
||||
|
||||
using namespace obsffmpeg::codecs::hevc;
|
||||
|
||||
std::map<profile, std::string> profiles{
|
||||
@@ -82,15 +62,11 @@ INITIALIZER(nvenc_hevc_handler_init)
|
||||
});
|
||||
};
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::override_visible_name(const AVCodec*, std::string& name)
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback)
|
||||
{
|
||||
name = "H.265/HEVC Encoder (NVidia NVENC)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context)
|
||||
{
|
||||
nvenc::override_lag_in_frames(lag, settings, codec, context);
|
||||
main->readable_name = "H.265/HEVC Nvidia NVENC (Hardware)";
|
||||
fallback->readable_name = "H.265/HEVC Nvidia NVENC (Software)";
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
@@ -103,6 +79,11 @@ void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const
|
||||
obs_data_set_default_int(settings, P_HEVC_LEVEL, static_cast<int64_t>(codecs::hevc::level::UNKNOWN));
|
||||
}
|
||||
|
||||
bool obsffmpeg::ui::nvenc_hevc_handler::has_keyframe_support(obsffmpeg::encoder*)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool)
|
||||
{
|
||||
@@ -139,25 +120,37 @@ void obsffmpeg::ui::nvenc_hevc_handler::update(obs_data_t* settings, const AVCod
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::override_update(obsffmpeg::encoder* instance, obs_data_t* settings)
|
||||
{
|
||||
nvenc::override_update(instance, settings);
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
nvenc::log_options(settings, codec, context);
|
||||
|
||||
profile cfg_profile = static_cast<profile>(obs_data_get_int(settings, P_HEVC_PROFILE));
|
||||
tier cfg_tier = static_cast<tier>(obs_data_get_int(settings, P_HEVC_TIER));
|
||||
level cfg_level = static_cast<level>(obs_data_get_int(settings, P_HEVC_LEVEL));
|
||||
|
||||
auto found1 = profiles.find(cfg_profile);
|
||||
if (found1 != profiles.end())
|
||||
PLOG_INFO("[%s] H.265 Profile: %s", codec->name, found1->second.c_str());
|
||||
|
||||
auto found2 = levels.find(cfg_level);
|
||||
if (found2 != levels.end())
|
||||
PLOG_INFO("[%s] H.265 Level: %s", codec->name, found2->second.c_str());
|
||||
|
||||
auto found3 = tiers.find(cfg_tier);
|
||||
if (found3 != tiers.end())
|
||||
PLOG_INFO("[%s] H.265 Tier: %s", codec->name, found3->second.c_str());
|
||||
PLOG_INFO("[%s] H.265/HEVC:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
|
||||
profile val = static_cast<profile>(v);
|
||||
auto index = profiles.find(val);
|
||||
if (index != profiles.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
|
||||
level val = static_cast<level>(v);
|
||||
auto index = levels.find(val);
|
||||
if (index != levels.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "tier", " Tier", [](int64_t v) {
|
||||
tier val = static_cast<tier>(v);
|
||||
auto index = tiers.find(val);
|
||||
if (index != tiers.end())
|
||||
return index->second;
|
||||
return std::string("<Unknown>");
|
||||
});
|
||||
}
|
||||
|
||||
void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
|
||||
|
||||
@@ -33,23 +33,28 @@ extern "C" {
|
||||
namespace obsffmpeg {
|
||||
namespace ui {
|
||||
class nvenc_hevc_handler : public handler {
|
||||
public:
|
||||
virtual void override_visible_name(const AVCodec* codec, std::string& name) override;
|
||||
|
||||
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
public /*factory*/:
|
||||
virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
|
||||
obsffmpeg::encoder_info* main,
|
||||
obsffmpeg::encoder_info* fallback);
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
bool hw_encode);
|
||||
|
||||
public /*settings*/:
|
||||
virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
AVCodecContext* context, bool hw_encode);
|
||||
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
virtual void override_update(obsffmpeg::encoder* 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);
|
||||
|
||||
private:
|
||||
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
|
||||
|
||||
+144
-156
@@ -22,6 +22,8 @@
|
||||
#include "nvenc_shared.hpp"
|
||||
#include <algorithm>
|
||||
#include "codecs/hevc.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "ffmpeg/tools.hpp"
|
||||
#include "plugin.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "utility.hpp"
|
||||
@@ -132,17 +134,34 @@ std::map<b_ref_mode, std::string> obsffmpeg::nvenc::b_ref_mode_to_opt{
|
||||
{b_ref_mode::MIDDLE, "middle"},
|
||||
};
|
||||
|
||||
void obsffmpeg::nvenc::override_lag_in_frames(size_t& lag, obs_data_t*, const AVCodec*, AVCodecContext* context)
|
||||
void obsffmpeg::nvenc::override_update(obsffmpeg::encoder* instance, obs_data_t*)
|
||||
{
|
||||
// With NVENC, the number of frames lagged before we get our first
|
||||
// packet is determined by the number of b-frames. Threads, lookahead
|
||||
// frames and various other settings are ignored.
|
||||
// The minimum number of lagged frames is 1.
|
||||
AVCodecContext* context = const_cast<AVCodecContext*>(instance->get_avcodeccontext());
|
||||
|
||||
int64_t rcla = 0;
|
||||
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rcla);
|
||||
int64_t rclookahead = 0;
|
||||
int64_t surfaces = 0;
|
||||
int64_t async_depth = 0;
|
||||
|
||||
lag = static_cast<size_t>(std::max(1ll + static_cast<int64_t>(context->max_b_frames), rcla));
|
||||
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rclookahead);
|
||||
av_opt_get_int(context, "surfaces", AV_OPT_SEARCH_CHILDREN, &surfaces);
|
||||
av_opt_get_int(context, "async_depth", AV_OPT_SEARCH_CHILDREN, &async_depth);
|
||||
|
||||
// Calculate and set the number of surfaces to allocate (if not user overridden).
|
||||
if (surfaces == 0) {
|
||||
surfaces = std::max(4ll, (context->max_b_frames + 1ll) * 4ll);
|
||||
if (rclookahead > 0) {
|
||||
surfaces = std::max(1ll, std::max(surfaces, rclookahead + (context->max_b_frames + 5ll)));
|
||||
} else if (context->max_b_frames > 0) {
|
||||
surfaces = std::max(4ll, (context->max_b_frames + 1ll) * 4ll);
|
||||
} else {
|
||||
surfaces = 4;
|
||||
}
|
||||
|
||||
av_opt_set_int(context, "surfaces", surfaces, AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
|
||||
// Set delay
|
||||
context->delay = static_cast<int>(std::min(std::max(async_depth, 3ll), surfaces - 1));
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCodecContext*)
|
||||
@@ -152,8 +171,8 @@ void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCode
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_MODE, static_cast<int64_t>(ratecontrolmode::CBR_HQ));
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_TWOPASS, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_LOOKAHEAD, 0);
|
||||
obs_data_set_default_bool(settings, ST_RATECONTROL_ADAPTIVEI, true);
|
||||
obs_data_set_default_bool(settings, ST_RATECONTROL_ADAPTIVEB, true);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEI, -1);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEB, -1);
|
||||
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_TARGET, 6000);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM, 6000);
|
||||
@@ -170,15 +189,15 @@ void obsffmpeg::nvenc::get_defaults(obs_data_t* settings, const AVCodec*, AVCode
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B, 21);
|
||||
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1);
|
||||
|
||||
obs_data_set_default_bool(settings, ST_AQ_SPATIAL, true);
|
||||
obs_data_set_default_int(settings, ST_AQ_SPATIAL, -1);
|
||||
obs_data_set_default_int(settings, ST_AQ_STRENGTH, 8);
|
||||
obs_data_set_default_bool(settings, ST_AQ_TEMPORAL, true);
|
||||
obs_data_set_default_int(settings, ST_AQ_TEMPORAL, -1);
|
||||
|
||||
obs_data_set_default_int(settings, ST_OTHER_BFRAMES, 2);
|
||||
obs_data_set_default_int(settings, ST_OTHER_BFRAME_REFERENCEMODE, static_cast<int64_t>(b_ref_mode::DISABLED));
|
||||
obs_data_set_default_bool(settings, ST_OTHER_ZEROLATENCY, false);
|
||||
obs_data_set_default_bool(settings, ST_OTHER_WEIGHTED_PREDICTION, false);
|
||||
obs_data_set_default_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES, false);
|
||||
obs_data_set_default_int(settings, ST_OTHER_ZEROLATENCY, -1);
|
||||
obs_data_set_default_int(settings, ST_OTHER_WEIGHTED_PREDICTION, -1);
|
||||
obs_data_set_default_int(settings, ST_OTHER_NONREFERENCE_PFRAMES, -1);
|
||||
}
|
||||
|
||||
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||
@@ -241,7 +260,7 @@ static bool modified_quality(obs_properties_t* props, obs_property_t*, obs_data_
|
||||
|
||||
static bool modified_aq(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
|
||||
{
|
||||
bool spatial_aq = obs_data_get_bool(settings, ST_AQ_SPATIAL);
|
||||
bool spatial_aq = obs_data_get_int(settings, ST_AQ_SPATIAL) == 1;
|
||||
obs_property_set_visible(obs_properties_get(props, ST_AQ_STRENGTH), spatial_aq);
|
||||
return true;
|
||||
}
|
||||
@@ -280,12 +299,9 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_list(grp, ST_RATECONTROL_TWOPASS, TRANSLATE(ST_RATECONTROL_TWOPASS),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_TWOPASS,
|
||||
TRANSLATE(ST_RATECONTROL_TWOPASS));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_TWOPASS)));
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), -1);
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DISABLED), 0);
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_ENABLED), 1);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -295,13 +311,13 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
||||
obs_property_int_set_suffix(p, " frames");
|
||||
}
|
||||
{
|
||||
auto p =
|
||||
obs_properties_add_bool(grp, ST_RATECONTROL_ADAPTIVEI, TRANSLATE(ST_RATECONTROL_ADAPTIVEI));
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEI,
|
||||
TRANSLATE(ST_RATECONTROL_ADAPTIVEI));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEI)));
|
||||
}
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||
auto p =
|
||||
obs_properties_add_bool(grp, ST_RATECONTROL_ADAPTIVEB, TRANSLATE(ST_RATECONTROL_ADAPTIVEB));
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEB,
|
||||
TRANSLATE(ST_RATECONTROL_ADAPTIVEB));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEB)));
|
||||
}
|
||||
}
|
||||
@@ -417,7 +433,7 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_bool(grp, ST_AQ_SPATIAL, TRANSLATE(ST_AQ_SPATIAL));
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_AQ_SPATIAL, TRANSLATE(ST_AQ_SPATIAL));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_SPATIAL)));
|
||||
obs_property_set_modified_callback(p, modified_aq);
|
||||
}
|
||||
@@ -427,7 +443,7 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_STRENGTH)));
|
||||
}
|
||||
{
|
||||
auto p = obs_properties_add_bool(grp, ST_AQ_TEMPORAL, TRANSLATE(ST_AQ_TEMPORAL));
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_AQ_TEMPORAL, TRANSLATE(ST_AQ_TEMPORAL));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_TEMPORAL)));
|
||||
}
|
||||
}
|
||||
@@ -458,18 +474,19 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_bool(grp, ST_OTHER_ZEROLATENCY, TRANSLATE(ST_OTHER_ZEROLATENCY));
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_ZEROLATENCY,
|
||||
TRANSLATE(ST_OTHER_ZEROLATENCY));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_ZEROLATENCY)));
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_bool(grp, ST_OTHER_WEIGHTED_PREDICTION,
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_WEIGHTED_PREDICTION,
|
||||
TRANSLATE(ST_OTHER_WEIGHTED_PREDICTION));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_WEIGHTED_PREDICTION)));
|
||||
}
|
||||
|
||||
{
|
||||
auto p = obs_properties_add_bool(grp, ST_OTHER_NONREFERENCE_PFRAMES,
|
||||
auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_OTHER_NONREFERENCE_PFRAMES,
|
||||
TRANSLATE(ST_OTHER_NONREFERENCE_PFRAMES));
|
||||
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_NONREFERENCE_PFRAMES)));
|
||||
}
|
||||
@@ -537,6 +554,7 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
||||
av_opt_set(context->priv_data, "rc", rcopt->second.c_str(), 0);
|
||||
}
|
||||
|
||||
av_opt_set_int(context->priv_data, "cbr", 0, 0);
|
||||
switch (rc) {
|
||||
case ratecontrolmode::CQP:
|
||||
have_qp = true;
|
||||
@@ -562,14 +580,18 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
||||
}
|
||||
|
||||
int la = static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD));
|
||||
av_opt_set_int(context->priv_data, "lookahead", la, 0);
|
||||
av_opt_set_int(context->priv_data, "rc-lookahead", la, 0);
|
||||
if (la > 0) {
|
||||
bool adapt_i = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEI);
|
||||
av_opt_set_int(context->priv_data, "no-scenecut", !adapt_i ? 1 : 0, 0);
|
||||
int64_t adapt_i = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEI);
|
||||
if (!is_tristate_default(adapt_i)) {
|
||||
av_opt_set_int(context->priv_data, "no-scenecut", adapt_i, AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
|
||||
if (strcmp(codec->name, "h264_nvenc")) {
|
||||
bool adapt_b = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEB);
|
||||
av_opt_set_int(context->priv_data, "b_adapt", adapt_b ? 1 : 0, 0);
|
||||
int64_t adapt_b = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEB);
|
||||
if (!is_tristate_default(adapt_b)) {
|
||||
av_opt_set_int(context->priv_data, "b_adapt", adapt_b, AV_OPT_SEARCH_CHILDREN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,37 +640,42 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
||||
}
|
||||
|
||||
{ // AQ
|
||||
bool saq = obs_data_get_bool(settings, ST_AQ_SPATIAL);
|
||||
bool taq = obs_data_get_bool(settings, ST_AQ_TEMPORAL);
|
||||
int64_t saq = obs_data_get_int(settings, ST_AQ_SPATIAL);
|
||||
int64_t taq = obs_data_get_int(settings, ST_AQ_TEMPORAL);
|
||||
|
||||
if (strcmp(codec->name, "h264_nvenc")) {
|
||||
av_opt_set_int(context->priv_data, "spatial-aq", saq ? 1 : 0, 0);
|
||||
av_opt_set_int(context->priv_data, "temporal-aq", taq ? 1 : 0, 0);
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||
if (!is_tristate_default(saq))
|
||||
av_opt_set_int(context->priv_data, "spatial-aq", saq, 0);
|
||||
if (!is_tristate_default(taq))
|
||||
av_opt_set_int(context->priv_data, "temporal-aq", taq, 0);
|
||||
} else {
|
||||
av_opt_set_int(context->priv_data, "spatial_aq", saq ? 1 : 0, 0);
|
||||
av_opt_set_int(context->priv_data, "temporal_aq", taq ? 1 : 0, 0);
|
||||
if (!is_tristate_default(saq))
|
||||
av_opt_set_int(context->priv_data, "spatial_aq", saq, 0);
|
||||
if (!is_tristate_default(taq))
|
||||
av_opt_set_int(context->priv_data, "temporal_aq", taq, 0);
|
||||
}
|
||||
if (saq) {
|
||||
if (is_tristate_enabled(saq))
|
||||
av_opt_set_int(context->priv_data, "aq-strength",
|
||||
static_cast<int>(obs_data_get_int(settings, ST_AQ_STRENGTH)), 0);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Other
|
||||
bool zl = obs_data_get_bool(settings, ST_OTHER_ZEROLATENCY);
|
||||
bool wp = obs_data_get_bool(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
||||
bool nrp = obs_data_get_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
||||
int64_t zl = obs_data_get_int(settings, ST_OTHER_ZEROLATENCY);
|
||||
int64_t wp = obs_data_get_int(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
||||
int64_t nrp = obs_data_get_int(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
||||
|
||||
context->max_b_frames = static_cast<int>(obs_data_get_int(settings, ST_OTHER_BFRAMES));
|
||||
|
||||
av_opt_set_int(context->priv_data, "zerolatency", zl ? 1 : 0, 0);
|
||||
av_opt_set_int(context->priv_data, "nonref_p", nrp ? 1 : 0, 0);
|
||||
if (!is_tristate_default(zl))
|
||||
av_opt_set_int(context->priv_data, "zerolatency", zl, 0);
|
||||
if (!is_tristate_default(nrp))
|
||||
av_opt_set_int(context->priv_data, "nonref_p", nrp, 0);
|
||||
|
||||
if ((context->max_b_frames != 0) && wp) {
|
||||
PLOG_WARNING(
|
||||
"Automatically disabled weighted prediction due to being incompatible with B-Frames.");
|
||||
} else {
|
||||
av_opt_set_int(context->priv_data, "weighted_pred", wp ? 1 : 0, 0);
|
||||
if ((context->max_b_frames != 0) && is_tristate_enabled(wp)) {
|
||||
PLOG_WARNING("[%s] Weighted Prediction disabled because of B-Frames being used.", codec->name);
|
||||
av_opt_set_int(context->priv_data, "weighted_pred", 0, 0);
|
||||
} else if (!is_tristate_default(wp)) {
|
||||
av_opt_set_int(context->priv_data, "weighted_pred", wp, 0);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -661,112 +688,73 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
|
||||
}
|
||||
}
|
||||
|
||||
void obsffmpeg::nvenc::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
|
||||
void obsffmpeg::nvenc::log_options(obs_data_t*, const AVCodec* codec, AVCodecContext* context)
|
||||
{
|
||||
preset cfg_preset = static_cast<preset>(obs_data_get_int(settings, ST_PRESET));
|
||||
PLOG_INFO("[%s] Nvidia NVENC:", codec->name);
|
||||
ffmpeg::tools::print_av_option_string(context, "preset", " Preset", [](int64_t v) {
|
||||
preset val = static_cast<preset>(v);
|
||||
std::string name = "<Unknown>";
|
||||
auto index = preset_to_opt.find(val);
|
||||
if (index != preset_to_opt.end())
|
||||
name = index->second;
|
||||
return name;
|
||||
});
|
||||
ffmpeg::tools::print_av_option_string(context, "rc", " Rate Control", [](int64_t v) {
|
||||
ratecontrolmode val = static_cast<ratecontrolmode>(v);
|
||||
std::string name = "<Unknown>";
|
||||
auto index = ratecontrolmode_to_opt.find(val);
|
||||
if (index != ratecontrolmode_to_opt.end())
|
||||
name = index->second;
|
||||
return name;
|
||||
});
|
||||
ffmpeg::tools::print_av_option_bool(context, "2pass", " Two Pass");
|
||||
ffmpeg::tools::print_av_option_int(context, "rc-lookahead", " Look-Ahead", "Frames");
|
||||
ffmpeg::tools::print_av_option_bool(context, "no-scenecut", " Adaptive I-Frames");
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0)
|
||||
ffmpeg::tools::print_av_option_bool(context, "b_adapt", " Adaptive B-Frames");
|
||||
|
||||
auto found1 = preset_to_opt.find(cfg_preset);
|
||||
if (found1 != preset_to_opt.end())
|
||||
PLOG_INFO("[%s] Preset: %s", codec->name, found1->second.c_str());
|
||||
PLOG_INFO("[%s] Bitrate:", codec->name);
|
||||
ffmpeg::tools::print_av_option_int(context, "bitrate", " Target", "bits/sec");
|
||||
ffmpeg::tools::print_av_option_int(context, "rc_max_rate", " Maximum", "bits/sec");
|
||||
ffmpeg::tools::print_av_option_int(context, "rc_buffer_size", " Buffer", "bits");
|
||||
PLOG_INFO("[%s] Quality:", codec->name);
|
||||
ffmpeg::tools::print_av_option_int(context, "qmin", " Minimum", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "cq", " Target", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "qmax", " Maximum", "");
|
||||
PLOG_INFO("[%s] Quantization Parameters:", codec->name);
|
||||
ffmpeg::tools::print_av_option_int(context, "init_qpI", " I-Frame", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "init_qpP", " P-Frame", "");
|
||||
ffmpeg::tools::print_av_option_int(context, "init_qpB", " B-Frame", "");
|
||||
|
||||
ratecontrolmode cfg_rc_mode = static_cast<ratecontrolmode>(obs_data_get_int(settings, ST_RATECONTROL_MODE));
|
||||
int64_t cfg_rc_2pass = obs_data_get_int(settings, ST_RATECONTROL_TWOPASS);
|
||||
int64_t cfg_rc_lahead = obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD);
|
||||
bool cfg_rc_adapti = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEI);
|
||||
bool cfg_rc_adaptb = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEB);
|
||||
ffmpeg::tools::print_av_option_int(context, "max_b_frames", " B-Frames", "Frames");
|
||||
ffmpeg::tools::print_av_option_string(context, "b_ref_mode", " Reference Mode", [](int64_t v) {
|
||||
b_ref_mode val = static_cast<b_ref_mode>(v);
|
||||
std::string name = "<Unknown>";
|
||||
auto index = b_ref_mode_to_opt.find(val);
|
||||
if (index != b_ref_mode_to_opt.end())
|
||||
name = index->second;
|
||||
return name;
|
||||
});
|
||||
|
||||
auto found2 = ratecontrolmode_to_opt.find(cfg_rc_mode);
|
||||
if (found2 != ratecontrolmode_to_opt.end())
|
||||
PLOG_INFO("[%s] Rate Control: %s", codec->name, found2->second.c_str());
|
||||
PLOG_INFO("[%s] Two Pass: %s", codec->name,
|
||||
cfg_rc_2pass == 1 ? "Enabled" : (cfg_rc_2pass == 0 ? "Disabled" : "Default"));
|
||||
PLOG_INFO("[%s] Lookahead: %" PRId64 " Frames", codec->name, cfg_rc_lahead);
|
||||
if (cfg_rc_adapti && cfg_rc_lahead > 0)
|
||||
PLOG_INFO("[%s] Adaptive I-Frames Enabled", codec->name);
|
||||
if (cfg_rc_adaptb && cfg_rc_lahead > 0)
|
||||
PLOG_INFO("[%s] Adaptive B-Frames Enabled", codec->name);
|
||||
|
||||
int64_t cfg_rc_bitrate = obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET);
|
||||
int64_t cfg_rc_max_bitrate = obs_data_get_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM);
|
||||
int64_t cfg_rc_bufsize = obs_data_get_int(settings, S_RATECONTROL_BUFFERSIZE);
|
||||
bool cfg_rc_quality = obs_data_get_bool(settings, ST_RATECONTROL_QUALITY);
|
||||
int64_t cfg_rc_quality_min = obs_data_get_int(settings, ST_RATECONTROL_QUALITY_MINIMUM);
|
||||
int64_t cfg_rc_quality_max = obs_data_get_int(settings, ST_RATECONTROL_QUALITY_MAXIMUM);
|
||||
double_t cfg_rc_quality_tgt = obs_data_get_double(settings, ST_RATECONTROL_QUALITY_TARGET) / 100.0 * 51.0;
|
||||
int64_t cfg_rc_qp_i = obs_data_get_int(settings, ST_RATECONTROL_QP_I);
|
||||
int64_t cfg_rc_qp_p = obs_data_get_int(settings, ST_RATECONTROL_QP_P);
|
||||
int64_t cfg_rc_qp_b = obs_data_get_int(settings, ST_RATECONTROL_QP_B);
|
||||
int64_t cfg_rc_qp_i_init = obs_data_get_int(settings, ST_RATECONTROL_QP_I_INITIAL);
|
||||
int64_t cfg_rc_qp_p_init = obs_data_get_int(settings, ST_RATECONTROL_QP_P_INITIAL);
|
||||
int64_t cfg_rc_qp_b_init = obs_data_get_int(settings, ST_RATECONTROL_QP_B_INITIAL);
|
||||
|
||||
{
|
||||
bool have_bitrate = false;
|
||||
bool have_bitrate_max = false;
|
||||
bool have_quality = false;
|
||||
bool have_qp = false;
|
||||
bool have_qp_init = false;
|
||||
|
||||
switch (cfg_rc_mode) {
|
||||
case ratecontrolmode::CQP:
|
||||
have_qp = true;
|
||||
break;
|
||||
case ratecontrolmode::CBR:
|
||||
case ratecontrolmode::CBR_HQ:
|
||||
case ratecontrolmode::CBR_LD_HQ:
|
||||
have_bitrate = true;
|
||||
av_opt_set_int(context->priv_data, "cbr", 1, 0);
|
||||
break;
|
||||
case ratecontrolmode::VBR:
|
||||
case ratecontrolmode::VBR_HQ:
|
||||
have_bitrate_max = true;
|
||||
have_bitrate = true;
|
||||
have_quality = true;
|
||||
have_qp_init = true;
|
||||
break;
|
||||
PLOG_INFO("[%s] Adaptive Quantization:", codec->name);
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0) {
|
||||
ffmpeg::tools::print_av_option_bool(context, "spatial-aq", " Spatial AQ");
|
||||
ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
||||
ffmpeg::tools::print_av_option_bool(context, "temporal-aq", " Temporal AQ");
|
||||
} else {
|
||||
ffmpeg::tools::print_av_option_bool(context, "spatial_aq", " Spatial AQ");
|
||||
ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
|
||||
ffmpeg::tools::print_av_option_bool(context, "temporal_aq", " Temporal AQ");
|
||||
}
|
||||
|
||||
PLOG_INFO("[%s] Buffer Size: %" PRId64, codec->name, cfg_rc_bufsize);
|
||||
if (have_bitrate)
|
||||
PLOG_INFO("[%s] Bitrate Target: %" PRId64, codec->name, cfg_rc_bitrate);
|
||||
if (have_bitrate_max)
|
||||
PLOG_INFO("[%s] Bitrate Maximum: %" PRId64, codec->name, cfg_rc_max_bitrate);
|
||||
if (have_quality && cfg_rc_quality) {
|
||||
PLOG_INFO("[%s] Quality Limits:", codec->name);
|
||||
PLOG_INFO("[%s] Minimum: %" PRId64, codec->name, cfg_rc_quality_min);
|
||||
PLOG_INFO("[%s] Maximum: %" PRId64, codec->name, cfg_rc_quality_max);
|
||||
PLOG_INFO("[%s] Target: %f", codec->name, cfg_rc_quality_tgt);
|
||||
}
|
||||
if (have_qp)
|
||||
PLOG_INFO("[%s] QP Values: %" PRId64 "/%" PRId64 "/%" PRId64, codec->name, cfg_rc_qp_i,
|
||||
cfg_rc_qp_p, cfg_rc_qp_b);
|
||||
if (have_qp_init)
|
||||
PLOG_INFO("[%s] Initial QP Values: %" PRId64 "/%" PRId64 "/%" PRId64, codec->name,
|
||||
cfg_rc_qp_i_init, cfg_rc_qp_p_init, cfg_rc_qp_b_init);
|
||||
}
|
||||
|
||||
bool cfg_aq_spatial = obs_data_get_bool(settings, ST_AQ_SPATIAL);
|
||||
int64_t cfg_aq_strength = obs_data_get_int(settings, ST_AQ_STRENGTH);
|
||||
bool cfg_aq_temporal = obs_data_get_bool(settings, ST_AQ_TEMPORAL);
|
||||
if (cfg_aq_spatial)
|
||||
PLOG_INFO("[%s] Spatial AQ Enabled: Strength %" PRId64, codec->name, cfg_aq_strength);
|
||||
if (cfg_aq_temporal)
|
||||
PLOG_INFO("[%s] Temporal AQ Enabled", codec->name);
|
||||
|
||||
int64_t cfg_bf = obs_data_get_int(settings, ST_OTHER_BFRAMES);
|
||||
b_ref_mode cfg_bf_mode = static_cast<b_ref_mode>(obs_data_get_int(settings, ST_OTHER_BFRAME_REFERENCEMODE));
|
||||
bool cfg_zerolatency = obs_data_get_bool(settings, ST_OTHER_ZEROLATENCY);
|
||||
bool cfg_weightp = obs_data_get_bool(settings, ST_OTHER_WEIGHTED_PREDICTION);
|
||||
bool cfg_nonrefp = obs_data_get_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES);
|
||||
|
||||
PLOG_INFO("[%s] B-Frames: %" PRId64, codec->name, cfg_bf);
|
||||
auto found3 = b_ref_mode_to_opt.find(cfg_bf_mode);
|
||||
if (found3 != b_ref_mode_to_opt.end())
|
||||
PLOG_INFO("[%s] Reference Mode: %s", codec->name, found3->second.c_str());
|
||||
if (cfg_zerolatency)
|
||||
PLOG_INFO("[%s] Zero Latency Enabled", codec->name);
|
||||
if (cfg_weightp)
|
||||
PLOG_INFO("[%s] Weighted Prediction Enabled", codec->name);
|
||||
if (cfg_nonrefp)
|
||||
PLOG_INFO("[%s] Non-Ref P-Frames Enabled", codec->name);
|
||||
PLOG_INFO("[%s] Other:", codec->name);
|
||||
ffmpeg::tools::print_av_option_bool(context, "zerolatency", " Zero Latency");
|
||||
ffmpeg::tools::print_av_option_bool(context, "weighted_pred", " Weighted Prediction");
|
||||
ffmpeg::tools::print_av_option_bool(context, "nonref_p", " Non-reference P-Frames");
|
||||
ffmpeg::tools::print_av_option_bool(context, "strict_gop", " Strict GOP");
|
||||
ffmpeg::tools::print_av_option_bool(context, "aud", " Access Unit Delimiters");
|
||||
ffmpeg::tools::print_av_option_bool(context, "bluray-compat", " Bluray Compatibility");
|
||||
if (strcmp(codec->name, "h264_nvenc") == 0)
|
||||
ffmpeg::tools::print_av_option_bool(context, "a53cc", " A53 Closed Captions");
|
||||
ffmpeg::tools::print_av_option_int(context, "dpb_size", " DPB Size", "");
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ extern "C" {
|
||||
}
|
||||
|
||||
namespace obsffmpeg {
|
||||
class encoder;
|
||||
|
||||
namespace nvenc {
|
||||
enum class preset : int64_t {
|
||||
DEFAULT,
|
||||
@@ -46,6 +48,8 @@ namespace obsffmpeg {
|
||||
LOW_LATENCY_HIGH_QUALITY,
|
||||
LOSSLESS,
|
||||
LOSSLESS_HIGH_PERFORMANCE,
|
||||
// Append things before this.
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
enum class ratecontrolmode : int64_t {
|
||||
@@ -55,12 +59,16 @@ namespace obsffmpeg {
|
||||
CBR,
|
||||
CBR_HQ,
|
||||
CBR_LD_HQ,
|
||||
// Append things before this.
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
enum class b_ref_mode : int64_t {
|
||||
DISABLED,
|
||||
EACH,
|
||||
MIDDLE,
|
||||
// Append things before this.
|
||||
INVALID = -1,
|
||||
};
|
||||
|
||||
extern std::map<preset, std::string> presets;
|
||||
@@ -75,8 +83,7 @@ namespace obsffmpeg {
|
||||
|
||||
extern std::map<b_ref_mode, std::string> b_ref_mode_to_opt;
|
||||
|
||||
void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context);
|
||||
void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
|
||||
|
||||
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace obsffmpeg {
|
||||
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
|
||||
const AVCodec* codec, AVCodecContext* context) override;
|
||||
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
|
||||
bool hw_encode) override;
|
||||
|
||||
virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
|
||||
AVCodecContext* context, bool hw_encode) override;
|
||||
@@ -49,7 +49,8 @@ namespace obsffmpeg {
|
||||
virtual void log_options(obs_data_t* settings, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
|
||||
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context) override;
|
||||
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec,
|
||||
AVCodecContext* context) override;
|
||||
};
|
||||
} // namespace ui
|
||||
} // namespace obsffmpeg
|
||||
|
||||
@@ -18,3 +18,16 @@
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "utility.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "plugin.hpp"
|
||||
|
||||
obs_property_t* obsffmpeg::obs_properties_add_tristate(obs_properties_t* props, const char* name, const char* desc)
|
||||
{
|
||||
obs_property_t* p = obs_properties_add_list(props, name, desc, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DEFAULT), -1);
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_DISABLED), 0);
|
||||
obs_property_list_add_int(p, TRANSLATE(S_STATE_ENABLED), 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -87,4 +87,29 @@ namespace obsffmpeg {
|
||||
{
|
||||
return obs_get_version() < MAKE_SEMANTIC_VERSION(24, 0, 0);
|
||||
}
|
||||
|
||||
struct obs_graphics {
|
||||
obs_graphics()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
}
|
||||
~obs_graphics()
|
||||
{
|
||||
obs_leave_graphics();
|
||||
}
|
||||
};
|
||||
|
||||
obs_property_t* obs_properties_add_tristate(obs_properties_t* props, const char* name, const char* desc);
|
||||
|
||||
inline bool is_tristate_enabled(int64_t tristate) {
|
||||
return tristate == 1;
|
||||
}
|
||||
|
||||
inline bool is_tristate_disabled(int64_t tristate) {
|
||||
return tristate == 0;
|
||||
}
|
||||
|
||||
inline bool is_tristate_default(int64_t tristate) {
|
||||
return tristate == -1;
|
||||
}
|
||||
} // namespace obsffmpeg
|
||||
|
||||
Reference in New Issue
Block a user