32 Commits

Author SHA1 Message Date
Michael Fabian 'Xaymar' Dirks 9f23ffc009 plugin: Don't add audio encoders
This is a temporary workaround until OBS supports selecting audio format for each stream.
2020-01-02 12:31:38 +01:00
Michael Fabian 'Xaymar' Dirks d21687e348 ui/prores_aw: Fix support for future FFmpeg versions
The encoder changes profile names in an update to FFmpeg which makes it completely incompatible with the current plugin.
2019-12-14 00:22:23 +01:00
Michael Fabian 'Xaymar' Dirks 0d574b81ff ui/nvenc: Export bitrate for OBS Studio to use
This should improve Replay Buffer and Low Latency Networking code, which may result in drastically better streaming with less dropped frames. I don't know why OBS Studio does it this way instead of just calculating the true kbit/sec bitrate by itself, but I guess this is just another case of "it works so it stayed in".
2019-11-17 12:21:02 +01:00
Michael Fabian 'Xaymar' Dirks 3a75b9f963 cmake: Version 0.4.0 2019-11-16 23:46:52 +01:00
Michael Fabian 'Xaymar' Dirks 815152d024 encoder: Actually fix parsing this time 2019-11-16 23:46:20 +01:00
Michael Fabian 'Xaymar' Dirks e4474c29c4 ui/nvenc: Improve and fix some options and logging
Some of the options shown to the user were never actually being applied, which resulted in encoding that looked worse than it could have. With this these options are now applied and users may experience a sudden increase or decrease in quality if they had these options changed.

In addition to that, some options now use a triple state field, which allows them to also have a default/middle state instead of being On or Off.

Furthermore, log output is now read from the actual AVCodecContext, which contains the actual values used in the encoding, instead of relying on the OBS settings to be the actual values.
2019-11-16 21:44:03 +01:00
Michael Fabian 'Xaymar' Dirks 2a75d5fabd ffmpeg/tools: Add functions for easy logging 2019-11-16 21:19:30 +01:00
Michael Fabian 'Xaymar' Dirks a0bfac9319 encoder: Fix crash because of missing string parameter 2019-11-16 21:19:17 +01:00
Michael Fabian 'Xaymar' Dirks 65dddb5aa8 utility: Add functions to check the state of tristates 2019-11-16 21:18:58 +01:00
Michael Fabian 'Xaymar' Dirks 59e33a132b ui/nvenc: Remove "Encoder" from name
It's already known that this is an encoder.
2019-11-16 13:04:10 +01:00
Michael Fabian 'Xaymar' Dirks be7b2737b4 encoder, ui/handler: Massive Refactor
This refactor is primarily to make the entire interface more unified and capable of more than before.
2019-11-15 17:29:24 +01:00
Michael Fabian 'Xaymar' Dirks 8f20a45ee8 hwapi/d3d11: Remove unused variables 2019-11-15 17:20:37 +01:00
Michael Fabian 'Xaymar' Dirks 300531ad8a ffmpeg/tools: Extra name functions 2019-11-15 13:31:27 +01:00
Michael Fabian 'Xaymar' Dirks c775ae93b7 utility: Add helper for tristate property 2019-11-15 01:12:32 +01:00
Michael Fabian 'Xaymar' Dirks 18c50b3cbb ui/handler: Refactoring 2019-11-13 11:44:40 +01:00
Michael Fabian 'Xaymar' Dirks 04a22a13dc encoder: Fix incorrect command line parsing code 2019-11-12 13:36:02 +01:00
Michael Fabian 'Xaymar' Dirks d8eb38037b encoder: Improve custom options parsing drastically
Splits the parsing into two steps, one for quotes and escaping, the other for actual option parsing. This drastically lowers the chances of messing up, and makes the entire thing much safer than a really complex monolithic loop.
2019-11-12 01:05:29 +01:00
Michael Fabian 'Xaymar' Dirks 81164d0f43 encoder: Cleanup and refactor for stability 2019-11-11 23:24:19 +01:00
Michael Fabian 'Xaymar' Dirks 05d8e74088 encoder: Use ffmpeg::tools::get_error_description 2019-11-11 21:15:56 +01:00
Michael Fabian 'Xaymar' Dirks d233b52e1f ffmpeg/tools: Rewrite ffmpeg::tools::get_error_description 2019-11-11 21:15:38 +01:00
UserNaem e89ef57bda locale: Additional and improved descriptions for options 2019-11-11 20:58:43 +01:00
Cristobal E 2759153b0e encoder: Show proper warning messages for custom options 2019-11-11 20:57:55 +01:00
Cristobal E 629afe3661 encoder: Add field for GPU selection 2019-11-11 20:57:53 +01:00
Michael Fabian 'Xaymar' Dirks 21629f1e13 cmake: Update libOBS to 24.0.3 2019-11-07 22:18:32 +01:00
Michael Fabian 'Xaymar' Dirks 80d104bdeb encoder: Use new graphics lock and create_from_obs method 2019-11-07 22:09:58 +01:00
Michael Fabian 'Xaymar' Dirks ffb348b6c8 hwapi/d3d11: Implement create_from_obs
Allows zero-copy with zero-overhead hardware encoders in OBS Studio.
2019-11-07 22:09:34 +01:00
Michael Fabian 'Xaymar' Dirks b67f51df76 hwapi/base: Create from OBS context 2019-11-07 22:08:46 +01:00
Michael Fabian 'Xaymar' Dirks 24d92e4b92 utility: Managed OBS Graphics Context 2019-11-07 22:08:21 +01:00
Michael Fabian 'Xaymar' Dirks 412c9fc18b locale: Fix description for Custom Settings 2019-11-04 18:40:06 +01:00
Michael Fabian 'Xaymar' Dirks 2184e5bc71 project: Reapply formatting 2019-11-04 18:32:47 +01:00
Michael Fabian 'Xaymar' Dirks ad761834ee encoder: Improve parsing of custom settings
Implements a parsing mechanism similar to that of an FFmpeg on the command line. Instead of separating options by ';' (key=value;key=value) the custom settings now take things as '-key=value -key=value -key="value with space"'.

A partial escaping of characters is also supported, however it does not modify the actual string. Proper support for escaping may land in a future update.

