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
This commit is contained in:
+123
-23
@@ -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>
|
||||||
@@ -66,7 +67,8 @@ extern "C" {
|
|||||||
|
|
||||||
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 +81,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 +101,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 +114,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 +128,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 +142,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 +154,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 +166,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 +187,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 +208,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 +222,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 +236,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 +250,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 +263,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 +278,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 +294,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 +306,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 +321,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
|
||||||
@@ -469,7 +488,8 @@ void obsffmpeg::encoder_factory::get_defaults(obs_data_t* settings, bool hw_enco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -915,10 +935,90 @@ bool obsffmpeg::encoder::update(obs_data_t* settings)
|
|||||||
_context->keyint_min = _context->gop_size;
|
_context->keyint_min = _context->gop_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // FFmpeg
|
{ // FFmpeg Custom Options
|
||||||
// Apply custom options.
|
const char* opts = obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS);
|
||||||
av_opt_set_from_string(_context->priv_data, obs_data_get_string(settings, ST_FFMPEG_CUSTOMSETTINGS),
|
size_t opts_len = strnlen(opts, 65535);
|
||||||
nullptr, "=", ";");
|
|
||||||
|
// Parses options like FFmpeg's command line would:
|
||||||
|
// '-key=value -key=value -key=value'
|
||||||
|
|
||||||
|
std::stack<char> quote_stack;
|
||||||
|
|
||||||
|
bool have_param = false;
|
||||||
|
bool have_key = false;
|
||||||
|
bool have_value = false;
|
||||||
|
|
||||||
|
bool value_is_quoted = false;
|
||||||
|
|
||||||
|
size_t p_key = 0;
|
||||||
|
size_t p_value = 0;
|
||||||
|
|
||||||
|
std::string key;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
for (size_t p = 0; p <= opts_len; p++) {
|
||||||
|
char here = opts[p];
|
||||||
|
|
||||||
|
if (!have_param) {
|
||||||
|
if (here == '-') {
|
||||||
|
have_param = true;
|
||||||
|
p_key = p + 1;
|
||||||
|
}
|
||||||
|
} else if (!have_key) {
|
||||||
|
if (here == '=') {
|
||||||
|
have_key = true;
|
||||||
|
key = std::string(&opts[p_key], &opts[p]);
|
||||||
|
p_value = p + 1;
|
||||||
|
while (quote_stack.size() > 0)
|
||||||
|
quote_stack.pop();
|
||||||
|
} else if (p == opts_len) {
|
||||||
|
PLOG_WARNING("Option '%s' has no value, ignoring.", &opts[p_key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// To support quotes, some interesting stuff needs to be done.
|
||||||
|
// First we need to parse \ as escaping the next character, and just jump over it.
|
||||||
|
if (here == '\\') {
|
||||||
|
p++;
|
||||||
|
} else {
|
||||||
|
if ((here == '"') || (here == '\'')) {
|
||||||
|
// Dealing with quotes is only possible with a stack.
|
||||||
|
if ((quote_stack.size() > 0) && (quote_stack.top() == here)) {
|
||||||
|
quote_stack.pop();
|
||||||
|
} else {
|
||||||
|
quote_stack.push(here);
|
||||||
|
value_is_quoted = true;
|
||||||
|
}
|
||||||
|
} else if ((here == ' ') || (p == opts_len)) {
|
||||||
|
if (quote_stack.size() == 0) {
|
||||||
|
have_value = true;
|
||||||
|
} else if (p == opts_len) {
|
||||||
|
PLOG_WARNING(
|
||||||
|
"Option '%.*s' has mismatched quotations, ignoring.",
|
||||||
|
p_value - p_key, &opts[p_key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_value) {
|
||||||
|
if (value_is_quoted) {
|
||||||
|
value = std::string(&opts[p_value + 1], &opts[p - 1]);
|
||||||
|
} else {
|
||||||
|
value = std::string(&opts[p_value], &opts[p]);
|
||||||
|
}
|
||||||
|
int ret = 0;
|
||||||
|
if ((ret = av_opt_set(_context, key.c_str(), value.c_str(),
|
||||||
|
AV_OPT_SEARCH_CHILDREN))
|
||||||
|
< 0) {
|
||||||
|
PLOG_WARNING("Option '%s' could not be set to '%s'.",
|
||||||
|
key.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
have_param = false;
|
||||||
|
have_key = false;
|
||||||
|
have_value = false;
|
||||||
|
value_is_quoted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_handler)
|
if (_handler)
|
||||||
|
|||||||
Reference in New Issue
Block a user