Fixes #5
Related #12
2019-11-04 18:32:36 +01:00
Michael Fabian 'Xaymar' Dirks a0b667c26f encoder: Rename all encoders to fix name collisions
This is a breaking change that will make all previous configuration no longer work.
2019-11-04 15:02:13 +01:00
24 changed files with 844 additions and 498 deletions
+3 -3
View File
@@ -25,8 +25,8 @@ Include("cmake/util.cmake")
# Automatic Versioning # Automatic Versioning
set(VERSION_MAJOR 0) set(VERSION_MAJOR 0)
set(VERSION_MINOR 3) set(VERSION_MINOR 4)
set(VERSION_PATCH 1) set(VERSION_PATCH 0)
set(VERSION_TWEAK 0) set(VERSION_TWEAK 0)
set(PROJECT_COMMIT "N/A") set(PROJECT_COMMIT "N/A")
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") 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) if(NOT TARGET libobs)
set(${PropertyPrefix}OBS_STUDIO_DIR "" CACHE PATH "OBS Studio Source/Package Directory") 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() endif()
if(NOT ${PropertyPrefix}OBS_NATIVE) if(NOT ${PropertyPrefix}OBS_NATIVE)
+26 -10
View File
@@ -8,7 +8,7 @@ State.Manual="Manual"
# FFmpeg # FFmpeg
FFmpeg="FFmpeg Options" FFmpeg="FFmpeg Options"
FFmpeg.CustomSettings="Custom Settings" 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="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.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" FFmpeg.ColorFormat="Override Color Format"
@@ -20,6 +20,9 @@ FFmpeg.StandardCompliance.Strict="Strict"
FFmpeg.StandardCompliance.Normal="Normal" FFmpeg.StandardCompliance.Normal="Normal"
FFmpeg.StandardCompliance.Unofficial="Unofficial" FFmpeg.StandardCompliance.Unofficial="Unofficial"
FFmpeg.StandardCompliance.Experimental="Experimental" 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 # Rate Control
RateControl="Rate Control" RateControl="Rate Control"
@@ -46,6 +49,8 @@ KeyFrames="Key Frames"
KeyFrames.IntervalType="Interval Type" KeyFrames.IntervalType="Interval Type"
KeyFrames.IntervalType.Frames="Frames" KeyFrames.IntervalType.Frames="Frames"
KeyFrames.IntervalType.Seconds="Seconds" KeyFrames.IntervalType.Seconds="Seconds"
KeyFrames.IntervalType.Description="Keyframe interval type"
KeyFrames.Interval.Description="Distance between key frames, in frames or seconds."
KeyFrames.Interval="Interval" KeyFrames.Interval="Interval"
# Codec: H264 # Codec: H264
@@ -55,7 +60,9 @@ Codec.H264.Profile.baseline="Baseline"
Codec.H264.Profile.main="Main" Codec.H264.Profile.main="Main"
Codec.H264.Profile.high="High" Codec.H264.Profile.high="High"
Codec.H264.Profile.high444p="High 4:4:4 Predictive" 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="Level"
Codec.H264.Level.Description="Level determines the upper limits of resolution, frame rate and bitrate for the video."
# Codec: HEVC # Codec: HEVC
Codec.HEVC="HEVC" Codec.HEVC="HEVC"
@@ -67,6 +74,7 @@ Codec.HEVC.Tier="Tier"
Codec.HEVC.Tier.main="Main" Codec.HEVC.Tier.main="Main"
Codec.HEVC.Tier.high="High" Codec.HEVC.Tier.high="High"
Codec.HEVC.Level="Level" 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: Apple ProRes
Codec.ProRes.Profile="Profile" Codec.ProRes.Profile="Profile"
@@ -79,6 +87,7 @@ Codec.ProRes.Profile.AP4X="4444 Extra Quality/XQ (AP4X)"
# NVENC # NVENC
NVENC.Preset="Preset" NVENC.Preset="Preset"
NVENC.Preset.Description="Presets are NVIDIA's preconfigured default settings."
NVENC.Preset.Default="Default" NVENC.Preset.Default="Default"
NVENC.Preset.Slow="Slow" NVENC.Preset.Slow="Slow"
NVENC.Preset.Medium="Medium" NVENC.Preset.Medium="Medium"
@@ -93,19 +102,26 @@ NVENC.Preset.Lossless="Lossless"
NVENC.Preset.LosslessHighPerformance="Lossless High Performance" NVENC.Preset.LosslessHighPerformance="Lossless High Performance"
NVENC.RateControl="Rate Control Options" NVENC.RateControl="Rate Control Options"
NVENC.RateControl.Mode="Mode" NVENC.RateControl.Mode="Mode"
NVENC.RateControl.Mode.Description="Rate control mode selection"
NVENC.RateControl.Mode.CQP="Constant Quantization Parameter" 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="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="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="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="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="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="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.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.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.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.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="Bitrate Limits"
NVENC.RateControl.Bitrate.Target="Target Bitrate" 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="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.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="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.Spatial.Description="Enable spatial adaptive quantization, also sometimes referred to as Psychovisual Adaptive Quantization."
NVENC.AQ.Strength="Spatial AQ Strength" 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.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.AQ.Temporal.Description="Enable temporal adaptive quantization."
NVENC.Other="Other Options" NVENC.Other="Other Options"
NVENC.Other.BFrames="Maximum B-Frames" 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="B-Frame Reference Mode"
NVENC.Other.BFrameReferenceMode.Each="Each B-Frame will be used for references" 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.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.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.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." NVENC.Other.NonReferencePFrames.Description="Enable the automatic insertion of non-reference P-Frames."
+2 -2
View File
@@ -197,8 +197,8 @@ void progress_parse(uint8_t*& ptr, uint8_t* end, size_t& sz)
sz = get_nal_size(ptr, end); sz = get_nal_size(ptr, end);
} }
void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data, void obsffmpeg::codecs::hevc::extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
std::vector<uint8_t>& header, std::vector<uint8_t>& sei) std::vector<uint8_t>& sei)
{ {
uint8_t* ptr = data; uint8_t* ptr = data;
uint8_t* end = data + sz_data; uint8_t* end = data + sz_data;
+2 -2
View File
@@ -61,8 +61,8 @@ namespace obsffmpeg {
UNKNOWN = -1, UNKNOWN = -1,
}; };
void extract_header_sei(uint8_t* data, size_t sz_data, void extract_header_sei(uint8_t* data, size_t sz_data, std::vector<uint8_t>& header,
std::vector<uint8_t>& header, std::vector<uint8_t>& sei); std::vector<uint8_t>& sei);
} // namespace hevc } // namespace hevc
} // namespace codecs } // namespace codecs
+293 -92
View File
@@ -23,6 +23,7 @@
#include <iomanip> #include <iomanip>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <stack>
#include <thread> #include <thread>
#include <util/profiler.hpp> #include <util/profiler.hpp>
#include <vector> #include <vector>
@@ -63,10 +64,12 @@ extern "C" {
#define ST_FFMPEG_THREADS "FFmpeg.Threads" #define ST_FFMPEG_THREADS "FFmpeg.Threads"
#define ST_FFMPEG_COLORFORMAT "FFmpeg.ColorFormat" #define ST_FFMPEG_COLORFORMAT "FFmpeg.ColorFormat"
#define ST_FFMPEG_STANDARDCOMPLIANCE "FFmpeg.StandardCompliance" #define ST_FFMPEG_STANDARDCOMPLIANCE "FFmpeg.StandardCompliance"
#define ST_FFMPEG_GPU "FFmpeg.GPU"
enum class keyframe_type { SECONDS, FRAMES }; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, encoder); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, encoder);
#endif #endif
@@ -79,7 +82,8 @@ static void* _create(obs_data_t* settings, obs_encoder_t* encoder) noexcept try
return nullptr; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, encoder); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, encoder);
#endif #endif
@@ -98,7 +102,8 @@ static void* _create_texture(obs_data_t* settings, obs_encoder_t* encoder) noexc
return nullptr; return nullptr;
} }
static void _destroy(void* ptr) noexcept try { static void _destroy(void* ptr) noexcept
try {
#ifdef DEBUG_CALL_ORDER #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX", __FUNCTION_NAME__, ptr); PLOG_INFO("%s %llX", __FUNCTION_NAME__, ptr);
#endif #endif
@@ -110,7 +115,8 @@ static void _destroy(void* ptr) noexcept try {
PLOG_ERROR("Unexpected exception in function '%s'.", __FUNCTION_NAME__); 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX", __FUNCTION_NAME__, type_data); PLOG_INFO("%s %llX", __FUNCTION_NAME__, type_data);
#endif #endif
@@ -123,7 +129,8 @@ static const char* _get_name(void* type_data) noexcept try {
return nullptr; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX", __FUNCTION_NAME__, type_data); PLOG_INFO("%s %llX", __FUNCTION_NAME__, type_data);
#endif #endif
@@ -136,7 +143,8 @@ static const char* _get_name_fallback(void* type_data) noexcept try {
return nullptr; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data);
#endif #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__); 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, settings, type_data);
#endif #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__); 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data);
#endif #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); 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, type_data);
#endif #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); 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, settings); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, settings);
#endif #endif
@@ -211,7 +223,8 @@ static bool _update(void* ptr, obs_data_t* settings) noexcept try {
return false; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX %llX", __FUNCTION_NAME__, ptr, sei_data, size); PLOG_INFO("%s %llX %llX %llX", __FUNCTION_NAME__, ptr, sei_data, size);
#endif #endif
@@ -224,7 +237,8 @@ static bool _get_sei_data(void* ptr, uint8_t** sei_data, size_t* size) noexcept
return false; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX %llX", __FUNCTION_NAME__, ptr, extra_data, size); PLOG_INFO("%s %llX %llX %llX", __FUNCTION_NAME__, ptr, extra_data, size);
#endif #endif
@@ -237,7 +251,8 @@ static bool _get_extra_data(void* ptr, uint8_t** extra_data, size_t* size) noexc
return false; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, info); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, info);
#endif #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, 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX %llX %llX", __FUNCTION_NAME__, ptr, frame, packet, received_packet); PLOG_INFO("%s %llX %llX %llX %llX", __FUNCTION_NAME__, ptr, frame, packet, received_packet);
#endif #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, 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %lI %llI %llU %llX %llX %llX", __FUNCTION_NAME__, ptr, handle, pts, lock_key, next_key, packet, PLOG_INFO("%s %lI %llI %llU %llX %llX %llX", __FUNCTION_NAME__, ptr, handle, pts, lock_key, next_key, packet,
received_packet); received_packet);
@@ -278,7 +295,8 @@ static bool _encode_texture(void* ptr, uint32_t handle, int64_t pts, uint64_t lo
return false; 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, info); PLOG_INFO("%s %llX %llX", __FUNCTION_NAME__, ptr, info);
#endif #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__); 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX", __FUNCTION_NAME__, ptr); PLOG_INFO("%s %llX", __FUNCTION_NAME__, ptr);
#endif #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, 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 #ifdef DEBUG_CALL_ORDER
PLOG_INFO("%s %llX %llX %llX %llX", __FUNCTION_NAME__, ptr, frame, packet, received_packet); PLOG_INFO("%s %llX %llX %llX %llX", __FUNCTION_NAME__, ptr, frame, packet, received_packet);
#endif #endif
@@ -322,7 +342,7 @@ obsffmpeg::encoder_factory::encoder_factory(const AVCodec* codec) : avcodec_ptr(
_handler = obsffmpeg::find_codec_handler(avcodec_ptr->name); _handler = obsffmpeg::find_codec_handler(avcodec_ptr->name);
// Unique Id is FFmpeg 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. // 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 << ")"; sstr << " (" << avcodec_ptr->name << ")";
} }
info.readable_name = sstr.str(); 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. // Assign Ids.
@@ -433,17 +449,22 @@ void obsffmpeg::encoder_factory::register_encoder()
info_fallback.oei.encode = _encode; info_fallback.oei.encode = _encode;
info_fallback.oei.type_data = this; info_fallback.oei.type_data = this;
obs_register_encoder(&info_fallback.oei);
PLOG_DEBUG("Registered software fallback for encoder #%llX", avcodec_ptr);
} else { } else {
// Is not a GPU Encoder, don't implement fallback. // Is not a GPU Encoder, don't implement fallback.
info.oei.create = _create; info.oei.create = _create;
info.oei.encode = _encode; info.oei.encode = _encode;
} }
if (_handler)
_handler->adjust_encoder_info(this, &info, &info_fallback);
obs_register_encoder(&info.oei); obs_register_encoder(&info.oei);
PLOG_DEBUG("Registered encoder #%llX with name '%s' and long name '%s' and caps %llX", avcodec_ptr, 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); 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) 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, obs_data_set_default_int(settings, ST_FFMPEG_COLORFORMAT,
static_cast<int64_t>(AV_PIX_FMT_NONE)); 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_THREADS, 0);
obs_data_set_default_int(settings, ST_FFMPEG_GPU, 0);
} }
obs_data_set_default_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE, FF_COMPLIANCE_STRICT); 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; 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_FRAMES), !is_seconds);
obs_property_set_visible(obs_properties_get(props, S_KEYFRAMES_INTERVAL_SECONDS), 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))); obs_property_set_long_description(p, TRANSLATE(DESC(ST_FFMPEG_CUSTOMSETTINGS)));
} }
if (!hw_encode) { 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) { if (avcodec_ptr->pix_fmts) {
auto p = obs_properties_add_list(grp, ST_FFMPEG_COLORFORMAT, auto p = obs_properties_add_list(grp, ST_FFMPEG_COLORFORMAT,
TRANSLATE(ST_FFMPEG_COLORFORMAT), OBS_COMBO_TYPE_LIST, 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) 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. // Find a handler
_factory = reinterpret_cast<encoder_factory*>(obs_encoder_get_type_data(_self));
_codec = _factory->get_avcodec();
_handler = obsffmpeg::find_codec_handler(_codec->name); _handler = obsffmpeg::find_codec_handler(_codec->name);
// Initialize GPU Stuff
if (is_texture_encode) { if (is_texture_encode) {
#ifdef WIN32 #ifdef WIN32
auto gctx = obsffmpeg::obs_graphics();
if (gs_get_device_type() == GS_DEVICE_DIRECT3D_11) {
_hwapi = std::make_shared<obsffmpeg::hwapi::d3d11>(); _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. // 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_init_packet(&_current_packet);
av_new_packet(&_current_packet, 8 * 1024 * 1024); // 8 MB precached Packet size. av_new_packet(&_current_packet, 8 * 1024 * 1024); // 8 MB precached Packet size.
if (!is_texture_encode) { if (is_texture_encode) {
initialize_sw(settings);
} else {
try {
initialize_hw(settings); 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 { } else {
PLOG_INFO("[%s] Video Input: %ldx%ld %s %s %s", _codec->name, _swscale.get_source_width(), initialize_sw(settings);
_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");
} }
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
update(settings); update(settings);
// Initialize Encoder // Initialize Encoder
auto gctx = obsffmpeg::obs_graphics();
int res = avcodec_open2(_context, _codec, NULL); int res = avcodec_open2(_context, _codec, NULL);
if (res < 0) { if (res < 0) {
std::stringstream sstr; std::stringstream sstr;
@@ -829,6 +824,7 @@ obsffmpeg::encoder::encoder(obs_data_t* settings, obs_encoder_t* encoder, bool i
obsffmpeg::encoder::~encoder() obsffmpeg::encoder::~encoder()
{ {
auto gctx = obsffmpeg::obs_graphics();
if (_context) { if (_context) {
// Flush encoders that require it. // Flush encoders that require it.
if ((_codec->capabilities & AV_CODEC_CAP_DELAY) != 0) { 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_COLORFORMAT), false);
obs_property_set_enabled(obs_properties_get(props, ST_FFMPEG_THREADS), 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_STANDARDCOMPLIANCE), false);
obs_property_set_enabled(obs_properties_get(props, ST_FFMPEG_GPU), false);
} }
bool obsffmpeg::encoder::update(obs_data_t* settings) bool obsffmpeg::encoder::update(obs_data_t* settings)
{ {
// Settings // FFmpeg Options
/// Rate Control
_context->strict_std_compliance = static_cast<int>(obs_data_get_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE));
_context->debug = 0; _context->debug = 0;
_context->strict_std_compliance = static_cast<int>(obs_data_get_int(settings, ST_FFMPEG_STANDARDCOMPLIANCE));
/// Threading /// Threading
if (_codec->capabilities & (AV_CODEC_CAP_AUTO_THREADS | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS) if (!_hwinst) {
&& !_hwinst) { _context->thread_type = 0;
if (_codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) { if (_codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
_context->thread_type |= FF_THREAD_FRAME; _context->thread_type |= FF_THREAD_FRAME;
} }
if (_codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) { if (_codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
_context->thread_type |= FF_THREAD_SLICE; _context->thread_type |= FF_THREAD_SLICE;
} }
if (_context->thread_type != 0) {
int64_t threads = obs_data_get_int(settings, ST_FFMPEG_THREADS); int64_t threads = obs_data_get_int(settings, ST_FFMPEG_THREADS);
if (threads > 0) { if (threads > 0) {
_context->thread_count = static_cast<int>(threads); _context->thread_count = static_cast<int>(threads);
_lag_in_frames = _context->thread_count;
} else { } else {
_context->thread_count = std::thread::hardware_concurrency(); _context->thread_count = std::thread::hardware_concurrency();
_lag_in_frames = _context->thread_count;
} }
} else { } else {
_context->thread_count = 1; _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) // Apply GPU Selection
_handler->update(settings, _codec, _context); 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 // Key-Frame Options
obs_video_info ovi; obs_video_info ovi;
if (!obs_get_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); 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; _context->keyint_min = _context->gop_size;
} }
{ // FFmpeg // Handler Options
// Apply custom options. if (_handler)
av_opt_set_from_string(_context->priv_data, obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS), _handler->update(settings, _codec, _context);
nullptr, "=", ";");
{ // 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) if (_handler)
_handler->override_lag_in_frames(_lag_in_frames, settings, _codec, _context); _handler->override_update(this, settings);
// Handler Logging // 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); _handler->log_options(settings, _codec, _context);
}
return true; 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 obsffmpeg::encoder::receive_packet(bool* received_packet, struct encoder_packet* packet)
{ {
int res = 0;
av_packet_unref(&_current_packet); 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) { if (res != 0) {
return res; 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 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) { if (res == 0) {
push_used_frame(frame); push_used_frame(frame);
} }
@@ -1226,3 +1287,143 @@ bool obsffmpeg::encoder::encode_avframe(std::shared_ptr<AVFrame> frame, encoder_
return true; 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())
}
}
}
+9
View File
@@ -151,5 +151,14 @@ namespace obsffmpeg {
bool encode_avframe(std::shared_ptr<AVFrame> frame, struct encoder_packet* packet, bool encode_avframe(std::shared_ptr<AVFrame> frame, struct encoder_packet* packet,
bool* received_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 } // namespace obsffmpeg
+85 -45
View File
@@ -24,12 +24,15 @@
#include <map> #include <map>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "plugin.hpp"
#include "utility.hpp"
extern "C" { extern "C" {
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4244) #pragma warning(disable : 4244)
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavutil/error.h> #include <libavutil/error.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
#pragma warning(pop) #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) const char* ffmpeg::tools::get_error_description(int error)
{ {
switch (error) { thread_local char error_buf[AV_ERROR_MAX_STRING_SIZE + 1];
case AVERROR(EPERM): if (av_strerror(error, error_buf, AV_ERROR_MAX_STRING_SIZE) < 0) {
return "Permission Denied"; snprintf(error_buf, AV_ERROR_MAX_STRING_SIZE, "Unknown Error (%i)", error);
// 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):
} }
return "Not Translated Yet"; return error_buf;
} }
static std::map<video_format, AVPixelFormat> obs_to_av_format_map = { 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 // Downscaling should result in downscaling, not pixelation
context->chroma_sample_location = AVCHROMA_LOC_CENTER; 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());
}
}
+14
View File
@@ -23,6 +23,7 @@
#define OBS_FFMPEG_FFMPEG_UTILITY #define OBS_FFMPEG_FFMPEG_UTILITY
#pragma once #pragma once
#include <functional>
#include <obs.h> #include <obs.h>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -57,6 +58,19 @@ namespace ffmpeg {
std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list); std::vector<AVPixelFormat> get_software_formats(const AVPixelFormat* list);
void setup_obs_color(video_colorspace colorspace, video_range_type range, AVCodecContext* context); 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 tools
} // namespace ffmpeg } // namespace ffmpeg
+2
View File
@@ -49,6 +49,8 @@ namespace obsffmpeg {
virtual std::list<obsffmpeg::hwapi::device> enumerate_adapters() = 0; 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(obsffmpeg::hwapi::device target) = 0;
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() = 0;
}; };
class instance { class instance {
+29 -1
View File
@@ -22,11 +22,14 @@
#include "d3d11.hpp" #include "d3d11.hpp"
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include "utility.hpp"
extern "C" { extern "C" {
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4244) #pragma warning(disable : 4244)
#include <graphics/graphics.h>
#include <libavutil/hwcontext_d3d11va.h> #include <libavutil/hwcontext_d3d11va.h>
#include <obs.h>
#pragma warning(pop) #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); 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 { struct D3D11AVFrame {
ATL::CComPtr<ID3D11Texture2D> handle; ATL::CComPtr<ID3D11Texture2D> handle;
}; };
@@ -150,8 +169,11 @@ AVBufferRef* obsffmpeg::hwapi::d3d11_instance::create_device_context()
d3d11va->device->AddRef(); d3d11va->device->AddRef();
d3d11va->device_context = _context; d3d11va->device_context = _context;
d3d11va->device_context->AddRef(); 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."); throw std::runtime_error("Failed to initialize AVHWDeviceContext.");
return dctx_ref; 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) 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) { auto frame = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame* frame) {
av_frame_unref(frame); av_frame_unref(frame);
av_frame_free(&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, 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) uint64_t* next_lock_key, std::shared_ptr<AVFrame> frame)
{ {
auto gctx = obsffmpeg::obs_graphics();
ATL::CComPtr<IDXGIKeyedMutex> mutex; ATL::CComPtr<IDXGIKeyedMutex> mutex;
ATL::CComPtr<ID3D11Texture2D> input; 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, 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) uint64_t lock_key, uint64_t* next_lock_key)
{ {
auto gctx = obsffmpeg::obs_graphics();
auto frame = this->allocate_frame(frames); auto frame = this->allocate_frame(frames);
this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame); this->copy_from_obs(frames, handle, lock_key, next_lock_key, frame);
return frame; return frame;
+2
View File
@@ -53,6 +53,8 @@ namespace obsffmpeg {
virtual std::shared_ptr<obsffmpeg::hwapi::instance> virtual std::shared_ptr<obsffmpeg::hwapi::instance>
create(obsffmpeg::hwapi::device target) override; create(obsffmpeg::hwapi::device target) override;
virtual std::shared_ptr<obsffmpeg::hwapi::instance> create_from_obs() override;
}; };
class d3d11_instance : public ::obsffmpeg::hwapi::instance { class d3d11_instance : public ::obsffmpeg::hwapi::instance {
+2 -2
View File
@@ -106,8 +106,8 @@ try {
if (!av_codec_is_encoder(cdc)) if (!av_codec_is_encoder(cdc))
continue; continue;
if ((cdc->type == AVMediaType::AVMEDIA_TYPE_AUDIO) if (/*(cdc->type == AVMediaType::AVMEDIA_TYPE_AUDIO)
|| (cdc->type == AVMediaType::AVMEDIA_TYPE_VIDEO)) { || */(cdc->type == AVMediaType::AVMEDIA_TYPE_VIDEO)) {
auto ptr = std::make_shared<obsffmpeg::encoder_factory>(cdc); auto ptr = std::make_shared<obsffmpeg::encoder_factory>(cdc);
ptr->register_encoder(); ptr->register_encoder();
generic_factories.emplace(cdc, ptr); generic_factories.emplace(cdc, ptr);
+11 -18
View File
@@ -20,34 +20,27 @@
// SOFTWARE. // SOFTWARE.
#include "handler.hpp" #include "handler.hpp"
#include "encoder.hpp"
void obsffmpeg::ui::handler::override_visible_name(const AVCodec*, std::string&) {} void obsffmpeg::ui::handler::adjust_encoder_info(obsffmpeg::encoder_factory*, obsffmpeg::encoder_info*,
obsffmpeg::encoder_info*)
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::get_defaults(obs_data_t*, const AVCodec*, AVCodecContext*, bool) {} 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) {} bool obsffmpeg::ui::handler::has_keyframe_support(obsffmpeg::encoder* instance)
obsffmpeg::hwapi::device obsffmpeg::ui::handler::find_hw_device(std::shared_ptr<obsffmpeg::hwapi::base>, const AVCodec*,
AVCodecContext*)
{ {
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::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::log_options(obs_data_t*, const AVCodec*, AVCodecContext*) {}
void obsffmpeg::ui::handler::import_from_ffmpeg(const std::string, obs_data_t*, const AVCodec*, AVCodecContext*) {} void obsffmpeg::ui::handler::override_colorformat(AVPixelFormat&, 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::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {} void obsffmpeg::ui::handler::process_avpacket(AVPacket&, const AVCodec*, AVCodecContext*) {}
+17 -17
View File
@@ -37,36 +37,36 @@ extern "C" {
} }
namespace obsffmpeg { namespace obsffmpeg {
struct encoder_info;
class encoder_factory;
class encoder;
namespace ui { namespace ui {
class handler { class handler {
public: public /*factory*/:
virtual void override_visible_name(const AVCodec* codec, std::string& name); 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, public /*settings*/:
const AVCodec* codec, AVCodecContext* context); virtual bool has_keyframe_support(obsffmpeg::encoder* instance);
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);
virtual void get_properties(obs_properties_t* props, const AVCodec* codec, virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
AVCodecContext* context, bool hw_encode); 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 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 log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
virtual void import_from_ffmpeg(const std::string ffmpeg, obs_data_t* settings, public /*instance*/:
const AVCodec* codec, AVCodecContext* context);
virtual std::string export_for_ffmpeg(obs_data_t* settings, const AVCodec* codec, virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
AVCodecContext* context); const AVCodec* codec, AVCodecContext* context);
virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context); virtual void process_avpacket(AVPacket& packet, const AVCodec* codec, AVCodecContext* context);
}; };
+31 -41
View File
@@ -21,6 +21,8 @@
#include "nvenc_h264_handler.hpp" #include "nvenc_h264_handler.hpp"
#include "codecs/h264.hpp" #include "codecs/h264.hpp"
#include "encoder.hpp"
#include "ffmpeg/tools.hpp"
#include "nvenc_shared.hpp" #include "nvenc_shared.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "strings.hpp" #include "strings.hpp"
@@ -34,30 +36,7 @@ extern "C" {
#pragma warning(pop) #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::codecs::h264;
using namespace obsffmpeg::nvenc;
std::map<profile, std::string> profiles{ std::map<profile, std::string> profiles{
{profile::BASELINE, "baseline"}, {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)"; main->readable_name = "H.264/AVC NVidia NVENC (Hardware)";
} fallback->readable_name = "H.264/AVC NVidia NVENC (Software)";
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);
} }
void obsffmpeg::ui::nvenc_h264_handler::get_defaults(obs_data_t* settings, const AVCodec* codec, 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)); 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, void obsffmpeg::ui::nvenc_h264_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
AVCodecContext* context, bool) 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) void obsffmpeg::ui::nvenc_h264_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{ {
nvenc::log_options(settings, codec, context); nvenc::log_options(settings, codec, context);
profile cfg_profile = static_cast<profile>(obs_data_get_int(settings, P_H264_PROFILE)); PLOG_INFO("[%s] H.265/HEVC:", codec->name);
level cfg_level = static_cast<level>(obs_data_get_int(settings, P_H264_LEVEL)); ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
profile val = static_cast<profile>(v);
auto found1 = profiles.find(cfg_profile); auto index = profiles.find(val);
if (found1 != profiles.end()) if (index != profiles.end())
PLOG_INFO("[%s] H.264 Profile: %s", codec->name, found1->second.c_str()); return index->second;
return std::string("<Unknown>");
auto found2 = levels.find(cfg_level); });
if (found2 != levels.end()) ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
PLOG_INFO("[%s] H.264 Level: %s", codec->name, found2->second.c_str()); 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) void obsffmpeg::ui::nvenc_h264_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
+16 -11
View File
@@ -33,23 +33,28 @@ extern "C" {
namespace obsffmpeg { namespace obsffmpeg {
namespace ui { namespace ui {
class nvenc_h264_handler : public handler { class nvenc_h264_handler : public handler {
public: public /*factory*/:
virtual void override_visible_name(const AVCodec* codec, std::string& name) override; virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
obsffmpeg::encoder_info* main,
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec, obsffmpeg::encoder_info* fallback);
AVCodecContext* context) override;
virtual void get_defaults(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) 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, 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, virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
AVCodecContext* context) override;
virtual void log_options(obs_data_t* settings, const AVCodec* codec, virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
AVCodecContext* context) override;
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: private:
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec); void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
+38 -45
View File
@@ -21,6 +21,8 @@
#include "nvenc_hevc_handler.hpp" #include "nvenc_hevc_handler.hpp"
#include "codecs/hevc.hpp" #include "codecs/hevc.hpp"
#include "encoder.hpp"
#include "ffmpeg/tools.hpp"
#include "nvenc_shared.hpp" #include "nvenc_shared.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "strings.hpp" #include "strings.hpp"
@@ -34,28 +36,6 @@ extern "C" {
#pragma warning(pop) #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; using namespace obsffmpeg::codecs::hevc;
std::map<profile, std::string> profiles{ 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)"; main->readable_name = "H.265/HEVC Nvidia NVENC (Hardware)";
} fallback->readable_name = "H.265/HEVC Nvidia NVENC (Software)";
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);
} }
void obsffmpeg::ui::nvenc_hevc_handler::get_defaults(obs_data_t* settings, const AVCodec* codec, 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)); 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, void obsffmpeg::ui::nvenc_hevc_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
AVCodecContext* context, bool) 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) void obsffmpeg::ui::nvenc_hevc_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{ {
nvenc::log_options(settings, codec, context); nvenc::log_options(settings, codec, context);
profile cfg_profile = static_cast<profile>(obs_data_get_int(settings, P_HEVC_PROFILE)); PLOG_INFO("[%s] H.265/HEVC:", codec->name);
tier cfg_tier = static_cast<tier>(obs_data_get_int(settings, P_HEVC_TIER)); ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [](int64_t v) {
level cfg_level = static_cast<level>(obs_data_get_int(settings, P_HEVC_LEVEL)); profile val = static_cast<profile>(v);
auto index = profiles.find(val);
auto found1 = profiles.find(cfg_profile); if (index != profiles.end())
if (found1 != profiles.end()) return index->second;
PLOG_INFO("[%s] H.265 Profile: %s", codec->name, found1->second.c_str()); return std::string("<Unknown>");
});
auto found2 = levels.find(cfg_level); ffmpeg::tools::print_av_option_string(context, "level", " Level", [](int64_t v) {
if (found2 != levels.end()) level val = static_cast<level>(v);
PLOG_INFO("[%s] H.265 Level: %s", codec->name, found2->second.c_str()); auto index = levels.find(val);
if (index != levels.end())
auto found3 = tiers.find(cfg_tier); return index->second;
if (found3 != tiers.end()) return std::string("<Unknown>");
PLOG_INFO("[%s] H.265 Tier: %s", codec->name, found3->second.c_str()); });
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) void obsffmpeg::ui::nvenc_hevc_handler::get_encoder_properties(obs_properties_t* props, const AVCodec* codec)
+16 -11
View File
@@ -33,23 +33,28 @@ extern "C" {
namespace obsffmpeg { namespace obsffmpeg {
namespace ui { namespace ui {
class nvenc_hevc_handler : public handler { class nvenc_hevc_handler : public handler {
public: public /*factory*/:
virtual void override_visible_name(const AVCodec* codec, std::string& name) override; virtual void adjust_encoder_info(obsffmpeg::encoder_factory* factory,
obsffmpeg::encoder_info* main,
virtual void override_lag_in_frames(size_t& lag, obs_data_t* settings, const AVCodec* codec, obsffmpeg::encoder_info* fallback);
AVCodecContext* context) override;
virtual void get_defaults(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) 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, 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, virtual void update(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
AVCodecContext* context) override;
virtual void log_options(obs_data_t* settings, const AVCodec* codec, virtual void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
AVCodecContext* context) override;
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: private:
void get_encoder_properties(obs_properties_t* props, const AVCodec* codec); void get_encoder_properties(obs_properties_t* props, const AVCodec* codec);
+152 -157
View File
@@ -22,6 +22,8 @@
#include "nvenc_shared.hpp" #include "nvenc_shared.hpp"
#include <algorithm> #include <algorithm>
#include "codecs/hevc.hpp" #include "codecs/hevc.hpp"
#include "encoder.hpp"
#include "ffmpeg/tools.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "strings.hpp" #include "strings.hpp"
#include "utility.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"}, {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 AVCodecContext* context = const_cast<AVCodecContext*>(instance->get_avcodeccontext());
// 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.
int64_t rcla = 0; int64_t rclookahead = 0;
av_opt_get_int(context, "rc-lookahead", AV_OPT_SEARCH_CHILDREN, &rcla); 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*) 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_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_TWOPASS, -1);
obs_data_set_default_int(settings, ST_RATECONTROL_LOOKAHEAD, 0); obs_data_set_default_int(settings, ST_RATECONTROL_LOOKAHEAD, 0);
obs_data_set_default_bool(settings, ST_RATECONTROL_ADAPTIVEI, true); obs_data_set_default_int(settings, ST_RATECONTROL_ADAPTIVEI, -1);
obs_data_set_default_bool(settings, ST_RATECONTROL_ADAPTIVEB, true); 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_TARGET, 6000);
obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM, 6000); obs_data_set_default_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM, 6000);
@@ -170,15 +189,18 @@ 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, 21);
obs_data_set_default_int(settings, ST_RATECONTROL_QP_B_INITIAL, -1); 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_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_BFRAMES, 2);
obs_data_set_default_int(settings, ST_OTHER_BFRAME_REFERENCEMODE, static_cast<int64_t>(b_ref_mode::DISABLED)); 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_int(settings, ST_OTHER_ZEROLATENCY, -1);
obs_data_set_default_bool(settings, ST_OTHER_WEIGHTED_PREDICTION, false); obs_data_set_default_int(settings, ST_OTHER_WEIGHTED_PREDICTION, -1);
obs_data_set_default_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES, false); obs_data_set_default_int(settings, ST_OTHER_NONREFERENCE_PFRAMES, -1);
// Replay Buffer
obs_data_set_default_int(settings, "bitrate", 0);
} }
static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings) static bool modified_ratecontrol(obs_properties_t* props, obs_property_t*, obs_data_t* settings)
@@ -241,7 +263,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) 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); obs_property_set_visible(obs_properties_get(props, ST_AQ_STRENGTH), spatial_aq);
return true; return true;
} }
@@ -280,12 +302,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), auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_TWOPASS,
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); TRANSLATE(ST_RATECONTROL_TWOPASS));
obs_property_set_long_description(p, TRANSLATE(DESC(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 +314,13 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
obs_property_int_set_suffix(p, " frames"); obs_property_int_set_suffix(p, " frames");
} }
{ {
auto p = auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEI,
obs_properties_add_bool(grp, ST_RATECONTROL_ADAPTIVEI, TRANSLATE(ST_RATECONTROL_ADAPTIVEI)); TRANSLATE(ST_RATECONTROL_ADAPTIVEI));
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEI))); obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEI)));
} }
if (strcmp(codec->name, "h264_nvenc") == 0) { if (strcmp(codec->name, "h264_nvenc") == 0) {
auto p = auto p = obsffmpeg::obs_properties_add_tristate(grp, ST_RATECONTROL_ADAPTIVEB,
obs_properties_add_bool(grp, ST_RATECONTROL_ADAPTIVEB, TRANSLATE(ST_RATECONTROL_ADAPTIVEB)); TRANSLATE(ST_RATECONTROL_ADAPTIVEB));
obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEB))); obs_property_set_long_description(p, TRANSLATE(DESC(ST_RATECONTROL_ADAPTIVEB)));
} }
} }
@@ -417,7 +436,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_long_description(p, TRANSLATE(DESC(ST_AQ_SPATIAL)));
obs_property_set_modified_callback(p, modified_aq); obs_property_set_modified_callback(p, modified_aq);
} }
@@ -427,7 +446,7 @@ void obsffmpeg::nvenc::get_properties_post(obs_properties_t* props, const AVCode
obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_STRENGTH))); 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))); obs_property_set_long_description(p, TRANSLATE(DESC(ST_AQ_TEMPORAL)));
} }
} }
@@ -458,18 +477,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))); 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)); TRANSLATE(ST_OTHER_WEIGHTED_PREDICTION));
obs_property_set_long_description(p, TRANSLATE(DESC(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)); TRANSLATE(ST_OTHER_NONREFERENCE_PFRAMES));
obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_NONREFERENCE_PFRAMES))); obs_property_set_long_description(p, TRANSLATE(DESC(ST_OTHER_NONREFERENCE_PFRAMES)));
} }
@@ -537,6 +557,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(context->priv_data, "rc", rcopt->second.c_str(), 0);
} }
av_opt_set_int(context->priv_data, "cbr", 0, 0);
switch (rc) { switch (rc) {
case ratecontrolmode::CQP: case ratecontrolmode::CQP:
have_qp = true; have_qp = true;
@@ -562,20 +583,28 @@ 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)); 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) { if (la > 0) {
bool adapt_i = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEI); int64_t adapt_i = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEI);
av_opt_set_int(context->priv_data, "no-scenecut", !adapt_i ? 1 : 0, 0); 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")) { if (strcmp(codec->name, "h264_nvenc")) {
bool adapt_b = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEB); int64_t adapt_b = obs_data_get_int(settings, ST_RATECONTROL_ADAPTIVEB);
av_opt_set_int(context->priv_data, "b_adapt", adapt_b ? 1 : 0, 0); if (!is_tristate_default(adapt_b)) {
av_opt_set_int(context->priv_data, "b_adapt", adapt_b, AV_OPT_SEARCH_CHILDREN);
}
} }
} }
if (have_bitrate) if (have_bitrate) {
context->bit_rate = context->bit_rate =
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET) * 1000); static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET) * 1000);
// Support for Replay Buffer
obs_data_set_int(settings, "bitrate",
obs_data_get_int(settings, ST_RATECONTROL_BITRATE_TARGET));
}
if (have_bitrate_max) if (have_bitrate_max)
context->rc_max_rate = context->rc_max_rate =
static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM) * 1000); static_cast<int>(obs_data_get_int(settings, ST_RATECONTROL_BITRATE_MAXIMUM) * 1000);
@@ -618,37 +647,42 @@ void obsffmpeg::nvenc::update(obs_data_t* settings, const AVCodec* codec, AVCode
} }
{ // AQ { // AQ
bool saq = obs_data_get_bool(settings, ST_AQ_SPATIAL); int64_t saq = obs_data_get_int(settings, ST_AQ_SPATIAL);
bool taq = obs_data_get_bool(settings, ST_AQ_TEMPORAL); int64_t taq = obs_data_get_int(settings, ST_AQ_TEMPORAL);
if (strcmp(codec->name, "h264_nvenc")) { if (strcmp(codec->name, "h264_nvenc") == 0) {
av_opt_set_int(context->priv_data, "spatial-aq", saq ? 1 : 0, 0); if (!is_tristate_default(saq))
av_opt_set_int(context->priv_data, "temporal-aq", taq ? 1 : 0, 0); 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 { } else {
av_opt_set_int(context->priv_data, "spatial_aq", saq ? 1 : 0, 0); if (!is_tristate_default(saq))
av_opt_set_int(context->priv_data, "temporal_aq", taq ? 1 : 0, 0); 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", av_opt_set_int(context->priv_data, "aq-strength",
static_cast<int>(obs_data_get_int(settings, ST_AQ_STRENGTH)), 0); static_cast<int>(obs_data_get_int(settings, ST_AQ_STRENGTH)), 0);
} }
}
{ // Other { // Other
bool zl = obs_data_get_bool(settings, ST_OTHER_ZEROLATENCY); int64_t zl = obs_data_get_int(settings, ST_OTHER_ZEROLATENCY);
bool wp = obs_data_get_bool(settings, ST_OTHER_WEIGHTED_PREDICTION); int64_t wp = obs_data_get_int(settings, ST_OTHER_WEIGHTED_PREDICTION);
bool nrp = obs_data_get_bool(settings, ST_OTHER_NONREFERENCE_PFRAMES); 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)); 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); if (!is_tristate_default(zl))
av_opt_set_int(context->priv_data, "nonref_p", nrp ? 1 : 0, 0); 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) { if ((context->max_b_frames != 0) && is_tristate_enabled(wp)) {
PLOG_WARNING( PLOG_WARNING("[%s] Weighted Prediction disabled because of B-Frames being used.", codec->name);
"Automatically disabled weighted prediction due to being incompatible with B-Frames."); av_opt_set_int(context->priv_data, "weighted_pred", 0, 0);
} else { } else if (!is_tristate_default(wp)) {
av_opt_set_int(context->priv_data, "weighted_pred", wp ? 1 : 0, 0); av_opt_set_int(context->priv_data, "weighted_pred", wp, 0);
} }
{ {
@@ -661,112 +695,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); PLOG_INFO("[%s] Bitrate:", codec->name);
if (found1 != preset_to_opt.end()) ffmpeg::tools::print_av_option_int(context, "bitrate", " Target", "bits/sec");
PLOG_INFO("[%s] Preset: %s", codec->name, found1->second.c_str()); 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)); ffmpeg::tools::print_av_option_int(context, "max_b_frames", " B-Frames", "Frames");
int64_t cfg_rc_2pass = obs_data_get_int(settings, ST_RATECONTROL_TWOPASS); ffmpeg::tools::print_av_option_string(context, "b_ref_mode", " Reference Mode", [](int64_t v) {
int64_t cfg_rc_lahead = obs_data_get_int(settings, ST_RATECONTROL_LOOKAHEAD); b_ref_mode val = static_cast<b_ref_mode>(v);
bool cfg_rc_adapti = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEI); std::string name = "<Unknown>";
bool cfg_rc_adaptb = obs_data_get_bool(settings, ST_RATECONTROL_ADAPTIVEB); 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); PLOG_INFO("[%s] Adaptive Quantization:", codec->name);
if (found2 != ratecontrolmode_to_opt.end()) if (strcmp(codec->name, "h264_nvenc") == 0) {
PLOG_INFO("[%s] Rate Control: %s", codec->name, found2->second.c_str()); ffmpeg::tools::print_av_option_bool(context, "spatial-aq", " Spatial AQ");
PLOG_INFO("[%s] Two Pass: %s", codec->name, ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
cfg_rc_2pass == 1 ? "Enabled" : (cfg_rc_2pass == 0 ? "Disabled" : "Default")); ffmpeg::tools::print_av_option_bool(context, "temporal-aq", " Temporal AQ");
PLOG_INFO("[%s] Lookahead: %" PRId64 " Frames", codec->name, cfg_rc_lahead); } else {
if (cfg_rc_adapti && cfg_rc_lahead > 0) ffmpeg::tools::print_av_option_bool(context, "spatial_aq", " Spatial AQ");
PLOG_INFO("[%s] Adaptive I-Frames Enabled", codec->name); ffmpeg::tools::print_av_option_int(context, "aq-strength", " Strength", "");
if (cfg_rc_adaptb && cfg_rc_lahead > 0) ffmpeg::tools::print_av_option_bool(context, "temporal_aq", " Temporal AQ");
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] Buffer Size: %" PRId64, codec->name, cfg_rc_bufsize); PLOG_INFO("[%s] Other:", codec->name);
if (have_bitrate) ffmpeg::tools::print_av_option_bool(context, "zerolatency", " Zero Latency");
PLOG_INFO("[%s] Bitrate Target: %" PRId64, codec->name, cfg_rc_bitrate); ffmpeg::tools::print_av_option_bool(context, "weighted_pred", " Weighted Prediction");
if (have_bitrate_max) ffmpeg::tools::print_av_option_bool(context, "nonref_p", " Non-reference P-Frames");
PLOG_INFO("[%s] Bitrate Maximum: %" PRId64, codec->name, cfg_rc_max_bitrate); ffmpeg::tools::print_av_option_bool(context, "strict_gop", " Strict GOP");
if (have_quality && cfg_rc_quality) { ffmpeg::tools::print_av_option_bool(context, "aud", " Access Unit Delimiters");
PLOG_INFO("[%s] Quality Limits:", codec->name); ffmpeg::tools::print_av_option_bool(context, "bluray-compat", " Bluray Compatibility");
PLOG_INFO("[%s] Minimum: %" PRId64, codec->name, cfg_rc_quality_min); if (strcmp(codec->name, "h264_nvenc") == 0)
PLOG_INFO("[%s] Maximum: %" PRId64, codec->name, cfg_rc_quality_max); ffmpeg::tools::print_av_option_bool(context, "a53cc", " A53 Closed Captions");
PLOG_INFO("[%s] Target: %f", codec->name, cfg_rc_quality_tgt); ffmpeg::tools::print_av_option_int(context, "dpb_size", " DPB Size", "");
}
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);
} }
+9 -2
View File
@@ -32,6 +32,8 @@ extern "C" {
} }
namespace obsffmpeg { namespace obsffmpeg {
class encoder;
namespace nvenc { namespace nvenc {
enum class preset : int64_t { enum class preset : int64_t {
DEFAULT, DEFAULT,
@@ -46,6 +48,8 @@ namespace obsffmpeg {
LOW_LATENCY_HIGH_QUALITY, LOW_LATENCY_HIGH_QUALITY,
LOSSLESS, LOSSLESS,
LOSSLESS_HIGH_PERFORMANCE, LOSSLESS_HIGH_PERFORMANCE,
// Append things before this.
INVALID = -1,
}; };
enum class ratecontrolmode : int64_t { enum class ratecontrolmode : int64_t {
@@ -55,12 +59,16 @@ namespace obsffmpeg {
CBR, CBR,
CBR_HQ, CBR_HQ,
CBR_LD_HQ, CBR_LD_HQ,
// Append things before this.
INVALID = -1,
}; };
enum class b_ref_mode : int64_t { enum class b_ref_mode : int64_t {
DISABLED, DISABLED,
EACH, EACH,
MIDDLE, MIDDLE,
// Append things before this.
INVALID = -1,
}; };
extern std::map<preset, std::string> presets; 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; 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, void override_update(obsffmpeg::encoder* instance, obs_data_t* settings);
AVCodecContext* context);
void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context); void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context);
+32 -25
View File
@@ -21,6 +21,7 @@
#include "prores_aw_handler.hpp" #include "prores_aw_handler.hpp"
#include "codecs/prores.hpp" #include "codecs/prores.hpp"
#include "ffmpeg/tools.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -65,6 +66,26 @@ void obsffmpeg::ui::prores_aw_handler::get_defaults(obs_data_t* settings, const
obs_data_set_default_int(settings, P_PRORES_PROFILE, 0); obs_data_set_default_int(settings, P_PRORES_PROFILE, 0);
} }
inline const char* profile_to_name(const AVProfile* ptr)
{
switch (ptr->profile) {
case 0:
return TRANSLATE(P_PRORES_PROFILE_APCO);
case 1:
return TRANSLATE(P_PRORES_PROFILE_APCS);
case 2:
return TRANSLATE(P_PRORES_PROFILE_APCN);
case 3:
return TRANSLATE(P_PRORES_PROFILE_APCH);
case 4:
return TRANSLATE(P_PRORES_PROFILE_AP4H);
case 5:
return TRANSLATE(P_PRORES_PROFILE_AP4X);
default:
return ptr->name;
}
}
void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec, void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, const AVCodec* codec,
AVCodecContext* context, bool) AVCodecContext* context, bool)
{ {
@@ -73,27 +94,7 @@ void obsffmpeg::ui::prores_aw_handler::get_properties(obs_properties_t* props, c
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_set_long_description(p, TRANSLATE(DESC(P_PRORES_PROFILE))); obs_property_set_long_description(p, TRANSLATE(DESC(P_PRORES_PROFILE)));
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) { for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) {
if (strcmp("apco", ptr->name) == 0) { obs_property_list_add_int(p, profile_to_name(ptr), static_cast<int64_t>(ptr->profile));
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCO),
static_cast<int64_t>(ptr->profile));
} else if (strcmp("apcs", ptr->name) == 0) {
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCS),
static_cast<int64_t>(ptr->profile));
} else if (strcmp("apcn", ptr->name) == 0) {
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCN),
static_cast<int64_t>(ptr->profile));
} else if (strcmp("apch", ptr->name) == 0) {
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_APCH),
static_cast<int64_t>(ptr->profile));
} else if (strcmp("ap4h", ptr->name) == 0) {
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_AP4H),
static_cast<int64_t>(ptr->profile));
} else if (strcmp("ap4x", ptr->name) == 0) {
obs_property_list_add_int(p, TRANSLATE(P_PRORES_PROFILE_AP4X),
static_cast<int64_t>(ptr->profile));
} else {
obs_property_list_add_int(p, ptr->name, ptr->profile);
}
} }
} else { } else {
obs_property_set_enabled(obs_properties_get(props, P_PRORES_PROFILE), false); obs_property_set_enabled(obs_properties_get(props, P_PRORES_PROFILE), false);
@@ -105,13 +106,19 @@ void obsffmpeg::ui::prores_aw_handler::update(obs_data_t* settings, const AVCode
context->profile = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE)); context->profile = static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE));
} }
void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext*) void obsffmpeg::ui::prores_aw_handler::log_options(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context)
{ {
for (auto ptr = codec->profiles; ptr->profile != FF_PROFILE_UNKNOWN; ptr++) { PLOG_INFO("[%s] Apple ProRes:", codec->name);
if (ptr->profile == static_cast<int>(obs_data_get_int(settings, P_PRORES_PROFILE))) ffmpeg::tools::print_av_option_string(context, "profile", " Profile", [&codec](int64_t v) {
PLOG_INFO("[%s] Profile: %s", codec->name, ptr->name); int val = static_cast<int>(v);
for (auto ptr = codec->profiles; (ptr->profile != FF_PROFILE_UNKNOWN) && (ptr != nullptr); ptr++) {
if (ptr->profile == val) {
return std::string(profile_to_name(ptr));
} }
} }
return std::string("<Unknown>");
});
}
void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*) void obsffmpeg::ui::prores_aw_handler::process_avpacket(AVPacket& packet, const AVCodec*, AVCodecContext*)
{ {
+4 -3
View File
@@ -37,8 +37,8 @@ namespace obsffmpeg {
virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings, virtual void override_colorformat(AVPixelFormat& target_format, obs_data_t* settings,
const AVCodec* codec, AVCodecContext* context) override; const AVCodec* codec, AVCodecContext* context) override;
virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, virtual void get_defaults(obs_data_t* settings, const AVCodec* codec, AVCodecContext* context,
AVCodecContext* context, bool hw_encode) override; bool hw_encode) override;
virtual void get_properties(obs_properties_t* props, const AVCodec* codec, virtual void get_properties(obs_properties_t* props, const AVCodec* codec,
AVCodecContext* context, bool hw_encode) override; AVCodecContext* context, bool hw_encode) override;
@@ -49,7 +49,8 @@ namespace obsffmpeg {
virtual void log_options(obs_data_t* settings, const AVCodec* codec, virtual void log_options(obs_data_t* settings, const AVCodec* codec,
AVCodecContext* context) override; 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 ui
} // namespace obsffmpeg } // namespace obsffmpeg
+13
View File
@@ -18,3 +18,16 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // 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;
}
+25
View File
@@ -87,4 +87,29 @@ namespace obsffmpeg {
{ {
return obs_get_version() < MAKE_SEMANTIC_VERSION(24, 0, 0); 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 } // namespace obsffmpeg