From 02ead5b096ed6433b2ab452ef5955b4041533801 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 14 Apr 2024 09:58:51 +0200 Subject: [PATCH 0001/1073] obs-ffmpeg: Fix unused variable in native NVENC --- plugins/obs-ffmpeg/obs-nvenc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-nvenc.c b/plugins/obs-ffmpeg/obs-nvenc.c index 97cb1f337c6b16..be735e20294762 100644 --- a/plugins/obs-ffmpeg/obs-nvenc.c +++ b/plugins/obs-ffmpeg/obs-nvenc.c @@ -1109,7 +1109,6 @@ static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings, static bool init_encoder_av1(struct nvenc_data *enc, obs_data_t *settings, int bf, bool compatibility) { - const char *rc = obs_data_get_string(settings, "rate_control"); int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); bool lossless; @@ -1117,7 +1116,6 @@ static bool init_encoder_av1(struct nvenc_data *enc, obs_data_t *settings, return false; } - NV_ENC_INITIALIZE_PARAMS *params = &enc->params; NV_ENC_CONFIG *config = &enc->config; NV_ENC_CONFIG_AV1 *av1_config = &config->encodeCodecConfig.av1Config; From d661160560fa3b2b6dc5b15a01c1525d86394ac0 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 14 Apr 2024 10:00:38 +0200 Subject: [PATCH 0002/1073] build-aux: Make Flatpak use CMake build framework 3.0 Also change CMake build type to RelWithDebInfo which is the preferred build type in Freedesktop SDK environments. --- build-aux/com.obsproject.Studio.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index 011ba0b7f39770..74468291a951a4 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -75,7 +75,8 @@ "buildsystem": "cmake-ninja", "builddir": true, "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_BUILD_TYPE=RelWithDebInfo", + "-DOBS_CMAKE_VERSION=3", "-DENABLE_WAYLAND=ON", "-DENABLE_BROWSER=ON", "-DCEF_ROOT_DIR=/app/cef", @@ -83,7 +84,6 @@ "-DENABLE_ALSA=OFF", "-DENABLE_PULSEAUDIO=ON", "-DENABLE_JACK=ON", - "-DENABLE_RTMPS=ON", "-DENABLE_VLC=OFF", "-DENABLE_AJA=ON", "-DENABLE_LIBFDK=ON", From cb1a4c1c6146b4dba52f58317870a8a314a2affc Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Mon, 15 Apr 2024 13:14:04 +0200 Subject: [PATCH 0003/1073] libobs: Fix relocatable Linux builds using legacy portable build flag --- libobs/obs-nix.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c index 275d25f1f05d8d..77c36be5e6c2bd 100644 --- a/libobs/obs-nix.c +++ b/libobs/obs-nix.c @@ -56,7 +56,7 @@ static const char *module_bin[] = { }; static const char *module_data[] = { - OBS_DATA_PATH "/%module%", + OBS_DATA_PATH "/obs-plugins/%module%", OBS_INSTALL_DATA_PATH "/obs-plugins/%module%", FLATPAK_PLUGIN_PATH "/share/obs/obs-plugins/%module%", }; @@ -76,7 +76,9 @@ void add_default_module_paths(void) if (module_bin_path && module_data_path) { char *abs_module_bin_path = os_get_abs_path_ptr(module_bin_path); - if (strcmp(abs_module_bin_path, OBS_INSTALL_PREFIX + + if (abs_module_bin_path && + strcmp(abs_module_bin_path, OBS_INSTALL_PREFIX "/" OBS_PLUGIN_DESTINATION) != 0) { obs_add_module_path(module_bin_path, module_data_path); } From 34c846cad9d51a734574b98a7bcac6dbb4f84a77 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Mon, 15 Apr 2024 13:14:16 +0200 Subject: [PATCH 0004/1073] UI: Fix relocatable Linux builds using legacy portable build flag --- UI/platform-x11.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index 1794f2ff601d87..d977facbca730e 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -184,7 +184,7 @@ static inline bool check_path(const char *data, const char *path, return (access(output.c_str(), R_OK) == 0); } -#define INSTALL_DATA_PATH OBS_INSTALL_PREFIX OBS_DATA_PATH "/obs-studio/" +#define INSTALL_DATA_PATH OBS_INSTALL_PREFIX "/" OBS_DATA_PATH "/obs-studio/" bool GetDataFilePath(const char *data, string &output) { @@ -200,7 +200,10 @@ bool GetDataFilePath(const char *data, string &output) if (relative_data_path) { bool result = check_path(data, relative_data_path, output); bfree(relative_data_path); - return result; + + if (result) { + return true; + } } if (check_path(data, OBS_DATA_PATH "/obs-studio/", output)) From 731c2d9c8211226a58f775af5480c3ddc991b333 Mon Sep 17 00:00:00 2001 From: Penwywern Date: Mon, 18 Mar 2024 17:08:42 +0100 Subject: [PATCH 0005/1073] UI: Change advanced audio controls to use audio_active --- UI/adv-audio-control.cpp | 9 ++++++-- UI/adv-audio-control.hpp | 2 ++ UI/window-basic-adv-audio.cpp | 41 ++++++++++++++++++++++++++++------- UI/window-basic-adv-audio.hpp | 3 +++ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index cb90e115036c90..277ac24d44d11c 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -54,6 +54,10 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) activateSignal.Connect(handler, "activate", OBSSourceActivated, this); deactivateSignal.Connect(handler, "deactivate", OBSSourceDeactivated, this); + audioActivateSignal.Connect(handler, "audio_activate", + OBSSourceActivated, this); + audioDeactivateSignal.Connect(handler, "audio_deactivate", + OBSSourceDeactivated, this); volChangedSignal.Connect(handler, "volume", OBSSourceVolumeChanged, this); syncOffsetSignal.Connect(handler, "audio_sync", OBSSourceSyncChanged, @@ -92,7 +96,8 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) SetSourceName(sourceName); nameLabel->setAlignment(Qt::AlignVCenter); - bool isActive = obs_source_active(source); + bool isActive = obs_source_active(source) && + obs_source_audio_active(source); active->setText(isActive ? QTStr("Basic.Stats.Status.Active") : QTStr("Basic.Stats.Status.Inactive")); if (isActive) @@ -369,7 +374,7 @@ static inline void setCheckboxState(QCheckBox *checkbox, bool checked) void OBSAdvAudioCtrl::SourceActiveChanged(bool isActive) { - if (isActive) { + if (isActive && obs_source_audio_active(source)) { active->setText(QTStr("Basic.Stats.Status.Active")); setThemeID(active, "error"); } else { diff --git a/UI/adv-audio-control.hpp b/UI/adv-audio-control.hpp index 196dd2130e99c7..ec88f1362bc63a 100644 --- a/UI/adv-audio-control.hpp +++ b/UI/adv-audio-control.hpp @@ -53,6 +53,8 @@ class OBSAdvAudioCtrl : public QObject { OBSSignal mixersSignal; OBSSignal activateSignal; OBSSignal deactivateSignal; + OBSSignal audioActivateSignal; + OBSSignal audioDeactivateSignal; OBSSignal balChangedSignal; OBSSignal renameSignal; diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index 6a8aa0ce2cff78..b2ef201dc7d515 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -12,10 +12,15 @@ Q_DECLARE_METATYPE(OBSSource); OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) : QDialog(parent), ui(new Ui::OBSAdvAudio), - sourceAddedSignal(obs_get_signal_handler(), "source_activate", + sourceAddedSignal(obs_get_signal_handler(), "source_audio_activate", OBSSourceAdded, this), - sourceRemovedSignal(obs_get_signal_handler(), "source_deactivate", - OBSSourceRemoved, this), + sourceRemovedSignal(obs_get_signal_handler(), + "source_audio_deactivate", OBSSourceRemoved, + this), + sourceActivatedSignal(obs_get_signal_handler(), "source_activate", + OBSSourceActivated, this), + sourceDeactivatedSignal(obs_get_signal_handler(), "source_deactivate", + OBSSourceRemoved, this), showInactive(false) { ui->setupUi(this); @@ -51,7 +56,8 @@ bool OBSBasicAdvAudio::EnumSources(void *param, obs_source_t *source) uint32_t flags = obs_source_get_output_flags(source); if ((flags & OBS_SOURCE_AUDIO) != 0 && - (dialog->showInactive || obs_source_active(source))) + (dialog->showInactive || + (obs_source_active(source) && obs_source_audio_active(source)))) dialog->AddAudioSource(source); return true; @@ -73,6 +79,16 @@ void OBSBasicAdvAudio::OBSSourceRemoved(void *param, calldata_t *calldata) "SourceRemoved", Q_ARG(OBSSource, source)); } +void OBSBasicAdvAudio::OBSSourceActivated(void *param, calldata_t *calldata) +{ + OBSSource source((obs_source_t *)calldata_ptr(calldata, "source")); + + if (obs_source_audio_active(source)) + QMetaObject::invokeMethod( + reinterpret_cast(param), + "SourceAdded", Q_ARG(OBSSource, source)); +} + inline void OBSBasicAdvAudio::AddAudioSource(obs_source_t *source) { for (size_t i = 0; i < controls.size(); i++) { @@ -144,6 +160,8 @@ void OBSBasicAdvAudio::SetShowInactive(bool show) sourceAddedSignal.Disconnect(); sourceRemovedSignal.Disconnect(); + sourceActivatedSignal.Disconnect(); + sourceDeactivatedSignal.Disconnect(); if (showInactive) { sourceAddedSignal.Connect(obs_get_signal_handler(), @@ -158,15 +176,22 @@ void OBSBasicAdvAudio::SetShowInactive(bool show) SetIconsVisible(showVisible); } else { sourceAddedSignal.Connect(obs_get_signal_handler(), - "source_activate", OBSSourceAdded, - this); + "source_audio_activate", + OBSSourceAdded, this); sourceRemovedSignal.Connect(obs_get_signal_handler(), - "source_deactivate", + "source_audio_deactivate", OBSSourceRemoved, this); + sourceActivatedSignal.Connect(obs_get_signal_handler(), + "source_activate", + OBSSourceActivated, this); + sourceDeactivatedSignal.Connect(obs_get_signal_handler(), + "source_deactivate", + OBSSourceRemoved, this); for (size_t i = 0; i < controls.size(); i++) { const auto source = controls[i]->GetSource(); - if (!obs_source_active(source)) { + if (!(obs_source_active(source) && + obs_source_audio_active(source))) { delete controls[i]; controls.erase(controls.begin() + i); i--; diff --git a/UI/window-basic-adv-audio.hpp b/UI/window-basic-adv-audio.hpp index 611448ca9ccf64..ab561a2da558d9 100644 --- a/UI/window-basic-adv-audio.hpp +++ b/UI/window-basic-adv-audio.hpp @@ -16,6 +16,8 @@ class OBSBasicAdvAudio : public QDialog { private: OBSSignal sourceAddedSignal; OBSSignal sourceRemovedSignal; + OBSSignal sourceActivatedSignal; + OBSSignal sourceDeactivatedSignal; bool showInactive; bool showVisible; @@ -27,6 +29,7 @@ class OBSBasicAdvAudio : public QDialog { static void OBSSourceAdded(void *param, calldata_t *calldata); static void OBSSourceRemoved(void *param, calldata_t *calldata); + static void OBSSourceActivated(void *param, calldata_t *calldata); std::unique_ptr ui; From cf5f6b6796c2143dec8baee6e898eca18c62a71e Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 16 Apr 2024 12:20:15 -0400 Subject: [PATCH 0006/1073] cmake: Deprecate legacy CMake for all platforms We now support the new, modern CMake path on all platforms. Encourage everyone to migrate by deprecating the legacy CMake path. The legacy CMake path will be removed in a future update. --- CMakeLists.txt | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e02244ece9ac2..d8af296a3d94e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,21 +34,19 @@ if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Darwin)" OR OBS_CMAKE_VERSION VERSION_GREATE return() endif() -if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Windows|Darwin)") - message( - DEPRECATION - "\n" - "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" - "\n" - "You are using the legacy build system to build OBS Studio. " - "The legacy build system is unsupported and will be removed in the near future." - "\n" - "To migrate to the new build system, familiarize yourself with CMake presets " - "(https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) and create " - "a user preset with your customized build settings, inheriting from one of the default presets." - "\n" - "============ LEGACY BUILD SYSTEM IS DEPRECATED ============") -endif() +message( + DEPRECATION + "\n" + "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" + "\n" + "You are using the legacy build system to build OBS Studio. " + "The legacy build system is unsupported and will be removed in the near future." + "\n" + "To migrate to the new build system, familiarize yourself with CMake presets " + "(https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) and create " + "a user preset with your customized build settings, inheriting from one of the default presets." + "\n" + "============ LEGACY BUILD SYSTEM IS DEPRECATED ============") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules") include(VersionConfig) From 414580b62427e734103802ca266d37f51888364a Mon Sep 17 00:00:00 2001 From: Sergio Garcia Murillo Date: Tue, 16 Apr 2024 08:50:06 +0200 Subject: [PATCH 0007/1073] obs-webrtc: Add HEVC support --- plugins/obs-webrtc/whip-output.cpp | 33 +++++++++++++++++------------ plugins/obs-webrtc/whip-output.h | 1 - plugins/obs-webrtc/whip-service.cpp | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index e83cc03b884da5..4d8606862e52fc 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -23,7 +23,6 @@ const uint8_t video_payload_type = 96; WHIPOutput::WHIPOutput(obs_data_t *, obs_output_t *output) : output(output), - is_av1(false), endpoint_url(), bearer_token(), resource_url(), @@ -55,13 +54,6 @@ bool WHIPOutput::Start() { std::lock_guard l(start_stop_mutex); - auto encoder = obs_output_get_video_encoder2(output, 0); - if (encoder == nullptr) { - return false; - } - - is_av1 = (strcmp("av1", obs_encoder_get_codec(encoder)) == 0); - if (!obs_output_can_begin_data_capture(output, 0)) return false; if (!obs_output_initialize_encoders(output, 0)) @@ -148,16 +140,31 @@ void WHIPOutput::ConfigureVideoTrack(std::string media_stream_id, ssrc, cname, video_payload_type, rtc::H264RtpPacketizer::defaultClockRate); - if (is_av1) { + const obs_encoder_t *encoder = obs_output_get_video_encoder2(output, 0); + if (!encoder) + return; + + const char *codec = obs_encoder_get_codec(encoder); + if (strcmp("h264", codec) == 0) { + video_description.addH264Codec(video_payload_type); + packetizer = std::make_shared( + rtc::H264RtpPacketizer::Separator::StartSequence, + rtp_config, MAX_VIDEO_FRAGMENT_SIZE); +#if ENABLE_HEVC + } else if (strcmp("hevc", codec) == 0) { + video_description.addH265Codec(video_payload_type); + packetizer = std::make_shared( + rtc::H265RtpPacketizer::Separator::StartSequence, + rtp_config, MAX_VIDEO_FRAGMENT_SIZE); +#endif + } else if (strcmp("av1", codec) == 0) { video_description.addAV1Codec(video_payload_type); packetizer = std::make_shared( rtc::AV1RtpPacketizer::Packetization::TemporalUnit, rtp_config, MAX_VIDEO_FRAGMENT_SIZE); } else { - video_description.addH264Codec(video_payload_type); - packetizer = std::make_shared( - rtc::H264RtpPacketizer::Separator::StartSequence, - rtp_config, MAX_VIDEO_FRAGMENT_SIZE); + do_log(LOG_ERROR, "Video codec not supported: %s", codec); + return; } video_sr_reporter = std::make_shared(rtp_config); diff --git a/plugins/obs-webrtc/whip-output.h b/plugins/obs-webrtc/whip-output.h index 2933dfae64fb1b..0f9a66e9e4d313 100644 --- a/plugins/obs-webrtc/whip-output.h +++ b/plugins/obs-webrtc/whip-output.h @@ -43,7 +43,6 @@ class WHIPOutput { std::shared_ptr rtcp_sr_reporter); obs_output_t *output; - bool is_av1; std::string endpoint_url; std::string bearer_token; diff --git a/plugins/obs-webrtc/whip-service.cpp b/plugins/obs-webrtc/whip-service.cpp index bc6ef47d649bd2..add9525f63aae0 100644 --- a/plugins/obs-webrtc/whip-service.cpp +++ b/plugins/obs-webrtc/whip-service.cpp @@ -1,7 +1,7 @@ #include "whip-service.h" const char *audio_codecs[MAX_CODECS] = {"opus"}; -const char *video_codecs[MAX_CODECS] = {"h264", "av1"}; +const char *video_codecs[MAX_CODECS] = {"h264", "hevc", "av1"}; WHIPService::WHIPService(obs_data_t *settings, obs_service_t *) : server(), From 3cbe864b33fea45f5ee8f359fb9e6e38b7ccce88 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 17 Apr 2024 18:30:16 -0400 Subject: [PATCH 0008/1073] CI: Fix generate-docs action Effectively revert 743117f08060d3d79f92740f46a60cbeed2e2699. This action began failing recently. Updating the Dockerfile per the action's repo did not fix this. Unpinning Sphinx fixed it. --- .github/actions/generate-docs/action.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/generate-docs/action.yaml b/.github/actions/generate-docs/action.yaml index d698edd6c0303b..73b05d6added8c 100644 --- a/.github/actions/generate-docs/action.yaml +++ b/.github/actions/generate-docs/action.yaml @@ -52,7 +52,6 @@ runs: build_only: true target_branch: master target_path: '../home/_build' - pre_build_commands: 'pip install -Iv sphinx==5.1.1' - uses: actions/upload-artifact@v4 with: From 8a7ad1026212e103c3c8b2dc0a0afcc907f83f89 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 16 Apr 2024 16:24:54 -0400 Subject: [PATCH 0009/1073] cmake: Remove find_qt macro We no longer need this functionality in the new CMake paths. It is still available in the legacy CMake path. --- cmake/common/helpers_common.cmake | 46 ------------------------------- 1 file changed, 46 deletions(-) diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index 81a540595a81be..c73c3190a51efa 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -107,52 +107,6 @@ function(target_disable target) set_property(GLOBAL APPEND PROPERTY OBS_MODULES_DISABLED ${target}) endfunction() -# find_qt: Macro to find best possible Qt version for use with the project: -macro(find_qt) - set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX) - cmake_parse_arguments(find_qt "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Do not use versionless targets in the first step to avoid Qt::Core being clobbered by later opportunistic - # find_package runs - set(QT_NO_CREATE_VERSIONLESS_TARGETS TRUE) - - message(DEBUG "Attempting to find Qt 6") - find_package( - Qt6 - COMPONENTS Core - REQUIRED) - - # Enable versionless targets for the remaining Qt components - set(QT_NO_CREATE_VERSIONLESS_TARGETS FALSE) - - set(qt_components ${find_qt_COMPONENTS}) - if(OS_WINDOWS) - list(APPEND qt_components ${find_qt_COMPONENTS_WIN}) - elseif(OS_MACOS) - list(APPEND qt_components ${find_qt_COMPONENTS_MAC}) - else() - list(APPEND qt_components ${find_qt_COMPONENTS_LINUX}) - endif() - message(DEBUG "Trying to find Qt components ${qt_components}...") - - find_package(Qt6 REQUIRED ${qt_components}) - - list(APPEND qt_components Core) - - if("Gui" IN_LIST find_qt_COMPONENTS_LINUX) - list(APPEND qt_components "GuiPrivate") - endif() - - # Check for versionless targets of each requested component and create if necessary - foreach(component IN LISTS qt_components) - message(DEBUG "Checking for target Qt::${component}") - if(NOT TARGET Qt::${component} AND TARGET Qt6::${component}) - add_library(Qt::${component} INTERFACE IMPORTED) - set_target_properties(Qt::${component} PROPERTIES INTERFACE_LINK_LIBRARIES Qt6::${component}) - endif() - endforeach() -endmacro() - # _handle_generator_expression_dependency: Helper function to yield dependency from a generator expression function(_handle_generator_expression_dependency library) set(oneValueArgs FOUND_VAR) From eb65ebdc5ee98199c7af624341df46c1d0df81ba Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 17 Apr 2024 17:27:16 -0400 Subject: [PATCH 0010/1073] UI: Use System theme volume meter colors These comments do not actually match the color values here: * (0xff, 0xff, 0xff) is white * (0xcc, 0xcc, 0xcc) is light gray Instead of just fixing the comments, use the values from the System theme and also correct the comments. --- UI/volume-control.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 88477300f9788d..3d33edb0a769d1 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -853,8 +853,8 @@ VolumeMeter::VolumeMeter(QWidget *parent, obs_volmeter_t *obs_volmeter, clipColor.setRgb(0xff, 0xff, 0xff); // Bright white magnitudeColor.setRgb(0x00, 0x00, 0x00); // Black - majorTickColor.setRgb(0xff, 0xff, 0xff); // Black - minorTickColor.setRgb(0xcc, 0xcc, 0xcc); // Black + majorTickColor.setRgb(0x00, 0x00, 0x00); // Black + minorTickColor.setRgb(0x32, 0x32, 0x32); // Dark gray minimumLevel = -60.0; // -60 dB warningLevel = -20.0; // -20 dB errorLevel = -9.0; // -9 dB From 83383c13bc5657fb7ef071d6e250a66d66cdcc95 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 22 Mar 2024 14:00:32 +0100 Subject: [PATCH 0011/1073] UI: Reformat .ui files with Qt Designer Files have been formatted with Qt Designer (bin\designer.exe) 6.6.2. --- UI/forms/AutoConfigStreamPage.ui | 2 +- UI/forms/OBSAdvAudio.ui | 13 +- UI/forms/OBSBasic.ui | 72 ++++---- UI/forms/OBSBasicFilters.ui | 34 ++-- UI/forms/OBSBasicSettings.ui | 174 +++++++++--------- UI/forms/OBSBasicTransform.ui | 2 +- UI/forms/OBSBasicVCamConfig.ui | 6 +- UI/forms/OBSExtraBrowsers.ui | 2 +- UI/forms/OBSImporter.ui | 2 +- UI/forms/OBSLogReply.ui | 2 +- UI/forms/OBSMissingFiles.ui | 2 +- UI/forms/OBSPermissions.ui | 4 +- UI/forms/OBSRemux.ui | 2 +- UI/forms/OBSUpdate.ui | 9 +- UI/forms/OBSYoutubeActions.ui | 15 +- UI/forms/source-toolbar/media-controls.ui | 10 +- .../frontend-tools/forms/captions.ui | 2 +- 17 files changed, 173 insertions(+), 180 deletions(-) diff --git a/UI/forms/AutoConfigStreamPage.ui b/UI/forms/AutoConfigStreamPage.ui index a6b858082c2aaa..b52f4d02a40528 100644 --- a/UI/forms/AutoConfigStreamPage.ui +++ b/UI/forms/AutoConfigStreamPage.ui @@ -6,7 +6,7 @@ 0 0 - 692 + 726 407 diff --git a/UI/forms/OBSAdvAudio.ui b/UI/forms/OBSAdvAudio.ui index e07710851b3206..b72c53fa8ff2c6 100644 --- a/UI/forms/OBSAdvAudio.ui +++ b/UI/forms/OBSAdvAudio.ui @@ -97,8 +97,8 @@ 0 0 - 1346 - 254 + 1157 + 262 @@ -143,7 +143,6 @@ - 75 true @@ -162,7 +161,6 @@ - 75 true @@ -175,7 +173,6 @@ - 75 true @@ -188,7 +185,6 @@ - 75 true @@ -201,7 +197,6 @@ - 75 true @@ -214,7 +209,6 @@ - 75 true @@ -227,7 +221,6 @@ - 75 true @@ -263,7 +256,6 @@ - 75 true @@ -276,7 +268,6 @@ - 75 true diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 7f1afce76a5157..29b93f9ea4d769 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -7,7 +7,7 @@ 0 0 - 1079 + 1086 729 @@ -176,7 +176,7 @@ - + 0 @@ -486,8 +486,8 @@ 0 0 - 1079 - 21 + 1086 + 22 @@ -677,6 +677,11 @@ + + + MultiviewProjector + + Basic.MainMenu.View.ResetUI @@ -706,11 +711,6 @@ - - - MultiviewProjector - - Basic.MainMenu.Tools @@ -739,7 +739,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable Basic.Main.Scenes @@ -808,9 +808,6 @@ 0 - - 0 - Qt::CustomContextMenu @@ -832,6 +829,9 @@ Qt::TargetMoveAction + + 0 + @@ -879,7 +879,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable Basic.Main.Sources @@ -948,9 +948,6 @@ 0 - - 0 - Qt::CustomContextMenu @@ -972,6 +969,9 @@ QAbstractItemView::ExtendedSelection + + 0 + @@ -1019,7 +1019,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable Mixer @@ -1094,7 +1094,7 @@ 0 0 - 74 + 217 16 @@ -1202,7 +1202,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable Basic.SceneTransitions @@ -1348,7 +1348,7 @@ - + :/res/images/plus.svg:/res/images/plus.svg @@ -1380,7 +1380,7 @@ - + :/res/images/minus.svg:/res/images/minus.svg @@ -1412,7 +1412,7 @@ - + :/settings/images/settings/general.svg:/settings/images/settings/general.svg @@ -1449,7 +1449,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable Basic.Main.Controls @@ -1650,7 +1650,7 @@ - + :/res/images/plus.svg:/res/images/plus.svg @@ -1665,7 +1665,7 @@ - + :/res/images/plus.svg:/res/images/plus.svg @@ -1680,7 +1680,7 @@ - + :/res/images/minus.svg:/res/images/minus.svg @@ -1704,7 +1704,7 @@ - + :/res/images/minus.svg:/res/images/minus.svg @@ -1731,7 +1731,7 @@ true - + :/settings/images/settings/general.svg:/settings/images/settings/general.svg @@ -1746,7 +1746,7 @@ - + :/res/images/up.svg:/res/images/up.svg @@ -1764,7 +1764,7 @@ true - + :/res/images/up.svg:/res/images/up.svg @@ -1779,7 +1779,7 @@ - + :/res/images/down.svg:/res/images/down.svg @@ -1797,7 +1797,7 @@ true - + :/res/images/down.svg:/res/images/down.svg @@ -2013,12 +2013,12 @@ - - false - Basic.MainMenu.Help.ReleaseNotes + + false + diff --git a/UI/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui index 7cab6c1529da6a..ea0d5ae526e882 100644 --- a/UI/forms/OBSBasicFilters.ui +++ b/UI/forms/OBSBasicFilters.ui @@ -69,9 +69,6 @@ Qt::CustomContextMenu - - 1 - true @@ -81,6 +78,9 @@ Qt::TargetMoveAction + + 1 + @@ -116,7 +116,7 @@ - + :/res/images/plus.svg:/res/images/plus.svg @@ -145,7 +145,7 @@ - + :/res/images/minus.svg:/res/images/minus.svg @@ -174,7 +174,7 @@ - + :/res/images/up.svg:/res/images/up.svg @@ -203,7 +203,7 @@ - + :/res/images/down.svg:/res/images/down.svg @@ -291,9 +291,6 @@ Qt::CustomContextMenu - - 1 - true @@ -303,6 +300,9 @@ Qt::TargetMoveAction + + 1 + @@ -338,7 +338,7 @@ - + :/res/images/plus.svg:/res/images/plus.svg @@ -367,7 +367,7 @@ - + :/res/images/minus.svg:/res/images/minus.svg @@ -396,7 +396,7 @@ - + :/res/images/up.svg:/res/images/up.svg @@ -425,7 +425,7 @@ - + :/res/images/down.svg:/res/images/down.svg @@ -606,7 +606,7 @@ - + :/res/images/minus.svg:/res/images/minus.svg @@ -621,7 +621,7 @@ - + :/res/images/up.svg:/res/images/up.svg @@ -633,7 +633,7 @@ - + :/res/images/down.svg:/res/images/down.svg diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 4fd2d7543592ec..cbe2f31c0c9603 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -169,8 +169,8 @@ 0 0 - 767 - 1326 + 764 + 1298 @@ -281,12 +281,12 @@ Basic.Settings.General.Updater - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + @@ -1571,8 +1571,8 @@ 0 0 - 772 - 651 + 518 + 609 @@ -2167,15 +2167,15 @@ + + ReplayBuffer + true true - - ReplayBuffer - QFormLayout::AllNonFixedFieldsGrow @@ -2368,8 +2368,8 @@ 0 0 - 759 - 616 + 431 + 180 @@ -2485,7 +2485,7 @@ 3 - + @@ -2510,64 +2510,64 @@ - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - - - 2 - - - - - - - 3 - - - - - - - 4 - - - - - - - 5 - - - - - - - 6 - - - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + + + 2 + + + + + + + 3 + + + + + + + 4 + + + + + + + 5 + + + + + + + 6 + + + + + + @@ -2608,7 +2608,7 @@ - + @@ -2801,8 +2801,8 @@ 0 0 - 759 - 587 + 518 + 371 @@ -3238,7 +3238,7 @@ 0 - + @@ -3448,8 +3448,8 @@ 0 0 - 759 - 587 + 634 + 467 @@ -3985,8 +3985,8 @@ 0 0 - 759 - 616 + 267 + 510 @@ -5058,8 +5058,8 @@ 0 0 - 781 - 640 + 608 + 520 @@ -5991,8 +5991,8 @@ 0 0 - 761 - 644 + 196 + 28 @@ -6069,8 +6069,8 @@ 0 0 - 799 - 666 + 704 + 347 @@ -6978,8 +6978,8 @@ 0 0 - 826 - 1000 + 713 + 955 diff --git a/UI/forms/OBSBasicTransform.ui b/UI/forms/OBSBasicTransform.ui index 56a36a0a4cd6e1..6fb029bbbfa1d7 100644 --- a/UI/forms/OBSBasicTransform.ui +++ b/UI/forms/OBSBasicTransform.ui @@ -7,7 +7,7 @@ 0 0 564 - 313 + 320 diff --git a/UI/forms/OBSBasicVCamConfig.ui b/UI/forms/OBSBasicVCamConfig.ui index 81c1f18790d16c..3798f96b1d5968 100644 --- a/UI/forms/OBSBasicVCamConfig.ui +++ b/UI/forms/OBSBasicVCamConfig.ui @@ -36,12 +36,12 @@ + + false + Basic.VCam.RestartWarning - - false - diff --git a/UI/forms/OBSExtraBrowsers.ui b/UI/forms/OBSExtraBrowsers.ui index 462aebb3749c86..6700c35a245c96 100644 --- a/UI/forms/OBSExtraBrowsers.ui +++ b/UI/forms/OBSExtraBrowsers.ui @@ -42,7 +42,7 @@ false - 23 + 24 diff --git a/UI/forms/OBSImporter.ui b/UI/forms/OBSImporter.ui index 908fdb71b3ea0c..0528d543d1d3d8 100644 --- a/UI/forms/OBSImporter.ui +++ b/UI/forms/OBSImporter.ui @@ -53,7 +53,7 @@ false - 23 + 24 diff --git a/UI/forms/OBSLogReply.ui b/UI/forms/OBSLogReply.ui index 6d4b7f4309025a..544018b7d762f9 100644 --- a/UI/forms/OBSLogReply.ui +++ b/UI/forms/OBSLogReply.ui @@ -7,7 +7,7 @@ 0 0 600 - 95 + 96 diff --git a/UI/forms/OBSMissingFiles.ui b/UI/forms/OBSMissingFiles.ui index f7cbdb2d715944..0d2b475ca14dee 100644 --- a/UI/forms/OBSMissingFiles.ui +++ b/UI/forms/OBSMissingFiles.ui @@ -59,7 +59,7 @@ false - 23 + 24 diff --git a/UI/forms/OBSPermissions.ui b/UI/forms/OBSPermissions.ui index a494678d86142f..8d65da91d0e2c1 100644 --- a/UI/forms/OBSPermissions.ui +++ b/UI/forms/OBSPermissions.ui @@ -7,7 +7,7 @@ 0 0 692 - 550 + 613 @@ -95,7 +95,7 @@ true - + 8 diff --git a/UI/forms/OBSRemux.ui b/UI/forms/OBSRemux.ui index 3ac8125facd212..9235ec912fcf45 100644 --- a/UI/forms/OBSRemux.ui +++ b/UI/forms/OBSRemux.ui @@ -53,7 +53,7 @@ false - 23 + 24 diff --git a/UI/forms/OBSUpdate.ui b/UI/forms/OBSUpdate.ui index d9a003409365ee..8d94b327d47c7f 100644 --- a/UI/forms/OBSUpdate.ui +++ b/UI/forms/OBSUpdate.ui @@ -28,10 +28,13 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p></body></html> true diff --git a/UI/forms/OBSYoutubeActions.ui b/UI/forms/OBSYoutubeActions.ui index 0594dc942349ce..3c7ddcc786427d 100644 --- a/UI/forms/OBSYoutubeActions.ui +++ b/UI/forms/OBSYoutubeActions.ui @@ -82,8 +82,8 @@ 0 0 - 783 - 666 + 795 + 657 @@ -299,7 +299,6 @@ - 75 true @@ -522,8 +521,8 @@ 0 0 - 218 - 200 + 191 + 216 @@ -643,12 +642,12 @@ - - QLayout::SetNoConstraint - 10 + + QLayout::SetNoConstraint + diff --git a/UI/forms/source-toolbar/media-controls.ui b/UI/forms/source-toolbar/media-controls.ui index 57ba7879b252a0..ceac1550b3f917 100644 --- a/UI/forms/source-toolbar/media-controls.ui +++ b/UI/forms/source-toolbar/media-controls.ui @@ -294,16 +294,16 @@ - - MediaSlider - QSlider -
media-slider.hpp
-
ClickableLabel QLabel
clickable-label.hpp
+ + MediaSlider + QSlider +
media-slider.hpp
+
diff --git a/UI/frontend-plugins/frontend-tools/forms/captions.ui b/UI/frontend-plugins/frontend-tools/forms/captions.ui index f5c0b30c23b734..95fd8611b3e74e 100644 --- a/UI/frontend-plugins/frontend-tools/forms/captions.ui +++ b/UI/frontend-plugins/frontend-tools/forms/captions.ui @@ -7,7 +7,7 @@ 0 0 519 - 152 + 162 From 10bfa99365c1ed1df27f3a0af60c218a46d9071b Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Thu, 18 Apr 2024 08:49:41 -0700 Subject: [PATCH 0012/1073] libobs: Export NV12/P010 functions NV12 and P010 device functions were not exported on all platforms. Windows was exporting from C files instead. After CMake 3.0 we started hiding symbols and resolution failed. --- libobs/graphics/device-exports.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libobs/graphics/device-exports.h b/libobs/graphics/device-exports.h index 50fadef99d65b6..650aadaa7ec4e2 100644 --- a/libobs/graphics/device-exports.h +++ b/libobs/graphics/device-exports.h @@ -180,6 +180,8 @@ EXPORT void device_debug_marker_begin(gs_device_t *device, EXPORT void device_debug_marker_end(gs_device_t *device); EXPORT bool device_is_monitor_hdr(gs_device_t *device, void *monitor); EXPORT bool device_shared_texture_available(void); +EXPORT bool device_nv12_available(gs_device_t *device); +EXPORT bool device_p010_available(gs_device_t *device); #ifdef __APPLE__ EXPORT gs_texture_t *device_texture_create_from_iosurface(gs_device_t *device, From b4a061dcd8abf8188dce856a942706c3014c9f23 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sun, 14 Apr 2024 09:56:30 +0900 Subject: [PATCH 0013/1073] libobs: Remove unused variable in obs_source --- libobs/obs-internal.h | 1 - libobs/obs-source.c | 1 - 2 files changed, 2 deletions(-) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index a9cf5f056346fa..91a53d7f2dc659 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -756,7 +756,6 @@ struct obs_source { volatile bool timing_set; volatile uint64_t timing_adjust; uint64_t resample_offset; - uint64_t last_audio_ts; uint64_t next_audio_ts_min; uint64_t next_audio_sys_ts_min; uint64_t last_frame_ts; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index dd45a8f5d6fa45..01abb87d042cdc 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -1576,7 +1576,6 @@ static void source_output_audio_data(obs_source_t *source, } } - source->last_audio_ts = in.timestamp; source->next_audio_ts_min = in.timestamp + conv_frames_to_time(sample_rate, in.frames); From e9b42e6c5863e343d676733c908dc502d3cf77fe Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Sun, 14 Apr 2024 21:45:40 -0700 Subject: [PATCH 0014/1073] text-freetype2: Use cached glyph advances We had these advances laying around but didn't use them here. Loading the glyphs from the fonts is extremely slow, so let's avoid that since we take the time to cache these already. --- plugins/text-freetype2/text-functionality.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/text-freetype2/text-functionality.c b/plugins/text-freetype2/text-functionality.c index 25772c50ab10dd..e20176a8f7539f 100644 --- a/plugins/text-freetype2/text-functionality.c +++ b/plugins/text-freetype2/text-functionality.c @@ -570,12 +570,17 @@ uint32_t get_ft2_text_width(wchar_t *text, struct ft2_source *srcdata) const FT_UInt glyph_index = FT_Get_Char_Index(srcdata->font_face, text[i]); - load_glyph(srcdata, glyph_index, get_render_mode(srcdata)); - if (text[i] == L'\n') w = 0; else { - w += slot->advance.x >> 6; + if (src_glyph) { + // Use the cached values. + w += src_glyph->xadv; + } else { + load_glyph(srcdata, glyph_index, + get_render_mode(srcdata)); + w += slot->advance.x >> 6; + } if (w > max_w) max_w = w; } From d966742a6317b1f1ed1aaaef8fa64ac62c6f79d8 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 9 Feb 2024 14:32:54 +0100 Subject: [PATCH 0015/1073] libobs: Fix `highest_video_ts` tracking Calling `set_higher_ts` before offsets are known pollutes `highest_video_ts` with timestamps that are out of range of actual timestamps. They're generally somewhere high above 0 until an offset for all streams is found, where they are then reset to 0 or slightly below 0 in the presence of b-frames. `highest_video_ts` also needs to start below 0 for the same reason. Even with timestamps being reset to close to 0, b-frames will cause initial DTS to drop below 0, thus we need a value that should "always" be below any "real" timestamps observed. `highest_video_ts` tracking now only starts once all input streams are ready, and is computed based on all buffered packets at that point. --- libobs/obs-output.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 49459c31eade54..566432eb075baa 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -1919,11 +1919,6 @@ static bool initialize_interleaved_packets(struct obs_output *output) /* subtract offsets from highest TS offset variables */ output->highest_audio_ts -= audio[first_audio_idx]->dts_usec; - for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { - if (video[i]) - output->highest_video_ts[i] -= video[i]->dts_usec; - } - /* apply new offsets to all existing packet DTS/PTS values */ for (size_t i = 0; i < output->interleaved_packets.num; i++) { struct encoder_packet *packet = @@ -1970,8 +1965,11 @@ static void resort_interleaved_packets(struct obs_output *output) memset(&output->interleaved_packets, 0, sizeof(output->interleaved_packets)); - for (size_t i = 0; i < old_array.num; i++) + for (size_t i = 0; i < old_array.num; i++) { + set_higher_ts(output, &old_array.array[i]); + insert_interleaved_packet(output, &old_array.array[i]); + } da_free(old_array); } @@ -2119,7 +2117,6 @@ static void interleave_packets(void *data, struct encoder_packet *packet) check_received(output, packet); insert_interleaved_packet(output, &out); - set_higher_ts(output, &out); received_video = true; for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { @@ -2139,6 +2136,8 @@ static void interleave_packets(void *data, struct encoder_packet *packet) } } } else { + set_higher_ts(output, &out); + send_interleaved(output); } } @@ -2322,7 +2321,7 @@ static void reset_packet_data(obs_output_t *output) for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { output->received_video[i] = false; output->video_offsets[i] = 0; - output->highest_video_ts[i] = 0; + output->highest_video_ts[i] = INT64_MIN; } for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) output->audio_offsets[i] = 0; From 7b2d555061d3c6b9d6cf4fc0bcdfa7a9e8306d8b Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Fri, 19 Apr 2024 08:30:41 +0900 Subject: [PATCH 0016/1073] UI: Set dock floating after hiding it Setting the dock floating before hiding it created a transparent window on X11. --- UI/api-interface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 8fb3bea3a3816d..e150511b99a8c5 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -416,8 +416,8 @@ struct OBSStudioAPI : obs_frontend_callbacks { main->AddDockWidget(dock, Qt::RightDockWidgetArea); - dock->setFloating(true); dock->setVisible(false); + dock->setFloating(true); return true; } From 7a3f5d784a86fa3f5123885e6c9077d198ad03c2 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Thu, 18 Apr 2024 22:06:28 -0400 Subject: [PATCH 0017/1073] UI: Clean up main window dock structure --- UI/data/themes/Acri.qss | 24 +- UI/data/themes/Dark.qss | 21 ++ UI/data/themes/Grey.qss | 24 +- UI/data/themes/Light.qss | 24 +- UI/data/themes/Rachni.qss | 24 +- UI/data/themes/Yami.qss | 30 +-- UI/forms/OBSBasic.ui | 463 ++++++++++++++++++-------------------- 7 files changed, 281 insertions(+), 329 deletions(-) diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index 2bd3a359be17d9..593a75536f4e70 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -284,24 +284,12 @@ OBSDock > QWidget { border-bottom-right-radius: 4px; } -OBSDock QFrame { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -#transitionsContainer QPushButton { - margin: 0px 0px; - padding: 4px 6px; -} - OBSDock QLabel { background: transparent; } -OBSDock QComboBox, -OBSDock QPushButton { - margin: 1px 2px; +#transitionsFrame { + padding: 4px 8px; } QDockWidget { @@ -774,8 +762,12 @@ QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { /* Controls Dock */ -#controlsDock QPushButton { - margin: 1px; +#controlsFrame { + padding: 4px 3px; +} + +#controlsFrame QPushButton { + margin: 2px 1px; } #streamButton, diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index de0dc214953140..06b066ec50bcf8 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -131,6 +131,27 @@ QMainWindow::separator { /* Dock Widget */ +OBSDock > QWidget { + border: 1px solid palette(base); + border-top: none; +} + +#transitionsFrame { + padding: 4px; +} + +#transitionsFrame > QWidget { + margin-bottom: 2px; +} + +#controlsFrame { + padding: 4px 3px; +} + +#controlsFrame QPushButton { + margin: 1px 1px; +} + QDockWidget { titlebar-close-icon: url(theme:Dark/close.svg); titlebar-normal-icon: url(theme:Dark/popout.svg); diff --git a/UI/data/themes/Grey.qss b/UI/data/themes/Grey.qss index c9a7208b663929..b324ef3c97801c 100644 --- a/UI/data/themes/Grey.qss +++ b/UI/data/themes/Grey.qss @@ -284,24 +284,12 @@ OBSDock > QWidget { border-bottom-right-radius: 4px; } -OBSDock QFrame { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -#transitionsContainer QPushButton { - margin: 0px 0px; - padding: 4px 6px; -} - OBSDock QLabel { background: transparent; } -OBSDock QComboBox, -OBSDock QPushButton { - margin: 1px 2px; +#transitionsFrame { + padding: 4px 8px; } QDockWidget { @@ -772,8 +760,12 @@ QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { /* Controls Dock */ -#controlsDock QPushButton { - margin: 1px; +#controlsFrame { + padding: 4px 3px; +} + +#controlsFrame QPushButton { + margin: 2px 1px; } #streamButton, diff --git a/UI/data/themes/Light.qss b/UI/data/themes/Light.qss index 8a0c8bab443e0d..bf4ae85feb7045 100644 --- a/UI/data/themes/Light.qss +++ b/UI/data/themes/Light.qss @@ -284,24 +284,12 @@ OBSDock > QWidget { border-bottom-right-radius: 4px; } -OBSDock QFrame { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -#transitionsContainer QPushButton { - margin: 0px 0px; - padding: 4px 6px; -} - OBSDock QLabel { background: transparent; } -OBSDock QComboBox, -OBSDock QPushButton { - margin: 1px 2px; +#transitionsFrame { + padding: 4px 8px; } QDockWidget { @@ -772,8 +760,12 @@ QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { /* Controls Dock */ -#controlsDock QPushButton { - margin: 1px; +#controlsFrame { + padding: 4px 3px; +} + +#controlsFrame QPushButton { + margin: 2px 1px; } #streamButton, diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 4deca3d3de262d..b46a437d0366a8 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -282,24 +282,12 @@ OBSDock > QWidget { border-bottom-right-radius: 4px; } -OBSDock QFrame { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -#transitionsContainer QPushButton { - margin: 0px 0px; - padding: 4px 6px; -} - OBSDock QLabel { background: transparent; } -OBSDock QComboBox, -OBSDock QPushButton { - margin: 1px 2px; +#transitionsFrame { + padding: 4px 8px; } QDockWidget { @@ -772,8 +760,12 @@ QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { /* Controls Dock */ -#controlsDock QPushButton { - margin: 1px; +#controlsFrame { + padding: 4px 3px; +} + +#controlsFrame QPushButton { + margin: 2px 1px; } #streamButton, diff --git a/UI/data/themes/Yami.qss b/UI/data/themes/Yami.qss index e6a74bb09b53fd..7fb9c9dc713523 100644 --- a/UI/data/themes/Yami.qss +++ b/UI/data/themes/Yami.qss @@ -288,24 +288,12 @@ OBSDock > QWidget { border-bottom-right-radius: 4px; } -OBSDock QFrame { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -#transitionsContainer QPushButton { - margin: 0px 0px; - padding: 4px 6px; -} - OBSDock QLabel { background: transparent; } -OBSDock QComboBox, -OBSDock QPushButton { - margin: 1px 2px; +#transitionsFrame { + padding: 4px 8px; } QDockWidget { @@ -387,7 +375,7 @@ QGroupBox::title { } QScrollBar:vertical { - background-color: transparent; + background-color: #1F212A; width: 14px; margin: 0px; } @@ -405,7 +393,7 @@ QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add- } QScrollBar:horizontal { - background-color: transparent; + background: #1F212A; height: 14px; margin: 0px; } @@ -774,10 +762,13 @@ QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { padding: 2px; } - /* Controls Dock */ -#controlsDock QPushButton { - margin: 1px; +#controlsFrame { + padding: 4px 3px; +} + +#controlsFrame QPushButton { + margin: 2px 1px; } #streamButton, @@ -918,7 +909,6 @@ QSlider::handle:disabled { } /* Volume Control */ - #stackedMixerArea QPushButton { min-width: 16px; padding: 4px 8px; diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 29b93f9ea4d769..a0313b2b4e6549 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -753,37 +753,19 @@ 0 - 0 + 1 0 - 0 + 1 - 0 + 1 - - - 0 - 0 - - - - - 160 - 0 - - - - QFrame::StyledPanel - - - QFrame::Sunken - 0 @@ -893,37 +875,19 @@ 0 - 0 + 1 0 - 0 + 1 - 0 + 1 - - - 0 - 0 - - - - - 160 - 0 - - - - QFrame::StyledPanel - - - QFrame::Sunken - 0 @@ -1033,25 +997,19 @@ 0 - 0 + 1 0 - 0 + 1 - 0 + 1 - - QFrame::StyledPanel - - - QFrame::Sunken - 0 @@ -1075,10 +1033,10 @@ Qt::CustomContextMenu - QFrame::StyledPanel + QFrame::NoFrame - QFrame::Sunken + QFrame::Plain Qt::ScrollBarAsNeeded @@ -1094,7 +1052,7 @@ 0 0 - 217 + 225 16 @@ -1128,10 +1086,10 @@ Qt::CustomContextMenu - QFrame::StyledPanel + QFrame::NoFrame - QFrame::Sunken + QFrame::Plain Qt::ScrollBarAlwaysOff @@ -1148,7 +1106,7 @@ 0 0 16 - 28 + 192 @@ -1216,40 +1174,34 @@ 0 - 0 + 1 0 - 0 + 1 - 0 + 1 - - QFrame::StyledPanel - - - QFrame::Sunken - - 2 + 0 - 4 + 0 - 4 + 0 - 4 + 0 - 4 + 0 @@ -1458,193 +1410,214 @@ 8 - + - 2 + 0 - 4 + 1 - 4 + 0 - 4 + 1 - 4 + 1 - - - - - true - - - - 0 - 0 - - - - - 150 - 0 - - - - Basic.Main.StartStreaming - - - true - - - - - - - true - - - - 0 - 0 - - - - - 150 - 0 - - - - Basic.Main.StartBroadcast - - - true - - - - - - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - Basic.Main.StartRecording - - - true - - - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - Basic.TogglePreviewProgramMode - - - true - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - Settings - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - Exit - + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + true + + + + 0 + 0 + + + + + 150 + 0 + + + + Basic.Main.StartStreaming + + + true + + + + + + + true + + + + 0 + 0 + + + + + 150 + 0 + + + + Basic.Main.StartBroadcast + + + true + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + Basic.Main.StartRecording + + + true + + + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Basic.TogglePreviewProgramMode + + + true + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Settings + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Exit + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + - - - - Qt::Vertical - - - - 0 - 0 - - - - From 2edc555af79e001b4cf53a57eb732029eb9491c1 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Apr 2024 18:04:46 +0200 Subject: [PATCH 0018/1073] libobs: Fix grouped encoders never starting again after disconnect (based on ) --- libobs/obs-encoder.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 1324ec325b580d..3a0bea971cb914 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -354,6 +354,9 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown) if (encoder->encoder_group) { pthread_mutex_lock(&encoder->encoder_group->mutex); encoder->encoder_group->encoders_started -= 1; + if (encoder->encoder_group->encoders_started == 0) + encoder->encoder_group->start_timestamp = 0; + pthread_mutex_unlock(&encoder->encoder_group->mutex); } From 65295eaf9333557469fc2e3fa652510b5d410e52 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Fri, 19 Apr 2024 21:18:17 +0200 Subject: [PATCH 0019/1073] libobs/util: Prevent leaking pipe file descriptors to subprocesses --- libobs/util/pipe-posix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libobs/util/pipe-posix.c b/libobs/util/pipe-posix.c index c8bf502b7a06ab..e78c0944375e9c 100644 --- a/libobs/util/pipe-posix.c +++ b/libobs/util/pipe-posix.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "bmem.h" #include "pipe.h" @@ -66,6 +67,11 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, return NULL; } + fcntl(mainfds[0], F_SETFD, FD_CLOEXEC); + fcntl(mainfds[1], F_SETFD, FD_CLOEXEC); + fcntl(errfds[0], F_SETFD, FD_CLOEXEC); + fcntl(errfds[1], F_SETFD, FD_CLOEXEC); + if (process_pipe.read_pipe) { posix_spawn_file_actions_addclose(&file_actions, mainfds[0]); if (mainfds[1] != STDOUT_FILENO) { From 06e364b67067730de04129a91b21fdaf5782beb9 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Sun, 30 Apr 2023 15:26:50 +0200 Subject: [PATCH 0020/1073] obs-ffmpeg: Implement texture encoding for VAAPI --- plugins/obs-ffmpeg/CMakeLists.txt | 1 + plugins/obs-ffmpeg/cmake/dependencies.cmake | 1 + plugins/obs-ffmpeg/cmake/legacy.cmake | 3 +- plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c | 388 +++++++++++++++++--- plugins/obs-ffmpeg/obs-ffmpeg.c | 6 + 5 files changed, 353 insertions(+), 46 deletions(-) diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 0fa71e58689b4a..4a928c82cf5208 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -66,6 +66,7 @@ target_link_libraries( $<$:Libva::va> $<$:Libva::drm> $<$:Libpci::pci> + $<$:Libdrm::Libdrm> $<$:Librist::Librist> $<$:Libsrt::Libsrt>) diff --git a/plugins/obs-ffmpeg/cmake/dependencies.cmake b/plugins/obs-ffmpeg/cmake/dependencies.cmake index 442c8ea18325d1..087e8c2569b93c 100644 --- a/plugins/obs-ffmpeg/cmake/dependencies.cmake +++ b/plugins/obs-ffmpeg/cmake/dependencies.cmake @@ -33,6 +33,7 @@ elseif( OR OS_OPENBSD) find_package(Libva REQUIRED) find_package(Libpci REQUIRED) + find_package(Libdrm REQUIRED) endif() if(OS_WINDOWS OR (OS_LINUX AND ENABLE_NATIVE_NVENC)) diff --git a/plugins/obs-ffmpeg/cmake/legacy.cmake b/plugins/obs-ffmpeg/cmake/legacy.cmake index 0b3491a07feded..558a34df62656e 100644 --- a/plugins/obs-ffmpeg/cmake/legacy.cmake +++ b/plugins/obs-ffmpeg/cmake/legacy.cmake @@ -111,8 +111,9 @@ if(OS_WINDOWS) elseif(OS_POSIX AND NOT OS_MACOS) find_package(Libva REQUIRED) find_package(Libpci REQUIRED) + find_package(Libdrm REQUIRED) target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-vaapi.c vaapi-utils.c vaapi-utils.h) - target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI) + target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI Libdrm::Libdrm) if(ENABLE_NATIVE_NVENC) find_package(FFnvcodec 12.0.0.0...<12.2.0.0 REQUIRED) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c index fabde2aa99b393..96464abd98c2e1 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,9 @@ #include "vaapi-utils.h" #include "obs-ffmpeg-formats.h" +#include +#include + #define do_log(level, format, ...) \ blog(level, "[FFmpeg VAAPI encoder: '%s'] " format, \ obs_encoder_get_name(enc->encoder), ##__VA_ARGS__) @@ -58,12 +62,19 @@ enum codec_type { CODEC_AV1, }; +struct vaapi_surface { + AVFrame *frame; + gs_texture_t *textures[4]; + uint32_t num_textures; +}; + struct vaapi_encoder { obs_encoder_t *encoder; enum codec_type codec; AVBufferRef *vadevice_ref; AVBufferRef *vaframes_ref; + VADisplay va_dpy; const AVCodec *vaapi; AVCodecContext *context; @@ -146,6 +157,11 @@ static bool vaapi_init_codec(struct vaapi_encoder *enc, const char *path) return false; } + AVHWDeviceContext *vahwctx = + (AVHWDeviceContext *)enc->vadevice_ref->data; + AVVAAPIDeviceContext *vadevctx = vahwctx->hwctx; + enc->va_dpy = vadevctx->display; + enc->vaframes_ref = av_hwframe_ctx_alloc(enc->vadevice_ref); if (!enc->vaframes_ref) { warn("Failed to alloc HW frames context"); @@ -158,7 +174,6 @@ static bool vaapi_init_codec(struct vaapi_encoder *enc, const char *path) frames_ctx->sw_format = enc->context->pix_fmt; frames_ctx->width = enc->context->width; frames_ctx->height = enc->context->height; - frames_ctx->initial_pool_size = 20; ret = av_hwframe_ctx_init(enc->vaframes_ref); if (ret < 0) { @@ -376,6 +391,117 @@ static bool vaapi_update(void *data, obs_data_t *settings) return vaapi_init_codec(enc, device); } +static inline enum gs_color_format drm_to_gs_color_format(int format) +{ + switch (format) { + case DRM_FORMAT_R8: + return GS_R8; + case DRM_FORMAT_R16: + return GS_R16; + case DRM_FORMAT_GR88: + return GS_R8G8; + case DRM_FORMAT_GR1616: + return GS_RG16; + default: + blog(LOG_ERROR, "Unsupported DRM format %d", format); + return GS_UNKNOWN; + } +} + +static void vaapi_destroy_surface(struct vaapi_surface *out) +{ + obs_enter_graphics(); + + for (uint32_t i = 0; i < out->num_textures; ++i) { + if (out->textures[i]) { + gs_texture_destroy(out->textures[i]); + out->textures[i] = NULL; + } + } + + obs_leave_graphics(); + + av_frame_free(&out->frame); +} + +static bool vaapi_create_surface(struct vaapi_encoder *enc, + struct vaapi_surface *out) +{ + int ret; + VAStatus vas; + VADRMPRIMESurfaceDescriptor desc; + const AVPixFmtDescriptor *fmt_desc; + bool ok = true; + + memset(out, 0, sizeof(*out)); + + fmt_desc = av_pix_fmt_desc_get(enc->context->pix_fmt); + if (!fmt_desc) { + warn("Failed to get pix fmt descriptor"); + return false; + } + + out->frame = av_frame_alloc(); + if (!out->frame) { + warn("Failed to allocate hw frame"); + return false; + } + + ret = av_hwframe_get_buffer(enc->vaframes_ref, out->frame, 0); + if (ret < 0) { + warn("Failed to get hw frame buffer: %s", av_err2str(ret)); + goto fail; + } + + vas = vaExportSurfaceHandle(enc->va_dpy, (uintptr_t)out->frame->data[3], + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_WRITE_ONLY | + VA_EXPORT_SURFACE_SEPARATE_LAYERS, + &desc); + if (vas != VA_STATUS_SUCCESS) { + warn("Failed to export VA surface handle: %s", vaErrorStr(vas)); + goto fail; + } + + obs_enter_graphics(); + + for (uint32_t i = 0; i < desc.num_layers; ++i) { + unsigned int width = desc.width; + unsigned int height = desc.height; + if (i) { + width /= 1 << fmt_desc->log2_chroma_w; + height /= 1 << fmt_desc->log2_chroma_h; + } + + out->textures[i] = gs_texture_create_from_dmabuf( + width, height, desc.layers[i].drm_format, + drm_to_gs_color_format(desc.layers[i].drm_format), 1, + &desc.objects[desc.layers[i].object_index[0]].fd, + &desc.layers[i].pitch[0], &desc.layers[i].offset[0], + &desc.objects[desc.layers[i].object_index[0]] + .drm_format_modifier); + + if (!out->textures[i]) { + warn("Failed to import VA surface texture"); + ok = false; + } + + out->num_textures++; + } + + obs_leave_graphics(); + + for (uint32_t i = 0; i < desc.num_objects; ++i) + close(desc.objects[i].fd); + + if (ok) + return true; + +fail: + vaapi_destroy_surface(out); + return false; +} + static inline void flush_remaining_packets(struct vaapi_encoder *enc) { int r_pkt = 1; @@ -457,21 +583,72 @@ static void *vaapi_create_internal(obs_data_t *settings, obs_encoder_t *encoder, return NULL; } +static inline bool vaapi_test_texencode(struct vaapi_encoder *enc) +{ + struct vaapi_surface surface; + + if (obs_encoder_scaling_enabled(enc->encoder) && + !obs_encoder_gpu_scaling_enabled(enc->encoder)) + return false; + + if (!vaapi_create_surface(enc, &surface)) + return false; + + vaapi_destroy_surface(&surface); + return true; +} + +static void *vaapi_create_tex_internal(obs_data_t *settings, + obs_encoder_t *encoder, + enum codec_type codec, + const char *fallback) +{ + void *enc = vaapi_create_internal(settings, encoder, codec); + if (!enc) { + return NULL; + } + if (!vaapi_test_texencode(enc)) { + vaapi_destroy(enc); + blog(LOG_WARNING, "VAAPI: Falling back to %s encoder", + fallback); + return obs_encoder_create_rerouted(encoder, fallback); + } + return enc; +} + static void *h264_vaapi_create(obs_data_t *settings, obs_encoder_t *encoder) { return vaapi_create_internal(settings, encoder, CODEC_H264); } +static void *h264_vaapi_create_tex(obs_data_t *settings, obs_encoder_t *encoder) +{ + return vaapi_create_tex_internal(settings, encoder, CODEC_H264, + "ffmpeg_vaapi"); +} + static void *av1_vaapi_create(obs_data_t *settings, obs_encoder_t *encoder) { return vaapi_create_internal(settings, encoder, CODEC_AV1); } +static void *av1_vaapi_create_tex(obs_data_t *settings, obs_encoder_t *encoder) +{ + return vaapi_create_tex_internal(settings, encoder, CODEC_AV1, + "av1_ffmpeg_vaapi"); +} + #ifdef ENABLE_HEVC static void *hevc_vaapi_create(obs_data_t *settings, obs_encoder_t *encoder) { return vaapi_create_internal(settings, encoder, CODEC_HEVC); } + +static void *hevc_vaapi_create_tex(obs_data_t *settings, obs_encoder_t *encoder) +{ + return vaapi_create_tex_internal(settings, encoder, CODEC_HEVC, + "hevc_ffmpeg_vaapi"); +} #endif static inline void copy_data(AVFrame *pic, const struct encoder_frame *frame, @@ -500,49 +677,14 @@ static inline void copy_data(AVFrame *pic, const struct encoder_frame *frame, } } -static bool vaapi_encode(void *data, struct encoder_frame *frame, - struct encoder_packet *packet, bool *received_packet) +static bool vaapi_encode_internal(struct vaapi_encoder *enc, AVFrame *frame, + struct encoder_packet *packet, + bool *received_packet) { - struct vaapi_encoder *enc = data; - AVFrame *hwframe = NULL; int got_packet; int ret; - hwframe = av_frame_alloc(); - if (!hwframe) { - warn("vaapi_encode: failed to allocate hw frame"); - return false; - } - - ret = av_hwframe_get_buffer(enc->vaframes_ref, hwframe, 0); - if (ret < 0) { - warn("vaapi_encode: failed to get buffer for hw frame: %s", - av_err2str(ret)); - goto fail; - } - - copy_data(enc->vframe, frame, enc->height, enc->context->pix_fmt); - - enc->vframe->pts = frame->pts; - hwframe->pts = frame->pts; - hwframe->width = enc->vframe->width; - hwframe->height = enc->vframe->height; - - ret = av_hwframe_transfer_data(hwframe, enc->vframe, 0); - if (ret < 0) { - warn("vaapi_encode: failed to upload hw frame: %s", - av_err2str(ret)); - goto fail; - } - - ret = av_frame_copy_props(hwframe, enc->vframe); - if (ret < 0) { - warn("vaapi_encode: failed to copy props to hw frame: %s", - av_err2str(ret)); - goto fail; - } - - ret = avcodec_send_frame(enc->context, hwframe); + ret = avcodec_send_frame(enc->context, frame); if (ret == 0 || ret == AVERROR(EAGAIN)) ret = avcodec_receive_packet(enc->context, enc->packet); @@ -619,11 +761,63 @@ static bool vaapi_encode(void *data, struct encoder_frame *frame, obs_av1_keyframe(packet->data, packet->size); } *received_packet = true; - } else { - *received_packet = false; } av_packet_unref(enc->packet); + return true; + +fail: + av_packet_unref(enc->packet); + return false; +} + +static bool vaapi_encode_copy(void *data, struct encoder_frame *frame, + struct encoder_packet *packet, + bool *received_packet) +{ + struct vaapi_encoder *enc = data; + AVFrame *hwframe = NULL; + int ret; + + *received_packet = false; + + hwframe = av_frame_alloc(); + if (!hwframe) { + warn("vaapi_encode_copy: failed to allocate hw frame"); + return false; + } + + ret = av_hwframe_get_buffer(enc->vaframes_ref, hwframe, 0); + if (ret < 0) { + warn("vaapi_encode_copy: failed to get buffer for hw frame: %s", + av_err2str(ret)); + goto fail; + } + + copy_data(enc->vframe, frame, enc->height, enc->context->pix_fmt); + + enc->vframe->pts = frame->pts; + hwframe->pts = frame->pts; + hwframe->width = enc->vframe->width; + hwframe->height = enc->vframe->height; + + ret = av_hwframe_transfer_data(hwframe, enc->vframe, 0); + if (ret < 0) { + warn("vaapi_encode_copy: failed to upload hw frame: %s", + av_err2str(ret)); + goto fail; + } + + ret = av_frame_copy_props(hwframe, enc->vframe); + if (ret < 0) { + warn("vaapi_encode_copy: failed to copy props to hw frame: %s", + av_err2str(ret)); + goto fail; + } + + if (!vaapi_encode_internal(enc, hwframe, packet, received_packet)) + goto fail; + av_frame_free(&hwframe); return true; @@ -632,6 +826,59 @@ static bool vaapi_encode(void *data, struct encoder_frame *frame, return false; } +static bool vaapi_encode_tex(void *data, struct encoder_texture *texture, + int64_t pts, uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, + bool *received_packet) +{ + UNUSED_PARAMETER(lock_key); + UNUSED_PARAMETER(next_key); + + struct vaapi_encoder *enc = data; + struct vaapi_surface surface; + int ret; + + *received_packet = false; + + if (!vaapi_create_surface(enc, &surface)) { + warn("vaapi_encode_tex: failed to create texture hw frame"); + return false; + } + + obs_enter_graphics(); + + for (uint32_t i = 0; i < surface.num_textures; ++i) { + if (!texture->tex[i]) { + warn("vaapi_encode_tex: unexpected number of textures"); + goto fail; + } + gs_copy_texture(surface.textures[i], texture->tex[i]); + } + + gs_flush(); + + obs_leave_graphics(); + + enc->vframe->pts = pts; + + ret = av_frame_copy_props(surface.frame, enc->vframe); + if (ret < 0) { + warn("vaapi_encode_tex: failed to copy props to hw frame: %s", + av_err2str(ret)); + goto fail; + } + + if (!vaapi_encode_internal(enc, surface.frame, packet, received_packet)) + goto fail; + + vaapi_destroy_surface(&surface); + return true; + +fail: + vaapi_destroy_surface(&surface); + return false; +} + static void set_visible(obs_properties_t *ppts, const char *name, bool visible) { obs_property_t *p = obs_properties_get(ppts, name); @@ -1047,12 +1294,29 @@ struct obs_encoder_info h264_vaapi_encoder_info = { .get_name = h264_vaapi_getname, .create = h264_vaapi_create, .destroy = vaapi_destroy, - .encode = vaapi_encode, + .encode = vaapi_encode_copy, .get_defaults = h264_vaapi_defaults, .get_properties = h264_vaapi_properties, .get_extra_data = vaapi_extra_data, .get_sei_data = vaapi_sei_data, .get_video_info = vaapi_video_info, + .caps = OBS_ENCODER_CAP_INTERNAL, +}; + +struct obs_encoder_info h264_vaapi_encoder_tex_info = { + .id = "ffmpeg_vaapi_tex", + .type = OBS_ENCODER_VIDEO, + .codec = "h264", + .get_name = h264_vaapi_getname, + .create = h264_vaapi_create_tex, + .destroy = vaapi_destroy, + .encode_texture2 = vaapi_encode_tex, + .get_defaults = h264_vaapi_defaults, + .get_properties = h264_vaapi_properties, + .get_extra_data = vaapi_extra_data, + .get_sei_data = vaapi_sei_data, + .get_video_info = vaapi_video_info, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE, }; struct obs_encoder_info av1_vaapi_encoder_info = { @@ -1062,12 +1326,29 @@ struct obs_encoder_info av1_vaapi_encoder_info = { .get_name = av1_vaapi_getname, .create = av1_vaapi_create, .destroy = vaapi_destroy, - .encode = vaapi_encode, + .encode = vaapi_encode_copy, + .get_defaults = av1_vaapi_defaults, + .get_properties = av1_vaapi_properties, + .get_extra_data = vaapi_extra_data, + .get_sei_data = vaapi_sei_data, + .get_video_info = vaapi_video_info, + .caps = OBS_ENCODER_CAP_INTERNAL, +}; + +struct obs_encoder_info av1_vaapi_encoder_tex_info = { + .id = "av1_ffmpeg_vaapi_tex", + .type = OBS_ENCODER_VIDEO, + .codec = "av1", + .get_name = av1_vaapi_getname, + .create = av1_vaapi_create_tex, + .destroy = vaapi_destroy, + .encode_texture2 = vaapi_encode_tex, .get_defaults = av1_vaapi_defaults, .get_properties = av1_vaapi_properties, .get_extra_data = vaapi_extra_data, .get_sei_data = vaapi_sei_data, .get_video_info = vaapi_video_info, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE, }; #ifdef ENABLE_HEVC @@ -1078,11 +1359,28 @@ struct obs_encoder_info hevc_vaapi_encoder_info = { .get_name = hevc_vaapi_getname, .create = hevc_vaapi_create, .destroy = vaapi_destroy, - .encode = vaapi_encode, + .encode = vaapi_encode_copy, + .get_defaults = hevc_vaapi_defaults, + .get_properties = hevc_vaapi_properties, + .get_extra_data = vaapi_extra_data, + .get_sei_data = vaapi_sei_data, + .get_video_info = vaapi_video_info, + .caps = OBS_ENCODER_CAP_INTERNAL, +}; + +struct obs_encoder_info hevc_vaapi_encoder_tex_info = { + .id = "hevc_ffmpeg_vaapi_tex", + .type = OBS_ENCODER_VIDEO, + .codec = "hevc", + .get_name = hevc_vaapi_getname, + .create = hevc_vaapi_create_tex, + .destroy = vaapi_destroy, + .encode_texture2 = vaapi_encode_tex, .get_defaults = hevc_vaapi_defaults, .get_properties = hevc_vaapi_properties, .get_extra_data = vaapi_extra_data, .get_sei_data = vaapi_sei_data, .get_video_info = vaapi_video_info, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE, }; #endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c index 451b4f022e546f..c20281f631986b 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg.c @@ -50,9 +50,12 @@ extern struct obs_encoder_info aom_av1_encoder_info; #ifdef LIBAVUTIL_VAAPI_AVAILABLE extern struct obs_encoder_info h264_vaapi_encoder_info; +extern struct obs_encoder_info h264_vaapi_encoder_tex_info; extern struct obs_encoder_info av1_vaapi_encoder_info; +extern struct obs_encoder_info av1_vaapi_encoder_tex_info; #ifdef ENABLE_HEVC extern struct obs_encoder_info hevc_vaapi_encoder_info; +extern struct obs_encoder_info hevc_vaapi_encoder_tex_info; #endif #endif @@ -404,6 +407,7 @@ bool obs_module_load(void) if (h264_vaapi_supported()) { blog(LOG_INFO, "FFmpeg VAAPI H264 encoding supported"); obs_register_encoder(&h264_vaapi_encoder_info); + obs_register_encoder(&h264_vaapi_encoder_tex_info); } else { blog(LOG_INFO, "FFmpeg VAAPI H264 encoding not supported"); } @@ -411,6 +415,7 @@ bool obs_module_load(void) if (av1_vaapi_supported()) { blog(LOG_INFO, "FFmpeg VAAPI AV1 encoding supported"); obs_register_encoder(&av1_vaapi_encoder_info); + obs_register_encoder(&av1_vaapi_encoder_tex_info); } else { blog(LOG_INFO, "FFmpeg VAAPI AV1 encoding not supported"); } @@ -419,6 +424,7 @@ bool obs_module_load(void) if (hevc_vaapi_supported()) { blog(LOG_INFO, "FFmpeg VAAPI HEVC encoding supported"); obs_register_encoder(&hevc_vaapi_encoder_info); + obs_register_encoder(&hevc_vaapi_encoder_tex_info); } else { blog(LOG_INFO, "FFmpeg VAAPI HEVC encoding not supported"); } From ebea7093811f5ee7d9af97fd64807a0ecbe6a845 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Thu, 2 Feb 2023 21:00:26 -0800 Subject: [PATCH 0021/1073] obs-qsv11: Implement texture encoder on Linux Implements the texture allocator for QSV using VA-API surfaces and implement encode_texture2 for using OpenGL textures directly. --- plugins/obs-qsv11/QSV_Encoder.cpp | 6 +- plugins/obs-qsv11/QSV_Encoder.h | 2 +- plugins/obs-qsv11/QSV_Encoder_Internal.cpp | 23 +- plugins/obs-qsv11/QSV_Encoder_Internal.h | 7 +- plugins/obs-qsv11/common_directx11.cpp | 9 +- plugins/obs-qsv11/common_utils.h | 4 +- plugins/obs-qsv11/common_utils_linux.cpp | 271 ++++++++++++++++++--- plugins/obs-qsv11/obs-qsv11.c | 23 +- 8 files changed, 276 insertions(+), 69 deletions(-) diff --git a/plugins/obs-qsv11/QSV_Encoder.cpp b/plugins/obs-qsv11/QSV_Encoder.cpp index 14ea63d93b57ec..d6f1d7b50d3160 100644 --- a/plugins/obs-qsv11/QSV_Encoder.cpp +++ b/plugins/obs-qsv11/QSV_Encoder.cpp @@ -106,7 +106,7 @@ qsv_t *qsv_encoder_open(qsv_param_t *pParams, enum qsv_codec codec) WARN_ERR(MFX_ERR_NOT_FOUND, "Specified object/item/sync point not found."); WARN_ERR(MFX_ERR_MEMORY_ALLOC, - "Gailed to allocate memory"); + "Failed to allocate memory"); WARN_ERR(MFX_ERR_LOCK_MEMORY, "failed to lock the memory block " "(external allocator)."); @@ -212,14 +212,14 @@ int qsv_encoder_encode(qsv_t *pContext, uint64_t ts, uint8_t *pDataY, return -1; } -int qsv_encoder_encode_tex(qsv_t *pContext, uint64_t ts, uint32_t tex_handle, +int qsv_encoder_encode_tex(qsv_t *pContext, uint64_t ts, void *tex, uint64_t lock_key, uint64_t *next_key, mfxBitstream **pBS) { QSV_Encoder_Internal *pEncoder = (QSV_Encoder_Internal *)pContext; mfxStatus sts = MFX_ERR_NONE; - sts = pEncoder->Encode_tex(ts, tex_handle, lock_key, next_key, pBS); + sts = pEncoder->Encode_tex(ts, tex, lock_key, next_key, pBS); if (sts == MFX_ERR_NONE) return 0; diff --git a/plugins/obs-qsv11/QSV_Encoder.h b/plugins/obs-qsv11/QSV_Encoder.h index 8bbd9b2db5f321..bacab5d5042b61 100644 --- a/plugins/obs-qsv11/QSV_Encoder.h +++ b/plugins/obs-qsv11/QSV_Encoder.h @@ -162,7 +162,7 @@ void qsv_encoder_add_roi(qsv_t *, const struct obs_encoder_roi *roi); void qsv_encoder_clear_roi(qsv_t *pContext); int qsv_encoder_encode(qsv_t *, uint64_t, uint8_t *, uint8_t *, uint32_t, uint32_t, mfxBitstream **pBS); -int qsv_encoder_encode_tex(qsv_t *, uint64_t, uint32_t, uint64_t, uint64_t *, +int qsv_encoder_encode_tex(qsv_t *, uint64_t, void *, uint64_t, uint64_t *, mfxBitstream **pBS); int qsv_encoder_headers(qsv_t *, uint8_t **pSPS, uint8_t **pPPS, uint16_t *pnSPS, uint16_t *pnPPS); diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp index f8c681ab4837b3..782bde40128ed1 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp @@ -68,7 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) -mfxHDL QSV_Encoder_Internal::g_DX_Handle = NULL; +mfxHDL QSV_Encoder_Internal::g_GFX_Handle = NULL; mfxU16 QSV_Encoder_Internal::g_numEncodersOpen = 0; QSV_Encoder_Internal::QSV_Encoder_Internal(mfxVersion &version) @@ -109,7 +109,7 @@ QSV_Encoder_Internal::QSV_Encoder_Internal(mfxVersion &version) cfg, (const mfxU8 *)"mfxImplDescription.AccelerationMode", tempImpl); #else - m_bUseTexAlloc = false; + m_bUseTexAlloc = true; tempImpl.Type = MFX_VARIANT_TYPE_U32; tempImpl.Data.U32 = MFX_ACCEL_MODE_VIA_VAAPI; MFXSetConfigFilterProperty( @@ -140,18 +140,13 @@ mfxStatus QSV_Encoder_Internal::Open(qsv_param_t *pParams, enum qsv_codec codec) { mfxStatus sts = MFX_ERR_NONE; -#if defined(_WIN32) - if (m_bUseD3D11) - // Use D3D11 surface + if (m_bUseD3D11 | m_bUseTexAlloc) + // Use texture surface sts = Initialize(m_ver, &m_session, &m_mfxAllocator, - &g_DX_Handle, false, codec, &m_sessionData); + &g_GFX_Handle, false, codec, &m_sessionData); else - sts = Initialize(m_ver, &m_session, NULL, NULL, NULL, codec, + sts = Initialize(m_ver, &m_session, NULL, NULL, false, codec, &m_sessionData); -#else - sts = Initialize(m_ver, &m_session, NULL, NULL, false, codec, - &m_sessionData); -#endif MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); @@ -801,7 +796,7 @@ mfxStatus QSV_Encoder_Internal::Encode(uint64_t ts, uint8_t *pDataY, return sts; } -mfxStatus QSV_Encoder_Internal::Encode_tex(uint64_t ts, uint32_t tex_handle, +mfxStatus QSV_Encoder_Internal::Encode_tex(uint64_t ts, void *tex, uint64_t lock_key, uint64_t *next_key, mfxBitstream **pBS) @@ -838,7 +833,7 @@ mfxStatus QSV_Encoder_Internal::Encode_tex(uint64_t ts, uint32_t tex_handle, if (m_bUseTexAlloc) { // mfxU64 isn't consistent with stdint, requiring a cast to be multi-platform. sts = simple_copytex(m_mfxAllocator.pthis, pSurface->Data.MemId, - tex_handle, lock_key, (mfxU64 *)next_key); + tex, lock_key, (mfxU64 *)next_key); MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); } @@ -923,7 +918,7 @@ mfxStatus QSV_Encoder_Internal::ClearData() if ((m_bUseTexAlloc) && (g_numEncodersOpen <= 0)) { Release(); - g_DX_Handle = NULL; + g_GFX_Handle = NULL; } MFXVideoENCODE_Close(m_session); ReleaseSessionData(m_sessionData); diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.h b/plugins/obs-qsv11/QSV_Encoder_Internal.h index 9377c7453ab3ef..bfe421003357ae 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.h +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.h @@ -74,9 +74,8 @@ class QSV_Encoder_Internal { mfxStatus Encode(uint64_t ts, uint8_t *pDataY, uint8_t *pDataUV, uint32_t strideY, uint32_t strideUV, mfxBitstream **pBS); - mfxStatus Encode_tex(uint64_t ts, uint32_t tex_handle, - uint64_t lock_key, uint64_t *next_key, - mfxBitstream **pBS); + mfxStatus Encode_tex(uint64_t ts, void *tex, uint64_t lock_key, + uint64_t *next_key, mfxBitstream **pBS); mfxStatus ClearData(); mfxStatus Reset(qsv_param_t *pParams, enum qsv_codec codec); mfxStatus ReconfigureEncoder(); @@ -134,7 +133,7 @@ class QSV_Encoder_Internal { bool m_bUseTexAlloc; static mfxU16 g_numEncodersOpen; static mfxHDL - g_DX_Handle; // we only want one handle for all instances to use; + g_GFX_Handle; // we only want one handle for all instances to use; mfxEncodeCtrl m_ctrl; mfxExtEncoderROI m_roi; diff --git a/plugins/obs-qsv11/common_directx11.cpp b/plugins/obs-qsv11/common_directx11.cpp index 2bf9378ffc8821..8793cbc09be364 100644 --- a/plugins/obs-qsv11/common_directx11.cpp +++ b/plugins/obs-qsv11/common_directx11.cpp @@ -1,5 +1,7 @@ #include "common_directx11.h" +#include +#include #include ID3D11Device *g_pD3D11Device = nullptr; @@ -433,10 +435,11 @@ mfxStatus simple_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) return MFX_ERR_NONE; } -mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, mfxU32 tex_handle, - mfxU64 lock_key, mfxU64 *next_key) +mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, void *tex, mfxU64 lock_key, + mfxU64 *next_key) { pthis; // To suppress warning for this unused parameter + struct encoder_texture *ptex = (struct encoder_texture *)tex; CustomMemId *memId = (CustomMemId *)mid; ID3D11Texture2D *pSurface = (ID3D11Texture2D *)memId->memId; @@ -445,7 +448,7 @@ mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, mfxU32 tex_handle, ID3D11Texture2D *input_tex; HRESULT hr; - hr = g_pD3D11Device->OpenSharedResource((HANDLE)(uintptr_t)tex_handle, + hr = g_pD3D11Device->OpenSharedResource((HANDLE)(uintptr_t)ptex->handle, IID_ID3D11Texture2D, (void **)&input_tex); if (FAILED(hr)) { diff --git a/plugins/obs-qsv11/common_utils.h b/plugins/obs-qsv11/common_utils.h index 46fcabe4c0a2e6..2c4251205d563c 100644 --- a/plugins/obs-qsv11/common_utils.h +++ b/plugins/obs-qsv11/common_utils.h @@ -117,8 +117,8 @@ mfxStatus simple_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); mfxStatus simple_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr); mfxStatus simple_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle); mfxStatus simple_free(mfxHDL pthis, mfxFrameAllocResponse *response); -mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, mfxU32 tex_handle, - mfxU64 lock_key, mfxU64 *next_key); +mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, void *tex, mfxU64 lock_key, + mfxU64 *next_key); // ================================================================= // Utility functions, not directly tied to VPL functionality diff --git a/plugins/obs-qsv11/common_utils_linux.cpp b/plugins/obs-qsv11/common_utils_linux.cpp index 4ddb3a127c1b28..3b4e5de3b28293 100644 --- a/plugins/obs-qsv11/common_utils_linux.cpp +++ b/plugins/obs-qsv11/common_utils_linux.cpp @@ -2,9 +2,9 @@ #include #include -#include -#include + #include +#include #include #include #include @@ -14,7 +14,12 @@ #include #include +#include #include +#include +#include +#include +#include // Set during check_adapters to work-around VPL dispatcher not setting a VADisplay // for the MSDK runtime. @@ -27,13 +32,145 @@ struct linux_data { VADisplay vaDisplay; }; +#define DEVICE_MGR_TYPE MFX_HANDLE_VA_DISPLAY +// This ends up at like 72 for 1440p@120 AV1. +// We may end up hitting this in practice? +constexpr int32_t MAX_ALLOCABLE_SURFACES = 128; + +struct surface_info { + VASurfaceID id; + int32_t width, height; + gs_texture_t *tex_y; + gs_texture_t *tex_uv; +}; + mfxStatus simple_alloc(mfxHDL pthis, mfxFrameAllocRequest *request, mfxFrameAllocResponse *response) { - UNUSED_PARAMETER(pthis); - UNUSED_PARAMETER(request); - UNUSED_PARAMETER(response); - return MFX_ERR_UNSUPPORTED; + if (request->Type & (MFX_MEMTYPE_SYSTEM_MEMORY | + MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)) + return MFX_ERR_UNSUPPORTED; + + response->mids = (mfxMemId *)nullptr; + response->NumFrameActual = 0; + + mfxSession *session = (mfxSession *)pthis; + VADisplay display; + mfxStatus sts = + MFXVideoCORE_GetHandle(*session, DEVICE_MGR_TYPE, &display); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + // https://ffmpeg.org/doxygen/5.1/hwcontext__vaapi_8c_source.html#l00109 + // though earlier comments suggest the driver ignores rt_format so we could choose whatever. + unsigned int rt_format; + int32_t pix_format; + switch (request->Info.FourCC) { + case MFX_FOURCC_P010: + rt_format = VA_RT_FORMAT_YUV420_10; + pix_format = VA_FOURCC_P010; + break; + case MFX_FOURCC_NV12: + default: + rt_format = VA_RT_FORMAT_YUV420; + pix_format = VA_FOURCC_NV12; + break; + } + + int num_attrs = 2; + VASurfaceAttrib attrs[2] = { + { + .type = VASurfaceAttribMemoryType, + .flags = VA_SURFACE_ATTRIB_SETTABLE, + .value = + { + .type = VAGenericValueTypeInteger, + .value = + {.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2}, + }, + }, + { + .type = VASurfaceAttribPixelFormat, + .flags = VA_SURFACE_ATTRIB_SETTABLE, + .value = + { + .type = VAGenericValueTypeInteger, + .value = {.i = (int)pix_format}, + }, + }}; + + unsigned int num_surfaces = request->NumFrameSuggested; + VASurfaceID temp_surfaces[MAX_ALLOCABLE_SURFACES] = {0}; + assert(num_surfaces < MAX_ALLOCABLE_SURFACES); + VAStatus vasts; + if ((vasts = vaCreateSurfaces(display, rt_format, request->Info.Width, + request->Info.Height, temp_surfaces, + num_surfaces, attrs, num_attrs)) != + VA_STATUS_SUCCESS) { + blog(LOG_ERROR, "failed to create surfaces: %d", vasts); + return MFX_ERR_MEMORY_ALLOC; + } + + // Follow the FFmpeg trick and stuff our pointer at the end. + mfxMemId *mids = + (mfxMemId *)bmalloc(sizeof(mfxMemId) * num_surfaces + 1); + struct surface_info *surfaces = (struct surface_info *)bmalloc( + sizeof(struct surface_info) * num_surfaces); + + mids[num_surfaces] = surfaces; // stuff it + for (uint64_t i = 0; i < num_surfaces; i++) { + surfaces[i].id = temp_surfaces[i]; + surfaces[i].width = request->Info.Width; + surfaces[i].height = request->Info.Height; + mids[i] = &surfaces[i]; + + VADRMPRIMESurfaceDescriptor surfDesc = {0}; + if (vaExportSurfaceHandle(display, surfaces[i].id, + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_READ_WRITE, + &surfDesc) != VA_STATUS_SUCCESS) + return MFX_ERR_MEMORY_ALLOC; + + obs_enter_graphics(); + // TODO: P010 format support + assert(surfDesc.num_objects == 1); + int fds[4] = {0}; + uint32_t strides[4] = {0}; + uint32_t offsets[4] = {0}; + uint64_t modifiers[4] = {0}; + fds[0] = + surfDesc.objects[surfDesc.layers[0].object_index[0]].fd; + fds[1] = + surfDesc.objects[surfDesc.layers[1].object_index[0]].fd; + strides[0] = surfDesc.layers[0].pitch[0]; + strides[1] = surfDesc.layers[1].pitch[0]; + offsets[0] = surfDesc.layers[0].offset[0]; + offsets[1] = surfDesc.layers[1].offset[0]; + modifiers[0] = + surfDesc.objects[surfDesc.layers[0].object_index[0]] + .drm_format_modifier; + modifiers[1] = + surfDesc.objects[surfDesc.layers[1].object_index[0]] + .drm_format_modifier; + + surfaces[i].tex_y = gs_texture_create_from_dmabuf( + surfDesc.width, surfDesc.height, + surfDesc.layers[0].drm_format, GS_R8, 1, fds, strides, + offsets, modifiers); + surfaces[i].tex_uv = gs_texture_create_from_dmabuf( + surfDesc.width / 2, surfDesc.height, + surfDesc.layers[1].drm_format, GS_R8G8, 1, fds + 1, + strides + 1, offsets + 1, modifiers + 1); + obs_leave_graphics(); + + close(surfDesc.objects[surfDesc.layers[0].object_index[0]].fd); + if (!surfaces[i].tex_y || !surfaces[i].tex_uv) { + return MFX_ERR_MEMORY_ALLOC; + } + } + + response->mids = (mfxMemId *)mids; + response->NumFrameActual = num_surfaces; + return MFX_ERR_NONE; } mfxStatus simple_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) @@ -55,33 +192,78 @@ mfxStatus simple_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) mfxStatus simple_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) { UNUSED_PARAMETER(pthis); - UNUSED_PARAMETER(mid); - UNUSED_PARAMETER(handle); - return MFX_ERR_UNSUPPORTED; + if (NULL == handle) + return MFX_ERR_INVALID_HANDLE; + + // Seemingly undocumented, but Pair format defined by + // oneVPL-intel-gpu-intel-onevpl-23.1.0/_studio/mfx_lib/encode_hw/av1/linux/base/av1ehw_base_va_packer_lin.cpp + // https://github.com/intel/vpl-gpu-rt/blob/4170dd9fa1ea319dda81b6189616ecc9b178a321/_studio/shared/src/libmfx_core_vaapi.cpp#L1464 + mfxHDLPair *pPair = (mfxHDLPair *)handle; + + // first must be a pointer to a VASurfaceID and will be dereferenced by + // the driver. + pPair->first = &((struct surface_info *)mid)->id; + pPair->second = 0; + + return MFX_ERR_NONE; } mfxStatus simple_free(mfxHDL pthis, mfxFrameAllocResponse *response) { - UNUSED_PARAMETER(pthis); - UNUSED_PARAMETER(response); - return MFX_ERR_UNSUPPORTED; + if (response->mids == nullptr || response->NumFrameActual == 0) + return MFX_ERR_NONE; + + mfxSession *session = (mfxSession *)pthis; + VADisplay display; + mfxStatus sts = + MFXVideoCORE_GetHandle(*session, DEVICE_MGR_TYPE, &display); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + struct surface_info *surfs = + (struct surface_info *)response->mids[response->NumFrameActual]; + VASurfaceID temp_surfaces[MAX_ALLOCABLE_SURFACES] = {0}; + obs_enter_graphics(); + for (int i = 0; i < response->NumFrameActual; i++) { + temp_surfaces[i] = *(VASurfaceID *)response->mids[i]; + gs_texture_destroy(surfs[i].tex_y); + gs_texture_destroy(surfs[i].tex_uv); + } + obs_leave_graphics(); + + bfree(surfs); + bfree(response->mids); + if (vaDestroySurfaces(display, temp_surfaces, + response->NumFrameActual) != VA_STATUS_SUCCESS) + return MFX_ERR_MEMORY_ALLOC; + + return MFX_ERR_NONE; } -mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, mfxU32 tex_handle, - mfxU64 lock_key, mfxU64 *next_key) +mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, void *tex, mfxU64 lock_key, + mfxU64 *next_key) { - UNUSED_PARAMETER(pthis); - UNUSED_PARAMETER(mid); - UNUSED_PARAMETER(tex_handle); UNUSED_PARAMETER(lock_key); UNUSED_PARAMETER(next_key); - return MFX_ERR_UNSUPPORTED; -} -#if 0 -void ClearYUVSurfaceVMem(mfxMemId memId); -void ClearRGBSurfaceVMem(mfxMemId memId); -#endif + profile_start("copy_tex"); + + mfxSession *session = (mfxSession *)pthis; + VADisplay display; + mfxStatus sts = + MFXVideoCORE_GetHandle(*session, DEVICE_MGR_TYPE, &display); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + struct encoder_texture *ptex = (struct encoder_texture *)tex; + struct surface_info *surf = (struct surface_info *)mid; + + obs_enter_graphics(); + gs_copy_texture(surf->tex_y, ptex->tex[0]); + gs_copy_texture(surf->tex_uv, ptex->tex[1]); + obs_leave_graphics(); + + profile_end("copy_tex"); + return MFX_ERR_NONE; +} // Initialize Intel VPL Session, device/display and memory manager mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, @@ -90,7 +272,6 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, void **data) { UNUSED_PARAMETER(ver); - UNUSED_PARAMETER(pmfxAllocator); UNUSED_PARAMETER(deviceHandle); UNUSED_PARAMETER(bCreateSharedHandles); mfxStatus sts = MFX_ERR_NONE; @@ -117,12 +298,24 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, impl); int fd = -1; - if (codec == QSV_CODEC_AVC && default_h264_device) - fd = open(default_h264_device, O_RDWR); - if (codec == QSV_CODEC_HEVC && default_hevc_device) - fd = open(default_hevc_device, O_RDWR); - if (codec == QSV_CODEC_AV1 && default_av1_device) - fd = open(default_av1_device, O_RDWR); + if (pmfxAllocator) { + // TODO: This is broken and we need the ovi adapter to be + // correct for checks earlier in encoder_create to fallback + // properly. + char device_path[128]; + obs_video_info ovi; + obs_get_video_info(&ovi); + // eglQueryDeviceStringEXT( device, EGL_DRM_DEVICE_FILE_EXT); + sprintf(device_path, "/dev/dri/renderD%d", 128 + ovi.adapter); + fd = open(device_path, O_RDWR); + } else { + if (codec == QSV_CODEC_AVC && default_h264_device) + fd = open(default_h264_device, O_RDWR); + else if (codec == QSV_CODEC_HEVC && default_hevc_device) + fd = open(default_hevc_device, O_RDWR); + else if (codec == QSV_CODEC_AV1 && default_av1_device) + fd = open(default_av1_device, O_RDWR); + } if (fd < 0) { blog(LOG_ERROR, "Failed to open device '%s'", default_h264_device); @@ -152,10 +345,22 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, return MFX_ERR_DEVICE_FAILED; } - sts = MFXVideoCORE_SetHandle(*pSession, MFX_HANDLE_VA_DISPLAY, - vaDisplay); + sts = MFXVideoCORE_SetHandle(*pSession, DEVICE_MGR_TYPE, vaDisplay); MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + if (pmfxAllocator) { + // Allow us to access the session during allocation. + pmfxAllocator->pthis = pSession; + pmfxAllocator->Alloc = simple_alloc; + pmfxAllocator->Free = simple_free; + pmfxAllocator->Lock = simple_lock; + pmfxAllocator->Unlock = simple_unlock; + pmfxAllocator->GetHDL = simple_gethdl; + + sts = MFXVideoCORE_SetFrameAllocator(*pSession, pmfxAllocator); + MSDK_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + } + struct linux_data *d = (struct linux_data *)bmalloc(sizeof(struct linux_data)); d->fd = fd; @@ -187,7 +392,7 @@ double TimeDiffMsec(mfxTime tfinish, mfxTime tstart) { UNUSED_PARAMETER(tfinish); UNUSED_PARAMETER(tstart); - //TODO, unused so far it seems + //unused so far return 0.0; } diff --git a/plugins/obs-qsv11/obs-qsv11.c b/plugins/obs-qsv11/obs-qsv11.c index 87f3ab35125939..e202e486e9ef6f 100644 --- a/plugins/obs-qsv11/obs-qsv11.c +++ b/plugins/obs-qsv11/obs-qsv11.c @@ -1420,14 +1420,19 @@ static bool obs_qsv_encode(void *data, struct encoder_frame *frame, return true; } -static bool obs_qsv_encode_tex(void *data, uint32_t handle, int64_t pts, - uint64_t lock_key, uint64_t *next_key, +static bool obs_qsv_encode_tex(void *data, struct encoder_texture *tex, + int64_t pts, uint64_t lock_key, + uint64_t *next_key, struct encoder_packet *packet, bool *received_packet) { struct obs_qsv *obsqsv = data; - if (handle == GS_INVALID_HANDLE) { +#ifdef _WIN32 + if (!tex || tex->handle == GS_INVALID_HANDLE) { +#else + if (!tex || !tex->tex[0] || !tex->tex[1]) { +#endif warn("Encode failed: bad texture handle"); *next_key = lock_key; return false; @@ -1450,8 +1455,8 @@ static bool obs_qsv_encode_tex(void *data, uint32_t handle, int64_t pts, if (obs_encoder_has_roi(obsqsv->encoder)) obs_qsv_setup_rois(obsqsv); - ret = qsv_encoder_encode_tex(obsqsv->context, qsvPTS, handle, lock_key, - next_key, &pBS); + ret = qsv_encoder_encode_tex(obsqsv->context, qsvPTS, (void *)tex, + lock_key, next_key, &pBS); if (ret < 0) { warn("encode failed"); @@ -1480,7 +1485,7 @@ struct obs_encoder_info obs_qsv_encoder_tex = { .destroy = obs_qsv_destroy, .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DEPRECATED, - .encode_texture = obs_qsv_encode_tex, + .encode_texture2 = obs_qsv_encode_tex, .update = obs_qsv_update, .get_properties = obs_qsv_props_h264, .get_defaults = obs_qsv_defaults_h264_v1, @@ -1516,7 +1521,7 @@ struct obs_encoder_info obs_qsv_encoder_tex_v2 = { .destroy = obs_qsv_destroy, .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_ROI, - .encode_texture = obs_qsv_encode_tex, + .encode_texture2 = obs_qsv_encode_tex, .update = obs_qsv_update, .get_properties = obs_qsv_props_h264_v2, .get_defaults = obs_qsv_defaults_h264_v2, @@ -1552,7 +1557,7 @@ struct obs_encoder_info obs_qsv_av1_encoder_tex = { .destroy = obs_qsv_destroy, .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_ROI, - .encode_texture = obs_qsv_encode_tex, + .encode_texture2 = obs_qsv_encode_tex, .update = obs_qsv_update, .get_properties = obs_qsv_props_av1, .get_defaults = obs_qsv_defaults_av1, @@ -1586,7 +1591,7 @@ struct obs_encoder_info obs_qsv_hevc_encoder_tex = { .destroy = obs_qsv_destroy, .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_ROI, - .encode_texture = obs_qsv_encode_tex, + .encode_texture2 = obs_qsv_encode_tex, .update = obs_qsv_update, .get_properties = obs_qsv_props_hevc, .get_defaults = obs_qsv_defaults_hevc, From 68768e13ae0304af3e124c878c3ca5059d2ee777 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Sat, 27 Jan 2024 14:58:33 -0800 Subject: [PATCH 0022/1073] obs-qsv11: Fix adapter detection and fallback to sysmem Pass down texture/host memory choice for fallback encoders. During fallback we don't (can't) initialize a shared texture pool and should use the regular host memory path. This fixes usage on multi-GPU systems, and enables texture encoders. --- plugins/obs-qsv11/QSV_Encoder.cpp | 6 ++-- plugins/obs-qsv11/QSV_Encoder.h | 2 +- plugins/obs-qsv11/QSV_Encoder_Internal.cpp | 6 ++-- plugins/obs-qsv11/QSV_Encoder_Internal.h | 2 +- plugins/obs-qsv11/common_utils_linux.cpp | 38 +++++++++++++++------- plugins/obs-qsv11/obs-qsv11.c | 18 ++++------ 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/plugins/obs-qsv11/QSV_Encoder.cpp b/plugins/obs-qsv11/QSV_Encoder.cpp index d6f1d7b50d3160..a9e6483beaad92 100644 --- a/plugins/obs-qsv11/QSV_Encoder.cpp +++ b/plugins/obs-qsv11/QSV_Encoder.cpp @@ -77,9 +77,11 @@ void qsv_encoder_version(unsigned short *major, unsigned short *minor) *minor = ver.Minor; } -qsv_t *qsv_encoder_open(qsv_param_t *pParams, enum qsv_codec codec) +qsv_t *qsv_encoder_open(qsv_param_t *pParams, enum qsv_codec codec, + bool useTexAlloc) { - QSV_Encoder_Internal *pEncoder = new QSV_Encoder_Internal(ver); + QSV_Encoder_Internal *pEncoder = + new QSV_Encoder_Internal(ver, useTexAlloc); mfxStatus sts = pEncoder->Open(pParams, codec); if (sts != MFX_ERR_NONE) { diff --git a/plugins/obs-qsv11/QSV_Encoder.h b/plugins/obs-qsv11/QSV_Encoder.h index bacab5d5042b61..ab266774c6fe14 100644 --- a/plugins/obs-qsv11/QSV_Encoder.h +++ b/plugins/obs-qsv11/QSV_Encoder.h @@ -157,7 +157,7 @@ int qsv_param_default_preset(qsv_param_t *, const char *preset, const char *tune); int qsv_encoder_reconfig(qsv_t *, qsv_param_t *); void qsv_encoder_version(unsigned short *major, unsigned short *minor); -qsv_t *qsv_encoder_open(qsv_param_t *, enum qsv_codec codec); +qsv_t *qsv_encoder_open(qsv_param_t *, enum qsv_codec codec, bool useTexAlloc); void qsv_encoder_add_roi(qsv_t *, const struct obs_encoder_roi *roi); void qsv_encoder_clear_roi(qsv_t *pContext); int qsv_encoder_encode(qsv_t *, uint64_t, uint8_t *, uint8_t *, uint32_t, diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp index 782bde40128ed1..695d84ef533882 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp @@ -71,7 +71,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mfxHDL QSV_Encoder_Internal::g_GFX_Handle = NULL; mfxU16 QSV_Encoder_Internal::g_numEncodersOpen = 0; -QSV_Encoder_Internal::QSV_Encoder_Internal(mfxVersion &version) +QSV_Encoder_Internal::QSV_Encoder_Internal(mfxVersion &version, + bool useTexAlloc) : m_pmfxSurfaces(NULL), m_pmfxENC(NULL), m_nSPSBufferSize(1024), @@ -81,6 +82,8 @@ QSV_Encoder_Internal::QSV_Encoder_Internal(mfxVersion &version) m_nTaskIdx(0), m_nFirstSyncTask(0), m_outBitstream(), + m_bUseD3D11(false), + m_bUseTexAlloc(useTexAlloc), m_sessionData(NULL), m_ver(version) { @@ -109,7 +112,6 @@ QSV_Encoder_Internal::QSV_Encoder_Internal(mfxVersion &version) cfg, (const mfxU8 *)"mfxImplDescription.AccelerationMode", tempImpl); #else - m_bUseTexAlloc = true; tempImpl.Type = MFX_VARIANT_TYPE_U32; tempImpl.Data.U32 = MFX_ACCEL_MODE_VIA_VAAPI; MFXSetConfigFilterProperty( diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.h b/plugins/obs-qsv11/QSV_Encoder_Internal.h index bfe421003357ae..59c078ca9b78e5 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.h +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.h @@ -63,7 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. class QSV_Encoder_Internal { public: - QSV_Encoder_Internal(mfxVersion &version); + QSV_Encoder_Internal(mfxVersion &version, bool useTexAlloc); ~QSV_Encoder_Internal(); mfxStatus Open(qsv_param_t *pParams, enum qsv_codec codec); diff --git a/plugins/obs-qsv11/common_utils_linux.cpp b/plugins/obs-qsv11/common_utils_linux.cpp index 3b4e5de3b28293..a54cdd0328b86c 100644 --- a/plugins/obs-qsv11/common_utils_linux.cpp +++ b/plugins/obs-qsv11/common_utils_linux.cpp @@ -265,6 +265,21 @@ mfxStatus simple_copytex(mfxHDL pthis, mfxMemId mid, void *tex, mfxU64 lock_key, return MFX_ERR_NONE; } +struct get_drm_device_params { + const char **out_path; + uint32_t idx; +}; + +bool get_drm_device(void *param, const char *node, uint32_t idx) +{ + struct get_drm_device_params *p = (struct get_drm_device_params *)param; + if (idx == p->idx) { + *p->out_path = node; + return false; + } + return true; +} + // Initialize Intel VPL Session, device/display and memory manager mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, mfxFrameAllocator *pmfxAllocator, mfxHDL *deviceHandle, @@ -297,28 +312,27 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, cfg, (const mfxU8 *)"mfxImplDescription.AccelerationMode", impl); + const char *device_path = NULL; int fd = -1; if (pmfxAllocator) { - // TODO: This is broken and we need the ovi adapter to be - // correct for checks earlier in encoder_create to fallback - // properly. - char device_path[128]; obs_video_info ovi; obs_get_video_info(&ovi); - // eglQueryDeviceStringEXT( device, EGL_DRM_DEVICE_FILE_EXT); - sprintf(device_path, "/dev/dri/renderD%d", 128 + ovi.adapter); - fd = open(device_path, O_RDWR); + struct get_drm_device_params params = {&device_path, + (uint32_t)ovi.adapter}; + obs_enter_graphics(); + gs_enum_adapters(get_drm_device, ¶ms); + obs_leave_graphics(); } else { if (codec == QSV_CODEC_AVC && default_h264_device) - fd = open(default_h264_device, O_RDWR); + device_path = default_h264_device; else if (codec == QSV_CODEC_HEVC && default_hevc_device) - fd = open(default_hevc_device, O_RDWR); + device_path = default_hevc_device; else if (codec == QSV_CODEC_AV1 && default_av1_device) - fd = open(default_av1_device, O_RDWR); + device_path = default_av1_device; } + fd = open(device_path, O_RDWR); if (fd < 0) { - blog(LOG_ERROR, "Failed to open device '%s'", - default_h264_device); + blog(LOG_ERROR, "Failed to open device '%s'", device_path); return MFX_ERR_DEVICE_FAILED; } diff --git a/plugins/obs-qsv11/obs-qsv11.c b/plugins/obs-qsv11/obs-qsv11.c index e202e486e9ef6f..b899087acb6353 100644 --- a/plugins/obs-qsv11/obs-qsv11.c +++ b/plugins/obs-qsv11/obs-qsv11.c @@ -824,7 +824,7 @@ static bool obs_qsv_update(void *data, obs_data_t *settings) } static void *obs_qsv_create(enum qsv_codec codec, obs_data_t *settings, - obs_encoder_t *encoder) + obs_encoder_t *encoder, bool useTexAlloc) { struct obs_qsv *obsqsv = bzalloc(sizeof(struct obs_qsv)); obsqsv->encoder = encoder; @@ -869,7 +869,8 @@ static void *obs_qsv_create(enum qsv_codec codec, obs_data_t *settings, if (update_settings(obsqsv, settings)) { pthread_mutex_lock(&g_QsvLock); - obsqsv->context = qsv_encoder_open(&obsqsv->params, codec); + obsqsv->context = + qsv_encoder_open(&obsqsv->params, codec, useTexAlloc); pthread_mutex_unlock(&g_QsvLock); if (obsqsv->context == NULL) @@ -922,17 +923,17 @@ static void *obs_qsv_create(enum qsv_codec codec, obs_data_t *settings, static void *obs_qsv_create_h264(obs_data_t *settings, obs_encoder_t *encoder) { - return obs_qsv_create(QSV_CODEC_AVC, settings, encoder); + return obs_qsv_create(QSV_CODEC_AVC, settings, encoder, false); } static void *obs_qsv_create_av1(obs_data_t *settings, obs_encoder_t *encoder) { - return obs_qsv_create(QSV_CODEC_AV1, settings, encoder); + return obs_qsv_create(QSV_CODEC_AV1, settings, encoder, false); } static void *obs_qsv_create_hevc(obs_data_t *settings, obs_encoder_t *encoder) { - return obs_qsv_create(QSV_CODEC_HEVC, settings, encoder); + return obs_qsv_create(QSV_CODEC_HEVC, settings, encoder, false); } static void *obs_qsv_create_tex(enum qsv_codec codec, obs_data_t *settings, @@ -941,11 +942,6 @@ static void *obs_qsv_create_tex(enum qsv_codec codec, obs_data_t *settings, struct obs_video_info ovi; obs_get_video_info(&ovi); -#if !defined(_WIN32) - blog(LOG_INFO, ">>> fall back to non-texture sharing on this platform"); - return obs_encoder_create_rerouted(encoder, (const char *)fallback_id); -#endif - if (!adapters[ovi.adapter].is_intel) { blog(LOG_INFO, ">>> app not on intel GPU, fall back to old qsv encoder"); @@ -984,7 +980,7 @@ static void *obs_qsv_create_tex(enum qsv_codec codec, obs_data_t *settings, } blog(LOG_INFO, ">>> new qsv encoder"); - return obs_qsv_create(codec, settings, encoder); + return obs_qsv_create(codec, settings, encoder, true); } static void *obs_qsv_create_tex_h264(obs_data_t *settings, From e885d25f5b7caa86943794a6062990eaa70c5dfb Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 14 Jan 2024 04:14:07 +0100 Subject: [PATCH 0023/1073] libobs: Add os_process_args_t and associated functions --- libobs/CMakeLists.txt | 1 + libobs/cmake/legacy.cmake | 1 + libobs/util/pipe.c | 76 +++++++++++++++++++++++++++++++++++++++ libobs/util/pipe.h | 15 ++++++++ 4 files changed, 93 insertions(+) create mode 100644 libobs/util/pipe.c diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 0cad40a2fc1b0e..affb0c966bcc07 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -118,6 +118,7 @@ target_sources( util/file-serializer.h util/lexer.c util/lexer.h + util/pipe.c util/pipe.h util/platform.c util/platform.h diff --git a/libobs/cmake/legacy.cmake b/libobs/cmake/legacy.cmake index 5e92972e53c84d..d3f307a57e4d86 100644 --- a/libobs/cmake/legacy.cmake +++ b/libobs/cmake/legacy.cmake @@ -198,6 +198,7 @@ target_sources( util/profiler.c util/profiler.h util/profiler.hpp + util/pipe.c util/pipe.h util/serializer.h util/sse-intrin.h diff --git a/libobs/util/pipe.c b/libobs/util/pipe.c new file mode 100644 index 00000000000000..59284877654d36 --- /dev/null +++ b/libobs/util/pipe.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Dennis Sädtler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "pipe.h" +#include "darray.h" +#include "dstr.h" + +struct os_process_args { + DARRAY(char *) arguments; +}; + +struct os_process_args *os_process_args_create(const char *executable) +{ + struct os_process_args *args = bzalloc(sizeof(struct os_process_args)); + char *str = bstrdup(executable); + da_push_back(args->arguments, &str); + /* Last item in argv must be NULL. */ + char *terminator = NULL; + da_push_back(args->arguments, &terminator); + return args; +} + +void os_process_args_add_arg(struct os_process_args *args, const char *arg) +{ + char *str = bstrdup(arg); + /* Insert before NULL list terminator. */ + da_insert(args->arguments, args->arguments.num - 1, &str); +} + +void os_process_args_add_argf(struct os_process_args *args, const char *format, + ...) +{ + va_list va_args; + struct dstr tmp = {0}; + + va_start(va_args, format); + dstr_vprintf(&tmp, format, va_args); + da_insert(args->arguments, args->arguments.num - 1, &tmp.array); + va_end(va_args); +} + +size_t os_process_args_get_argc(struct os_process_args *args) +{ + /* Do not count terminating NULL. */ + return args->arguments.num - 1; +} + +char **os_process_args_get_argv(const struct os_process_args *args) +{ + return args->arguments.array; +} + +void os_process_args_destroy(struct os_process_args *args) +{ + if (!args) + return; + + for (size_t idx = 0; idx < args->arguments.num; idx++) + bfree(args->arguments.array[idx]); + + da_free(args->arguments); + bfree(args); +} diff --git a/libobs/util/pipe.h b/libobs/util/pipe.h index 23353b8bee2139..bc80b74135c16b 100644 --- a/libobs/util/pipe.h +++ b/libobs/util/pipe.h @@ -25,6 +25,9 @@ extern "C" { struct os_process_pipe; typedef struct os_process_pipe os_process_pipe_t; +struct os_process_args; +typedef struct os_process_args os_process_args_t; + EXPORT os_process_pipe_t *os_process_pipe_create(const char *cmd_line, const char *type); EXPORT int os_process_pipe_destroy(os_process_pipe_t *pp); @@ -36,6 +39,18 @@ EXPORT size_t os_process_pipe_read_err(os_process_pipe_t *pp, uint8_t *data, EXPORT size_t os_process_pipe_write(os_process_pipe_t *pp, const uint8_t *data, size_t len); +EXPORT struct os_process_args *os_process_args_create(const char *executable); +EXPORT void os_process_args_add_arg(struct os_process_args *args, + const char *arg); +#ifndef _MSC_VER +__attribute__((__format__(__printf__, 2, 3))) +#endif +EXPORT void +os_process_args_add_argf(struct os_process_args *args, const char *format, ...); +EXPORT char **os_process_args_get_argv(const struct os_process_args *args); +EXPORT size_t os_process_args_get_argc(struct os_process_args *args); +EXPORT void os_process_args_destroy(struct os_process_args *args); + #ifdef __cplusplus } #endif From 9bc3082402cd842e7ce5c729700fe261ae6a6f0b Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 14 Jan 2024 04:17:53 +0100 Subject: [PATCH 0024/1073] libobs: Add os_process_pipe_create2 This new API uses the os_process_args_t object rather than a string for more safe and sane command line argument handling. --- libobs/util/pipe-posix.c | 28 +++++++++++++---- libobs/util/pipe-windows.c | 64 ++++++++++++++++++++++++++++++++++++++ libobs/util/pipe.h | 2 ++ 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/libobs/util/pipe-posix.c b/libobs/util/pipe-posix.c index e78c0944375e9c..6f06752d86f3b9 100644 --- a/libobs/util/pipe-posix.c +++ b/libobs/util/pipe-posix.c @@ -31,14 +31,14 @@ struct os_process_pipe { FILE *err_file; }; -os_process_pipe_t *os_process_pipe_create(const char *cmd_line, - const char *type) +os_process_pipe_t *os_process_pipe_create_internal(const char *bin, char **argv, + const char *type) { struct os_process_pipe process_pipe = {0}; struct os_process_pipe *out; posix_spawn_file_actions_t file_actions; - if (!cmd_line || !type) { + if (!bin || !argv || !type) { return NULL; } @@ -94,9 +94,8 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, STDERR_FILENO); int pid; - char *argv[4] = {"sh", "-c", (char *)cmd_line, NULL}; - - int ret = posix_spawn(&pid, "/bin/sh", &file_actions, NULL, argv, NULL); + int ret = posix_spawn(&pid, bin, &file_actions, NULL, + (char *const *)argv, NULL); posix_spawn_file_actions_destroy(&file_actions); @@ -127,6 +126,23 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, return out; } +os_process_pipe_t *os_process_pipe_create(const char *cmd_line, + const char *type) +{ + if (!cmd_line) + return NULL; + + char *argv[3] = {"-c", (char *)cmd_line, NULL}; + return os_process_pipe_create_internal("/bin/sh", argv, type); +} + +os_process_pipe_t *os_process_pipe_create2(const os_process_args_t *args, + const char *type) +{ + char **argv = os_process_args_get_argv(args); + return os_process_pipe_create_internal(argv[0], argv, type); +} + int os_process_pipe_destroy(os_process_pipe_t *pp) { int ret = 0; diff --git a/libobs/util/pipe-windows.c b/libobs/util/pipe-windows.c index 65d52ba1e145b7..3bceab522e0973 100644 --- a/libobs/util/pipe-windows.c +++ b/libobs/util/pipe-windows.c @@ -19,6 +19,7 @@ #include "platform.h" #include "bmem.h" +#include "dstr.h" #include "pipe.h" struct os_process_pipe { @@ -145,6 +146,69 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, return NULL; } +static inline void add_backslashes(struct dstr *str, size_t count) +{ + while (count--) + dstr_cat_ch(str, '\\'); +} + +os_process_pipe_t *os_process_pipe_create2(const os_process_args_t *args, + const char *type) +{ + struct dstr cmd_line = {0}; + + /* Convert list to command line as Windows does not have any API that + * allows us to just pass argc/argv. */ + char **argv = os_process_args_get_argv(args); + + /* Based on Python subprocess module implementation. */ + while (*argv) { + size_t bs_count = 0; + const char *arg = *argv; + bool needs_quotes = strlen(arg) == 0 || + strstr(arg, " ") != NULL || + strstr(arg, "\t") != NULL; + + if (cmd_line.len) + dstr_cat_ch(&cmd_line, ' '); + if (needs_quotes) + dstr_cat_ch(&cmd_line, '"'); + + while (*arg) { + if (*arg == '\\') { + bs_count++; + } else if (*arg == '"') { + add_backslashes(&cmd_line, bs_count * 2); + dstr_cat(&cmd_line, "\\\""); + bs_count = 0; + } else { + if (bs_count) { + add_backslashes(&cmd_line, bs_count); + bs_count = 0; + } + dstr_cat_ch(&cmd_line, *arg); + } + + arg++; + } + + if (bs_count) + add_backslashes(&cmd_line, bs_count); + + if (needs_quotes) { + add_backslashes(&cmd_line, bs_count); + dstr_cat_ch(&cmd_line, '"'); + } + + argv++; + } + + os_process_pipe_t *ret = os_process_pipe_create(cmd_line.array, type); + + dstr_free(&cmd_line); + return ret; +} + int os_process_pipe_destroy(os_process_pipe_t *pp) { int ret = 0; diff --git a/libobs/util/pipe.h b/libobs/util/pipe.h index bc80b74135c16b..2c39ad7547df3c 100644 --- a/libobs/util/pipe.h +++ b/libobs/util/pipe.h @@ -30,6 +30,8 @@ typedef struct os_process_args os_process_args_t; EXPORT os_process_pipe_t *os_process_pipe_create(const char *cmd_line, const char *type); +EXPORT os_process_pipe_t *os_process_pipe_create2(const os_process_args_t *args, + const char *type); EXPORT int os_process_pipe_destroy(os_process_pipe_t *pp); EXPORT size_t os_process_pipe_read(os_process_pipe_t *pp, uint8_t *data, From 8d2f4ed3e1da64b73444e9ce5353d4e65f34c9ad Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 14 Jan 2024 04:48:59 +0100 Subject: [PATCH 0025/1073] obs-ffmpeg-mux: Use new process args/pipe API --- plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 106 ++++++++++++++-------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index b6bc1da8cef5aa..b1b46f88fb4325 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -134,7 +134,8 @@ bool active(struct ffmpeg_muxer *stream) } static void add_video_encoder_params(struct ffmpeg_muxer *stream, - struct dstr *cmd, obs_encoder_t *vencoder) + os_process_args_t *args, + obs_encoder_t *vencoder) { obs_data_t *settings = obs_encoder_get_settings(vencoder); int bitrate = (int)obs_data_get_int(settings, "bitrate"); @@ -185,41 +186,48 @@ static void add_video_encoder_params(struct ffmpeg_muxer *stream, const enum AVColorRange range = (info->range == VIDEO_RANGE_FULL) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + const enum AVChromaLocation chroma_location = determine_chroma_location( + obs_to_ffmpeg_video_format(info->format), spc); const int max_luminance = (trc == AVCOL_TRC_SMPTE2084) ? (int)obs_get_video_hdr_nominal_peak_level() : ((trc == AVCOL_TRC_ARIB_STD_B67) ? 1000 : 0); - dstr_catf(cmd, "%s %d %d %d %d %d %d %d %d %d %d %d %d ", - obs_encoder_get_codec(vencoder), bitrate, - obs_output_get_width(stream->output), - obs_output_get_height(stream->output), (int)pri, (int)trc, - (int)spc, (int)range, - (int)determine_chroma_location( - obs_to_ffmpeg_video_format(info->format), spc), - max_luminance, (int)info->fps_num, (int)info->fps_den, - (int)codec_tag); + os_process_args_add_arg(args, obs_encoder_get_codec(vencoder)); + os_process_args_add_argf(args, "%d", bitrate); + os_process_args_add_argf(args, "%d", + obs_output_get_width(stream->output)); + os_process_args_add_argf(args, "%d", + obs_output_get_height(stream->output)); + os_process_args_add_argf(args, "%d", (int)pri); + os_process_args_add_argf(args, "%d", (int)trc); + os_process_args_add_argf(args, "%d", (int)spc); + os_process_args_add_argf(args, "%d", (int)range); + os_process_args_add_argf(args, "%d", (int)chroma_location); + os_process_args_add_argf(args, "%d", max_luminance); + os_process_args_add_argf(args, "%d", (int)info->fps_num); + os_process_args_add_argf(args, "%d", (int)info->fps_den); + os_process_args_add_argf(args, "%d", codec_tag); } -static void add_audio_encoder_params(struct dstr *cmd, obs_encoder_t *aencoder) +static void add_audio_encoder_params(os_process_args_t *args, + obs_encoder_t *aencoder) { obs_data_t *settings = obs_encoder_get_settings(aencoder); int bitrate = (int)obs_data_get_int(settings, "bitrate"); audio_t *audio = obs_get_audio(); - struct dstr name = {0}; obs_data_release(settings); - dstr_copy(&name, obs_encoder_get_name(aencoder)); - dstr_replace(&name, "\"", "\"\""); - - dstr_catf(cmd, "\"%s\" %d %d %d %d ", name.array, bitrate, - (int)obs_encoder_get_sample_rate(aencoder), - (int)obs_encoder_get_frame_size(aencoder), - (int)audio_output_get_channels(audio)); - - dstr_free(&name); + os_process_args_add_arg(args, obs_encoder_get_name(aencoder)); + os_process_args_add_argf(args, "%d", bitrate); + os_process_args_add_argf(args, "%d", + (int)obs_encoder_get_sample_rate(aencoder)); + os_process_args_add_argf(args, "%d", + (int)obs_encoder_get_frame_size(aencoder)); + os_process_args_add_argf(args, "%d", + (int)audio_output_get_channels(audio)); } static void log_muxer_params(struct ffmpeg_muxer *stream, const char *settings) @@ -250,15 +258,15 @@ static void log_muxer_params(struct ffmpeg_muxer *stream, const char *settings) av_dict_free(&dict); } -static void add_stream_key(struct dstr *cmd, struct ffmpeg_muxer *stream) +static void add_stream_key(os_process_args_t *args, struct ffmpeg_muxer *stream) { - dstr_catf(cmd, "\"%s\" ", - dstr_is_empty(&stream->stream_key) - ? "" - : stream->stream_key.array); + os_process_args_add_arg(args, dstr_is_empty(&stream->stream_key) + ? "" + : stream->stream_key.array); } -static void add_muxer_params(struct dstr *cmd, struct ffmpeg_muxer *stream) +static void add_muxer_params(os_process_args_t *args, + struct ffmpeg_muxer *stream) { struct dstr mux = {0}; @@ -272,16 +280,13 @@ static void add_muxer_params(struct dstr *cmd, struct ffmpeg_muxer *stream) } log_muxer_params(stream, mux.array); - - dstr_replace(&mux, "\"", "\\\""); - - dstr_catf(cmd, "\"%s\" ", mux.array ? mux.array : ""); + os_process_args_add_arg(args, mux.array ? mux.array : ""); dstr_free(&mux); } -static void build_command_line(struct ffmpeg_muxer *stream, struct dstr *cmd, - const char *path) +static void build_command_line(struct ffmpeg_muxer *stream, + os_process_args_t **args, const char *path) { obs_encoder_t *vencoder = obs_output_get_video_encoder(stream->output); obs_encoder_t *aencoders[MAX_AUDIO_MIXES]; @@ -297,39 +302,36 @@ static void build_command_line(struct ffmpeg_muxer *stream, struct dstr *cmd, num_tracks++; } - dstr_init_move_array(cmd, os_get_executable_path_ptr(FFMPEG_MUX)); - dstr_insert_ch(cmd, 0, '\"'); - dstr_cat(cmd, "\" \""); - - dstr_copy(&stream->path, path); - dstr_replace(&stream->path, "\"", "\"\""); - dstr_cat_dstr(cmd, &stream->path); + char *exe = os_get_executable_path_ptr(FFMPEG_MUX); + *args = os_process_args_create(exe); + bfree(exe); - dstr_catf(cmd, "\" %d %d ", vencoder ? 1 : 0, num_tracks); + os_process_args_add_arg(*args, path); + os_process_args_add_argf(*args, "%d", vencoder ? 1 : 0); + os_process_args_add_argf(*args, "%d", num_tracks); if (vencoder) - add_video_encoder_params(stream, cmd, vencoder); + add_video_encoder_params(stream, *args, vencoder); if (num_tracks) { - const char *codec = obs_encoder_get_codec(aencoders[0]); - dstr_cat(cmd, codec); - dstr_cat(cmd, " "); + os_process_args_add_arg(*args, + obs_encoder_get_codec(aencoders[0])); for (int i = 0; i < num_tracks; i++) { - add_audio_encoder_params(cmd, aencoders[i]); + add_audio_encoder_params(*args, aencoders[i]); } } - add_stream_key(cmd, stream); - add_muxer_params(cmd, stream); + add_stream_key(*args, stream); + add_muxer_params(*args, stream); } void start_pipe(struct ffmpeg_muxer *stream, const char *path) { - struct dstr cmd; - build_command_line(stream, &cmd, path); - stream->pipe = os_process_pipe_create(cmd.array, "w"); - dstr_free(&cmd); + os_process_args_t *args = NULL; + build_command_line(stream, &args, path); + stream->pipe = os_process_pipe_create2(args, "w"); + os_process_args_destroy(args); } static void set_file_not_readable_error(struct ffmpeg_muxer *stream, From 0a9f1e7391e8c142d5833af7df11b77b7d78752b Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 28 Sep 2023 04:43:42 +0200 Subject: [PATCH 0026/1073] UI: Add HighContrastEnabled() to platform utilities --- UI/platform-osx.mm | 5 +++++ UI/platform-windows.cpp | 11 +++++++++++ UI/platform-x11.cpp | 6 ++++++ UI/platform.hpp | 2 ++ 4 files changed, 24 insertions(+) diff --git a/UI/platform-osx.mm b/UI/platform-osx.mm index 526347eb56cb97..75f4adfba71c40 100644 --- a/UI/platform-osx.mm +++ b/UI/platform-osx.mm @@ -361,3 +361,8 @@ void InstallNSApplicationSubclass() { [OBSApplication sharedApplication]; } + +bool HighContrastEnabled() +{ + return [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldIncreaseContrast]; +} diff --git a/UI/platform-windows.cpp b/UI/platform-windows.cpp index a752936897b83f..303d918a9becdf 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -494,3 +494,14 @@ void TaskbarOverlaySetStatus(TaskbarOverlayStatus status) DestroyIcon(hicon); taskbarIcon->Release(); } + +bool HighContrastEnabled() +{ + HIGHCONTRAST hc = {}; + hc.cbSize = sizeof(HIGHCONTRAST); + + if (SystemParametersInfo(SPI_GETHIGHCONTRAST, hc.cbSize, &hc, 0)) + return hc.dwFlags & HCF_HIGHCONTRASTON; + + return false; +} diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index d977facbca730e..a9ef1797f2b620 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -269,3 +269,9 @@ bool SetDisplayAffinitySupported(void) // Not implemented yet void TaskbarOverlayInit() {} void TaskbarOverlaySetStatus(TaskbarOverlayStatus) {} + +bool HighContrastEnabled() +{ + // Note implemented yet + return false; +} diff --git a/UI/platform.hpp b/UI/platform.hpp index 6fddfa9a9682b2..bf45879f9fbbeb 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -36,6 +36,8 @@ void SetAlwaysOnTop(QWidget *window, bool enable); bool SetDisplayAffinitySupported(void); +bool HighContrastEnabled(); + enum TaskbarOverlayStatus { TaskbarOverlayStatusInactive, TaskbarOverlayStatusActive, From 41ba8bdfdd02762ea72fd72dc0d05cf6357a7241 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 3 Apr 2024 11:01:39 +0200 Subject: [PATCH 0027/1073] UI: Add HighContrastEnabled implementation for Linux --- UI/cmake/legacy.cmake | 4 ++-- UI/cmake/os-linux.cmake | 2 +- UI/cmake/ui-qt.cmake | 2 +- UI/platform-x11.cpp | 49 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 650638163fe02a..806e382bcaf97e 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -71,7 +71,7 @@ find_package(CURL REQUIRED) add_subdirectory(frontend-plugins) add_executable(obs) -find_qt(COMPONENTS Widgets Network Svg Xml COMPONENTS_LINUX Gui) +find_qt(COMPONENTS Widgets Network Svg Xml COMPONENTS_LINUX Gui DBus) target_link_libraries(obs PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network) @@ -459,7 +459,7 @@ elseif(OS_MACOS) elseif(OS_POSIX) target_sources(obs PRIVATE platform-x11.cpp) - target_link_libraries(obs PRIVATE Qt::GuiPrivate) + target_link_libraries(obs PRIVATE Qt::GuiPrivate Qt::DBus) target_compile_definitions(obs PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" "$<$:LINUX_PORTABLE>") diff --git a/UI/cmake/os-linux.cmake b/UI/cmake/os-linux.cmake index 0646f68b70aad0..91d9175dc14dac 100644 --- a/UI/cmake/os-linux.cmake +++ b/UI/cmake/os-linux.cmake @@ -1,6 +1,6 @@ target_sources(obs-studio PRIVATE platform-x11.cpp) target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}") -target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate) +target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate Qt::DBus) if(TARGET OBS::python) find_package(Python REQUIRED COMPONENTS Interpreter Development) diff --git a/UI/cmake/ui-qt.cmake b/UI/cmake/ui-qt.cmake index 6108dfd81e052f..4989d2d1153a5d 100644 --- a/UI/cmake/ui-qt.cmake +++ b/UI/cmake/ui-qt.cmake @@ -5,7 +5,7 @@ find_package(Qt6 REQUIRED Widgets Network Svg Xml) if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) - find_package(Qt6 REQUIRED Gui) + find_package(Qt6 REQUIRED Gui DBus) endif() target_link_libraries(obs-studio PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network) diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index a9ef1797f2b620..7de1008e454181 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #include #include @@ -272,6 +275,48 @@ void TaskbarOverlaySetStatus(TaskbarOverlayStatus) {} bool HighContrastEnabled() { - // Note implemented yet - return false; + QDBusReply reply; + QDBusMessage msgXdpSettingsVersion = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.DBus.Properties", "Get"); + msgXdpSettingsVersion << "org.freedesktop.portal.Settings" + << "version"; + + reply = QDBusConnection::sessionBus().call(msgXdpSettingsVersion); + + if (!reply.isValid()) { + blog(LOG_WARNING, + "Get on org.freedesktop.portal.Settings returned an invalid reply"); + return false; + } + + /* NOTE: org.freedesktop.portal.Settings got its contrast settings after + * the ReadOne method. So assumes that if ReadOne is not available, contrast + * isn't available either. */ + if (uint32_t version = reply.value().toUInt() < 2) { + blog(LOG_WARNING, + "org.freedesktop.portal.Settings version %u does not support ReadOne", + version); + return false; + } + + /* NOTE: If contrast is not available if will return 0 (false). */ + QDBusMessage msgXdpSettingsContrast = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.Settings", "ReadOne"); + + msgXdpSettingsContrast << "org.freedesktop.appearance" + << "contrast"; + + reply = QDBusConnection::sessionBus().call(msgXdpSettingsContrast); + + if (!reply.isValid()) { + blog(LOG_WARNING, + "ReadOne on org.freedesktop.portal.Settings returned an invalid reply"); + return false; + } + + return reply.value().toUInt() != 0; } From b76e0cd31b405184d1e9e46c35f6f0912944856c Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 28 Sep 2023 04:55:19 +0200 Subject: [PATCH 0028/1073] UI: Add 'Appearance' settings page --- UI/data/locale/en-US.ini | 4 + UI/data/themes/Dark/settings/appearance.svg | 2 + UI/forms/OBSBasicSettings.ui | 112 +++++++++++++++++--- UI/forms/images/settings/appearance.svg | 2 + UI/forms/obs.qrc | 1 + UI/window-basic-settings.cpp | 38 ++++--- UI/window-basic-settings.hpp | 18 ++++ 7 files changed, 148 insertions(+), 29 deletions(-) create mode 100644 UI/data/themes/Dark/settings/appearance.svg create mode 100644 UI/forms/images/settings/appearance.svg diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 9c3f387779133c..a5c043843241d9 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -925,6 +925,10 @@ Basic.Settings.General.ChannelDescription.stable="Latest stable release" Basic.Settings.General.ChannelName.beta="Betas / Release Candidates" Basic.Settings.General.ChannelDescription.beta="Potentially unstable pre-release versions" +# basic mode 'appearance' settings +Basic.Settings.Appearance="Appearance" +Basic.Settings.Appearance.General="General" + # basic mode 'stream' settings Basic.Settings.Stream="Stream" Basic.Settings.Stream.Custom.UseAuthentication="Use authentication" diff --git a/UI/data/themes/Dark/settings/appearance.svg b/UI/data/themes/Dark/settings/appearance.svg new file mode 100644 index 00000000000000..0038a7618e6106 --- /dev/null +++ b/UI/data/themes/Dark/settings/appearance.svg @@ -0,0 +1,2 @@ + + diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index cbe2f31c0c9603..18d02da6fca5b5 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -69,6 +69,15 @@ :/settings/images/settings/general.svg:/settings/images/settings/general.svg + + + Basic.Settings.Appearance + + + + :/settings/images/settings/appearance.svg:/settings/images/settings/appearance.svg + + Basic.Settings.Stream @@ -227,19 +236,6 @@ - - - Basic.Settings.General.Theme - - - theme - - - - - - - Qt::Horizontal @@ -255,14 +251,14 @@ - + Basic.Settings.General.OpenStatsOnStartup - + Basic.Settings.General.HideOBSWindowsFromCapture.Tooltip @@ -890,6 +886,90 @@ + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + 781 + 680 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Basic.Settings.Appearance.General + + + false + + + + + + Basic.Settings.General.Theme + + + theme + + + + + + + + + + + + + + + + + + @@ -7874,7 +7954,6 @@ listWidget scrollArea_2 language - theme openStatsOnStartup hideOBSFromCapture updateChannelBox @@ -7911,6 +7990,7 @@ multiviewDrawNames multiviewDrawAreas multiviewLayout + theme service moreInfoButton connectAccount diff --git a/UI/forms/images/settings/appearance.svg b/UI/forms/images/settings/appearance.svg new file mode 100644 index 00000000000000..57a79c8e714407 --- /dev/null +++ b/UI/forms/images/settings/appearance.svg @@ -0,0 +1,2 @@ + + diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index 4386adf7a0574f..226ee6ed997c54 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -80,6 +80,7 @@ images/settings/general.svg images/settings/hotkeys.svg images/settings/accessibility.svg + images/settings/appearance.svg fonts/OpenSans-Regular.ttf diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index d94ff32fc6a8c7..2a19e9131908f7 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -1217,10 +1217,10 @@ void OBSBasicSettings::ReloadCodecs(const FFmpegFormat &format) for (auto &codec : supportedCodecs) { switch (codec.type) { - case AUDIO: + case FFmpegCodecType::AUDIO: AddCodec(ui->advOutFFAEncoder, codec); break; - case VIDEO: + case FFmpegCodecType::VIDEO: AddCodec(ui->advOutFFVEncoder, codec); break; default: @@ -4313,7 +4313,7 @@ void OBSBasicSettings::on_listWidget_itemSelectionChanged() if (loading || row == pageIndex) return; - if (!hotkeysLoaded && row == 5) { + if (!hotkeysLoaded && row == Pages::HOTKEYS) { setCursor(Qt::BusyCursor); /* Look, I know this /feels/ wrong, but the specific issue we're dealing with * here means that the UI locks up immediately even when using "invokeMethod". @@ -4511,7 +4511,8 @@ void OBSBasicSettings::on_advOutFFAEncoder_currentIndexChanged(int idx) if (!itemDataVariant.isNull()) { auto desc = itemDataVariant.value(); SetAdvOutputFFmpegEnablement( - AUDIO, desc.id != 0 || desc.name != nullptr, true); + FFmpegCodecType::AUDIO, + desc.id != 0 || desc.name != nullptr, true); } } @@ -4521,7 +4522,8 @@ void OBSBasicSettings::on_advOutFFVEncoder_currentIndexChanged(int idx) if (!itemDataVariant.isNull()) { auto desc = itemDataVariant.value(); SetAdvOutputFFmpegEnablement( - VIDEO, desc.id != 0 || desc.name != nullptr, true); + FFmpegCodecType::VIDEO, + desc.id != 0 || desc.name != nullptr, true); } } @@ -6089,6 +6091,11 @@ QIcon OBSBasicSettings::GetGeneralIcon() const return generalIcon; } +QIcon OBSBasicSettings::GetAppearanceIcon() const +{ + return appearanceIcon; +} + QIcon OBSBasicSettings::GetStreamIcon() const { return streamIcon; @@ -6126,42 +6133,47 @@ QIcon OBSBasicSettings::GetAdvancedIcon() const void OBSBasicSettings::SetGeneralIcon(const QIcon &icon) { - ui->listWidget->item(0)->setIcon(icon); + ui->listWidget->item(Pages::GENERAL)->setIcon(icon); +} + +void OBSBasicSettings::SetAppearanceIcon(const QIcon &icon) +{ + ui->listWidget->item(Pages::APPEARANCE)->setIcon(icon); } void OBSBasicSettings::SetStreamIcon(const QIcon &icon) { - ui->listWidget->item(1)->setIcon(icon); + ui->listWidget->item(Pages::STREAM)->setIcon(icon); } void OBSBasicSettings::SetOutputIcon(const QIcon &icon) { - ui->listWidget->item(2)->setIcon(icon); + ui->listWidget->item(Pages::OUTPUT)->setIcon(icon); } void OBSBasicSettings::SetAudioIcon(const QIcon &icon) { - ui->listWidget->item(3)->setIcon(icon); + ui->listWidget->item(Pages::AUDIO)->setIcon(icon); } void OBSBasicSettings::SetVideoIcon(const QIcon &icon) { - ui->listWidget->item(4)->setIcon(icon); + ui->listWidget->item(Pages::VIDEO)->setIcon(icon); } void OBSBasicSettings::SetHotkeysIcon(const QIcon &icon) { - ui->listWidget->item(5)->setIcon(icon); + ui->listWidget->item(Pages::HOTKEYS)->setIcon(icon); } void OBSBasicSettings::SetAccessibilityIcon(const QIcon &icon) { - ui->listWidget->item(6)->setIcon(icon); + ui->listWidget->item(Pages::ACCESSIBILITY)->setIcon(icon); } void OBSBasicSettings::SetAdvancedIcon(const QIcon &icon) { - ui->listWidget->item(7)->setIcon(icon); + ui->listWidget->item(Pages::ADVANCED)->setIcon(icon); } int OBSBasicSettings::CurrentFLVTrack() diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 7f0db483dc2b36..62f5800f094e14 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -72,6 +72,8 @@ class OBSBasicSettings : public QDialog { Q_OBJECT Q_PROPERTY(QIcon generalIcon READ GetGeneralIcon WRITE SetGeneralIcon DESIGNABLE true) + Q_PROPERTY(QIcon appearanceIcon READ GetAppearanceIcon WRITE + SetAppearanceIcon DESIGNABLE true) Q_PROPERTY(QIcon streamIcon READ GetStreamIcon WRITE SetStreamIcon DESIGNABLE true) Q_PROPERTY(QIcon outputIcon READ GetOutputIcon WRITE SetOutputIcon @@ -87,6 +89,19 @@ class OBSBasicSettings : public QDialog { Q_PROPERTY(QIcon advancedIcon READ GetAdvancedIcon WRITE SetAdvancedIcon DESIGNABLE true) + enum Pages { + GENERAL, + APPEARANCE, + STREAM, + OUTPUT, + AUDIO, + VIDEO, + HOTKEYS, + ACCESSIBILITY, + ADVANCED, + NUM_PAGES + }; + private: OBSBasic *main; @@ -355,6 +370,7 @@ private slots: void UpdateYouTubeAppDockSettings(); QIcon generalIcon; + QIcon appearanceIcon; QIcon streamIcon; QIcon outputIcon; QIcon audioIcon; @@ -364,6 +380,7 @@ private slots: QIcon advancedIcon; QIcon GetGeneralIcon() const; + QIcon GetAppearanceIcon() const; QIcon GetStreamIcon() const; QIcon GetOutputIcon() const; QIcon GetAudioIcon() const; @@ -465,6 +482,7 @@ private slots: OBSService SpawnTempService(); void SetGeneralIcon(const QIcon &icon); + void SetAppearanceIcon(const QIcon &icon); void SetStreamIcon(const QIcon &icon); void SetOutputIcon(const QIcon &icon); void SetAudioIcon(const QIcon &icon); From 503968671da824d1bacbf0de10f909d70b93902f Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 30 Sep 2023 11:24:21 +0200 Subject: [PATCH 0029/1073] UI: Add composable themes feature Co-authored-by: Warchamp7 --- UI/CMakeLists.txt | 2 + UI/cmake/legacy.cmake | 3 + UI/cmake/ui-windows.cmake | 1 + UI/data/locale/en-US.ini | 4 +- UI/forms/OBSBasicSettings.ui | 16 +- UI/obs-app-theming.cpp | 984 ++++++++++++++++++++++++ UI/obs-app-theming.hpp | 66 ++ UI/obs-app.cpp | 377 --------- UI/obs-app.hpp | 35 +- UI/ui-config.h.in | 2 +- UI/window-basic-settings-appearance.cpp | 127 +++ UI/window-basic-settings.cpp | 87 +-- UI/window-basic-settings.hpp | 22 +- 13 files changed, 1258 insertions(+), 468 deletions(-) create mode 100644 UI/obs-app-theming.cpp create mode 100644 UI/obs-app-theming.hpp create mode 100644 UI/window-basic-settings-appearance.cpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 1dbc267e4d7cee..d060dd96b0f9b6 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -71,6 +71,8 @@ target_sources( multiview.hpp obf.c obf.h + obs-app-theming.cpp + obs-app-theming.hpp obs-app.cpp obs-app.hpp obs-proxy-style.cpp diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 806e382bcaf97e..c59013173a7b7f 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -133,6 +133,8 @@ target_sources( auth-listener.hpp obf.c obf.h + obs-app-theming.cpp + obs-app-theming.hpp obs-app.cpp obs-app.hpp obs-proxy-style.cpp @@ -250,6 +252,7 @@ target_sources( window-basic-settings.cpp window-basic-settings.hpp window-basic-settings-a11y.cpp + window-basic-settings-appearance.cpp window-basic-settings-stream.cpp window-basic-source-select.cpp window-basic-source-select.hpp diff --git a/UI/cmake/ui-windows.cmake b/UI/cmake/ui-windows.cmake index 1c08ba5ee4fec2..40675ecc6ca687 100644 --- a/UI/cmake/ui-windows.cmake +++ b/UI/cmake/ui-windows.cmake @@ -28,6 +28,7 @@ target_sources( window-basic-properties.cpp window-basic-properties.hpp window-basic-settings-a11y.cpp + window-basic-settings-appearance.cpp window-basic-settings-stream.cpp window-basic-settings.cpp window-basic-settings.hpp diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index a5c043843241d9..b9325190d31380 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -862,7 +862,6 @@ Basic.Settings.Confirm="You have unsaved changes. Save changes?" # basic mode 'general' settings Basic.Settings.General="General" -Basic.Settings.General.Theme="Theme" Basic.Settings.General.Language="Language" Basic.Settings.General.Updater="Updates" Basic.Settings.General.UpdateChannel="Update Channel" @@ -928,6 +927,9 @@ Basic.Settings.General.ChannelDescription.beta="Potentially unstable pre-release # basic mode 'appearance' settings Basic.Settings.Appearance="Appearance" Basic.Settings.Appearance.General="General" +Basic.Settings.Appearance.General.Theme="Theme" +Basic.Settings.Appearance.General.Variant="Style" +Basic.Settings.Appearance.General.NoVariant="No Styles Available" # basic mode 'stream' settings Basic.Settings.Stream="Stream" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 18d02da6fca5b5..13772c172b3307 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -948,7 +948,7 @@ - Basic.Settings.General.Theme + Basic.Settings.Appearance.General.Theme theme @@ -958,6 +958,19 @@ + + + + Basic.Settings.Appearance.General.Variant + + + themeVariant + + + + + + @@ -7991,6 +8004,7 @@ multiviewDrawAreas multiviewLayout theme + themeVariant service moreInfoButton connectAccount diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp new file mode 100644 index 00000000000000..984a7136a2cfd1 --- /dev/null +++ b/UI/obs-app-theming.cpp @@ -0,0 +1,984 @@ +/****************************************************************************** + Copyright (C) 2023 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "qt-wrappers.hpp" +#include "obs-app.hpp" +#include "obs-app-theming.hpp" +#include "obs-proxy-style.hpp" +#include "platform.hpp" + +#include "ui-config.h" + +using namespace std; + +struct CFParser { + cf_parser cfp = {}; + ~CFParser() { cf_parser_free(&cfp); } + operator cf_parser *() { return &cfp; } + cf_parser *operator->() { return &cfp; } +}; + +static OBSTheme *ParseThemeMeta(const QString &path) +{ + QFile themeFile(path); + if (!themeFile.open(QIODeviceBase::ReadOnly)) + return nullptr; + + OBSTheme *meta = nullptr; + const QByteArray data = themeFile.readAll(); + CFParser cfp; + int ret; + + if (!cf_parser_parse(cfp, data.constData(), QT_TO_UTF8(path))) + return nullptr; + + if (cf_token_is(cfp, "@") || cf_go_to_token(cfp, "@", nullptr)) { + while (cf_next_token(cfp)) { + if (cf_token_is(cfp, "OBSThemeMeta")) + break; + + if (!cf_go_to_token(cfp, "@", nullptr)) + return nullptr; + } + + if (!cf_next_token(cfp)) + return nullptr; + + if (!cf_token_is(cfp, "{")) + return nullptr; + + meta = new OBSTheme(); + + for (;;) { + if (!cf_next_token(cfp)) { + delete meta; + return nullptr; + } + + ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", + nullptr); + if (ret != PARSE_SUCCESS) + break; + + string name(cfp->cur_token->str.array, + cfp->cur_token->str.len); + + ret = cf_next_token_should_be(cfp, ":", ";", nullptr); + if (ret != PARSE_SUCCESS) + continue; + + if (!cf_next_token(cfp)) { + delete meta; + return nullptr; + } + + ret = cf_token_is_type(cfp, CFTOKEN_STRING, "value", + ";"); + + if (ret != PARSE_SUCCESS) + continue; + + BPtr str = cf_literal_to_str(cfp->cur_token->str.array, + cfp->cur_token->str.len); + + if (str) { + if (name == "dark") + meta->isDark = strcmp(str, "true") == 0; + else if (name == "extends") + meta->extends = str; + else if (name == "author") + meta->author = str; + else if (name == "id") + meta->id = str; + else if (name == "name") + meta->name = str; + } + + if (!cf_go_to_token(cfp, ";", nullptr)) { + delete meta; + return nullptr; + } + } + } + + if (meta) { + auto filepath = filesystem::u8path(path.toStdString()); + meta->isBaseTheme = filepath.extension() == ".obt"; + meta->filename = filepath.stem(); + + if (meta->id.isEmpty() || meta->name.isEmpty() || + (!meta->isBaseTheme && meta->extends.isEmpty())) { + /* Theme is invalid */ + delete meta; + meta = nullptr; + } else { + meta->location = absolute(filepath); + meta->isHighContrast = path.endsWith(".oha"); + meta->isVisible = !path.contains("System"); + } + } + + return meta; +} + +static bool ParseVarName(CFParser &cfp, QString &value) +{ + int ret; + + ret = cf_next_token_should_be(cfp, "(", ";", nullptr); + if (ret != PARSE_SUCCESS) + return false; + ret = cf_next_token_should_be(cfp, "-", ";", nullptr); + if (ret != PARSE_SUCCESS) + return false; + ret = cf_next_token_should_be(cfp, "-", ";", nullptr); + if (ret != PARSE_SUCCESS) + return false; + if (!cf_next_token(cfp)) + return false; + + value = QString::fromUtf8(cfp->cur_token->str.array, + cfp->cur_token->str.len); + + ret = cf_next_token_should_be(cfp, ")", ";", nullptr); + if (ret != PARSE_SUCCESS) + return false; + + return !value.isEmpty(); +} + +static QColor ParseColor(CFParser &cfp) +{ + const char *array; + uint32_t color = 0; + QColor res(QColor::Invalid); + + if (cf_token_is(cfp, "#")) { + if (!cf_next_token(cfp)) + return res; + + color = strtol(cfp->cur_token->str.array, nullptr, 16); + } else if (cf_token_is(cfp, "rgb")) { + int ret = cf_next_token_should_be(cfp, "(", ";", nullptr); + if (ret != PARSE_SUCCESS || !cf_next_token(cfp)) + return res; + + array = cfp->cur_token->str.array; + color |= strtol(array, nullptr, 10) << 16; + + ret = cf_next_token_should_be(cfp, ",", ";", nullptr); + if (ret != PARSE_SUCCESS || !cf_next_token(cfp)) + return res; + + array = cfp->cur_token->str.array; + color |= strtol(array, nullptr, 10) << 8; + + ret = cf_next_token_should_be(cfp, ",", ";", nullptr); + if (ret != PARSE_SUCCESS || !cf_next_token(cfp)) + return res; + + array = cfp->cur_token->str.array; + color |= strtol(array, nullptr, 10); + + ret = cf_next_token_should_be(cfp, ")", ";", nullptr); + if (ret != PARSE_SUCCESS) + return res; + } else if (cf_token_is(cfp, "bikeshed")) { + color |= QRandomGenerator::global()->bounded(INT8_MAX) << 16; + color |= QRandomGenerator::global()->bounded(INT8_MAX) << 8; + color |= QRandomGenerator::global()->bounded(INT8_MAX); + } + + res = color; + return res; +} + +static bool ParseCalc(CFParser &cfp, QStringList &calc, + vector &vars) +{ + int ret = cf_next_token_should_be(cfp, "(", ";", nullptr); + if (ret != PARSE_SUCCESS) + return false; + if (!cf_next_token(cfp)) + return false; + + while (!cf_token_is(cfp, ")")) { + if (cf_token_is(cfp, ";")) + break; + + if (cf_token_is(cfp, "calc")) { + /* Internal calc's do not have proper names. + * They are anonymous variables */ + OBSThemeVariable var; + QStringList subcalc; + + var.name = QString("__unnamed_%1") + .arg(QRandomGenerator::global() + ->generate64()); + + if (!ParseCalc(cfp, subcalc, vars)) + return false; + + var.type = OBSThemeVariable::Calc; + var.value = subcalc; + calc << var.name; + vars.push_back(std::move(var)); + } else if (cf_token_is(cfp, "var")) { + QString value; + if (!ParseVarName(cfp, value)) + return false; + + calc << value; + } else { + calc << QString::fromUtf8(cfp->cur_token->str.array, + cfp->cur_token->str.len); + } + + if (!cf_next_token(cfp)) + return false; + } + + return !calc.isEmpty(); +} + +static vector ParseThemeVariables(const char *themeData) +{ + CFParser cfp; + int ret; + + std::vector vars; + + if (!cf_parser_parse(cfp, themeData, nullptr)) + return vars; + + if (!cf_token_is(cfp, "@") && !cf_go_to_token(cfp, "@", nullptr)) + return vars; + + while (cf_next_token(cfp)) { + if (cf_token_is(cfp, "OBSThemeVars")) + break; + + if (!cf_go_to_token(cfp, "@", nullptr)) + return vars; + } + + if (!cf_next_token(cfp)) + return {}; + + if (!cf_token_is(cfp, "{")) + return {}; + + for (;;) { + if (!cf_next_token(cfp)) + return vars; + + if (!cf_token_is(cfp, "-")) + return vars; + + ret = cf_next_token_should_be(cfp, "-", ";", nullptr); + if (ret != PARSE_SUCCESS) + continue; + + if (!cf_next_token(cfp)) + return vars; + + ret = cf_token_is_type(cfp, CFTOKEN_NAME, "key", nullptr); + if (ret != PARSE_SUCCESS) + break; + + QString key = QString::fromUtf8(cfp->cur_token->str.array, + cfp->cur_token->str.len); + OBSThemeVariable var; + var.name = key; + + ret = cf_next_token_should_be(cfp, ":", ";", nullptr); + if (ret != PARSE_SUCCESS) + continue; + + if (!cf_next_token(cfp)) + return vars; + + if (cfp->cur_token->type == CFTOKEN_NUM) { + const char *ch = cfp->cur_token->str.array; + const char *end = ch + cfp->cur_token->str.len; + double f = os_strtod(ch); + + var.value = f; + var.type = OBSThemeVariable::Number; + + /* Look for a suffix and mark variable as size if it exists */ + while (ch < end) { + if (!isdigit(*ch) && !isspace(*ch) && + *ch != '.') { + var.suffix = + QString::fromUtf8(ch, end - ch); + var.type = OBSThemeVariable::Size; + break; + } + ch++; + } + } else if (cf_token_is(cfp, "rgb") || cf_token_is(cfp, "#") || + cf_token_is(cfp, "bikeshed")) { + QColor color = ParseColor(cfp); + if (!color.isValid()) + continue; + + var.value = color; + var.type = OBSThemeVariable::Color; + } else if (cf_token_is(cfp, "var")) { + QString value; + + if (!ParseVarName(cfp, value)) + continue; + + var.value = value; + var.type = OBSThemeVariable::Alias; + } else if (cf_token_is(cfp, "calc")) { + QStringList calc; + + if (!ParseCalc(cfp, calc, vars)) + continue; + + var.type = OBSThemeVariable::Calc; + var.value = calc; + } else { + var.type = OBSThemeVariable::String; + BPtr strVal = + cf_literal_to_str(cfp->cur_token->str.array, + cfp->cur_token->str.len); + var.value = QString::fromUtf8(strVal.Get()); + } + + if (!cf_next_token(cfp)) + return vars; + + if (cf_token_is(cfp, "!") && + cf_next_token_should_be(cfp, "editable", nullptr, + nullptr) == PARSE_SUCCESS) { + if (var.type == OBSThemeVariable::Calc || + var.type == OBSThemeVariable::Alias) { + blog(LOG_WARNING, + "Variable of calc/alias type cannot be editable: %s", + QT_TO_UTF8(var.name)); + } else { + var.editable = true; + } + } + + vars.push_back(std::move(var)); + + if (!cf_token_is(cfp, ";") && + !cf_go_to_token(cfp, ";", nullptr)) + return vars; + } + + return vars; +} + +void OBSApp::FindThemes() +{ + string themeDir; + themeDir.resize(512); + + QStringList filters; + filters << "*.obt" // OBS Base Theme + << "*.ovt" // OBS Variant Theme + << "*.oha" // OBS High-contrast Adjustment layer + ; + + if (GetConfigPath(themeDir.data(), themeDir.capacity(), + "obs-studio/themes/") > 0) { + QDirIterator it(QT_UTF8(themeDir.c_str()), filters, + QDir::Files); + + while (it.hasNext()) { + OBSTheme *theme = ParseThemeMeta(it.next()); + if (theme && !themes.contains(theme->id)) + themes[theme->id] = std::move(*theme); + else + delete theme; + } + } + + GetDataFilePath("themes/", themeDir); + QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); + while (it.hasNext()) { + OBSTheme *theme = ParseThemeMeta(it.next()); + if (theme && !themes.contains(theme->id)) + themes[theme->id] = std::move(*theme); + else + delete theme; + } + + /* Build dependency tree for all themes, removing ones that have items missing. */ + QSet invalid; + + for (OBSTheme &theme : themes) { + if (theme.extends.isEmpty()) { + if (!theme.isBaseTheme) { + blog(LOG_ERROR, + R"(Theme "%s" is not base, but does not specify parent!)", + QT_TO_UTF8(theme.id)); + invalid.insert(theme.id); + } + + continue; + } + + QString parentId = theme.extends; + while (!parentId.isEmpty()) { + OBSTheme *parent = GetTheme(parentId); + if (!parent) { + blog(LOG_ERROR, + R"(Theme "%s" is missing ancestor "%s"!)", + QT_TO_UTF8(theme.id), + QT_TO_UTF8(parentId)); + invalid.insert(theme.id); + break; + } + + if (theme.isBaseTheme && !parent->isBaseTheme) { + blog(LOG_ERROR, + R"(Ancestor "%s" of base theme "%s" is not a base theme!)", + QT_TO_UTF8(parent->id), + QT_TO_UTF8(theme.id)); + invalid.insert(theme.id); + break; + } + + /* Mark this theme as a variant of first parent that is a base theme. */ + if (!theme.isBaseTheme && parent->isBaseTheme && + theme.parent.isEmpty()) + theme.parent = parent->id; + + theme.dependencies.push_front(parent->id); + parentId = parent->extends; + + if (parentId.isEmpty() && !parent->isBaseTheme) { + blog(LOG_ERROR, + R"(Final ancestor of "%s" ("%s") is not a base theme!)", + QT_TO_UTF8(theme.id), + QT_TO_UTF8(parent->id)); + invalid.insert(theme.id); + break; + } + } + } + + for (const QString &name : invalid) { + themes.remove(name); + } +} + +static bool ResolveVariable(const QHash &vars, + OBSThemeVariable &var) +{ + const OBSThemeVariable *varPtr = &var; + const OBSThemeVariable *realVar = varPtr; + + while (realVar->type == OBSThemeVariable::Alias) { + QString newKey = realVar->value.toString(); + + if (!vars.contains(newKey)) { + blog(LOG_ERROR, + R"(Variable "%s" (aliased by "%s") does not exist!)", + QT_TO_UTF8(newKey), QT_TO_UTF8(var.name)); + return false; + } + + const OBSThemeVariable &newVar = vars[newKey]; + realVar = &newVar; + } + + if (realVar != varPtr) + var = *realVar; + + return true; +} + +static QString EvalCalc(const QHash &vars, + const OBSThemeVariable &var, const int recursion = 0); + +static OBSThemeVariable +ParseCalcVariable(const QHash &vars, + const QString &value, const int recursion = 0) +{ + OBSThemeVariable var; + const QByteArray utf8 = value.toUtf8(); + const char *data = utf8.constData(); + + if (isdigit(*data)) { + double f = os_strtod(data); + var.type = OBSThemeVariable::Number; + var.value = f; + + const char *dataEnd = data + utf8.size(); + while (data < dataEnd) { + if (*data && !isdigit(*data) && *data != '.') { + var.suffix = + QString::fromUtf8(data, dataEnd - data); + var.type = OBSThemeVariable::Size; + break; + } + + data++; + } + } else { + /* Treat value as an alias/key and resolve it */ + var.type = OBSThemeVariable::Alias; + var.value = value; + ResolveVariable(vars, var); + + /* Handle nested calc()s */ + if (var.type == OBSThemeVariable::Calc) { + QString val = EvalCalc(vars, var, recursion + 1); + var = ParseCalcVariable(vars, val); + } + + /* Only number or size would be valid here */ + if (var.type != OBSThemeVariable::Number && + var.type != OBSThemeVariable::Size) { + blog(LOG_ERROR, + "calc() operand is not a size or number: %s", + QT_TO_UTF8(var.value.toString())); + throw invalid_argument("Operand not of numeric type"); + } + } + + return var; +} + +static QString EvalCalc(const QHash &vars, + const OBSThemeVariable &var, const int recursion) +{ + if (recursion >= 10) { + /* Abort after 10 levels of recursion */ + blog(LOG_ERROR, "Maximum calc() recursion levels hit!"); + return "'Invalid expression'"; + } + + QStringList args = var.value.toStringList(); + if (args.length() != 3) { + blog(LOG_ERROR, + "calc() had invalid number of arguments: %lld (%s)", + args.length(), QT_TO_UTF8(args.join(", "))); + return "'Invalid expression'"; + } + + QString &opt = args[1]; + if (opt != '*' && opt != '+' && opt != '-' && opt != '/') { + blog(LOG_ERROR, "Unknown/invalid calc() operator: %s", + QT_TO_UTF8(opt)); + return "'Invalid expression'"; + } + + OBSThemeVariable val1, val2; + try { + val1 = ParseCalcVariable(vars, args[0], recursion); + val2 = ParseCalcVariable(vars, args[2], recursion); + } catch (...) { + return "'Invalid expression'"; + } + + /* Ensure that suffixes match (if any) */ + if (!val1.suffix.isEmpty() && !val2.suffix.isEmpty() && + val1.suffix != val2.suffix) { + blog(LOG_ERROR, + "calc() requires suffixes to match or only one to be present! %s != %s", + QT_TO_UTF8(val1.suffix), QT_TO_UTF8(val2.suffix)); + return "'Invalid expression'"; + } + + double val = numeric_limits::quiet_NaN(); + double d1 = val1.userValue.isValid() ? val1.userValue.toDouble() + : val1.value.toDouble(); + double d2 = val2.userValue.isValid() ? val2.userValue.toDouble() + : val2.value.toDouble(); + + if (!isfinite(d1) || !isfinite(d2)) { + blog(LOG_ERROR, + "calc() received at least one invalid value:" + " op1: %f, op2: %f", + d1, d2); + return "'Invalid expression'"; + } + + if (opt == "+") + val = d1 + d2; + else if (opt == "-") + val = d1 - d2; + else if (opt == "*") + val = d1 * d2; + else if (opt == "/") + val = d1 / d2; + + if (!isnormal(val)) { + blog(LOG_ERROR, + "Invalid calc() math resulted in non-normal number:" + " %f %s %f = %f", + d1, QT_TO_UTF8(opt), d2, val); + return "'Invalid expression'"; + } + + bool isInteger = ceill(val) == val; + QString result = QString::number(val, 'f', isInteger ? 0 : -1); + + /* Carry-over suffix */ + if (!val1.suffix.isEmpty()) + result += val1.suffix; + else if (!val2.suffix.isEmpty()) + result += val2.suffix; + + return result; +} + +static qsizetype FindEndOfOBSMetadata(const QString &content) +{ + /* Find end of last OBS-specific section and strip it, kinda jank but should work */ + qsizetype end = 0; + + for (auto section : {"OBSThemeMeta", "OBSThemeVars", "OBSTheme"}) { + qsizetype idx = content.indexOf(section, 0); + if (idx > end) { + end = content.indexOf('}', idx) + 1; + } + } + + return end; +} + +static QString PrepareQSS(const QHash &vars, + const QStringList &contents) +{ + QString stylesheet; + QString needleTemplate("var(--%1)"); + + for (const QString &content : contents) { + qsizetype offset = FindEndOfOBSMetadata(content); + if (offset >= 0) { + stylesheet += "\n"; + stylesheet += content.sliced(offset); + } + } + + for (const OBSThemeVariable &var_ : vars) { + OBSThemeVariable var(var_); + + if (!ResolveVariable(vars, var)) + continue; + + QString needle = needleTemplate.arg(var_.name); + QString replace; + + QVariant value = var.userValue.isValid() ? var.userValue + : var.value; + + if (var.type == OBSThemeVariable::Color) { + replace = value.value().name(QColor::HexRgb); + } else if (var.type == OBSThemeVariable::Calc) { + replace = EvalCalc(vars, var); + } else if (var.type == OBSThemeVariable::Size || + var.type == OBSThemeVariable::Number) { + double val = value.toDouble(); + bool isInteger = ceill(val) == val; + replace = QString::number(val, 'f', isInteger ? 0 : -1); + + if (!var.suffix.isEmpty()) + replace += var.suffix; + } else { + replace = value.toString(); + } + + stylesheet = stylesheet.replace(needle, replace); + } + + return stylesheet; +} + +template static void FillEnumMap(QHash &map) +{ + QMetaEnum meta = QMetaEnum::fromType(); + + int numKeys = meta.keyCount(); + for (int i = 0; i < numKeys; i++) { + const char *key = meta.key(i); + QString keyName(key); + map[keyName.toLower()] = static_cast(meta.keyToValue(key)); + } +} + +static QPalette PreparePalette(const QHash &vars, + const QPalette &defaultPalette) +{ + static QHash roleMap; + static QHash groupMap; + + if (roleMap.empty()) + FillEnumMap(roleMap); + if (groupMap.empty()) + FillEnumMap(groupMap); + + QPalette pal(defaultPalette); + + for (const OBSThemeVariable &var_ : vars) { + if (!var_.name.startsWith("palette_")) + continue; + if (var_.name.count("_") < 1 || var_.name.count("_") > 2) + continue; + + OBSThemeVariable var(var_); + if (!ResolveVariable(vars, var) || + var.type != OBSThemeVariable::Color) + continue; + + /* Determine role and optionally group based on name. + * Format is: palette_[_] */ + QPalette::ColorRole role = QPalette::NoRole; + QPalette::ColorGroup group = QPalette::All; + + QStringList parts = var_.name.split("_"); + if (parts.length() >= 2) { + QString key = parts[1].toLower(); + if (!roleMap.contains(key)) { + blog(LOG_WARNING, + "Palette role \"%s\" is not valid!", + QT_TO_UTF8(parts[1])); + continue; + } + role = roleMap[key]; + } + + if (parts.length() == 3) { + QString key = parts[2].toLower(); + if (!groupMap.contains(key)) { + blog(LOG_WARNING, + "Palette group \"%s\" is not valid!", + QT_TO_UTF8(parts[2])); + continue; + } + group = groupMap[key]; + } + + QVariant value = var.userValue.isValid() ? var.userValue + : var.value; + + QColor color = value.value().name(QColor::HexRgb); + pal.setColor(group, role, color); + } + + return pal; +} + +OBSTheme *OBSApp::GetTheme(const QString &name) +{ + if (!themes.contains(name)) + return nullptr; + + return &themes[name]; +} + +bool OBSApp::SetTheme(const QString &name) +{ + OBSTheme *theme = GetTheme(name); + if (!theme) + return false; + + if (themeWatcher) { + themeWatcher->blockSignals(true); + themeWatcher->removePaths(themeWatcher->files()); + } + + setStyleSheet(""); + currentTheme = theme; + + QStringList contents; + QHash vars; + /* Build list of themes to load (in order) */ + QStringList themeIds(theme->dependencies); + themeIds << theme->id; + + /* Find and add high contrast adjustment layer if available */ + if (HighContrastEnabled()) { + for (const OBSTheme &theme_ : themes) { + if (!theme_.isHighContrast) + continue; + if (theme_.parent != theme->id) + continue; + themeIds << theme_.id; + break; + } + } + + QStringList filenames; + for (const QString &themeId : themeIds) { + OBSTheme *cur = GetTheme(themeId); + + QFile file(cur->location); + filenames << file.fileName(); + + if (!file.open(QIODeviceBase::ReadOnly)) + return false; + const QByteArray content = file.readAll(); + + for (OBSThemeVariable &var : + ParseThemeVariables(content.constData())) { + vars[var.name] = std::move(var); + } + + contents.emplaceBack(content.constData()); + } + + const QString stylesheet = PrepareQSS(vars, contents); + const QPalette palette = PreparePalette(vars, defaultPalette); + setPalette(palette); + setStyleSheet(stylesheet); + +#ifdef _DEBUG + /* Write resulting QSS to file in config dir "themes" folder. */ + string filename("obs-studio/themes/"); + filename += theme->id.toStdString(); + filename += ".out"; + + filesystem::path debugOut; + char configPath[512]; + if (GetConfigPath(configPath, sizeof(configPath), filename.c_str())) { + debugOut = absolute(filesystem::u8path(configPath)); + filesystem::create_directories(debugOut.parent_path()); + } + + QFile debugFile(debugOut); + if (debugFile.open(QIODeviceBase::WriteOnly)) { + debugFile.write(stylesheet.toUtf8()); + debugFile.flush(); + } +#endif + +#ifdef __APPLE__ + SetMacOSDarkMode(theme->isDark); +#endif + + emit StyleChanged(); + + if (themeWatcher) { + themeWatcher->addPaths(filenames); + /* Give it 250 ms before re-enabling the watcher to prevent too + * many reloads when edited with an auto-saving IDE. */ + QTimer::singleShot(250, this, + [&] { themeWatcher->blockSignals(false); }); + } + + return true; +} + +void OBSApp::themeFileChanged(const QString &path) +{ + themeWatcher->blockSignals(true); + blog(LOG_INFO, "Theme file \"%s\" changed, reloading...", + QT_TO_UTF8(path)); + SetTheme(currentTheme->id); +} + +static map themeMigrations = { + {"Yami", DEFAULT_THEME}, + {"Grey", "com.obsproject.Yami.Grey"}, + {"Rachni", "com.obsproject.Yami.Rachni"}, + {"Light", "com.obsproject.Yami.Light"}, + {"Dark", "com.obsproject.Yami.Classic"}, + {"Acri", "com.obsproject.Yami.Acri"}, + {"System", "com.obsproject.System"}, +}; + +bool OBSApp::InitTheme() +{ + defaultPalette = palette(); + setStyle(new OBSProxyStyle()); + + /* Set search paths for custom 'theme:' URI prefix */ + string searchDir; + if (GetDataFilePath("themes", searchDir)) { + auto installSearchDir = filesystem::u8path(searchDir); + QDir::addSearchPath("theme", absolute(installSearchDir)); + } + + char userDir[512]; + if (GetConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) { + auto configSearchDir = filesystem::u8path(userDir); + QDir::addSearchPath("theme", absolute(configSearchDir)); + } + + /* Load list of themes and read their metadata */ + FindThemes(); + + if (config_get_bool(globalConfig, "Appearance", "AutoReload")) { + /* Set up Qt file watcher to automatically reload themes */ + themeWatcher = new QFileSystemWatcher(this); + connect(themeWatcher.get(), &QFileSystemWatcher::fileChanged, + this, &OBSApp::themeFileChanged); + } + + /* Migrate old theme config key */ + if (config_has_user_value(globalConfig, "General", "CurrentTheme3") && + !config_has_user_value(globalConfig, "Appearance", "Theme")) { + const char *old = config_get_string(globalConfig, "General", + "CurrentTheme3"); + + if (themeMigrations.count(old)) { + config_set_string(globalConfig, "Appearance", "Theme", + themeMigrations[old].c_str()); + } + } + + QString themeName = + config_get_string(globalConfig, "Appearance", "Theme"); + + if (themeName.isEmpty() || !GetTheme(themeName)) { + if (!themeName.isEmpty()) { + blog(LOG_WARNING, + "Loading theme \"%s\" failed, falling back to " + "default theme (\"%s\").", + QT_TO_UTF8(themeName), DEFAULT_THEME); + } +#ifdef _WIN32 + themeName = HighContrastEnabled() ? "com.obsproject.System" + : DEFAULT_THEME; +#else + themeName = DEFAULT_THEME; +#endif + } + + if (!SetTheme(themeName)) { + blog(LOG_ERROR, + "Loading default theme \"%s\" failed, falling back to " + "system theme as last resort.", + QT_TO_UTF8(themeName)); + return SetTheme("com.obsproject.System"); + } + + return true; +} diff --git a/UI/obs-app-theming.hpp b/UI/obs-app-theming.hpp new file mode 100644 index 00000000000000..cbaed7f24723a7 --- /dev/null +++ b/UI/obs-app-theming.hpp @@ -0,0 +1,66 @@ +/****************************************************************************** + Copyright (C) 2023 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include + +#include + +struct OBSThemeVariable; + +struct OBSTheme { + /* internal name, must be unique */ + QString id; + QString name; + QString author; + QString extends; + + /* First ancestor base theme */ + QString parent; + /* Dependencies from root to direct ancestor */ + QStringList dependencies; + /* File path */ + std::filesystem::path location; + std::filesystem::path filename; /* Filename without extension */ + + bool isDark; + bool isVisible; /* Whether it should be shown to the user */ + bool isBaseTheme; /* Whether it is a "style" or variant */ + bool isHighContrast; /* Whether it is a high-contrast adjustment layer */ +}; + +struct OBSThemeVariable { + enum VariableType { + Color, /* RGB color value*/ + Size, /* Number with suffix denoting size (e.g. px, pt, em) */ + Number, /* Number without suffix */ + String, /* Raw string (e.g. color name, border style, etc.) */ + Alias, /* Points at another variable, value will be the key */ + Calc, /* Simple calculation with two operands */ + }; + + /* Whether the variable should be editable in the UI */ + bool editable = false; + /* Used for VariableType::Size only */ + QString suffix; + + VariableType type; + QString name; + QVariant value; + QVariant userValue; /* If overwritten by user, use this value instead */ +}; diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 887d6bb6378615..781838e5e17179 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -923,383 +923,6 @@ bool OBSApp::InitLocale() return true; } -void OBSApp::AddExtraThemeColor(QPalette &pal, int group, const char *name, - uint32_t color) -{ - std::function func; - -#define DEF_PALETTE_ASSIGN(name) \ - do { \ - func = [&](QPalette::ColorGroup group) { \ - pal.setColor(group, QPalette::name, \ - QColor::fromRgb(color)); \ - }; \ - } while (false) - - if (astrcmpi(name, "alternateBase") == 0) { - DEF_PALETTE_ASSIGN(AlternateBase); - } else if (astrcmpi(name, "base") == 0) { - DEF_PALETTE_ASSIGN(Base); - } else if (astrcmpi(name, "brightText") == 0) { - DEF_PALETTE_ASSIGN(BrightText); - } else if (astrcmpi(name, "button") == 0) { - DEF_PALETTE_ASSIGN(Button); - } else if (astrcmpi(name, "buttonText") == 0) { - DEF_PALETTE_ASSIGN(ButtonText); - } else if (astrcmpi(name, "brightText") == 0) { - DEF_PALETTE_ASSIGN(BrightText); - } else if (astrcmpi(name, "dark") == 0) { - DEF_PALETTE_ASSIGN(Dark); - } else if (astrcmpi(name, "highlight") == 0) { - DEF_PALETTE_ASSIGN(Highlight); - } else if (astrcmpi(name, "highlightedText") == 0) { - DEF_PALETTE_ASSIGN(HighlightedText); - } else if (astrcmpi(name, "light") == 0) { - DEF_PALETTE_ASSIGN(Light); - } else if (astrcmpi(name, "link") == 0) { - DEF_PALETTE_ASSIGN(Link); - } else if (astrcmpi(name, "linkVisited") == 0) { - DEF_PALETTE_ASSIGN(LinkVisited); - } else if (astrcmpi(name, "mid") == 0) { - DEF_PALETTE_ASSIGN(Mid); - } else if (astrcmpi(name, "midlight") == 0) { - DEF_PALETTE_ASSIGN(Midlight); - } else if (astrcmpi(name, "shadow") == 0) { - DEF_PALETTE_ASSIGN(Shadow); - } else if (astrcmpi(name, "text") == 0 || - astrcmpi(name, "foreground") == 0) { - DEF_PALETTE_ASSIGN(Text); - } else if (astrcmpi(name, "toolTipBase") == 0) { - DEF_PALETTE_ASSIGN(ToolTipBase); - } else if (astrcmpi(name, "toolTipText") == 0) { - DEF_PALETTE_ASSIGN(ToolTipText); - } else if (astrcmpi(name, "windowText") == 0) { - DEF_PALETTE_ASSIGN(WindowText); - } else if (astrcmpi(name, "window") == 0 || - astrcmpi(name, "background") == 0) { - DEF_PALETTE_ASSIGN(Window); - } else { - return; - } - -#undef DEF_PALETTE_ASSIGN - - switch (group) { - case QPalette::Disabled: - case QPalette::Active: - case QPalette::Inactive: - func((QPalette::ColorGroup)group); - break; - default: - func((QPalette::ColorGroup)QPalette::Disabled); - func((QPalette::ColorGroup)QPalette::Active); - func((QPalette::ColorGroup)QPalette::Inactive); - } -} - -struct CFParser { - cf_parser cfp = {}; - inline ~CFParser() { cf_parser_free(&cfp); } - inline operator cf_parser *() { return &cfp; } - inline cf_parser *operator->() { return &cfp; } -}; - -void OBSApp::ParseExtraThemeData(const char *path) -{ - BPtr data = os_quick_read_utf8_file(path); - QPalette pal = palette(); - CFParser cfp; - int ret; - - cf_parser_parse(cfp, data, path); - - while (cf_go_to_token(cfp, "OBSTheme", nullptr)) { - if (!cf_next_token(cfp)) - return; - - int group = -1; - - if (cf_token_is(cfp, ":")) { - ret = cf_next_token_should_be(cfp, ":", nullptr, - nullptr); - if (ret != PARSE_SUCCESS) - continue; - - if (!cf_next_token(cfp)) - return; - - if (cf_token_is(cfp, "disabled")) { - group = QPalette::Disabled; - } else if (cf_token_is(cfp, "active")) { - group = QPalette::Active; - } else if (cf_token_is(cfp, "inactive")) { - group = QPalette::Inactive; - } else { - continue; - } - - if (!cf_next_token(cfp)) - return; - } - - if (!cf_token_is(cfp, "{")) - continue; - - for (;;) { - if (!cf_next_token(cfp)) - return; - - ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", - nullptr); - if (ret != PARSE_SUCCESS) - break; - - DStr name; - dstr_copy_strref(name, &cfp->cur_token->str); - - ret = cf_next_token_should_be(cfp, ":", ";", nullptr); - if (ret != PARSE_SUCCESS) - continue; - - if (!cf_next_token(cfp)) - return; - - const char *array; - uint32_t color = 0; - - if (cf_token_is(cfp, "#")) { - array = cfp->cur_token->str.array; - color = strtol(array + 1, nullptr, 16); - - } else if (cf_token_is(cfp, "rgb")) { - ret = cf_next_token_should_be(cfp, "(", ";", - nullptr); - if (ret != PARSE_SUCCESS) - continue; - if (!cf_next_token(cfp)) - return; - - array = cfp->cur_token->str.array; - color |= strtol(array, nullptr, 10) << 16; - - ret = cf_next_token_should_be(cfp, ",", ";", - nullptr); - if (ret != PARSE_SUCCESS) - continue; - if (!cf_next_token(cfp)) - return; - - array = cfp->cur_token->str.array; - color |= strtol(array, nullptr, 10) << 8; - - ret = cf_next_token_should_be(cfp, ",", ";", - nullptr); - if (ret != PARSE_SUCCESS) - continue; - if (!cf_next_token(cfp)) - return; - - array = cfp->cur_token->str.array; - color |= strtol(array, nullptr, 10); - - } else if (cf_token_is(cfp, "white")) { - color = 0xFFFFFF; - - } else if (cf_token_is(cfp, "black")) { - color = 0; - } - - if (!cf_go_to_token(cfp, ";", nullptr)) - return; - - AddExtraThemeColor(pal, group, name->array, color); - } - - ret = cf_token_should_be(cfp, "}", "}", nullptr); - if (ret != PARSE_SUCCESS) - continue; - } - - setPalette(pal); -} - -OBSThemeMeta *OBSApp::ParseThemeMeta(const char *path) -{ - BPtr data = os_quick_read_utf8_file(path); - CFParser cfp; - int ret; - - if (!cf_parser_parse(cfp, data, path)) - return nullptr; - - if (cf_token_is(cfp, "OBSThemeMeta") || - cf_go_to_token(cfp, "OBSThemeMeta", nullptr)) { - - if (!cf_next_token(cfp)) - return nullptr; - - if (!cf_token_is(cfp, "{")) - return nullptr; - - OBSThemeMeta *meta = new OBSThemeMeta(); - - for (;;) { - if (!cf_next_token(cfp)) { - delete meta; - return nullptr; - } - - ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", - nullptr); - if (ret != PARSE_SUCCESS) - break; - - DStr name; - dstr_copy_strref(name, &cfp->cur_token->str); - - ret = cf_next_token_should_be(cfp, ":", ";", nullptr); - if (ret != PARSE_SUCCESS) - continue; - - if (!cf_next_token(cfp)) { - delete meta; - return nullptr; - } - - ret = cf_token_is_type(cfp, CFTOKEN_STRING, "value", - ";"); - - if (ret != PARSE_SUCCESS) - continue; - - char *str; - str = cf_literal_to_str(cfp->cur_token->str.array, - cfp->cur_token->str.len); - - if (strcmp(name->array, "dark") == 0 && str) { - meta->dark = strcmp(str, "true") == 0; - } else if (strcmp(name->array, "parent") == 0 && str) { - meta->parent = std::string(str); - } else if (strcmp(name->array, "author") == 0 && str) { - meta->author = std::string(str); - } - bfree(str); - - if (!cf_go_to_token(cfp, ";", nullptr)) { - delete meta; - return nullptr; - } - } - return meta; - } - return nullptr; -} - -std::string OBSApp::GetTheme(std::string name, std::string path) -{ - /* Check user dir first, then preinstalled themes. */ - if (path == "") { - char userDir[512]; - name = "themes/" + name + ".qss"; - string temp = "obs-studio/" + name; - int ret = GetConfigPath(userDir, sizeof(userDir), temp.c_str()); - - if (ret > 0 && QFile::exists(userDir)) { - path = string(userDir); - } else if (!GetDataFilePath(name.c_str(), path)) { - OBSErrorBox(NULL, "Failed to find %s.", name.c_str()); - return ""; - } - } - return path; -} - -std::string OBSApp::SetParentTheme(std::string name) -{ - string path = GetTheme(name, ""); - if (path.empty()) - return path; - - setPalette(defaultPalette); - - ParseExtraThemeData(path.c_str()); - return path; -} - -bool OBSApp::SetTheme(std::string name, std::string path) -{ - theme = name; - - path = GetTheme(name, path); - if (path.empty()) - return false; - - setStyleSheet(""); - unique_ptr themeMeta; - themeMeta.reset(ParseThemeMeta(path.c_str())); - string parentPath; - - if (themeMeta && !themeMeta->parent.empty()) { - parentPath = SetParentTheme(themeMeta->parent); - } - - string lpath = path; - if (parentPath.empty()) { - setPalette(defaultPalette); - } else { - lpath = parentPath; - } - - QString mpath = QString("file:///") + lpath.c_str(); - ParseExtraThemeData(path.c_str()); - setStyleSheet(mpath); - if (themeMeta) { - themeDarkMode = themeMeta->dark; - } else { - QColor color = palette().text().color(); - themeDarkMode = !(color.redF() < 0.5); - } - -#ifdef __APPLE__ - SetMacOSDarkMode(themeDarkMode); -#endif - - emit StyleChanged(); - return true; -} - -bool OBSApp::InitTheme() -{ - defaultPalette = palette(); - setStyle(new OBSProxyStyle()); - - /* Set search paths for custom 'theme:' URI prefix */ - string searchDir; - if (GetDataFilePath("themes", searchDir)) { - auto installSearchDir = filesystem::u8path(searchDir); - QDir::addSearchPath("theme", absolute(installSearchDir)); - } - - char userDir[512]; - if (GetConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) { - auto configSearchDir = filesystem::u8path(userDir); - QDir::addSearchPath("theme", absolute(configSearchDir)); - } - - const char *themeName = - config_get_string(globalConfig, "General", "CurrentTheme3"); - if (!themeName) - themeName = DEFAULT_THEME; - - if (strcmp(themeName, "Default") == 0) - themeName = "System"; - - if (strcmp(themeName, "System") != 0 && SetTheme(themeName)) - return true; - - return SetTheme("System"); -} - #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) void ParseBranchesJson(const std::string &jsonString, vector &out, std::string &error) diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index 94c3f349bb38b6..ee5c24848fb4bf 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -20,6 +20,8 @@ #include #include #include +#include + #ifndef _WIN32 #include #else @@ -38,6 +40,7 @@ #include #include "window-main.hpp" +#include "obs-app-theming.hpp" std::string CurrentTimeString(); std::string CurrentDateTimeString(); @@ -74,12 +77,6 @@ class OBSTranslator : public QTranslator { typedef std::function VoidFunc; -struct OBSThemeMeta { - bool dark; - std::string parent; - std::string author; -}; - struct UpdateBranch { QString name; QString display_name; @@ -93,9 +90,7 @@ class OBSApp : public QApplication { private: std::string locale; - std::string theme; - bool themeDarkMode = true; ConfigFile globalConfig; TextLookup textLookup; QPointer mainWindow; @@ -123,11 +118,11 @@ class OBSApp : public QApplication { inline void ResetHotkeyState(bool inFocus); QPalette defaultPalette; + OBSTheme *currentTheme = nullptr; + QHash themes; + QPointer themeWatcher; - void ParseExtraThemeData(const char *path); - static OBSThemeMeta *ParseThemeMeta(const char *path); - void AddExtraThemeColor(QPalette &pal, int group, const char *name, - uint32_t color); + void FindThemes(); bool notify(QObject *receiver, QEvent *e) override; @@ -139,6 +134,9 @@ private slots: void commitData(QSessionManager &manager); #endif +private slots: + void themeFileChanged(const QString &); + public: OBSApp(int &argc, char **argv, profiler_name_store_t *store); ~OBSApp(); @@ -160,11 +158,14 @@ private slots: inline const char *GetLocale() const { return locale.c_str(); } - inline const char *GetTheme() const { return theme.c_str(); } - std::string GetTheme(std::string name, std::string path); - std::string SetParentTheme(std::string name); - bool SetTheme(std::string name, std::string path = ""); - inline bool IsThemeDark() const { return themeDarkMode; }; + OBSTheme *GetTheme() const { return currentTheme; } + QList GetThemes() const { return themes.values(); } + OBSTheme *GetTheme(const QString &name); + bool SetTheme(const QString &name); + bool IsThemeDark() const + { + return currentTheme ? currentTheme->isDark : false; + } void SetBranchData(const std::string &data); std::vector GetBranches(); diff --git a/UI/ui-config.h.in b/UI/ui-config.h.in index 3e40f75b470a55..25ec4699d6e07b 100644 --- a/UI/ui-config.h.in +++ b/UI/ui-config.h.in @@ -15,4 +15,4 @@ #define YOUTUBE_CLIENTID_HASH 0x@YOUTUBE_CLIENTID_HASH@ #define YOUTUBE_SECRET_HASH 0x@YOUTUBE_SECRET_HASH@ -#define DEFAULT_THEME "Yami" +#define DEFAULT_THEME "com.obsproject.Yami.Original" diff --git a/UI/window-basic-settings-appearance.cpp b/UI/window-basic-settings-appearance.cpp new file mode 100644 index 00000000000000..58c8401b6f0be4 --- /dev/null +++ b/UI/window-basic-settings-appearance.cpp @@ -0,0 +1,127 @@ +#include "window-basic-settings.hpp" +#include "window-basic-main.hpp" +#include "obs-frontend-api.h" +#include "qt-wrappers.hpp" +#include "platform.hpp" +#include "obs-app.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "util/profiler.hpp" + +using namespace std; + +void OBSBasicSettings::InitAppearancePage() +{ + savedTheme = App()->GetTheme(); + const QString currentBaseTheme = + savedTheme->isBaseTheme ? savedTheme->id : savedTheme->parent; + + for (const OBSTheme &theme : App()->GetThemes()) { + if (theme.isBaseTheme && + (HighContrastEnabled() || theme.isVisible || + theme.id == currentBaseTheme)) { + ui->theme->addItem(theme.name, theme.id); + } + } + + int idx = ui->theme->findData(currentBaseTheme); + if (idx != -1) + ui->theme->setCurrentIndex(idx); + + ui->themeVariant->setPlaceholderText( + QTStr("Basic.Settings.Appearance.General.NoVariant")); +} + +void OBSBasicSettings::LoadThemeList(bool reload) +{ + ProfileScope("OBSBasicSettings::LoadThemeList"); + + const OBSTheme *currentTheme = App()->GetTheme(); + const QString currentBaseTheme = currentTheme->isBaseTheme + ? currentTheme->id + : currentTheme->parent; + + /* Nothing to do if current and last base theme were the same */ + const QString baseThemeId = ui->theme->currentData().toString(); + if (reload && baseThemeId == currentBaseTheme) + return; + + ui->themeVariant->blockSignals(true); + ui->themeVariant->clear(); + + auto themes = App()->GetThemes(); + std::sort(themes.begin(), themes.end(), + [](const OBSTheme &a, const OBSTheme &b) -> bool { + return QString::compare(a.name, b.name, + Qt::CaseInsensitive) < 0; + }); + + QString defaultVariant; + const OBSTheme *baseTheme = App()->GetTheme(baseThemeId); + + for (const OBSTheme &theme : themes) { + /* Skip non-visible themes */ + if (!theme.isVisible || theme.isHighContrast) + continue; + /* Skip non-child themes */ + if (theme.isBaseTheme || theme.parent != baseThemeId) + continue; + + ui->themeVariant->addItem(theme.name, theme.id); + if (baseTheme && theme.filename == baseTheme->filename) + defaultVariant = theme.id; + } + + int idx = ui->themeVariant->findData(currentTheme->id); + if (idx != -1) + ui->themeVariant->setCurrentIndex(idx); + + ui->themeVariant->setEnabled(ui->themeVariant->count() > 0); + ui->themeVariant->blockSignals(false); + /* If no variant is selected but variants are available set the first one. */ + if (idx == -1 && ui->themeVariant->count() > 0) { + idx = ui->themeVariant->findData(defaultVariant); + ui->themeVariant->setCurrentIndex(idx != -1 ? idx : 0); + } +} + +void OBSBasicSettings::LoadAppearanceSettings(bool reload) +{ + LoadThemeList(reload); + + if (reload) { + QString themeId = ui->theme->currentData().toString(); + if (ui->themeVariant->currentIndex() != -1) + themeId = ui->themeVariant->currentData().toString(); + + App()->SetTheme(themeId); + } +} + +void OBSBasicSettings::SaveAppearanceSettings() +{ + config_t *config = GetGlobalConfig(); + + OBSTheme *currentTheme = App()->GetTheme(); + if (savedTheme != currentTheme) { + config_set_string(config, "Appearance", "Theme", + QT_TO_UTF8(currentTheme->id)); + } +} + +void OBSBasicSettings::on_theme_activated(int) +{ + LoadAppearanceSettings(true); +} + +void OBSBasicSettings::on_themeVariant_activated(int) +{ + LoadAppearanceSettings(true); +} diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 2a19e9131908f7..6d1b4ae943f27b 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -346,6 +346,7 @@ void RestrictResetBitrates(initializer_list boxes, int maxbitrate); #define VIDEO_RES &OBSBasicSettings::VideoChangedResolution #define VIDEO_CHANGED &OBSBasicSettings::VideoChanged #define A11Y_CHANGED &OBSBasicSettings::A11yChanged +#define APPEAR_CHANGED &OBSBasicSettings::AppearanceChanged #define ADV_CHANGED &OBSBasicSettings::AdvancedChanged #define ADV_RESTART &OBSBasicSettings::AdvancedChangedRestart /* clang-format on */ @@ -369,7 +370,6 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) /* clang-format off */ HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED); - HookWidget(ui->theme, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->updateChannelBox, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->enableAutoUpdates, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->openStatsOnStartup, CHECK_CHANGED, GENERAL_CHANGED); @@ -406,6 +406,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->multiviewDrawNames, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->multiviewDrawAreas, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->multiviewLayout, COMBO_CHANGED, GENERAL_CHANGED); + HookWidget(ui->theme, COMBO_CHANGED, APPEAR_CHANGED); + HookWidget(ui->themeVariant, COMBO_CHANGED, APPEAR_CHANGED); HookWidget(ui->service, COMBO_CHANGED, STREAM1_CHANGED); HookWidget(ui->server, COMBO_CHANGED, STREAM1_CHANGED); HookWidget(ui->customServer, EDIT_CHANGED, STREAM1_CHANGED); @@ -880,6 +882,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) obs_properties_destroy(ppts); InitStreamPage(); + InitAppearancePage(); LoadSettings(false); ui->advOutTrack1->setAccessibleName( @@ -1266,51 +1269,6 @@ void OBSBasicSettings::LoadLanguageList() ui->language->model()->sort(0); } -void OBSBasicSettings::LoadThemeList() -{ - /* Save theme if user presses Cancel */ - savedTheme = string(App()->GetTheme()); - - ui->theme->clear(); - QSet uniqueSet; - string themeDir; - char userThemeDir[512]; - int ret = GetConfigPath(userThemeDir, sizeof(userThemeDir), - "obs-studio/themes/"); - GetDataFilePath("themes/", themeDir); - - /* Check user dir first. */ - if (ret > 0) { - QDirIterator it(QString(userThemeDir), QStringList() << "*.qss", - QDir::Files); - while (it.hasNext()) { - it.next(); - QString name = it.fileInfo().completeBaseName(); - ui->theme->addItem(name, name); - uniqueSet.insert(name); - } - } - - /* Check shipped themes. */ - QDirIterator uIt(QString(themeDir.c_str()), QStringList() << "*.qss", - QDir::Files); - while (uIt.hasNext()) { - uIt.next(); - QString name = uIt.fileInfo().completeBaseName(); - QString value = name; - - if (name == DEFAULT_THEME) - name += " " + QTStr("Default"); - - if (!uniqueSet.contains(value) && name != "Default") - ui->theme->addItem(name, value); - } - - int idx = ui->theme->findData(QT_UTF8(App()->GetTheme())); - if (idx != -1) - ui->theme->setCurrentIndex(idx); -} - #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) void TranslateBranchInfo(const QString &name, QString &displayName, QString &description) @@ -1383,7 +1341,6 @@ void OBSBasicSettings::LoadGeneralSettings() loading = true; LoadLanguageList(); - LoadThemeList(); #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) bool enableAutoUpdates = config_get_bool(GetGlobalConfig(), "General", @@ -3329,6 +3286,8 @@ void OBSBasicSettings::LoadSettings(bool changedOnly) LoadVideoSettings(); if (!changedOnly || a11yChanged) LoadA11ySettings(); + if (!changedOnly || appearanceChanged) + LoadAppearanceSettings(); if (!changedOnly || advancedChanged) LoadAdvancedSettings(); } @@ -3343,15 +3302,6 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_string(GetGlobalConfig(), "General", "Language", language.c_str()); - int themeIndex = ui->theme->currentIndex(); - QString themeData = ui->theme->itemData(themeIndex).toString(); - - if (WidgetChanged(ui->theme)) { - savedTheme = themeData.toStdString(); - config_set_string(GetGlobalConfig(), "General", "CurrentTheme3", - QT_TO_UTF8(themeData)); - } - #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) if (WidgetChanged(ui->enableAutoUpdates)) config_set_bool(GetGlobalConfig(), "General", @@ -4142,7 +4092,8 @@ void OBSBasicSettings::SaveSettings() SaveA11ySettings(); if (advancedChanged) SaveAdvancedSettings(); - + if (appearanceChanged) + SaveAppearanceSettings(); if (videoChanged || advancedChanged) main->ResetVideo(); @@ -4166,6 +4117,8 @@ void OBSBasicSettings::SaveSettings() AddChangedVal(changed, "hotkeys"); if (a11yChanged) AddChangedVal(changed, "a11y"); + if (appearanceChanged) + AddChangedVal(changed, "appearance"); if (advancedChanged) AddChangedVal(changed, "advanced"); @@ -4204,7 +4157,7 @@ bool OBSBasicSettings::QueryChanges() SaveSettings(); } else { if (savedTheme != App()->GetTheme()) - App()->SetTheme(savedTheme); + App()->SetTheme(savedTheme->id); LoadSettings(true); restart = false; @@ -4299,13 +4252,6 @@ void OBSBasicSettings::reject() close(); } -void OBSBasicSettings::on_theme_activated(int idx) -{ - QString currT = ui->theme->itemData(idx).toString(); - - App()->SetTheme(currT.toUtf8().constData()); -} - void OBSBasicSettings::on_listWidget_itemSelectionChanged() { int row = ui->listWidget->currentRow(); @@ -4370,7 +4316,7 @@ void OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button) val == QDialogButtonBox::RejectRole) { if (val == QDialogButtonBox::RejectRole) { if (savedTheme != App()->GetTheme()) - App()->SetTheme(savedTheme); + App()->SetTheme(savedTheme->id); } ClearChanged(); close(); @@ -5015,6 +4961,15 @@ void OBSBasicSettings::A11yChanged() } } +void OBSBasicSettings::AppearanceChanged() +{ + if (!loading) { + appearanceChanged = true; + sender()->setProperty("changed", QVariant(true)); + EnableApplyButton(true); + } +} + void OBSBasicSettings::AdvancedChanged() { if (!loading) { diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 62f5800f094e14..c6f550c1443981 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -28,6 +28,7 @@ #include "auth-base.hpp" #include "ffmpeg-utils.hpp" +#include "obs-app-theming.hpp" class OBSBasic; class QAbstractButton; @@ -35,6 +36,7 @@ class QRadioButton; class QComboBox; class QCheckBox; class QLabel; +class QButtonGroup; class OBSPropertiesView; class OBSHotkeyWidget; @@ -116,12 +118,12 @@ class OBSBasicSettings : public QDialog { bool videoChanged = false; bool hotkeysChanged = false; bool a11yChanged = false; + bool appearanceChanged = false; bool advancedChanged = false; int pageIndex = 0; bool loading = true; bool forceAuthReload = false; bool forceUpdateCheck = false; - std::string savedTheme; int sampleRateIndex = 0; int channelIndex = 0; bool llBufferingEnabled = false; @@ -135,6 +137,8 @@ class OBSBasicSettings : public QDialog { static constexpr uint32_t ENCODER_HIDE_FLAGS = (OBS_ENCODER_CAP_DEPRECATED | OBS_ENCODER_CAP_INTERNAL); + OBSTheme *savedTheme = nullptr; + std::vector formats; OBSPropertiesView *streamProperties = nullptr; @@ -200,9 +204,9 @@ class OBSBasicSettings : public QDialog { inline bool Changed() const { - return generalChanged || outputsChanged || stream1Changed || - audioChanged || videoChanged || advancedChanged || - hotkeysChanged || a11yChanged; + return generalChanged || appearanceChanged || outputsChanged || + stream1Changed || audioChanged || videoChanged || + advancedChanged || hotkeysChanged || a11yChanged; } inline void EnableApplyButton(bool en) @@ -220,6 +224,7 @@ class OBSBasicSettings : public QDialog { hotkeysChanged = false; a11yChanged = false; advancedChanged = false; + appearanceChanged = false; EnableApplyButton(false); } @@ -253,6 +258,7 @@ class OBSBasicSettings : public QDialog { void LoadHotkeySettings(obs_hotkey_id ignoreKey = OBS_INVALID_HOTKEY_ID); void LoadA11ySettings(bool presetChange = false); + void LoadAppearanceSettings(bool reload = false); void LoadAdvancedSettings(); void LoadSettings(bool changedOnly); @@ -262,7 +268,7 @@ class OBSBasicSettings : public QDialog { /* general */ void LoadLanguageList(); - void LoadThemeList(); + void LoadThemeList(bool firstLoad); void LoadBranchesList(); /* stream */ @@ -287,6 +293,9 @@ class OBSBasicSettings : public QDialog { void UpdateMoreInfoLink(); void UpdateAdvNetworkGroup(); + /* Appearance */ + void InitAppearancePage(); + private slots: void RecreateOutputResolutionWidget(); bool UpdateResFPSLimits(); @@ -351,6 +360,7 @@ private slots: void SaveVideoSettings(); void SaveHotkeySettings(); void SaveA11ySettings(); + void SaveAppearanceSettings(); void SaveAdvancedSettings(); void SaveSettings(); @@ -405,6 +415,7 @@ private slots: private slots: void on_theme_activated(int idx); + void on_themeVariant_activated(int idx); void on_listWidget_itemSelectionChanged(); void on_buttonBox_clicked(QAbstractButton *button); @@ -459,6 +470,7 @@ private slots: bool ScanDuplicateHotkeys(QFormLayout *layout); void ReloadHotkeys(obs_hotkey_id ignoreKey = OBS_INVALID_HOTKEY_ID); void A11yChanged(); + void AppearanceChanged(); void AdvancedChanged(); void AdvancedChangedRestart(); From eea0e3d2ccd874275f087641b0647d95502bd33c Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 6 Apr 2024 01:17:28 +0200 Subject: [PATCH 0030/1073] cmake: Remove font size patch on macOS --- cmake/macos/helpers.cmake | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cmake/macos/helpers.cmake b/cmake/macos/helpers.cmake index c918dd721d7971..954ee0c5ea2712 100644 --- a/cmake/macos/helpers.cmake +++ b/cmake/macos/helpers.cmake @@ -146,18 +146,6 @@ function(set_target_properties_obs target) set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${entitlements_file}") - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND - /usr/bin/sed -i '' 's/font-size: 10pt\;/font-size: 12pt\;/' - "$/Resources/themes/Acri.qss" - "$/Resources/themes/Grey.qss" - "$/Resources/themes/Light.qss" - "$/Resources/themes/Rachni.qss" - "$/Resources/themes/Yami.qss" - COMMENT "Patch Qt stylesheets to use larger default font size on macOS") - add_custom_command( TARGET ${target} POST_BUILD From a3a8f6dbfb28f892fcbdc0cd3eaf73a219b5929a Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sat, 6 Apr 2024 01:17:42 +0200 Subject: [PATCH 0031/1073] UI: Convert themes to composable format --- UI/data/themes/Acri.qss | 1550 --------------- UI/data/themes/Dark.qss | 1092 ----------- UI/data/themes/Grey.qss | 1538 --------------- UI/data/themes/Light.qss | 1544 --------------- UI/data/themes/Light/settings/appearance.svg | 2 + UI/data/themes/Rachni.qss | 1538 --------------- UI/data/themes/{System.qss => System.obt} | 18 +- UI/data/themes/Yami.obt | 1842 ++++++++++++++++++ UI/data/themes/Yami.qss | 1540 --------------- UI/data/themes/Yami_Acri.ovt | 232 +++ UI/data/themes/Yami_Classic.ovt | 228 +++ UI/data/themes/Yami_Default.ovt | 7 + UI/data/themes/Yami_Grey.ovt | 18 + UI/data/themes/Yami_Light.ovt | 313 +++ UI/data/themes/Yami_Rachni.ovt | 221 +++ 15 files changed, 2873 insertions(+), 8810 deletions(-) delete mode 100644 UI/data/themes/Acri.qss delete mode 100644 UI/data/themes/Dark.qss delete mode 100644 UI/data/themes/Grey.qss delete mode 100644 UI/data/themes/Light.qss create mode 100644 UI/data/themes/Light/settings/appearance.svg delete mode 100644 UI/data/themes/Rachni.qss rename UI/data/themes/{System.qss => System.obt} (96%) create mode 100644 UI/data/themes/Yami.obt delete mode 100644 UI/data/themes/Yami.qss create mode 100644 UI/data/themes/Yami_Acri.ovt create mode 100644 UI/data/themes/Yami_Classic.ovt create mode 100644 UI/data/themes/Yami_Default.ovt create mode 100644 UI/data/themes/Yami_Grey.ovt create mode 100644 UI/data/themes/Yami_Light.ovt create mode 100644 UI/data/themes/Yami_Rachni.ovt diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss deleted file mode 100644 index 593a75536f4e70..00000000000000 --- a/UI/data/themes/Acri.qss +++ /dev/null @@ -1,1550 +0,0 @@ -/******************************************************************************/ -/* Copyright (C) 2014-2015 by Philippe Groarke */ -/* */ -/* */ -/* This program is free software: you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation, either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program. If not, see . */ -/******************************************************************************/ - -/* Colors */ - -OBSThemeMeta { - dark: 'true'; - author: 'Warchamp7'; -} - -/* Custom theme information. This will set the application's QPalette, as - * well as pass to QML via the OBSTheme object. - * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. - * Using it without will set all three (making 'active' a bit redundant) */ -OBSTheme { - window: rgb(16,16,16); - windowText: rgb(255,254,255); - - base: rgb(24,24,25); - alternateBase: rgb(11,10,11); - - text: rgb(255,254,255); - - button: rgb(22,36,88); - buttonText: rgb(255,254,255); - - brightText: rgb(255,254,255); - - light: rgb(88,87,88); - mid: rgb(16,16,16); - dark: rgb(24,24,25); - shadow: rgb(11,10,11); - - primary: rgb(19,26,48); - primaryLight: rgb(54,92,192); - primaryDark: rgb(22,31,65); - - highlight: rgb(42,130,218); - highlightText: rgb(255,254,255); - - link: rgb(77,166,255); - linkVisited: rgb(77,166,255); -} - -OBSTheme::disabled { - windowText: rgb(153,153,153); - text: rgb(153,153,153); - button: rgb(27,29,34); - - buttonText: rgb(24,24,25); - brightText: rgb(24,24,25); -} - -OBSTheme::inactive { - text: rgb(255,254,255); - - highlight: rgb(25,28,34); - highlightText: rgb(255,255,255); -} - -/* Default widget style, we override only what is needed. */ - -QWidget { - alternate-background-color: palette(base); - color: palette(text); - selection-background-color: rgb(22,31,65); - selection-color: palette(text); - font-size: 10pt; - font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif; -} - -QWidget:disabled { - color: rgb(153,153,153); -} - -/* Container windows */ - -QDialog, -QMainWindow, -QStatusBar, -QMenuBar, -QMenu { - background-color: palette(window); -} - -/* macOS Separator Fix */ - -QMainWindow::separator { - background: transparent; - width: 4px; - height: 4px; -} - -/* General Widgets */ - -QLabel, -QGroupBox, -QCheckBox { - background: transparent; -} - -QComboBox, -QCheckBox, -QPushButton, -QSpinBox, -QDoubleSpinBox { - margin-top: 3px; - margin-bottom: 3px; -} - -QListWidget QWidget, -SceneTree QWidget, -SourceTree QWidget { - margin-top: 0; - margin-bottom: 0; -} - -* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { - border: 1px solid palette(dark); -} - - -/* Misc */ - -QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { - background-color: palette(base); -} - -QToolTip { - background-color: palette(base); - color: palette(text); - border: none; -} - -/* Context Menu */ - -QMenu::icon { - left: 4px; -} - -QMenu::separator { - background: rgb(42,42,44); - height: 1px; - margin: 3px 6px; -} - -QMenu::item:disabled { - color: rgb(153,153,153); - background: transparent; -} - -QMenu::right-arrow { - image: url(theme:Dark/expand.svg); -} - -/* Top Menu Bar Items */ -QMenuBar::item { - background-color: transparent; -} - -QMenuBar::item:selected { - background: rgb(19,26,48); -} - -/* Item Lists */ -QListWidget { - border-radius: 4px; -} - -QListWidget::item { - color: palette(text); -} - -QListWidget, -QMenu, -SceneTree, -SourceTree { - padding: 3px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item { - padding: 6px; -} - -QMenu::item { - padding-right: 20px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item, -SourceTree::item { - border-radius: 4px; - color: palette(text); - border: 0px solid transparent; -} - -QMenu::item:selected, -QListWidget::item:selected, -SceneTree::item:selected, -SourceTree::item:selected { - background-color: rgb(19,26,48); -} - -QMenu::item:hover, -QListWidget::item:hover, -SceneTree::item:hover, -SourceTree::item:hover, -QMenu::item:selected:hover, -QListWidget::item:selected:hover, -SceneTree::item:selected:hover, -SourceTree::item:selected:hover { - background-color: rgb(33,33,33); - color: palette(text); -} - -QListWidget::item:disabled, -QListWidget::item:disabled:hover { - background: transparent; - color: rgb(153,153,153); -} - -QListWidget QLineEdit, -SceneTree QLineEdit, -SourceTree QLineEdit { - padding: 0px; - padding-bottom: 2px; - margin: 0px; - border: 1px solid #FFF; - border-radius: 4px; -} - -QListWidget QLineEdit:focus, -SceneTree QLineEdit:focus, -SourceTree QLineEdit:focus { - border: 1px solid #FFF; -} - -/* Settings QList */ - -OBSBasicSettings QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicSettings QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -/* Settings properties view */ -OBSBasicSettings #PropertiesContainer { - background-color: palette(dark); -} - -#PropertiesContainer QListWidget { - background-color: palette(button); -} - -/* Dock Widget */ -OBSDock > QWidget { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -OBSDock QLabel { - background: transparent; -} - -#transitionsFrame { - padding: 4px 8px; -} - -QDockWidget { - font-size: 10.5pt; - font-weight: bold; - - titlebar-close-icon: url(theme:Dark/close.svg); - titlebar-normal-icon: url(theme:Dark/popout.svg); -} - -QDockWidget::title { - text-align: left; - background-color: palette(base); - padding: 6px 8px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 0px solid transparent; - border-radius: 4px; - background: transparent; - margin-right: 1px; - opacity: .5; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgb(61,61,63); - opacity: 1; -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; -} - -QScrollArea { - border-radius: 4px; -} - -/* Qt enforces a padding inside its status bar, so we - * oversize it and use margin to crunch it back down - */ -OBSBasicStatusBar { - margin-top: 4px; - border-top: 1px solid #3c404b; - background: palette(dark); -} - -StatusBarWidget > QFrame { - margin-top: 1px; - border: 0px solid #3c404b; - border-left-width: 1px; - padding: 0px 12px 2px; -} - -/* Group Box */ - -QGroupBox { - background: palette(dark); - border-radius: 4px; - padding-top: 32px; - padding-bottom: 8px; - font-weight: bold; - margin-bottom: 6px; -} - -QGroupBox::title { - subcontrol-origin: margin; - left: 8px; - top: 8px; -} - - -/* ScrollBars */ - -::corner { - background-color: palette(window); - border: none; -} - -QScrollBar:vertical { - background-color: transparent; - width: 14px; - margin: 0px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - border: none; - background: none; - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - border: none; - background: none; - color: none; -} - -QScrollBar:horizontal { - background-color: transparent; - height: 14px; - margin: 0px; -} - -QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { - border: none; - background: none; - width: 0px; -} - -QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - border: none; - background: none; - color: none; -} - -QScrollBar::handle { - background-color: rgb(40,40,42); - margin: 2px; - border-radius: 2px; - border: 1px solid rgb(40,40,42); -} - -QScrollBar::handle:hover { - background-color: rgb(42,58,117); - border-color: rgb(42,58,117); -} - -QScrollBar::handle:pressed { - background-color: rgb(40,40,42); - border-color: rgb(40,40,42); -} - -QScrollBar::handle:vertical { - min-height: 20px; -} - -QScrollBar::handle:horizontal { - min-width: 20px; -} - -/* Source Context Bar */ - -#contextContainer { - background-color: palette(dark); - margin-top: 4px; - border-radius: 4px; -} - -#contextContainer QPushButton { - padding-left: 12px; - padding-right: 12px; -} - -QPushButton#sourcePropertiesButton { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -QPushButton#sourceFiltersButton { - qproperty-icon: url(theme:Dark/filter.svg); -} - -/* Scenes and Sources toolbar */ - -QToolBar { - background-color: palette(dark); - border: none; - padding: 0px; - margin: 4px 0px; -} - -QPushButton[toolButton="true"], -QToolButton, -QPushButton[toolButton="true"]:disabled, -QToolButton:disabled { - background-color: palette(base); - padding: 4px 6px; - margin: 0px 2px; - border-radius: 4px; -} - -QPushButton[toolButton="true"]:last-child, -QToolButton:last-child { - margin-right: 0px; -} - -QToolButton:hover { - background-color: rgb(42,58,117); -} - -QToolButton:pressed { - background-color: rgb(22,31,65); -} - -* [themeID="addIconSmall"] { - qproperty-icon: url(theme:Dark/plus.svg); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(theme:Dark/trash.svg); -} - -* [themeID="clearIconSmall"] { - qproperty-icon: url(theme:Dark/entry-clear.svg); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="menuIconSmall"] { - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(theme:Dark/refresh.svg); -} - -* [themeID="cogsIcon"] { - qproperty-icon: url(theme:Dark/cogs.svg); -} - -#sourceInteractButton { - qproperty-icon: url(theme:Dark/interact.svg); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(theme:Dark/up.svg); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(theme:Dark/down.svg); -} - -* [themeID="pauseIconSmall"] { - qproperty-icon: url(theme:Dark/media-pause.svg); -} - -* [themeID="filtersIcon"] { - qproperty-icon: url(theme:Dark/filter.svg); -} - -QToolBarExtension { - background: palette(button); - min-width: 12px; - max-width: 12px; - padding: 4px 0px; - margin-left: 0px; - - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - - -/* Tab Widget */ - -QTabWidget::pane { /* The tab widget frame */ - border-top: 4px solid palette(base); -} - -QTabWidget::tab-bar { - alignment: left; -} - -QTabBar QToolButton { - background: rgb(44,46,53); - border: none; -} - -QTabBar::tab:top { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QTabBar::tab:bottom { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -QTabBar::tab { - background: palette(dark); - color: palette(text); - border: none; - padding: 8px 12px; - min-width: 50px; - margin: 1px 2px; -} - -QTabBar::tab:pressed { - background: rgb(22,31,65); -} - -QTabBar::tab:hover { - background: rgb(42,58,117); - color: palette(text); -} - -QTabBar::tab:selected { - background: rgb(22,36,88); - color: palette(text); -} - -QTabBar::tab:top:selected { - border-bottom: 2px solid rgb(250,250,250); -} - -QTabBar::tab:bottom:selected { - border-top: 2px solid rgb(250,250,250); -} - -QTabBar QToolButton { - background: palette(base); - min-width: 16px; - padding: 0px; -} - -/* ComboBox */ - -QComboBox, -QDateTimeEdit { - background-color: rgb(40,40,42); - border-style: solid; - border: 1px; - border-radius: 4px; - border-color: rgb(40,40,42); - padding: 4px; - padding-left: 10px; -} - -QComboBox:hover, -QComboBox:selected, -QDateTimeEdit:hover, -QDateTimeEdit:selected { - background-color: rgb(61,61,63); -} - -QComboBox::drop-down, -QDateTimeEdit::drop-down { - border:none; - border-left: 1px solid rgb(25,28,34); - width: 20px; -} - -QComboBox::down-arrow, -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/updown.svg); - width: 100%; -} - -QComboBox:on, -QDateTimeEdit:on { - background-color: rgb(42,58,117); -} - -QComboBox:editable:hover { - -} - -QComboBox::drop-down:editable, -QDateTimeEdit::drop-down:editable { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} - -QComboBox::down-arrow:editable, -QDateTimeEdit::down-arrow:editable { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 8%; -} - -/* Textedits etc */ - -QLineEdit, QTextEdit, QPlainTextEdit { - background-color: rgb(40,40,42); - border: none; - border-radius: 4px; - padding: 5px 2px 5px 7px; - border: 2px solid transparent; -} - -QLineEdit:hover, -QTextEdit:hover, -QPlainTextEdit:hover { - border: 2px solid rgb(99,102,111); -} - -QLineEdit:focus, -QTextEdit:focus, -QPlainTextEdit:focus { - background-color: palette(mid); - border: 2px solid rgb(19,26,48); -} - -/* Spinbox and doubleSpinbox */ - -QSpinBox, -QDoubleSpinBox { - background-color: rgb(40,40,42); - border: 2px solid rgb(40,40,42); - border-radius: 4px; - margin-right: 3px; - padding: 3px 0px 4px 5px; -} - -QSpinBox:hover, -QDoubleSpinBox:hover { - border: 2px solid rgb(99,102,111); -} - -QSpinBox:focus, -QDoubleSpinBox:focus { - background-color: palette(mid); - border: 2px solid rgb(19,26,48); -} - -QSpinBox::up-button, QDoubleSpinBox::up-button { - subcontrol-origin: padding; - subcontrol-position: top right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom-width: 0; -} - -QSpinBox::down-button, QDoubleSpinBox::down-button { - subcontrol-origin: padding; - subcontrol-position: bottom right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - border-top-width: 0; -} - -QSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { - background-color: rgb(61,61,63); -} - -QSpinBox::up-button:pressed, QSpinBox::down-button:pressed, QDoubleSpinBox::up-button:pressed, QDoubleSpinBox::down-button:pressed { - background-color: rgb(22,31,65); -} - -QSpinBox::up-button:disabled, QSpinBox::up-button:off, QSpinBox::down-button:disabled, QSpinBox::down-button:off { - background-color: rgb(22,31,65); -} - -QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBox::down-button:disabled, QDoubleSpinBox::down-button:off { - background-color: rgb(22,31,65); -} - -QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(theme:Dark/up.svg); - width: 100%; - margin: 2px; -} - -QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(theme:Dark/down.svg); - width: 100%; - padding: 2px; -} - - -/* Controls Dock */ -#controlsFrame { - padding: 4px 3px; -} - -#controlsFrame QPushButton { - margin: 2px 1px; -} - -#streamButton, -#recordButton, -QPushButton[themeID="replayBufferButton"], -#broadcastButton { - padding: 10px; -} - -/* Primary Control Button Checked Coloring */ -#streamButton:!hover:!pressed:checked, -#recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, -#modeSwitch:!hover:!pressed:checked, -#broadcastButton:!hover:!pressed:checked { - background: rgb(88,22,36); -} - -/* Primary Control Button Hover Coloring */ -#streamButton:hover:!pressed:checked, -#recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, -#modeSwitch:hover:!pressed:checked, -#broadcastButton:hover:!pressed:checked { - background: rgb(116,32,49); - color: palette(text); -} - -/* Primary Control Button Checked Coloring */ -#streamButton:pressed:checked, -#recordButton:pressed:checked, -QPushButton[themeID="replayBufferButton"]:pressed:checked, -QPushButton[themeID="vcamButton"]:pressed:checked, -#modeSwitch:pressed:checked, -#broadcastButton:pressed:checked { - background: rgb(63,21,30); -} - -/* Buttons */ - -QPushButton { - color: palette(text); - background-color: palette(button); - min-height: 18px; - border: none; - border-radius: 4px; - padding: 6px 16px; -} - -QPushButton::flat { - background-color: rgb(22,36,88); -} - -QPushButton:checked { - background-color: rgb(19,26,48); -} - -QPushButton:hover { - background-color: rgb(42,58,117); -} - -QPushButton:pressed { - background-color: rgb(22,31,65); -} - -QPushButton:disabled, QToolButton:disabled { - background-color: rgb(22,31,65); -} - -QPushButton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - subcontrol-origin: padding; - width: 25px; -} - -/* Sliders */ - -QSlider::groove:horizontal { - background-color: rgb(40,40,42); - height: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - height: 10px; - width: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:horizontal:pressed { - background-color: palette(text); -} - -QSlider::sub-page:horizontal { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::groove:vertical { - background-color: rgb(40,40,42); - width: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:vertical { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - width: 10px; - height: 18px; - margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:vertical:pressed { - background-color: palette(text); -} - -QSlider::add-page:vertical { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::add-page:vertical:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::handle:hover { - background-color: rgb(200,199,200); -} - -QSlider::handle:disabled { - background-color: rgb(68,75,110); -} - -/* Volume Control */ - -#stackedMixerArea QPushButton { - min-width: 16px; - padding: 4px 8px; -} - -/* This is an incredibly cursed but necessary fix */ -#stackedMixerArea QPushButton:!hover { - background-color: palette(base); -} - -#stackedMixerArea QPushButton:hover { - background-color: rgb(42,58,117); -} - -#stackedMixerArea QPushButton:pressed { - background-color: rgb(22,31,65); -} - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(66,116,12); - qproperty-backgroundWarningColor: rgb(152,143,15); - qproperty-backgroundErrorColor: rgb(128,32,4); - qproperty-foregroundNominalColor: rgb(132,216,43); - qproperty-foregroundWarningColor: rgb(228,215,23); - qproperty-foregroundErrorColor: rgb(215,65,22); - qproperty-magnitudeColor: rgb(49,54,59); - qproperty-majorTickColor: rgb(239,240,241); - qproperty-minorTickColor: rgb(118,121,124); - qproperty-peakDecayRate: 23.4; -} - -/* Status Bar */ - -QStatusBar::item { - border: none; -} - -/* Table View */ - -QTableView { - background: palette(base); - gridline-color: palette(light); -} - -QTableView::item { - margin: 0px; - padding: 0px; -} - -QTableView QLineEdit { - background: palette(mid); - padding: 0; - margin: 0; -} - -QTableView QPushButton, -QTableView QToolButton { - margin: 1px 1px 2px; -} - -QHeaderView::section { - background-color: rgb(40,40,42); - color: palette(text); - border: none; - border-left: 1px solid palette(window); - border-right: 1px solid palette(window); - padding: 2px 4px; - margin-bottom: 2px; -} - -/* Mute CheckBox */ - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:indeterminate { - image: url(theme:Dark/unassigned.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192,128,0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192,128,0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0,192,0); - font-weight: bold; -} - -/* About dialog */ - -* [themeID="aboutName"] { - font-size: 26pt; - font-weight: bold; -} - -* [themeID="aboutVersion"] { - font-size: 12pt; - margin-bottom: 20px; -} - -* [themeID="aboutInfo"] { - margin-bottom: 20px; -} - -* [themeID="aboutHLayout"] { - background-color: palette(base); -} - -/* Canvas / Preview background color */ - -OBSQTDisplay { - qproperty-displayBackgroundColor: rgb(40,40,42); - border-radius: 10px; -} - -/* Filters Window */ - -OBSBasicFilters QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicFilters QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -OBSBasicFilters #widget, -OBSBasicFilters #widget_2 { - margin: 0px; - padding: 0px; - padding-bottom: 4px; -} - -OBSBasicFilters #widget QPushButton, -OBSBasicFilters #widget_2 QPushButton { - min-width: 16px; - padding: 4px 8px; - margin-top: 0px; -} - -/* Preview/Program labels */ - -* [themeID="previewProgramLabels"] { - font-size: 14pt; - font-weight: bold; - color: rgb(210,210,210); - margin-bottom: 4px; -} - -/* Settings Icons */ - -OBSBasicSettings { - qproperty-generalIcon: url(theme:Dark/settings/general.svg); - qproperty-streamIcon: url(theme:Dark/settings/stream.svg); - qproperty-outputIcon: url(theme:Dark/settings/output.svg); - qproperty-audioIcon: url(theme:Dark/settings/audio.svg); - qproperty-videoIcon: url(theme:Dark/settings/video.svg); - qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg); - qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg); - qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg); -} - -/* Checkboxes */ -QCheckBox { - -} - -QCheckBox::indicator, -QGroupBox::indicator { - width: 18px; - height: 18px; -} - -QGroupBox::indicator { - margin-left: 2px; -} - -QCheckBox::indicator:unchecked, -QGroupBox::indicator:unchecked { - image: url(theme:Yami/checkbox_unchecked.svg); -} - -QCheckBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:hover { - border: none; - image: url(theme:Yami/checkbox_unchecked_focus.svg); -} - -QCheckBox::indicator:checked, -QGroupBox::indicator:checked { - image: url(theme:Yami/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QGroupBox::indicator:checked:hover { - border: none; - image: url(theme:Yami/checkbox_checked_focus.svg); -} - -QCheckBox::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled { - image: url(theme:Yami/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled { - image: url(theme:Yami/checkbox_unchecked_disabled.svg); -} - -/* Locked CheckBox */ - -QCheckBox[lockCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[lockCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[lockCheckBox=true]::indicator:checked, -QCheckBox[lockCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/locked.svg); -} - -QCheckBox[lockCheckBox=true]::indicator:unchecked, -QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/unlocked.svg); -} - -/* Visibility CheckBox */ - -QCheckBox[visibilityCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[visibilityCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[visibilityCheckBox=true]::indicator:checked, -QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/visible.svg); -} - -QCheckBox[visibilityCheckBox=true]::indicator:unchecked, -QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/invisible.svg); -} - -* [themeID="revertIcon"] { - qproperty-icon: url(theme:Dark/revert.svg); -} - -QPushButton#extraPanelDelete { - background-color: palette(mid); - margin: 0; - padding: 0; -} - -QPushButton#extraPanelDelete:hover { - background-color: rgb(68,75,110); -} - -QPushButton#extraPanelDelete:pressed { - background-color: palette(dark); -} - -/* Mute CheckBox */ - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator { - width: 16px; - height: 16px; -} - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:hover { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:focus { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:checked:hover { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:focus { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:disabled { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked:disabled { - image: url(theme:Dark/settings/audio.svg); -} - -#hotkeyFilterReset { - margin-top: 0px; -} - -OBSHotkeyWidget { - padding: 8px 0px; - margin: 2px 0px; -} - -OBSHotkeyLabel { - padding: 4px 0px; -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -OBSHotkeyWidget QPushButton { - min-width: 16px; - padding: 4px 4px; - margin-top: 0px; - margin-left: 4px; -} - - -/* Sources List Group Collapse Checkbox */ - -QCheckBox[sourceTreeSubItem=true] { - background: transparent; - outline: none; - padding: 0px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator { - width: 12px; - height: 12px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator:checked, -QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { - image: url(theme:Dark/expand.svg); -} - -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { - image: url(theme:Dark/collapse.svg); -} - -/* Source Icons */ - -OBSBasic { - qproperty-imageIcon: url(theme:Dark/sources/image.svg); - qproperty-colorIcon: url(theme:Dark/sources/brush.svg); - qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg); - qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg); - qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg); - qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg); - qproperty-windowCapIcon: url(theme:Dark/sources/window.svg); - qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg); - qproperty-cameraIcon: url(theme:Dark/sources/camera.svg); - qproperty-textIcon: url(theme:Dark/sources/text.svg); - qproperty-mediaIcon: url(theme:Dark/sources/media.svg); - qproperty-browserIcon: url(theme:Dark/sources/globe.svg); - qproperty-groupIcon: url(theme:Dark/sources/group.svg); - qproperty-sceneIcon: url(theme:Dark/sources/scene.svg); - qproperty-defaultIcon: url(theme:Dark/sources/default.svg); - qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg); -} - -/* Scene Tree Grid Mode */ - -SceneTree { - qproperty-gridItemWidth: 154; - qproperty-gridItemHeight: 31; -} - -*[gridMode="true"] SceneTree::item { - color: palette(text); - background-color: palette(button); - border-radius: 4px; - margin: 2px; -} - -*[gridMode="true"] SceneTree::item:selected { - background-color: rgb(88,22,36); -} - -*[gridMode="true"] SceneTree::item:checked { - background-color: rgb(88,22,36); -} - -*[gridMode="true"] SceneTree::item:hover { - background-color: rgb(42,58,117); -} - -*[gridMode="true"] SceneTree::item:selected:hover { - background-color: rgb(116,32,49); -} - -/* Save icon */ - -* [themeID="replayIconSmall"] { - qproperty-icon: url(theme:Dark/save.svg); -} - -/* Studio Mode T-Bar */ - -QSlider[themeID="tBarSlider"] { - height: 24px; -} - -QSlider::groove:horizontal[themeID="tBarSlider"] { - border: 1px solid #4c4c4c; - height: 5px; - background: palette(dark); -} - -QSlider::sub-page:horizontal[themeID="tBarSlider"] { - background: palette(dark); - border: 1px solid #4c4c4c; -} - -QSlider::handle:horizontal[themeID="tBarSlider"] { - background-color: #d2d2d2; - width: 12px; - height: 24px; - margin: -24px 0px; -} - -/* Media icons */ - -* [themeID="playIcon"] { - qproperty-icon: url(theme:Dark/media/media_play.svg); -} - -* [themeID="pauseIcon"] { - qproperty-icon: url(theme:Dark/media/media_pause.svg); -} - -* [themeID="restartIcon"] { - qproperty-icon: url(theme:Dark/media/media_restart.svg); -} - -* [themeID="stopIcon"] { - qproperty-icon: url(theme:Dark/media/media_stop.svg); -} - -* [themeID="nextIcon"] { - qproperty-icon: url(theme:Dark/media/media_next.svg); -} - -* [themeID="previousIcon"] { - qproperty-icon: url(theme:Dark/media/media_previous.svg); -} - -/* YouTube Integration */ -OBSYoutubeActions { - qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg); -} - -#ytEventList QLabel { - color: palette(text); - background-color: rgb(40,40,42); - border: none; - border-radius: 4px; - padding: 4px 20px; -} - -#ytEventList QLabel:hover { - background-color: rgb(61,61,63); -} - -#ytEventList QLabel[isSelectedEvent=true] { - background-color: rgb(19,26,48); - border: none; -} - -#ytEventList QLabel[isSelectedEvent=true]:hover { - background-color: rgb(54,92,192); - color: palette(text); -} - -/* Calendar Widget */ -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 100%; -} - -QDateTimeEdit:on { - background-color: palette(mid); -} - -/* Calendar Top Bar */ -QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: palette(base); - padding: 4px 8px; -} - -/* Calendar Top Bar Buttons */ -QCalendarWidget QToolButton { - background-color: palette(base); - padding: 2px 16px; - border-radius: 4px; - margin: 2px; -} - -#qt_calendar_monthbutton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - padding-top: 2px; - padding-right: 6px; - height: 10px; - width: 10px; -} - -QCalendarWidget #qt_calendar_prevmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/left.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget #qt_calendar_nextmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/right.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget QToolButton:hover { - background-color: rgb(42,58,117); - border-radius: 4px; -} - -QCalendarWidget QToolButton:pressed { - background-color: rgb(22,31,65); -} - -/* Month Dropdown Menu */ -QCalendarWidget QMenu { - -} -/* Year spinbox */ -QCalendarWidget QSpinBox { - background-color: rgb(22,31,65); - border: none; - border-radius: 4px; - margin: 0px 3px 0px 0px; - padding: 4px 16px; -} - -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} -QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } -QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } - -/* Days of the Week Bar */ -QCalendarWidget QWidget { alternate-background-color: palette(mid); } - -QCalendarWidget QAbstractItemView:enabled { - background-color: palette(base); - color: palette(text); -} - -QCalendarWidget QAbstractItemView:disabled { - color: rgb(122,121,122); -} - -/* VirtualCam Plugin Fixes */ - -#VirtualProperties QWidget { - margin-top: 0; - margin-bottom: 0; -} - -/* Disable icons on QDialogButtonBox */ -QDialogButtonBox { - dialogbuttonbox-buttons-have-icons: 0; -} - -/* Stats dialog */ -OBSBasicStats { - background: palette(dark); -} - -/* Advanced audio dialog */ -OBSBasicAdvAudio #scrollAreaWidgetContents { - background: palette(dark); -} diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss deleted file mode 100644 index 06b066ec50bcf8..00000000000000 --- a/UI/data/themes/Dark.qss +++ /dev/null @@ -1,1092 +0,0 @@ -/******************************************************************************/ -/* Copyright (C) 2014-2015 by Philippe Groarke */ -/* */ -/* */ -/* This program is free software: you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation, either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program. If not, see . */ -/******************************************************************************/ - -/* Colors */ - -/* rgb(254,253,254); /* veryLight */ -/* rgb(200,199,200); /* lighter */ -/* rgb(122,121,122); /* light */ -/* rgb(88,87,88); /* kindaDark */ -/* rgb(58,57,58); /* dark */ -/* rgb(31,30,31); /* veryDark */ -/* rgb(11,10,11); /* veryVeryDark */ -/* rgb(42,130,218); /* blue */ - -OBSThemeMeta { - dark: 'true'; - author: 'Warchamp7'; -} - -/* Custom theme information. This will set the application's QPalette, as - * well as pass to QML via the OBSTheme object. - * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. - * Using it without will set all three (making 'active' a bit redundant) */ -OBSTheme { - window: rgb(58,57,58); /* dark */ - windowText: rgb(254,253,254); /* veryLight */ - base: rgb(31,30,31); /* veryDark */ - alternateBase: rgb(11,10,11); /* veryVeryDark */ - text: rgb(254,253,254); /* veryLight */ - button: rgb(88,87,88); /* kindaDark */ - buttonText: rgb(254,253,254); /* veryLight */ - brightText: rgb(200,199,200); /* lighter */ - - light: rgb(88,87,88); /* kindaDark */ - mid: rgb(58,57,58); /* dark */ - dark: rgb(31,30,31); /* veryDark */ - shadow: rgb(11,10,11); /* veryVeryDark */ - - highlight: rgb(42,130,218); /* blue */ - highlightedText: rgb(0,0,0); - - link: rgb(114,162,255); /* OBS blue */ - linkVisited: rgb(114,162,255); /* OBS blue */ -} - -OBSTheme::disabled { - windowText: rgb(165,164,165); /* disabledText */ - text: rgb(165,164,165); /* disabledText */ - buttonText: rgb(165,164,165); /* disabledText */ - brightText: rgb(165,164,165); /* disabledText */ -} - -OBSTheme::inactive { - highlight: rgb(48,47,48); - highlightedText: rgb(255,255,255); -} - - -/* General style, we override only what is needed. */ -QWidget { - background-color: palette(window); - alternate-background-color: palette(base); - color: palette(window-text); - selection-background-color: palette(highlight); - selection-color: palette(highlighted-text); -} - -* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { - border: 1px solid palette(base); -} - - -/* Misc */ - -QWidget::disabled { - color: rgb(165,164,165); /* disabledText */ -} - -QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { - background-color: palette(base); -} - -QToolTip { - background-color: palette(base); - color: rgb(205,205,205); - border: none; -} - -QMenuBar::item { - background-color: palette(window); -} - -QListView::item:selected:!active, -SourceTree::item:selected:!active { - color: rgb(255,255,255); - background-color: rgb(48,47,48); -} - -QListView QLineEdit, -SourceTree QLineEdit { - padding-top: 0px; - padding-bottom: 0px; - padding-right: 0; - padding-left: 2px; - border: none; - border-radius: none; -} - -/* macOS Separator Fix */ -QMainWindow::separator { - background: transparent; - width: 4px; - height: 4px; -} - -/* Dock Widget */ - -OBSDock > QWidget { - border: 1px solid palette(base); - border-top: none; -} - -#transitionsFrame { - padding: 4px; -} - -#transitionsFrame > QWidget { - margin-bottom: 2px; -} - -#controlsFrame { - padding: 4px 3px; -} - -#controlsFrame QPushButton { - margin: 1px 1px; -} - -QDockWidget { - titlebar-close-icon: url(theme:Dark/close.svg); - titlebar-normal-icon: url(theme:Dark/popout.svg); -} - -QDockWidget::title { - text-align: center; - background-color: rgb(70,69,70); -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 1px solid transparent; - background: transparent; - padding: 0px; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: transparent; -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; -} - -/* Group Box */ - -QGroupBox { - border: 1px solid palette(base); - border-radius: 5px; - padding-top: 24px; - font-weight: bold; -} - -QGroupBox::title { - subcontrol-origin: margin; - left: 4px; - top: 4px; -} - - -/* ScrollBars */ - -::corner { - background-color: palette(window); - border: none; -} - -QScrollBar:vertical { - background-color: palette(window); - width: 14px; - margin: 0px; -} - -QScrollBar::handle:vertical { - background-color: rgb(76,76,76); - min-height: 20px; - margin: 2px; - border-radius: 5px; - border-width: 1px; - border: 1px solid rgb(76,76,76); -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - border: none; - background: none; - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - border: none; - background: none; - color: none; -} - -QScrollBar:horizontal { - background-color: palette(window); - height: 14px; - margin: 0px; -} - -QScrollBar::handle:horizontal { - background-color: rgb(76,76,76); - min-width: 20px; - margin: 2px; - border-radius: 5px; - border-width: 1px; - border: 1px solid rgb(76,76,76); -} - -QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { - border: none; - background: none; - width: 0px; -} - -QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - border: none; - background: none; - color: none; -} - -/* Source Context */ -#contextContainer QPushButton { - padding: 4px 10px; -} - -#contextContainer QPushButton[themeID2=contextBarButton] { - padding: 4px 6px; -} - -#contextContainer QPushButton#sourcePropertiesButton { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -#contextContainer QPushButton#sourceFiltersButton { - qproperty-icon: url(theme:Dark/filter.svg); -} - -#contextContainer QPushButton#sourceInteractButton { - qproperty-icon: url(theme:Dark/interact.svg); -} - -/* Scenes and Sources toolbar */ - -QToolBar { - background-color: palette(window); - border: none; -} - -QPushButton[toolButton="true"], -QToolButton { - background: transparent; - border: none; - padding: 1px; - margin: 1px; -} - -QPushButton[toolButton="true"]:last-child, -QToolButton:last-child { - margin-right: 0px; -} - -QPushButton[toolButton="true"]:hover, -QToolButton:hover { - background-color: rgb(122,121,122); /* light */ - border-radius: none; -} - -QPushButton[toolButton="true"]:pressed, -QToolButton:pressed { - background-color: palette(base); - border-radius: none; -} - -* [themeID="addIconSmall"] { - qproperty-icon: url(theme:Dark/plus.svg); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(theme:Dark/minus.svg); -} - -* [themeID="clearIconSmall"] { - qproperty-icon: url(theme:Dark/entry-clear.svg); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(theme:Dark/refresh.svg); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(theme:Dark/up.svg); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(theme:Dark/down.svg); -} - -* [themeID="pauseIconSmall"] { - qproperty-icon: url(theme:Dark/media-pause.svg); -} - -* [themeID="menuIconSmall"] { - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - -* [themeID="cogsIcon"] { - qproperty-icon: url(theme:Dark/cogs.svg); -} - -* [themeID="filtersIcon"] { - qproperty-icon: url(theme:Dark/filter.svg); -} - -/* Tab Widget */ - -QTabWidget::pane { /* The tab widget frame */ - border-top: 1px solid palette(base); /* veryDark */ -} - -QTabWidget::tab-bar { - alignment: left; -} - -QTabBar::tab { - background-color: rgb(76,76,76); - border: none; - padding: 5px; - min-width: 50px; - margin: 1px; -} - -QTabBar::tab:top { - border-bottom: 1px transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - -} - -QTabBar::tab:bottom { - padding-top: 1px; - margin-bottom: 4px; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - height: 14px; -} - -QTabBar::tab:selected { - background-color: palette(base); -} - -QTabBar::tab:hover { - background-color: rgb(122,121,122); /* light */ -} - -QTabBar::tab:pressed { - background-color: palette(base); -} - - -/* ComboBox */ - -QDateTimeEdit, -QComboBox { - background-color: rgb(76,76,76); - border-style: solid; - border: 1px; - border-radius: 3px; - border-color: rgb(76,76,76); /* veryDark */ - padding: 2px; - padding-left: 10px; -} - -QDateTimeEdit:hover, -QComboBox:hover { - background-color: palette(button); -} - -QDateTimeEdit::drop-down, -QComboBox::drop-down { - border:none; - border-left: 1px solid rgba(31,30,31,155); /* veryDark */ - width: 20px; -} - -QDateTimeEdit::down-arrow, -QComboBox::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/updown.svg); - width: 100%; -} - -QDateTimeEdit:on, -QComboBox:on { - background-color: palette(base); -} - -QDateTimeEdit:editable, -QComboBox:editable { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; -} - -QDateTimeEdit::drop-down:editable, -QComboBox::drop-down:editable { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} - -QDateTimeEdit::down-arrow:editable, -QComboBox::down-arrow:editable { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 8%; -} - - -/* Textedits etc */ - -QLineEdit, QTextEdit, QPlainTextEdit { - background-color: palette(base); - border: none; - border-radius: 3px; - padding: 2px 2px 3px 7px; -} - -/* Spinbox and doubleSpinbox */ - -QSpinBox, QDoubleSpinBox { - background-color: palette(base); - border: none; - border-radius: 3px; - margin: 0px 3px 0px 0px; - padding: 2px 2px 3px 7px; -} - -QSpinBox::up-button, QDoubleSpinBox::up-button { - subcontrol-origin: margin; - subcontrol-position: top right; /* position at the top right corner */ - - background-color: rgb(76,76,76); - border: 1px solid palette(base); - border-radius: 3px; - border-width: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom-width: 0; -} - -QSpinBox::down-button, QDoubleSpinBox::down-button { - subcontrol-origin: margin; - subcontrol-position: bottom right; /* position at the top right corner */ - background-color: rgb(76,76,76); - border: 1px solid palette(base); - border-radius: 3px; - border-width: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - border-top-width: 0; -} - -QSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { - background-color: rgb(122,121,122); /* light */ -} - -QSpinBox::up-button:pressed, QSpinBox::down-button:pressed, QDoubleSpinBox::up-button:pressed, QDoubleSpinBox::down-button:pressed { - background-color: palette(window); -} - -QSpinBox::up-button:disabled, QSpinBox::up-button:off, QSpinBox::down-button:disabled, QSpinBox::down-button:off { - background-color: palette(window); -} - -QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBox::down-button:disabled, QDoubleSpinBox::down-button:off { - background-color: palette(window); -} - -QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(theme:Dark/up.svg); - width: 100%; -} - -QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(theme:Dark/down.svg); - width: 100%; -} - - -/* Buttons */ - -QPushButton { - color: palette(window-text); - background-color: rgb(76,76,76); - border: none; - border-radius: 3px; - padding: 4px; - padding-left: 20px; - padding-right: 20px; -} - -QPushButton::flat { - background-color: palette(window); -} - -QPushButton:checked { - background-color: palette(base); -} - -QPushButton:hover { - background-color: rgb(122,121,122); /* light */ -} - -QPushButton:pressed { - background-color: palette(base); -} - -QPushButton:disabled, QToolButton:disabled { - background-color: rgb(46,45,46); -} - -QPushButton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - subcontrol-origin: padding; - width: 25px; -} - -/* Sliders */ - -QSlider::groove:horizontal { - background-color: rgb(76,76,76); - height: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background-color: rgb(210,210,210); - border: 1px solid palette(window); - border-radius: 3px; - height: 10px; - width: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:horizontal:pressed { - background-color: rgb(210,210,210); -} - -QSlider::sub-page:horizontal { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { - background-color: rgb(50,49,50); /* dark */ - border-radius: 2px; -} - -QSlider::groove:vertical { - background-color: rgb(76,76,76); - width: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:vertical { - background-color: rgb(210,210,210); - border: 1px solid palette(window); - border-radius: 3px; - width: 10px; - height: 18px; - margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:vertical:pressed { - background-color: rgb(210,210,210); -} - -QSlider::add-page:vertical { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::add-page:vertical:disabled { - background-color: rgb(50,49,50); /* dark */ - border-radius: 2px; -} - -QSlider::handle:hover { - background-color: palette(bright-text); -} - -QSlider::handle:disabled { - background-color: rgb(122,121,122); /* light */ -} - -/* Volume Control */ - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(38,127,38); - qproperty-backgroundWarningColor: rgb(127,127,38); - qproperty-backgroundErrorColor: rgb(127,38,38); - qproperty-foregroundNominalColor: rgb(76,255,76); - qproperty-foregroundWarningColor: rgb(255,255,76); - qproperty-foregroundErrorColor: rgb(255,76,76); - qproperty-magnitudeColor: rgb(0,0,0); - qproperty-majorTickColor: palette(window-text); - qproperty-minorTickColor: rgb(122,121,122); /* light */ - qproperty-meterThickness: 3; - - /* The meter scale numbers normally use your QWidget font, with size */ - /* multiplied by meterFontScaling to get a proportionally smaller font. */ - /* To use a unique font for the numbers, specify font-family and/or */ - /* font-size here, and set meterFontScaling to 1.0. */ - qproperty-meterFontScaling: 0.7; -} - - -/* Status Bar */ - -QStatusBar::item { - border: none; -} - -/* Qt enforces a padding inside its status bar, so we - * oversize it and use margin to crunch it back down - */ -OBSBasicStatusBar { - margin-top: 4px; - border-top: 1px solid palette(dark); - background: palette(window); -} - -StatusBarWidget > QFrame { - margin-top: 2px; - border: 0px solid palette(dark); - border-left-width: 1px; - padding: 0px 12px 4px; -} - -/* Table View */ - -QTableView { - gridline-color: palette(light); -} - -QHeaderView::section { - background-color: palette(window); - color: palette(window-text); - border: 1px solid palette(base); - border-radius: 5px; -} - -/* Mute CheckBox */ - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:indeterminate { - image: url(theme:Dark/unassigned.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: red; -} - -/* Group Collapse Checkbox */ - -QCheckBox[sourceTreeSubItem=true] { - background: transparent; - outline: none; -} - -QCheckBox[sourceTreeSubItem=true]::indicator { - width: 10px; - height: 10px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator:checked { - image: url(theme:Dark/expand.svg); -} - -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked { - image: url(theme:Dark/collapse.svg); -} - - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192,128,0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192,128,0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0,192,0); - font-weight: bold; -} - -/* About dialog */ - -* [themeID="aboutName"] { - font-size: 36px; - font-weight: bold; -} - -* [themeID="aboutVersion"] { - font-size: 16px; - margin-bottom: 20px; -} - -* [themeID="aboutInfo"] { - margin-bottom: 20px; -} - -* [themeID="aboutHLayout"] { - background-color: palette(base); -} - -/* Preview background color */ - -OBSQTDisplay { - qproperty-displayBackgroundColor: rgb(76,76,76); -} - -/* Preview/Program labels */ - -* [themeID="previewProgramLabels"] { - font-size: 18px; - font-weight: bold; - color: rgb(122,121,122); -} - -/* Settings Icons */ - -OBSBasicSettings { - qproperty-generalIcon: url(theme:Dark/settings/general.svg); - qproperty-streamIcon: url(theme:Dark/settings/stream.svg); - qproperty-outputIcon: url(theme:Dark/settings/output.svg); - qproperty-audioIcon: url(theme:Dark/settings/audio.svg); - qproperty-videoIcon: url(theme:Dark/settings/video.svg); - qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg); - qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg); - qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg); -} - -OBSBasicSettings QListWidget::item { - padding-top: 5px; - padding-bottom: 5px; -} - -/* Locked CheckBox */ - -QCheckBox[lockCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[lockCheckBox=true]::indicator:checked { - image: url(theme:Dark/locked.svg); -} - -QCheckBox[lockCheckBox=true]::indicator:unchecked { - image: url(:res/images/unlocked.svg); -} - -/* Visibility CheckBox */ - -QCheckBox[visibilityCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[visibilityCheckBox=true]::indicator:checked { - image: url(theme:Dark/visible.svg); -} - -QCheckBox[visibilityCheckBox=true]::indicator:unchecked { - image: url(:res/images/invisible.svg); -} - -* [themeID="revertIcon"] { - qproperty-icon: url(theme:Dark/revert.svg); -} - -QPushButton#extraPanelDelete { - background-color: palette(base); -} - -QPushButton#extraPanelDelete:hover { - background-color: rgb(122,121,122); -} - -QPushButton#extraPanelDelete:pressed { - background-color: palette(base); -} - -OBSMissingFiles { - qproperty-warningIcon: url(theme:Dark/alert.svg); -} - -/* Source Icons */ - -OBSBasic { - qproperty-imageIcon: url(theme:Dark/sources/image.svg); - qproperty-colorIcon: url(theme:Dark/sources/brush.svg); - qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg); - qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg); - qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg); - qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg); - qproperty-windowCapIcon: url(theme:Dark/sources/window.svg); - qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg); - qproperty-cameraIcon: url(theme:Dark/sources/camera.svg); - qproperty-textIcon: url(theme:Dark/sources/text.svg); - qproperty-mediaIcon: url(theme:Dark/sources/media.svg); - qproperty-browserIcon: url(theme:Dark/sources/globe.svg); - qproperty-groupIcon: url(theme:Dark/sources/group.svg); - qproperty-sceneIcon: url(theme:Dark/sources/scene.svg); - qproperty-defaultIcon: url(theme:Dark/sources/default.svg); - qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg); -} - -/* Scene Tree */ - -SceneTree { - qproperty-gridItemWidth: 150; - qproperty-gridItemHeight: 27; -} - -*[gridMode="true"] SceneTree::item { - color: palette(window-text); - background-color: rgb(76,76,76); - border: none; - border-radius: 3px; - padding: 4px; - padding-left: 10px; - padding-right: 10px; - margin: 1px; -} - -*[gridMode="true"] SceneTree::item:selected { - background-color: rgb(122,121,122); /* light */ -} - -*[gridMode="true"] SceneTree::item:hover { - background-color: rgb(122,121,122); /* light */ -} - -*[gridMode="true"] SceneTree::item:pressed { - background-color: palette(base); -} - -*[gridMode="true"] SceneTree::item:checked { - background-color: rgb(122,121,122); /* light */ -} - -/* Save icon */ - -* [themeID="replayIconSmall"] { - qproperty-icon: url(theme:Dark/save.svg); -} - -/* Studio Mode T-Bar */ - -QSlider[themeID="tBarSlider"] { - height: 24px; -} - -QSlider::groove:horizontal[themeID="tBarSlider"] { - border: 1px solid rgb(76,76,76); - height: 5px; - background: palette(base); -} - -QSlider::sub-page:horizontal[themeID="tBarSlider"] { - background: palette(base);; - border: 1px solid rgb(76,76,76); -} - -QSlider::handle:horizontal[themeID="tBarSlider"] { - background-color: rgb(210,210,210); - width: 12px; - height: 24px; - margin: -24px 0px; -} - -/* Media icons */ - -* [themeID="playIcon"] { - qproperty-icon: url(theme:Dark/media/media_play.svg); -} - -* [themeID="pauseIcon"] { - qproperty-icon: url(theme:Dark/media/media_pause.svg); -} - -* [themeID="restartIcon"] { - qproperty-icon: url(theme:Dark/media/media_restart.svg); -} - -* [themeID="stopIcon"] { - qproperty-icon: url(theme:Dark/media/media_stop.svg); -} - -* [themeID="nextIcon"] { - qproperty-icon: url(theme:Dark/media/media_next.svg); -} - -* [themeID="previousIcon"] { - qproperty-icon: url(theme:Dark/media/media_previous.svg); -} - -/* YouTube Integration */ -OBSYoutubeActions { - qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg); -} - -#ytEventList QLabel { - color: rgb(254,253,254); /* veryLight */ - background-color: rgb(76,76,76); - border: none; - border-radius: 3px; - padding: 4px 20px; -} - -#ytEventList QLabel:hover { - background: rgb(122,121,122); -} - -#ytEventList QLabel[isSelectedEvent=true] { - background: rgb(31,30,31); - border: none; -} - -/* Calendar Widget */ -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 100%; -} - -QDateTimeEdit:on { - background-color: rgb(31,30,31); /* veryDark */ -} - -/* Calendar Top Bar */ -QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: rgb(58,57,58); - padding: 4px 8px; -} - -/* Calendar Top Bar Buttons */ -QCalendarWidget QToolButton { - background-color: rgb(76,76,76); - padding: 2px 16px; - border-radius: 3px; - margin: 2px; -} - -#qt_calendar_monthbutton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - padding-top: 2px; - padding-right: 6px; - height: 10px; - width: 10px; -} - -QCalendarWidget #qt_calendar_prevmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/left.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget #qt_calendar_nextmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/right.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget QToolButton:hover { - background-color: rgb(122,121,122); - border-radius: 3px; -} - -QCalendarWidget QToolButton:pressed { - background-color: rgb(31,30,31); /* veryDark */ -} - -/* Month Dropdown Menu */ -QCalendarWidget QMenu { - -} -/* Year spinbox */ -QCalendarWidget QSpinBox { - background-color: rgb(31,30,31); /* veryDark */ - border: none; - border-radius: 3px; - margin: 0px 3px 0px 0px; - padding: 4px 16px; -} - -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} -QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } -QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } - -/* Days of the Week Bar */ -QCalendarWidget QWidget { alternate-background-color: rgb(70,69,70); } - -QCalendarWidget QAbstractItemView:enabled { - background-color: rgb(31,30,31); - color: rgb(254,253,254); - selection-background-color: rgb(25,51,75); - selection-color: rgb(254,253,254); -} - -QCalendarWidget QAbstractItemView:disabled { - color: rgb(165,164,165); /* disabledText */ -} - -/* Disable icons on QDialogButtonBox */ -QDialogButtonBox { - dialogbuttonbox-buttons-have-icons: 0; -} diff --git a/UI/data/themes/Grey.qss b/UI/data/themes/Grey.qss deleted file mode 100644 index b324ef3c97801c..00000000000000 --- a/UI/data/themes/Grey.qss +++ /dev/null @@ -1,1538 +0,0 @@ -/******************************************************************************/ -/* Copyright (C) 2014-2015 by Philippe Groarke */ -/* */ -/* */ -/* This program is free software: you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation, either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program. If not, see . */ -/******************************************************************************/ - -/* Colors */ - -OBSThemeMeta { - dark: 'true'; - author: 'Warchamp7'; -} - -/* Custom theme information. This will set the application's QPalette, as - * well as pass to QML via the OBSTheme object. - * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. - * Using it without will set all three (making 'active' a bit redundant) */ -OBSTheme { - window: rgb(33,33,33); - windowText: rgb(255,254,255); - - base: rgb(47,47,47); - alternateBase: rgb(11,10,11); - - text: rgb(255,254,255); - - button: rgb(67,67,67); - buttonText: rgb(255,254,255); - - brightText: rgb(255,254,255); - - light: rgb(88,87,88); - mid: rgb(33,33,33); - dark: rgb(47,47,47); - shadow: rgb(11,10,11); - - primary: rgb(40,76,184); - primaryLight: rgb(54,92,192); - primaryDark: rgb(28,28,28); - - highlight: rgb(42,130,218); - highlightText: rgb(255,254,255); - - link: rgb(77,166,255); - linkVisited: rgb(77,166,255); -} - -OBSTheme::disabled { - windowText: rgb(153,153,153); - text: rgb(153,153,153); - button: rgb(27,29,34); - - buttonText: rgb(43,46,56); - brightText: rgb(43,46,56); -} - -OBSTheme::inactive { - text: rgb(255,254,255); - - highlight: rgb(25,28,34); - highlightText: rgb(255,255,255); -} - -/* Default widget style, we override only what is needed. */ - -QWidget { - alternate-background-color: palette(base); - color: palette(text); - selection-background-color: rgb(40,76,184); - selection-color: palette(text); - font-size: 10pt; - font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif; -} - -QWidget:disabled { - color: rgb(153,153,153); -} - -/* Container windows */ - -QDialog, -QMainWindow, -QStatusBar, -QMenuBar, -QMenu { - background-color: palette(window); -} - -/* macOS Separator Fix */ - -QMainWindow::separator { - background: transparent; - width: 4px; - height: 4px; -} - -/* General Widgets */ - -QLabel, -QGroupBox, -QCheckBox { - background: transparent; -} - -QComboBox, -QCheckBox, -QPushButton, -QSpinBox, -QDoubleSpinBox { - margin-top: 3px; - margin-bottom: 3px; -} - -QListWidget QWidget, -SceneTree QWidget, -SourceTree QWidget { - margin-top: 0; - margin-bottom: 0; -} - -* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { - border: 1px solid palette(dark); -} - - -/* Misc */ - -QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { - background-color: palette(base); -} - -QToolTip { - background-color: palette(base); - color: palette(text); - border: none; -} - -/* Context Menu */ - -QMenu::icon { - left: 4px; -} - -QMenu::separator { - background: palette(button); - height: 1px; - margin: 3px 6px; -} - -QMenu::item:disabled { - color: rgb(153,153,153); - background: transparent; -} - -QMenu::right-arrow { - image: url(theme:Dark/expand.svg); -} - -/* Top Menu Bar Items */ -QMenuBar::item { - background-color: transparent; -} - -QMenuBar::item:selected { - background: rgb(40,76,184); -} - -/* Item Lists */ -QListWidget { - border-radius: 4px; -} - -QListWidget::item { - color: palette(text); -} - -QListWidget, -QMenu, -SceneTree, -SourceTree { - padding: 3px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item { - padding: 6px; -} - -QMenu::item { - padding-right: 20px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item, -SourceTree::item { - border-radius: 4px; - color: palette(text); - border: 0px solid transparent; -} - -QMenu::item:selected, -QListWidget::item:selected, -SceneTree::item:selected, -SourceTree::item:selected { - background-color: rgb(40,76,184); -} - -QMenu::item:hover, -QListWidget::item:hover, -SceneTree::item:hover, -SourceTree::item:hover, -QMenu::item:selected:hover, -QListWidget::item:selected:hover, -SceneTree::item:selected:hover, -SourceTree::item:selected:hover { - background-color: rgb(54,92,192); - color: palette(text); -} - -QListWidget::item:disabled, -QListWidget::item:disabled:hover { - background: transparent; - color: rgb(153,153,153); -} - -QListWidget QLineEdit, -SceneTree QLineEdit, -SourceTree QLineEdit { - padding: 0px; - padding-bottom: 2px; - margin: 0px; - border: 1px solid #FFF; - border-radius: 4px; -} - -QListWidget QLineEdit:focus, -SceneTree QLineEdit:focus, -SourceTree QLineEdit:focus { - border: 1px solid #FFF; -} - -/* Settings QList */ - -OBSBasicSettings QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicSettings QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -/* Settings properties view */ -OBSBasicSettings #PropertiesContainer { - background-color: palette(dark); -} - -#PropertiesContainer QListWidget { - background-color: palette(button); -} - -/* Dock Widget */ -OBSDock > QWidget { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -OBSDock QLabel { - background: transparent; -} - -#transitionsFrame { - padding: 4px 8px; -} - -QDockWidget { - font-size: 10.5pt; - font-weight: bold; - - titlebar-close-icon: url(theme:Dark/close.svg); - titlebar-normal-icon: url(theme:Dark/popout.svg); -} - -QDockWidget::title { - text-align: left; - background-color: palette(button); - padding: 6px 8px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 0px solid transparent; - border-radius: 4px; - background: transparent; - margin-right: 1px; - opacity: .5; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgb(89,89,89); - opacity: 1; -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; -} - -QScrollArea { - border-radius: 4px; -} - -/* Qt enforces a padding inside its status bar, so we - * oversize it and use margin to crunch it back down - */ -OBSBasicStatusBar { - margin-top: 4px; - border-top: 1px solid #3c404b; - background: palette(dark); -} - -StatusBarWidget > QFrame { - margin-top: 1px; - border: 0px solid #3c404b; - border-left-width: 1px; - padding: 0px 12px 2px; -} - -/* Group Box */ - -QGroupBox { - background: palette(dark); - border-radius: 4px; - padding-top: 32px; - padding-bottom: 8px; - font-weight: bold; - margin-bottom: 6px; -} - -QGroupBox::title { - subcontrol-origin: margin; - left: 8px; - top: 8px; -} - - -/* ScrollBars */ - -::corner { - background-color: palette(window); - border: none; -} - -QScrollBar:vertical { - background-color: transparent; - width: 14px; - margin: 0px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - border: none; - background: none; - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - border: none; - background: none; - color: none; -} - -QScrollBar:horizontal { - background-color: transparent; - height: 14px; - margin: 0px; -} - -QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { - border: none; - background: none; - width: 0px; -} - -QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - border: none; - background: none; - color: none; -} - -QScrollBar::handle { - background-color: palette(button); - margin: 2px; - border-radius: 2px; - border: 1px solid palette(button); -} - -QScrollBar::handle:hover { - background-color: rgb(89,89,89); - border-color: rgb(89,89,89); -} - -QScrollBar::handle:pressed { - background-color: rgb(40,76,184); - border-color: rgb(40,76,184); -} - -QScrollBar::handle:vertical { - min-height: 20px; -} - -QScrollBar::handle:horizontal { - min-width: 20px; -} - -/* Source Context Bar */ - -#contextContainer { - background-color: palette(dark); - margin-top: 4px; - border-radius: 4px; -} - -#contextContainer QPushButton { - padding-left: 12px; - padding-right: 12px; -} - -QPushButton#sourcePropertiesButton { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -QPushButton#sourceFiltersButton { - qproperty-icon: url(theme:Dark/filter.svg); -} - -/* Scenes and Sources toolbar */ - -QToolBar { - background-color: palette(dark); - border: none; - padding: 0px; - margin: 4px 0px; -} - -QPushButton[toolButton="true"], -QToolButton { - background-color: rgb(67,67,67); - padding: 4px 6px; - margin: 0px 2px; - border-radius: 4px; -} - -QPushButton[toolButton="true"]:last-child, -QToolButton:last-child { - margin-right: 0px; -} - -QToolButton:hover { - background-color: rgb(89,89,89); -} - -QToolButton:pressed { - background-color: rgb(28,28,28); -} - -* [themeID="addIconSmall"] { - qproperty-icon: url(theme:Dark/plus.svg); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(theme:Dark/trash.svg); -} - -* [themeID="clearIconSmall"] { - qproperty-icon: url(theme:Dark/entry-clear.svg); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="menuIconSmall"] { - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(theme:Dark/refresh.svg); -} - -* [themeID="cogsIcon"] { - qproperty-icon: url(theme:Dark/cogs.svg); -} - -#sourceInteractButton { - qproperty-icon: url(theme:Dark/interact.svg); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(theme:Dark/up.svg); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(theme:Dark/down.svg); -} - -* [themeID="pauseIconSmall"] { - qproperty-icon: url(theme:Dark/media-pause.svg); -} - -* [themeID="filtersIcon"] { - qproperty-icon: url(theme:Dark/filter.svg); -} - -QToolBarExtension { - background: palette(button); - min-width: 12px; - max-width: 12px; - padding: 4px 0px; - margin-left: 0px; - - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - - -/* Tab Widget */ - -QTabWidget::pane { /* The tab widget frame */ - border-top: 4px solid palette(base); -} - -QTabWidget::tab-bar { - alignment: left; -} - -QTabBar QToolButton { - background: rgb(44,46,53); - border: none; -} - -QTabBar::tab:top { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QTabBar::tab:bottom { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -QTabBar::tab { - background: palette(dark); - color: palette(text); - border: none; - padding: 8px 12px; - min-width: 50px; - margin: 1px 2px; -} - -QTabBar::tab:pressed { - background: rgb(28,28,28); -} - -QTabBar::tab:hover { - background: rgb(89,89,89); - color: palette(text); -} - -QTabBar::tab:selected { - background: rgb(67,67,67); - color: palette(text); -} - -QTabBar::tab:top:selected { - border-bottom: 2px solid rgb(250,250,250); -} - -QTabBar::tab:bottom:selected { - border-top: 2px solid rgb(250,250,250); -} - -QTabBar QToolButton { - background: palette(button); - min-width: 16px; - padding: 0px; -} - -/* ComboBox */ - -QComboBox, -QDateTimeEdit { - background-color: rgb(67,67,67); - border-style: solid; - border: 1px; - border-radius: 4px; - border-color: rgb(67,67,67); - padding: 4px; - padding-left: 10px; -} - -QComboBox:hover, -QComboBox:selected, -QDateTimeEdit:hover, -QDateTimeEdit:selected { - background-color: rgb(89,89,89); -} - -QComboBox::drop-down, -QDateTimeEdit::drop-down { - border:none; - border-left: 1px solid rgb(25,28,34); - width: 20px; -} - -QComboBox::down-arrow, -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/updown.svg); - width: 100%; -} - -QComboBox:on, -QDateTimeEdit:on { - background-color: rgb(28,28,28); -} - -QComboBox:editable:hover { - -} - -QComboBox::drop-down:editable, -QDateTimeEdit::drop-down:editable { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} - -QComboBox::down-arrow:editable, -QDateTimeEdit::down-arrow:editable { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 8%; -} - -/* Textedits etc */ - -QLineEdit, QTextEdit, QPlainTextEdit { - background-color: palette(button); - border: none; - border-radius: 4px; - padding: 5px 2px 5px 7px; - border: 2px solid transparent; -} - -QLineEdit:hover, -QTextEdit:hover, -QPlainTextEdit:hover { - border: 2px solid rgb(99,102,111); -} - -QLineEdit:focus, -QTextEdit:focus, -QPlainTextEdit:focus { - background-color: palette(mid); - border: 2px solid rgb(40,76,184); -} - -/* Spinbox and doubleSpinbox */ - -QSpinBox, -QDoubleSpinBox { - background-color: palette(button); - border: 2px solid palette(button); - border-radius: 4px; - margin-right: 3px; - padding: 3px 0px 4px 5px; -} - -QSpinBox:hover, -QDoubleSpinBox:hover { - border: 2px solid rgb(99,102,111); -} - -QSpinBox:focus, -QDoubleSpinBox:focus { - background-color: palette(mid); - border: 2px solid rgb(40,76,184); -} - -QSpinBox::up-button, QDoubleSpinBox::up-button { - subcontrol-origin: padding; - subcontrol-position: top right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom-width: 0; -} - -QSpinBox::down-button, QDoubleSpinBox::down-button { - subcontrol-origin: padding; - subcontrol-position: bottom right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - border-top-width: 0; -} - -QSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { - background-color: rgb(89,89,89); -} - -QSpinBox::up-button:pressed, QSpinBox::down-button:pressed, QDoubleSpinBox::up-button:pressed, QDoubleSpinBox::down-button:pressed { - background-color: rgb(28,28,28); -} - -QSpinBox::up-button:disabled, QSpinBox::up-button:off, QSpinBox::down-button:disabled, QSpinBox::down-button:off { - background-color: rgb(28,28,28); -} - -QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBox::down-button:disabled, QDoubleSpinBox::down-button:off { - background-color: rgb(28,28,28); -} - -QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(theme:Dark/up.svg); - width: 100%; - margin: 2px; -} - -QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(theme:Dark/down.svg); - width: 100%; - padding: 2px; -} - - -/* Controls Dock */ -#controlsFrame { - padding: 4px 3px; -} - -#controlsFrame QPushButton { - margin: 2px 1px; -} - -#streamButton, -#recordButton, -QPushButton[themeID="replayBufferButton"], -#broadcastButton { - padding: 10px; -} - -/* Primary Control Button Checked Coloring */ -#streamButton:!hover:!pressed:checked, -#recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, -#modeSwitch:!hover:!pressed:checked, -#broadcastButton:!hover:!pressed:checked { - background: rgb(40,76,184); -} - -/* Primary Control Button Hover Coloring */ -#streamButton:hover:!pressed:checked, -#recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, -#modeSwitch:hover:!pressed:checked, -#broadcastButton:hover:!pressed:checked { - background: rgb(54,92,192); - color: palette(text); -} - - -/* Buttons */ - -QPushButton { - color: palette(text); - background-color: palette(button); - min-height: 18px; - border: none; - border-radius: 4px; - padding: 6px 16px; -} - -QPushButton::flat { - background-color: rgb(67,67,67); -} - -QPushButton:checked { - background-color: rgb(40,76,184); -} - -QPushButton:hover { - background-color: rgb(89,89,89); -} - -QPushButton:pressed { - background-color: rgb(28,28,28); -} - -QPushButton:disabled, QToolButton:disabled { - background-color: rgb(28,28,28); -} - -QPushButton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - subcontrol-origin: padding; - width: 25px; -} - -/* Sliders */ - -QSlider::groove:horizontal { - background-color: rgb(67,67,67); - height: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - height: 10px; - width: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:horizontal:pressed { - background-color: palette(text); -} - -QSlider::sub-page:horizontal { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::groove:vertical { - background-color: rgb(67,67,67); - width: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:vertical { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - width: 10px; - height: 18px; - margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:vertical:pressed { - background-color: palette(text); -} - -QSlider::add-page:vertical { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::add-page:vertical:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::handle:hover { - background-color: rgb(200,199,200); -} - -QSlider::handle:disabled { - background-color: rgb(68,75,110); -} - -/* Volume Control */ - -#stackedMixerArea QPushButton { - min-width: 16px; - padding: 4px 8px; -} - -/* This is an incredibly cursed but necessary fix */ -#stackedMixerArea QPushButton:!hover { - background-color: palette(button); -} - -#stackedMixerArea QPushButton:hover { - background-color: rgb(89,89,89); -} - -#stackedMixerArea QPushButton:pressed { - background-color: rgb(28,28,28); -} - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(66,112,24); - qproperty-backgroundWarningColor: rgb(112,91,28); - qproperty-backgroundErrorColor: rgb(112,39,53); - qproperty-foregroundNominalColor: rgb(115,189,49); - qproperty-foregroundWarningColor: rgb(189,144,9); - qproperty-foregroundErrorColor: rgb(189,47,73); - qproperty-magnitudeColor: rgb(0,0,0); - qproperty-majorTickColor: palette(text); - qproperty-minorTickColor: palette(light); -} - -/* Status Bar */ - -QStatusBar::item { - border: none; -} - -/* Table View */ - -QTableView { - background: palette(base); - gridline-color: palette(light); -} - -QTableView::item { - margin: 0px; - padding: 0px; -} - -QTableView QLineEdit { - background: palette(mid); - padding: 0; - margin: 0; -} - -QTableView QPushButton, -QTableView QToolButton { - margin: 1px 1px 2px; -} - -QHeaderView::section { - background-color: palette(button); - color: palette(text); - border: none; - border-left: 1px solid palette(window); - border-right: 1px solid palette(window); - padding: 2px 4px; - margin-bottom: 2px; -} - -/* Mute CheckBox */ - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:indeterminate { - image: url(theme:Dark/unassigned.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192,128,0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192,128,0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0,192,0); - font-weight: bold; -} - -/* About dialog */ - -* [themeID="aboutName"] { - font-size: 26pt; - font-weight: bold; -} - -* [themeID="aboutVersion"] { - font-size: 12pt; - margin-bottom: 20px; -} - -* [themeID="aboutInfo"] { - margin-bottom: 20px; -} - -* [themeID="aboutHLayout"] { - background-color: palette(base); -} - -/* Canvas / Preview background color */ - -OBSQTDisplay { - qproperty-displayBackgroundColor: rgb(28,28,28); - border-radius: 10px; -} - -/* Filters Window */ - -OBSBasicFilters QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicFilters QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -OBSBasicFilters #widget, -OBSBasicFilters #widget_2 { - margin: 0px; - padding: 0px; - padding-bottom: 4px; -} - -OBSBasicFilters #widget QPushButton, -OBSBasicFilters #widget_2 QPushButton { - min-width: 16px; - padding: 4px 8px; - margin-top: 0px; -} - -/* Preview/Program labels */ - -* [themeID="previewProgramLabels"] { - font-size: 14pt; - font-weight: bold; - color: rgb(210,210,210); - margin-bottom: 4px; -} - -/* Settings Icons */ - -OBSBasicSettings { - qproperty-generalIcon: url(theme:Dark/settings/general.svg); - qproperty-streamIcon: url(theme:Dark/settings/stream.svg); - qproperty-outputIcon: url(theme:Dark/settings/output.svg); - qproperty-audioIcon: url(theme:Dark/settings/audio.svg); - qproperty-videoIcon: url(theme:Dark/settings/video.svg); - qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg); - qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg); - qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg); -} - -/* Checkboxes */ -QCheckBox { - -} - -QCheckBox::indicator, -QGroupBox::indicator { - width: 18px; - height: 18px; -} - -QGroupBox::indicator { - margin-left: 2px; -} - -QCheckBox::indicator:unchecked, -QGroupBox::indicator:unchecked { - image: url(theme:Yami/checkbox_unchecked.svg); -} - -QCheckBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:hover { - border: none; - image: url(theme:Yami/checkbox_unchecked_focus.svg); -} - -QCheckBox::indicator:checked, -QGroupBox::indicator:checked { - image: url(theme:Yami/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QGroupBox::indicator:checked:hover { - border: none; - image: url(theme:Yami/checkbox_checked_focus.svg); -} - -QCheckBox::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled { - image: url(theme:Yami/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled { - image: url(theme:Yami/checkbox_unchecked_disabled.svg); -} - -/* Locked CheckBox */ - -QCheckBox[lockCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[lockCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[lockCheckBox=true]::indicator:checked, -QCheckBox[lockCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/locked.svg); -} - -QCheckBox[lockCheckBox=true]::indicator:unchecked, -QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/unlocked.svg); -} - -/* Visibility CheckBox */ - -QCheckBox[visibilityCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[visibilityCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[visibilityCheckBox=true]::indicator:checked, -QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/visible.svg); -} - -QCheckBox[visibilityCheckBox=true]::indicator:unchecked, -QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/invisible.svg); -} - -* [themeID="revertIcon"] { - qproperty-icon: url(theme:Dark/revert.svg); -} - -QPushButton#extraPanelDelete { - background-color: palette(mid); - margin: 0; - padding: 0; -} - -QPushButton#extraPanelDelete:hover { - background-color: rgb(68,75,110); -} - -QPushButton#extraPanelDelete:pressed { - background-color: palette(dark); -} - -/* Mute CheckBox */ - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator { - width: 16px; - height: 16px; -} - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:hover { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:focus { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:checked:hover { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:focus { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:disabled { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked:disabled { - image: url(theme:Dark/settings/audio.svg); -} - -#hotkeyFilterReset { - margin-top: 0px; -} - -OBSHotkeyWidget { - padding: 8px 0px; - margin: 2px 0px; -} - -OBSHotkeyLabel { - padding: 4px 0px; -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -OBSHotkeyWidget QPushButton { - min-width: 16px; - padding: 4px 4px; - margin-top: 0px; - margin-left: 4px; -} - - -/* Sources List Group Collapse Checkbox */ - -QCheckBox[sourceTreeSubItem=true] { - background: transparent; - outline: none; - padding: 0px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator { - width: 12px; - height: 12px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator:checked, -QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { - image: url(theme:Dark/expand.svg); -} - -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { - image: url(theme:Dark/collapse.svg); -} - -/* Source Icons */ - -OBSBasic { - qproperty-imageIcon: url(theme:Dark/sources/image.svg); - qproperty-colorIcon: url(theme:Dark/sources/brush.svg); - qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg); - qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg); - qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg); - qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg); - qproperty-windowCapIcon: url(theme:Dark/sources/window.svg); - qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg); - qproperty-cameraIcon: url(theme:Dark/sources/camera.svg); - qproperty-textIcon: url(theme:Dark/sources/text.svg); - qproperty-mediaIcon: url(theme:Dark/sources/media.svg); - qproperty-browserIcon: url(theme:Dark/sources/globe.svg); - qproperty-groupIcon: url(theme:Dark/sources/group.svg); - qproperty-sceneIcon: url(theme:Dark/sources/scene.svg); - qproperty-defaultIcon: url(theme:Dark/sources/default.svg); - qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg); -} - -/* Scene Tree Grid Mode */ - -SceneTree { - qproperty-gridItemWidth: 154; - qproperty-gridItemHeight: 31; -} - -*[gridMode="true"] SceneTree::item { - color: palette(text); - background-color: palette(button); - border-radius: 4px; - margin: 2px; -} - -*[gridMode="true"] SceneTree::item:selected { - background-color: rgb(40,76,184); -} - -*[gridMode="true"] SceneTree::item:checked { - background-color: rgb(40,76,184); -} - -*[gridMode="true"] SceneTree::item:hover { - background-color: rgb(89,89,89); -} - -*[gridMode="true"] SceneTree::item:selected:hover { - background-color: rgb(54,92,192); -} - -/* Save icon */ - -* [themeID="replayIconSmall"] { - qproperty-icon: url(theme:Dark/save.svg); -} - -/* Studio Mode T-Bar */ - -QSlider[themeID="tBarSlider"] { - height: 24px; -} - -QSlider::groove:horizontal[themeID="tBarSlider"] { - border: 1px solid #4c4c4c; - height: 5px; - background: palette(dark); -} - -QSlider::sub-page:horizontal[themeID="tBarSlider"] { - background: palette(dark); - border: 1px solid #4c4c4c; -} - -QSlider::handle:horizontal[themeID="tBarSlider"] { - background-color: #d2d2d2; - width: 12px; - height: 24px; - margin: -24px 0px; -} - -/* Media icons */ - -* [themeID="playIcon"] { - qproperty-icon: url(theme:Dark/media/media_play.svg); -} - -* [themeID="pauseIcon"] { - qproperty-icon: url(theme:Dark/media/media_pause.svg); -} - -* [themeID="restartIcon"] { - qproperty-icon: url(theme:Dark/media/media_restart.svg); -} - -* [themeID="stopIcon"] { - qproperty-icon: url(theme:Dark/media/media_stop.svg); -} - -* [themeID="nextIcon"] { - qproperty-icon: url(theme:Dark/media/media_next.svg); -} - -* [themeID="previousIcon"] { - qproperty-icon: url(theme:Dark/media/media_previous.svg); -} - -/* YouTube Integration */ -OBSYoutubeActions { - qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg); -} - -#ytEventList QLabel { - color: palette(text); - background-color: palette(button); - border: none; - border-radius: 4px; - padding: 4px 20px; -} - -#ytEventList QLabel:hover { - background-color: rgb(89,89,89); -} - -#ytEventList QLabel[isSelectedEvent=true] { - background-color: rgb(40,76,184); - border: none; -} - -#ytEventList QLabel[isSelectedEvent=true]:hover { - background-color: rgb(54,92,192); - color: palette(text); -} - -/* Calendar Widget */ -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 100%; -} - -QDateTimeEdit:on { - background-color: palette(mid); -} - -/* Calendar Top Bar */ -QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: palette(base); - padding: 4px 8px; -} - -/* Calendar Top Bar Buttons */ -QCalendarWidget QToolButton { - background-color: palette(button); - padding: 2px 16px; - border-radius: 4px; - margin: 2px; -} - -#qt_calendar_monthbutton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - padding-top: 2px; - padding-right: 6px; - height: 10px; - width: 10px; -} - -QCalendarWidget #qt_calendar_prevmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/left.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget #qt_calendar_nextmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/right.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget QToolButton:hover { - background-color: rgb(89,89,89); - border-radius: 4px; -} - -QCalendarWidget QToolButton:pressed { - background-color: rgb(28,28,28); -} - -/* Month Dropdown Menu */ -QCalendarWidget QMenu { - -} -/* Year spinbox */ -QCalendarWidget QSpinBox { - background-color: rgb(28,28,28); - border: none; - border-radius: 4px; - margin: 0px 3px 0px 0px; - padding: 4px 16px; -} - -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} -QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } -QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } - -/* Days of the Week Bar */ -QCalendarWidget QWidget { alternate-background-color: palette(mid); } - -QCalendarWidget QAbstractItemView:enabled { - background-color: palette(base); - color: palette(text); -} - -QCalendarWidget QAbstractItemView:disabled { - color: rgb(122,121,122); -} - -/* VirtualCam Plugin Fixes */ - -#VirtualProperties QWidget { - margin-top: 0; - margin-bottom: 0; -} - -/* Disable icons on QDialogButtonBox */ -QDialogButtonBox { - dialogbuttonbox-buttons-have-icons: 0; -} - -/* Stats dialog */ -OBSBasicStats { - background: palette(dark); -} - -/* Advanced audio dialog */ -OBSBasicAdvAudio #scrollAreaWidgetContents { - background: palette(dark); -} diff --git a/UI/data/themes/Light.qss b/UI/data/themes/Light.qss deleted file mode 100644 index bf4ae85feb7045..00000000000000 --- a/UI/data/themes/Light.qss +++ /dev/null @@ -1,1544 +0,0 @@ -/******************************************************************************/ -/* Copyright (C) 2014-2015 by Philippe Groarke */ -/* */ -/* */ -/* This program is free software: you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation, either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program. If not, see . */ -/******************************************************************************/ - -/* Colors */ - -OBSThemeMeta { - dark: 'false'; - author: 'Warchamp7'; -} - -/* Custom theme information. This will set the application's QPalette, as - * well as pass to QML via the OBSTheme object. - * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. - * Using it without will set all three (making 'active' a bit redundant) */ -OBSTheme { - window: rgb(211,211,211); - windowText: rgb(255,254,255); - - base: rgb(229,229,229); - alternateBase: rgb(11,10,11); - - text: rgb(2,2,2); - - button: rgb(243,243,243); - buttonText: rgb(2,2,2); - - brightText: rgb(255,254,255); - - light: rgb(167,167,167); - mid: rgb(211,211,211); - dark: rgb(229,229,229); - shadow: rgb(11,10,11); - - primary: rgb(140,181,255); - primaryLight: rgb(178,207,255); - primaryDark: rgb(193,193,193); - - highlight: rgb(42,130,218); - highlightText: rgb(255,254,255); - - link: rgb(77,166,255); - linkVisited: rgb(77,166,255); -} - -OBSTheme::disabled { - windowText: rgb(74,74,74); - text: rgb(74,74,74); - button: rgb(27,29,34); - - buttonText: rgb(43,46,56); - brightText: rgb(43,46,56); -} - -OBSTheme::inactive { - text: rgb(255,254,255); - - highlight: rgb(25,28,34); - highlightText: rgb(255,255,255); -} - -/* Default widget style, we override only what is needed. */ - -QWidget { - alternate-background-color: palette(base); - color: palette(text); - selection-background-color: rgb(140,181,255); - selection-color: palette(text); - font-size: 10pt; - font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif; -} - -QWidget:disabled { - color: rgb(74,74,74); -} - -/* Container windows */ - -QDialog, -QMainWindow, -QStatusBar, -QMenuBar, -QMenu { - background-color: palette(window); -} - -/* macOS Separator Fix */ - -QMainWindow::separator { - background: transparent; - width: 4px; - height: 4px; -} - -/* General Widgets */ - -QLabel, -QGroupBox, -QCheckBox { - background: transparent; -} - -QComboBox, -QCheckBox, -QPushButton, -QSpinBox, -QDoubleSpinBox { - margin-top: 3px; - margin-bottom: 3px; -} - -QListWidget QWidget, -SceneTree QWidget, -SourceTree QWidget { - margin-top: 0; - margin-bottom: 0; -} - -* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { - border: 1px solid palette(dark); -} - - -/* Misc */ - -QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { - background-color: palette(base); -} - -QToolTip { - background-color: palette(base); - color: palette(text); - border: none; -} - -/* Context Menu */ - -QMenu::icon { - left: 4px; -} - -QMenu::separator { - background: rgb(192,192,192); - height: 1px; - margin: 3px 6px; -} - -QMenu::item:disabled { - color: rgb(74,74,74); - background: transparent; -} - -QMenu::right-arrow { - image: url(theme:Light/expand.svg); -} - -/* Top Menu Bar Items */ -QMenuBar::item { - background-color: transparent; -} - -QMenuBar::item:selected { - background: rgb(140,181,255); -} - -/* Item Lists */ -QListWidget { - border-radius: 4px; -} - -QListWidget::item { - color: palette(text); -} - -QListWidget, -QMenu, -SceneTree, -SourceTree { - padding: 3px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item { - padding: 6px; -} - -QMenu::item { - padding-right: 20px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item, -SourceTree::item { - border-radius: 4px; - color: palette(text); - border: 0px solid transparent; -} - -QMenu::item:selected, -QListWidget::item:selected, -SceneTree::item:selected, -SourceTree::item:selected { - background-color: rgb(140,181,255); -} - -QMenu::item:hover, -QListWidget::item:hover, -SceneTree::item:hover, -SourceTree::item:hover, -QMenu::item:selected:hover, -QListWidget::item:selected:hover, -SceneTree::item:selected:hover, -SourceTree::item:selected:hover { - background-color: rgb(178,207,255); - color: palette(text); -} - -QListWidget::item:disabled, -QListWidget::item:disabled:hover { - background: transparent; - color: rgb(74,74,74); -} - -QListWidget QLineEdit, -SceneTree QLineEdit, -SourceTree QLineEdit { - padding: 0px; - padding-bottom: 2px; - margin: 0px; - border: 1px solid #FFF; - border-radius: 4px; -} - -QListWidget QLineEdit:focus, -SceneTree QLineEdit:focus, -SourceTree QLineEdit:focus { - border: 1px solid #FFF; -} - -/* Settings QList */ - -OBSBasicSettings QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicSettings QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -/* Settings properties view */ -OBSBasicSettings #PropertiesContainer { - background-color: palette(dark); -} - -#PropertiesContainer QListWidget { - background-color: palette(button); -} - -/* Dock Widget */ -OBSDock > QWidget { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -OBSDock QLabel { - background: transparent; -} - -#transitionsFrame { - padding: 4px 8px; -} - -QDockWidget { - font-size: 10.5pt; - font-weight: bold; - - titlebar-close-icon: url(theme:Light/close.svg); - titlebar-normal-icon: url(theme:Light/popout.svg); -} - -QDockWidget::title { - text-align: left; - background-color: palette(button); - padding: 6px 8px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 0px solid transparent; - border-radius: 4px; - background: transparent; - margin-right: 1px; - opacity: .5; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgb(254,254,255); - opacity: 1; -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; -} - -QScrollArea { - border-radius: 4px; -} - -/* Qt enforces a padding inside its status bar, so we - * oversize it and use margin to crunch it back down - */ -OBSBasicStatusBar { - margin-top: 4px; - border-top: 1px solid rgb(192,192,192); - background: palette(dark); -} - -StatusBarWidget > QFrame { - margin-top: 1px; - border: 0px solid rgb(192,192,192); - border-left-width: 1px; - padding: 0px 12px 2px; -} - -/* Group Box */ - -QGroupBox { - background: palette(dark); - border-radius: 4px; - padding-top: 32px; - padding-bottom: 8px; - font-weight: bold; - margin-bottom: 6px; -} - -QGroupBox::title { - subcontrol-origin: margin; - left: 8px; - top: 8px; -} - - -/* ScrollBars */ - -::corner { - background-color: palette(window); - border: none; -} - -QScrollBar:vertical { - background-color: transparent; - width: 14px; - margin: 0px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - border: none; - background: none; - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - border: none; - background: none; - color: none; -} - -QScrollBar:horizontal { - background-color: transparent; - height: 14px; - margin: 0px; -} - -QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { - border: none; - background: none; - width: 0px; -} - -QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - border: none; - background: none; - color: none; -} - -QScrollBar::handle { - background-color: palette(button); - margin: 2px; - border-radius: 2px; - border: 1px solid palette(button); -} - -QScrollBar::handle:hover { - background-color: rgb(254,254,255); - border-color: rgb(254,254,255); -} - -QScrollBar::handle:pressed { - background-color: rgb(140,181,255); - border-color: rgb(140,181,255); -} - -QScrollBar::handle:vertical { - min-height: 20px; -} - -QScrollBar::handle:horizontal { - min-width: 20px; -} - -/* Source Context Bar */ - -#contextContainer { - background-color: palette(dark); - margin-top: 4px; - border-radius: 4px; -} - -#contextContainer QPushButton { - padding-left: 12px; - padding-right: 12px; -} - -QPushButton#sourcePropertiesButton { - qproperty-icon: url(theme:Light/settings/general.svg); -} - -QPushButton#sourceFiltersButton { - qproperty-icon: url(theme:Light/filter.svg); -} - -/* Scenes and Sources toolbar */ - -QToolBar { - background-color: palette(dark); - border: none; - padding: 0px; - margin: 4px 0px; -} - -QPushButton[toolButton="true"], -QToolButton { - background-color: rgb(243,243,243); - padding: 4px 6px; - margin: 0px 2px; - border-radius: 4px; -} - -QPushButton[toolButton="true"]:last-child, -QToolButton:last-child { - margin-right: 0px; -} - -QToolButton:hover { - background-color: rgb(254,254,255); -} - -QToolButton:pressed { - background-color: rgb(193,193,193); -} - -* [themeID="addIconSmall"] { - qproperty-icon: url(theme:Light/plus.svg); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(theme:Light/trash.svg); -} - -* [themeID="clearIconSmall"] { - qproperty-icon: url(theme:Light/entry-clear.svg); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(theme:Light/settings/general.svg); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(theme:Light/settings/general.svg); -} - -* [themeID="menuIconSmall"] { - qproperty-icon: url(theme:Light/dots-vert.svg); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(theme:Light/refresh.svg); -} - -* [themeID="cogsIcon"] { - qproperty-icon: url(theme:Light/cogs.svg); -} - -#sourceInteractButton { - qproperty-icon: url(theme:Light/interact.svg); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(theme:Light/up.svg); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(theme:Light/down.svg); -} - -* [themeID="pauseIconSmall"] { - qproperty-icon: url(theme:Light/media-pause.svg); -} - -* [themeID="filtersIcon"] { - qproperty-icon: url(theme:Light/filter.svg); -} - -QToolBarExtension { - background: palette(button); - min-width: 12px; - max-width: 12px; - padding: 4px 0px; - margin-left: 0px; - - qproperty-icon: url(theme:Light/dots-vert.svg); -} - - -/* Tab Widget */ - -QTabWidget::pane { /* The tab widget frame */ - border-top: 4px solid palette(base); -} - -QTabWidget::tab-bar { - alignment: left; -} - -QTabBar QToolButton { - background: rgb(44,46,53); - border: none; -} - -QTabBar::tab:top { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QTabBar::tab:bottom { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -QTabBar::tab { - background: palette(dark); - color: palette(text); - border: none; - padding: 8px 12px; - min-width: 50px; - margin: 1px 2px; -} - -QTabBar::tab:pressed { - background: rgb(193,193,193); -} - -QTabBar::tab:hover { - background: rgb(254,254,255); - color: palette(text); -} - -QTabBar::tab:selected { - background: rgb(243,243,243); - color: palette(text); -} - -QTabBar::tab:top:selected { - border-bottom: 2px solid rgb(250,250,250); -} - -QTabBar::tab:bottom:selected { - border-top: 2px solid rgb(250,250,250); -} - -QTabBar QToolButton { - background: palette(button); - min-width: 16px; - padding: 0px; -} - -/* ComboBox */ - -QComboBox, -QDateTimeEdit { - background-color: rgb(243,243,243); - border-style: solid; - border: 1px; - border-radius: 4px; - border-color: rgb(243,243,243); - padding: 4px; - padding-left: 10px; -} - -QComboBox:hover, -QComboBox:selected, -QDateTimeEdit:hover, -QDateTimeEdit:selected { - background-color: rgb(254,254,255); -} - -QComboBox::drop-down, -QDateTimeEdit::drop-down { - border:none; - border-left: 1px solid rgb(25,28,34); - width: 20px; -} - -QComboBox::down-arrow, -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Light/updown.svg); - width: 100%; -} - -QComboBox:on, -QDateTimeEdit:on { - background-color: rgb(193,193,193); -} - -QComboBox:editable:hover { - -} - -QComboBox::drop-down:editable, -QDateTimeEdit::drop-down:editable { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} - -QComboBox::down-arrow:editable, -QDateTimeEdit::down-arrow:editable { - qproperty-alignment: AlignTop; - image: url(theme:Light/down.svg); - width: 8%; -} - -/* Textedits etc */ - -QLineEdit, QTextEdit, QPlainTextEdit { - background-color: palette(button); - border: none; - border-radius: 4px; - padding: 5px 2px 5px 7px; - border: 2px solid transparent; -} - -QLineEdit:hover, -QTextEdit:hover, -QPlainTextEdit:hover { - border: 2px solid rgb(99,102,111); -} - -QLineEdit:focus, -QTextEdit:focus, -QPlainTextEdit:focus { - background-color: palette(mid); - border: 2px solid rgb(140,181,255); -} - -/* Spinbox and doubleSpinbox */ - -QSpinBox, -QDoubleSpinBox { - background-color: palette(button); - border: 2px solid palette(button); - border-radius: 4px; - margin-right: 3px; - padding: 3px 0px 4px 5px; -} - -QSpinBox:hover, -QDoubleSpinBox:hover { - border: 2px solid rgb(99,102,111); -} - -QSpinBox:focus, -QDoubleSpinBox:focus { - background-color: palette(mid); - border: 2px solid rgb(140,181,255); -} - -QSpinBox::up-button, QDoubleSpinBox::up-button { - subcontrol-origin: padding; - subcontrol-position: top right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom-width: 0; -} - -QSpinBox::down-button, QDoubleSpinBox::down-button { - subcontrol-origin: padding; - subcontrol-position: bottom right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - border-top-width: 0; -} - -QSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { - background-color: rgb(254,254,255); -} - -QSpinBox::up-button:pressed, QSpinBox::down-button:pressed, QDoubleSpinBox::up-button:pressed, QDoubleSpinBox::down-button:pressed { - background-color: rgb(193,193,193); -} - -QSpinBox::up-button:disabled, QSpinBox::up-button:off, QSpinBox::down-button:disabled, QSpinBox::down-button:off { - background-color: rgb(193,193,193); -} - -QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBox::down-button:disabled, QDoubleSpinBox::down-button:off { - background-color: rgb(193,193,193); -} - -QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(theme:Light/up.svg); - width: 100%; - margin: 2px; -} - -QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(theme:Light/down.svg); - width: 100%; - padding: 2px; -} - - -/* Controls Dock */ -#controlsFrame { - padding: 4px 3px; -} - -#controlsFrame QPushButton { - margin: 2px 1px; -} - -#streamButton, -#recordButton, -QPushButton[themeID="replayBufferButton"], -#broadcastButton { - padding: 10px; -} - -/* Primary Control Button Checked Coloring */ -#streamButton:!hover:!pressed:checked, -#recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, -#modeSwitch:!hover:!pressed:checked, -#broadcastButton:!hover:!pressed:checked { - background: rgb(140,181,255); -} - -/* Primary Control Button Hover Coloring */ -#streamButton:hover:!pressed:checked, -#recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, -#modeSwitch:hover:!pressed:checked, -#broadcastButton:hover:!pressed:checked { - background: rgb(178,207,255); - color: palette(text); -} - - -/* Buttons */ - -QPushButton { - color: palette(text); - background-color: palette(button); - min-height: 18px; - border: none; - border-radius: 4px; - padding: 6px 16px; -} - -QPushButton::flat { - background-color: rgb(243,243,243); -} - -QPushButton:checked { - background-color: rgb(140,181,255); -} - -QPushButton:hover { - background-color: rgb(254,254,255); -} - -QPushButton:pressed { - background-color: rgb(193,193,193); -} - -QPushButton:disabled, QToolButton:disabled { - background-color: rgb(193,193,193); -} - -QPushButton::menu-indicator { - image: url(theme:Light/down.svg); - subcontrol-position: right; - subcontrol-origin: padding; - width: 25px; -} - -/* Sliders */ - -QSlider::groove:horizontal { - background-color: rgb(243,243,243); - height: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - height: 10px; - width: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:horizontal:pressed { - background-color: palette(text); -} - -QSlider::sub-page:horizontal { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::groove:vertical { - background-color: rgb(243,243,243); - width: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:vertical { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - width: 10px; - height: 18px; - margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:vertical:pressed { - background-color: palette(text); -} - -QSlider::add-page:vertical { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::add-page:vertical:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::handle:hover { - background-color: rgb(200,199,200); -} - -QSlider::handle:disabled { - background-color: rgb(68,75,110); -} - -/* Volume Control */ - -#stackedMixerArea QPushButton { - min-width: 16px; - padding: 4px 8px; -} - -/* This is an incredibly cursed but necessary fix */ -#stackedMixerArea QPushButton:!hover { - background-color: palette(button); -} - -#stackedMixerArea QPushButton:hover { - background-color: rgb(254,254,255); -} - -#stackedMixerArea QPushButton:pressed { - background-color: rgb(193,193,193); -} - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(66,112,24); - qproperty-backgroundWarningColor: rgb(112,91,28); - qproperty-backgroundErrorColor: rgb(112,39,53); - qproperty-foregroundNominalColor: rgb(115,189,49); - qproperty-foregroundWarningColor: rgb(189,144,9); - qproperty-foregroundErrorColor: rgb(189,47,73); - qproperty-magnitudeColor: rgb(0,0,0); - qproperty-majorTickColor: palette(text); - qproperty-minorTickColor: palette(light); -} - -/* Status Bar */ - -QStatusBar::item { - border: none; -} - -/* Table View */ - -QTableView { - background: palette(base); - gridline-color: palette(light); -} - -QTableView::item { - margin: 0px; - padding: 0px; -} - -QTableView QLineEdit { - background: palette(mid); - padding: 0; - margin: 0; -} - -QTableView QPushButton, -QTableView QToolButton { - margin: 1px 1px 2px; -} - -QHeaderView::section { - background-color: palette(button); - color: palette(text); - border: none; - border-left: 1px solid palette(window); - border-right: 1px solid palette(window); - padding: 2px 4px; - margin-bottom: 2px; -} - -/* Mute CheckBox */ - -MuteCheckBox::indicator:checked { - image: url(theme:Light/mute.svg); -} - -MuteCheckBox::indicator:indeterminate { - image: url(theme:Dark/unassigned.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Light/settings/audio.svg); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192,128,0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192,128,0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0,192,0); - font-weight: bold; -} - -/* About dialog */ - -* [themeID="aboutName"] { - font-size: 26pt; - font-weight: bold; -} - -* [themeID="aboutVersion"] { - font-size: 12pt; - margin-bottom: 20px; -} - -* [themeID="aboutInfo"] { - margin-bottom: 20px; -} - -* [themeID="aboutHLayout"] { - background-color: palette(base); -} - -/* Canvas / Preview background color */ - -OBSQTDisplay { - qproperty-displayBackgroundColor: rgb(193,193,193); - border-radius: 10px; -} - -/* Filters Window */ - -OBSBasicFilters QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicFilters QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -OBSBasicFilters #widget, -OBSBasicFilters #widget_2 { - margin: 0px; - padding: 0px; - padding-bottom: 4px; -} - -OBSBasicFilters #widget QPushButton, -OBSBasicFilters #widget_2 QPushButton { - min-width: 16px; - padding: 4px 8px; - margin-top: 0px; -} - -/* Preview/Program labels */ - -* [themeID="previewProgramLabels"] { - font-size: 14pt; - font-weight: bold; - color: rgb(210,210,210); - margin-bottom: 4px; -} - -/* Settings Icons */ - -OBSBasicSettings { - qproperty-generalIcon: url(theme:Light/settings/general.svg); - qproperty-streamIcon: url(theme:Light/settings/stream.svg); - qproperty-outputIcon: url(theme:Light/settings/output.svg); - qproperty-audioIcon: url(theme:Light/settings/audio.svg); - qproperty-videoIcon: url(theme:Light/settings/video.svg); - qproperty-hotkeysIcon: url(theme:Light/settings/hotkeys.svg); - qproperty-accessibilityIcon: url(theme:Light/settings/accessibility.svg); - qproperty-advancedIcon: url(theme:Light/settings/advanced.svg); -} - -/* Checkboxes */ -QCheckBox { - -} - -QCheckBox::indicator, -QGroupBox::indicator { - width: 18px; - height: 18px; -} - -QGroupBox::indicator { - margin-left: 2px; -} - -QCheckBox::indicator:unchecked, -QGroupBox::indicator:unchecked { - image: url(theme:Light/checkbox_unchecked.svg); -} - -QCheckBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:hover { - border: none; - image: url(theme:Light/checkbox_unchecked_focus.svg); -} - -QCheckBox::indicator:checked, -QGroupBox::indicator:checked { - image: url(theme:Light/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QGroupBox::indicator:checked:hover { - border: none; - image: url(theme:Light/checkbox_checked_focus.svg); -} - -QCheckBox::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled { - image: url(theme:Light/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled { - image: url(theme:Light/checkbox_unchecked_disabled.svg); -} - -/* Locked CheckBox */ - -QCheckBox[lockCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[lockCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[lockCheckBox=true]::indicator:checked, -QCheckBox[lockCheckBox=true]::indicator:checked:hover { - image: url(theme:Light/locked.svg); -} - -QCheckBox[lockCheckBox=true]::indicator:unchecked, -QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/unlocked.svg); -} - -/* Visibility CheckBox */ - -QCheckBox[visibilityCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[visibilityCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[visibilityCheckBox=true]::indicator:checked, -QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { - image: url(theme:Light/visible.svg); -} - -QCheckBox[visibilityCheckBox=true]::indicator:unchecked, -QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/invisible.svg); -} - -* [themeID="revertIcon"] { - qproperty-icon: url(theme:Light/revert.svg); -} - -QPushButton#extraPanelDelete { - background-color: palette(mid); - margin: 0; - padding: 0; -} - -QPushButton#extraPanelDelete:hover { - background-color: rgb(68,75,110); -} - -QPushButton#extraPanelDelete:pressed { - background-color: palette(dark); -} - -/* Mute CheckBox */ - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator { - width: 16px; - height: 16px; -} - -MuteCheckBox::indicator:checked { - image: url(theme:Light/mute.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Light/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:hover { - image: url(theme:Light/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:focus { - image: url(theme:Light/settings/audio.svg); -} - -MuteCheckBox::indicator:checked:hover { - image: url(theme:Light/mute.svg); -} - -MuteCheckBox::indicator:checked:focus { - image: url(theme:Light/mute.svg); -} - -MuteCheckBox::indicator:checked:disabled { - image: url(theme:Light/mute.svg); -} - -MuteCheckBox::indicator:unchecked:disabled { - image: url(theme:Light/settings/audio.svg); -} - -#hotkeyFilterReset { - margin-top: 0px; -} - -OBSHotkeyWidget { - padding: 8px 0px; - margin: 2px 0px; -} - -OBSHotkeyLabel { - padding: 4px 0px; -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -OBSHotkeyWidget QPushButton { - min-width: 16px; - padding: 4px 4px; - margin-top: 0px; - margin-left: 4px; -} - - -/* Sources List Group Collapse Checkbox */ - -QCheckBox[sourceTreeSubItem=true] { - background: transparent; - outline: none; - padding: 0px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator { - width: 12px; - height: 12px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator:checked, -QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { - image: url(theme:Light/expand.svg); -} - -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { - image: url(theme:Light/collapse.svg); -} - -/* Source Icons */ - -OBSBasic { - qproperty-imageIcon: url(theme:Light/sources/image.svg); - qproperty-colorIcon: url(theme:Light/sources/brush.svg); - qproperty-slideshowIcon: url(theme:Light/sources/slideshow.svg); - qproperty-audioInputIcon: url(theme:Light/sources/microphone.svg); - qproperty-audioOutputIcon: url(theme:Light/settings/audio.svg); - qproperty-desktopCapIcon: url(theme:Light/settings/video.svg); - qproperty-windowCapIcon: url(theme:Light/sources/window.svg); - qproperty-gameCapIcon: url(theme:Light/sources/gamepad.svg); - qproperty-cameraIcon: url(theme:Light/sources/camera.svg); - qproperty-textIcon: url(theme:Light/sources/text.svg); - qproperty-mediaIcon: url(theme:Light/sources/media.svg); - qproperty-browserIcon: url(theme:Light/sources/globe.svg); - qproperty-groupIcon: url(theme:Light/sources/group.svg); - qproperty-sceneIcon: url(theme:Light/sources/scene.svg); - qproperty-defaultIcon: url(theme:Light/sources/default.svg); - qproperty-audioProcessOutputIcon: url(theme:Light/sources/windowaudio.svg); -} - -/* Scene Tree Grid Mode */ - -SceneTree { - qproperty-gridItemWidth: 154; - qproperty-gridItemHeight: 31; -} - -*[gridMode="true"] SceneTree::item { - color: palette(text); - background-color: palette(button); - border-radius: 4px; - margin: 2px; -} - -*[gridMode="true"] SceneTree::item:selected { - background-color: rgb(140,181,255); -} - -*[gridMode="true"] SceneTree::item:checked { - background-color: rgb(140,181,255); -} - -*[gridMode="true"] SceneTree::item:hover { - background-color: rgb(254,254,255); -} - -*[gridMode="true"] SceneTree::item:selected:hover { - background-color: rgb(178,207,255); -} - -/* Save icon */ - -* [themeID="replayIconSmall"] { - qproperty-icon: url(theme:Light/save.svg); -} - -/* Studio Mode Labels */ - -* [themeID="previewProgramLabels"] { - color: palette(text); -} - -/* Studio Mode T-Bar */ - -QSlider[themeID="tBarSlider"] { - height: 24px; -} - -QSlider::groove:horizontal[themeID="tBarSlider"] { - border: 1px solid #4c4c4c; - height: 5px; - background: palette(dark); -} - -QSlider::sub-page:horizontal[themeID="tBarSlider"] { - background: palette(dark); - border: 1px solid #4c4c4c; -} - -QSlider::handle:horizontal[themeID="tBarSlider"] { - background-color: palette(text); - width: 12px; - height: 24px; - margin: -24px 0px; -} - -/* Media icons */ - -* [themeID="playIcon"] { - qproperty-icon: url(theme:Light/media/media_play.svg); -} - -* [themeID="pauseIcon"] { - qproperty-icon: url(theme:Light/media/media_pause.svg); -} - -* [themeID="restartIcon"] { - qproperty-icon: url(theme:Light/media/media_restart.svg); -} - -* [themeID="stopIcon"] { - qproperty-icon: url(theme:Light/media/media_stop.svg); -} - -* [themeID="nextIcon"] { - qproperty-icon: url(theme:Light/media/media_next.svg); -} - -* [themeID="previousIcon"] { - qproperty-icon: url(theme:Light/media/media_previous.svg); -} - -/* YouTube Integration */ -OBSYoutubeActions { - qproperty-thumbPlaceholder: url(theme:Light/sources/image.svg); -} - -#ytEventList QLabel { - color: palette(text); - background-color: palette(button); - border: none; - border-radius: 4px; - padding: 4px 20px; -} - -#ytEventList QLabel:hover { - background-color: rgb(254,254,255); -} - -#ytEventList QLabel[isSelectedEvent=true] { - background-color: rgb(140,181,255); - border: none; -} - -#ytEventList QLabel[isSelectedEvent=true]:hover { - background-color: rgb(178,207,255); - color: palette(text); -} - -/* Calendar Widget */ -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Light/down.svg); - width: 100%; -} - -QDateTimeEdit:on { - background-color: palette(mid); -} - -/* Calendar Top Bar */ -QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: palette(base); - padding: 4px 8px; -} - -/* Calendar Top Bar Buttons */ -QCalendarWidget QToolButton { - background-color: palette(button); - padding: 2px 16px; - border-radius: 4px; - margin: 2px; -} - -#qt_calendar_monthbutton::menu-indicator { - image: url(theme:Light/down.svg); - subcontrol-position: right; - padding-top: 2px; - padding-right: 6px; - height: 10px; - width: 10px; -} - -QCalendarWidget #qt_calendar_prevmonth { - padding: 2px; - qproperty-icon: url(theme:Light/left.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget #qt_calendar_nextmonth { - padding: 2px; - qproperty-icon: url(theme:Light/right.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget QToolButton:hover { - background-color: rgb(254,254,255); - border-radius: 4px; -} - -QCalendarWidget QToolButton:pressed { - background-color: rgb(193,193,193); -} - -/* Month Dropdown Menu */ -QCalendarWidget QMenu { - -} -/* Year spinbox */ -QCalendarWidget QSpinBox { - background-color: rgb(193,193,193); - border: none; - border-radius: 4px; - margin: 0px 3px 0px 0px; - padding: 4px 16px; -} - -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} -QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } -QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } - -/* Days of the Week Bar */ -QCalendarWidget QWidget { alternate-background-color: palette(mid); } - -QCalendarWidget QAbstractItemView:enabled { - background-color: palette(base); - color: palette(text); -} - -QCalendarWidget QAbstractItemView:disabled { - color: rgb(122,121,122); -} - -/* VirtualCam Plugin Fixes */ - -#VirtualProperties QWidget { - margin-top: 0; - margin-bottom: 0; -} - -/* Disable icons on QDialogButtonBox */ -QDialogButtonBox { - dialogbuttonbox-buttons-have-icons: 0; -} - -/* Stats dialog */ -OBSBasicStats { - background: palette(dark); -} - -/* Advanced audio dialog */ -OBSBasicAdvAudio #scrollAreaWidgetContents { - background: palette(dark); -} diff --git a/UI/data/themes/Light/settings/appearance.svg b/UI/data/themes/Light/settings/appearance.svg new file mode 100644 index 00000000000000..ed6de819d78f94 --- /dev/null +++ b/UI/data/themes/Light/settings/appearance.svg @@ -0,0 +1,2 @@ + + diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss deleted file mode 100644 index b46a437d0366a8..00000000000000 --- a/UI/data/themes/Rachni.qss +++ /dev/null @@ -1,1538 +0,0 @@ -/******************************************************************************/ -/* Copyright (C) 2014-2015 by Philippe Groarke */ -/* */ -/* */ -/* This program is free software: you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation, either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program. If not, see . */ -/******************************************************************************/ - -/* Colors */ - -OBSThemeMeta { - dark: 'true'; - author: 'Warchamp7'; -} - -/* Custom theme information. This will set the application's QPalette, as - * well as pass to QML via the OBSTheme object. - * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. - * Using it without will set all three (making 'active' a bit redundant) */ -OBSTheme { - window: rgb(49,54,59); - windowText: rgb(255,254,255); - - base: rgb(35,38,41); - alternateBase: rgb(11,10,11); - - text: rgb(255,254,255); - - button: rgb(0,187,210); - buttonText: rgb(255,254,255); - - brightText: rgb(255,254,255); - - light: rgb(88,87,88); - mid: rgb(49,54,59); - dark: rgb(35,38,41); - shadow: rgb(11,10,11); - - primary: rgb(0,188,212); - primaryLight: rgb(0,188,212); - primaryDark: rgb(25,27,38); - - secondary: rgb(240,96,146); - - highlight: rgb(42,130,218); - highlightText: rgb(255,254,255); - - link: rgb(77,166,255); - linkVisited: rgb(77,166,255); -} - -OBSTheme::disabled { - windowText: rgb(153,153,153); - text: rgb(153,153,153); - button: rgb(27,29,34); - - buttonText: rgb(35,38,41); - brightText: rgb(35,38,41); -} - -OBSTheme::inactive { - text: rgb(255,254,255); - - highlight: rgb(25,28,34); - highlightText: rgb(255,255,255); -} - -/* Default widget style, we override only what is needed. */ - -QWidget { - alternate-background-color: palette(base); - color: palette(text); - selection-background-color: rgb(0,188,212); - selection-color: palette(text); - font-size: 10pt; - font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif; -} - -QWidget:disabled { - color: rgb(153,153,153); -} - -/* Container windows */ - -QDialog, -QMainWindow, -QStatusBar, -QMenuBar, -QMenu { - background-color: palette(window); -} - -/* macOS Separator Fix */ - -QMainWindow::separator { - background: transparent; - width: 4px; - height: 4px; -} - -/* General Widgets */ - -QLabel, -QGroupBox, -QCheckBox { - background: transparent; -} - -QComboBox, -QCheckBox, -QPushButton, -QSpinBox, -QDoubleSpinBox { - margin-top: 3px; - margin-bottom: 3px; -} - -QListWidget QWidget, -SceneTree QWidget, -SourceTree QWidget { - margin-top: 0; - margin-bottom: 0; -} - -* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { - border: 1px solid palette(dark); -} - - -/* Misc */ - -QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { - background-color: palette(base); -} - -QToolTip { - background-color: palette(base); - color: palette(text); - border: none; -} - -/* Context Menu */ - -QMenu::icon { - left: 4px; -} - -QMenu::separator { - background: palette(light); - height: 1px; - margin: 3px 6px; -} - -QMenu::item:disabled { - color: rgb(153,153,153); - background: transparent; -} - -QMenu::right-arrow { - image: url(theme:Dark/expand.svg); -} - -/* Top Menu Bar Items */ -QMenuBar::item { - background-color: transparent; -} - -QMenuBar::item:selected { - background: rgb(0,188,212); -} - -/* Item Lists */ -QListWidget { - border-radius: 4px; -} - -QListWidget::item { - color: palette(text); -} - -QListWidget, -QMenu, -SceneTree, -SourceTree { - padding: 3px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item { - padding: 6px; -} - -QMenu::item { - padding-right: 20px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item, -SourceTree::item { - border-radius: 4px; - color: palette(text); - border: 0px solid transparent; -} - -QMenu::item:selected, -QListWidget::item:selected, -SceneTree::item:selected, -SourceTree::item:selected { - background-color: rgb(89,66,79); -} - -QMenu::item:hover, -QListWidget::item:hover, -SceneTree::item:hover, -SourceTree::item:hover, -QMenu::item:selected:hover, -QListWidget::item:selected:hover, -SceneTree::item:selected:hover, -SourceTree::item:selected:hover { - background-color: rgb(0,188,212); - color: palette(text); -} - -QListWidget::item:disabled, -QListWidget::item:disabled:hover { - background: transparent; - color: rgb(153,153,153); -} - -QListWidget QLineEdit, -SceneTree QLineEdit, -SourceTree QLineEdit { - padding: 0px; - padding-bottom: 2px; - margin: 0px; - border: 1px solid #FFF; - border-radius: 4px; -} - -QListWidget QLineEdit:focus, -SceneTree QLineEdit:focus, -SourceTree QLineEdit:focus { - border: 1px solid #FFF; -} - -/* Settings QList */ - -OBSBasicSettings QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicSettings QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -/* Settings properties view */ -OBSBasicSettings #PropertiesContainer { - background-color: rgb(59,65,71); -} - -/* Dock Widget */ -OBSDock > QWidget { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -OBSDock QLabel { - background: transparent; -} - -#transitionsFrame { - padding: 4px 8px; -} - -QDockWidget { - font-size: 10.5pt; - font-weight: bold; - - titlebar-close-icon: url(theme:Dark/close.svg); - titlebar-normal-icon: url(theme:Dark/popout.svg); -} - -QDockWidget::title { - text-align: left; - background-color: palette(base); - padding: 6px 8px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 0px solid transparent; - border-radius: 4px; - background: transparent; - margin-right: 1px; - opacity: .5; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgb(145,76,103); - opacity: 1; -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; -} - -QScrollArea { - border-radius: 4px; -} - -/* Qt enforces a padding inside its status bar, so we - * oversize it and use margin to crunch it back down - */ -OBSBasicStatusBar { - margin-top: 4px; - border-top: 1px solid #3c404b; - background: palette(dark); -} - -StatusBarWidget > QFrame { - margin-top: 1px; - border: 0px solid #3c404b; - border-left-width: 1px; - padding: 0px 12px 2px; -} - -/* Group Box */ - -QGroupBox { - background: rgb(59,65,71); - border-radius: 4px; - padding-top: 32px; - padding-bottom: 8px; - font-weight: bold; - margin-bottom: 6px; -} - -QGroupBox::title { - color: rgb(240,98,146); - subcontrol-origin: margin; - left: 8px; - top: 8px; -} - - -/* ScrollBars */ - -::corner { - background-color: palette(window); - border: none; -} - -QScrollBar:vertical { - background-color: transparent; - width: 14px; - margin: 0px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - border: none; - background: none; - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - border: none; - background: none; - color: none; -} - -QScrollBar:horizontal { - background-color: transparent; - height: 14px; - margin: 0px; -} - -QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { - border: none; - background: none; - width: 0px; -} - -QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - border: none; - background: none; - color: none; -} - -QScrollBar::handle { - background-color: rgb(118,121,124); - margin: 2px; - border-radius: 2px; - border: 1px solid rgb(118,121,124); -} - -QScrollBar::handle:hover { - background-color: rgb(145,76,103); - border-color: rgb(145,76,103); -} - -QScrollBar::handle:pressed { - background-color: rgb(118,121,124); - border-color: rgb(118,121,124); -} - -QScrollBar::handle:vertical { - min-height: 20px; -} - -QScrollBar::handle:horizontal { - min-width: 20px; -} - -/* Source Context Bar */ - -#contextContainer { - background-color: palette(dark); - margin-top: 4px; - border-radius: 4px; -} - -#contextContainer QPushButton { - padding-left: 12px; - padding-right: 12px; -} - -QPushButton#sourcePropertiesButton { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -QPushButton#sourceFiltersButton { - qproperty-icon: url(theme:Dark/filter.svg); -} - -/* Scenes and Sources toolbar */ - -QToolBar { - background-color: rgb(49,54,59); - border: none; - padding: 4px; - margin: 0px 0px -4px; -} - -QPushButton[toolButton="true"], -QToolButton { - background-color: rgb(49,54,59); - padding: 4px 6px; - margin: 0px 2px; - border-radius: 4px; -} - -QPushButton[toolButton="true"]:disabled, -QToolButton:disabled { - background-color: transparent; -} - -QPushButton[toolButton="true"]:last-child, -QToolButton:last-child { - margin-right: 0px; -} - -QToolButton:hover { - background-color: rgb(145,76,103); -} - -QToolButton:pressed { - background-color: rgb(240,98,146); -} - -* [themeID="addIconSmall"] { - qproperty-icon: url(theme:Dark/plus.svg); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(theme:Dark/trash.svg); -} - -* [themeID="clearIconSmall"] { - qproperty-icon: url(theme:Dark/entry-clear.svg); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="menuIconSmall"] { - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(theme:Dark/refresh.svg); -} - -* [themeID="cogsIcon"] { - qproperty-icon: url(theme:Dark/cogs.svg); -} - -#sourceInteractButton { - qproperty-icon: url(theme:Dark/interact.svg); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(theme:Dark/up.svg); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(theme:Dark/down.svg); -} - -* [themeID="pauseIconSmall"] { - qproperty-icon: url(theme:Dark/media-pause.svg); -} - -* [themeID="filtersIcon"] { - qproperty-icon: url(theme:Dark/filter.svg); -} - -QToolBarExtension { - background: palette(button); - min-width: 12px; - max-width: 12px; - padding: 4px 0px; - margin-left: 0px; - - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - - -/* Tab Widget */ - -QTabWidget::pane { /* The tab widget frame */ - border-top: 4px solid palette(base); -} - -QTabWidget::tab-bar { - alignment: left; -} - -QTabBar QToolButton { - background: rgb(44,46,53); - border: none; -} - -QTabBar::tab:top { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QTabBar::tab:bottom { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -QTabBar::tab { - background: palette(dark); - color: palette(text); - border: none; - padding: 8px 12px; - min-width: 50px; - margin: 1px 2px; -} - -QTabBar::tab:pressed { - background: rgb(240,98,146); -} - -QTabBar::tab:hover { - background: rgb(145,76,103); - color: palette(text); -} - -QTabBar::tab:selected { - background: rgb(0,187,210); - color: palette(text); -} - -QTabBar::tab:top:selected { - border-bottom: 2px solid rgb(250,250,250); -} - -QTabBar::tab:bottom:selected { - border-top: 2px solid rgb(250,250,250); -} - -QTabBar QToolButton { - background: palette(button); - min-width: 16px; - padding: 0px; -} - -/* ComboBox */ - -QComboBox, -QDateTimeEdit { - background-color: palette(base); - border: 2px solid rgb(118,121,124); - border-radius: 4px; - padding: 4px; - padding-left: 10px; -} - -QComboBox:hover, -QComboBox:selected, -QDateTimeEdit:hover, -QDateTimeEdit:selected { - background-color: palette(base); - border: 2px solid rgb(0,188,212); -} - -QComboBox::drop-down, -QDateTimeEdit::drop-down { - border:none; - border-left: 1px solid rgb(25,28,34); - width: 20px; -} - -QComboBox::down-arrow, -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/updown.svg); - width: 100%; -} - -QComboBox:on, -QDateTimeEdit:on { - background-color: rgb(25,27,38); -} - -QComboBox:editable:hover { - -} - -QComboBox::drop-down:editable, -QDateTimeEdit::drop-down:editable { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} - -QComboBox::down-arrow:editable, -QDateTimeEdit::down-arrow:editable { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 8%; -} - -/* Textedits etc */ - -QLineEdit, QTextEdit, QPlainTextEdit { - background-color: palette(base); - border-radius: 4px; - padding: 5px 2px 5px 7px; - border: 2px solid rgb(118,121,124); -} - -QLineEdit:hover, -QTextEdit:hover, -QPlainTextEdit:hover { - border: 2px solid rgb(0,188,212); -} - -QLineEdit:focus, -QTextEdit:focus, -QPlainTextEdit:focus { - border: 2px solid rgb(0,188,212); -} - -/* Spinbox and doubleSpinbox */ - -QSpinBox, -QDoubleSpinBox { - background-color: palette(base); - border: 2px solid rgb(118,121,124); - border-radius: 4px; - margin-right: 3px; - padding: 3px 0px 4px 5px; -} - -QSpinBox:hover, -QDoubleSpinBox:hover { - border: 2px solid rgb(0,188,212); -} - -QSpinBox:focus, -QDoubleSpinBox:focus { - border: 2px solid rgb(0,188,212); -} - -QSpinBox::up-button, QDoubleSpinBox::up-button { - subcontrol-origin: padding; - subcontrol-position: top right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom-width: 0; -} - -QSpinBox::down-button, QDoubleSpinBox::down-button { - subcontrol-origin: padding; - subcontrol-position: bottom right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - border-top-width: 0; -} - -QSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { - background-color: rgb(145,76,103); -} - -QSpinBox::up-button:pressed, QSpinBox::down-button:pressed, QDoubleSpinBox::up-button:pressed, QDoubleSpinBox::down-button:pressed { - background-color: rgb(25,27,38); -} - -QSpinBox::up-button:disabled, QSpinBox::up-button:off, QSpinBox::down-button:disabled, QSpinBox::down-button:off { - background-color: rgb(25,27,38); -} - -QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBox::down-button:disabled, QDoubleSpinBox::down-button:off { - background-color: rgb(25,27,38); -} - -QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(theme:Dark/up.svg); - width: 100%; - margin: 2px; -} - -QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(theme:Dark/down.svg); - width: 100%; - padding: 2px; -} - - -/* Controls Dock */ -#controlsFrame { - padding: 4px 3px; -} - -#controlsFrame QPushButton { - margin: 2px 1px; -} - -#streamButton, -#recordButton, -QPushButton[themeID="replayBufferButton"], -#broadcastButton { - padding: 10px; -} - -/* Primary Control Button Checked Coloring */ -#streamButton:!hover:!pressed:checked, -#recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, -#modeSwitch:!hover:!pressed:checked, -#broadcastButton:!hover:!pressed:checked { - background: rgb(145,76,103); -} - -/* Primary Control Button Hover Coloring */ -#streamButton:hover:!pressed:checked, -#recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, -#modeSwitch:hover:!pressed:checked, -#broadcastButton:hover:!pressed:checked { - background: rgb(145,76,103); - color: palette(text); -} - - -/* Buttons */ - -QPushButton { - color: palette(text); - background-color: palette(button); - min-height: 18px; - border: none; - border-radius: 4px; - padding: 6px 16px; -} - -QPushButton::flat { - background-color: rgb(0,187,210); -} - -QPushButton:checked { - background-color: rgb(0,188,212); -} - -QPushButton:hover { - background-color: rgb(145,76,103); -} - -QPushButton:pressed { - background-color: rgb(240,98,146); -} - -QPushButton:disabled, QToolButton:disabled { - background-color: rgb(0,139,163); -} - -QPushButton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - subcontrol-origin: padding; - width: 25px; -} - -/* Sliders */ - -QSlider::groove:horizontal { - background-color: palette(window); - height: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - height: 10px; - width: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:horizontal:pressed { - background-color: palette(text); -} - -QSlider::sub-page:horizontal { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::groove:vertical { - background-color: palette(window); - width: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:vertical { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - width: 10px; - height: 18px; - margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:vertical:pressed { - background-color: palette(text); -} - -QSlider::add-page:vertical { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::add-page:vertical:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::handle:hover { - background-color: rgb(200,199,200); -} - -QSlider::handle:disabled { - background-color: rgb(68,75,110); -} - -/* Volume Control */ - -#stackedMixerArea QPushButton { - min-width: 16px; - padding: 4px 8px; -} - -/* This is an incredibly cursed but necessary fix */ -#stackedMixerArea QPushButton:!hover { - background-color: rgb(49,54,59); -} - -#stackedMixerArea QPushButton:hover { - background-color: rgb(145,76,103); -} - -#stackedMixerArea QPushButton:pressed { - background-color: rgb(240,98,146); -} - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(0, 128, 79); - qproperty-backgroundWarningColor: rgb(128, 57, 0); - qproperty-backgroundErrorColor: rgb(128, 9, 0); - qproperty-foregroundNominalColor: rgb(119, 255, 143); - qproperty-foregroundWarningColor: rgb(255, 157, 76); - qproperty-foregroundErrorColor: rgb(255, 89, 76); - qproperty-magnitudeColor: palette(window); - qproperty-majorTickColor: palette(window-text); - qproperty-minorTickColor: palette(mid); -} - -/* Status Bar */ - -QStatusBar::item { - border: none; -} - -/* Table View */ - -QTableView { - background: palette(base); - gridline-color: palette(light); -} - -QTableView::item { - margin: 0px; - padding: 0px; -} - -QTableView QLineEdit { - background: palette(mid); - padding: 0; - margin: 0; -} - -QTableView QPushButton, -QTableView QToolButton { - margin: 1px 1px 2px; -} - -QHeaderView::section { - background-color: palette(base); - color: palette(text); - border: none; - border-left: 1px solid palette(window); - border-right: 1px solid palette(window); - padding: 2px 4px; - margin-bottom: 2px; -} - -/* Mute CheckBox */ - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:indeterminate { - image: url(theme:Dark/unassigned.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(240,98,146); -} - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192,128,0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192,128,0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0,192,0); - font-weight: bold; -} - -/* About dialog */ - -* [themeID="aboutName"] { - font-size: 26pt; - font-weight: bold; -} - -* [themeID="aboutVersion"] { - font-size: 12pt; - margin-bottom: 20px; -} - -* [themeID="aboutInfo"] { - margin-bottom: 20px; -} - -* [themeID="aboutHLayout"] { - background-color: palette(base); -} - -/* Canvas / Preview background color */ - -OBSQTDisplay { - qproperty-displayBackgroundColor: rgb(34,37,40); - border-radius: 10px; -} - -/* Filters Window */ - -OBSBasicFilters QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicFilters QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -OBSBasicFilters #widget, -OBSBasicFilters #widget_2 { - margin: 0px; - padding: 0px; - padding-bottom: 4px; -} - -OBSBasicFilters #widget QPushButton, -OBSBasicFilters #widget_2 QPushButton { - min-width: 16px; - padding: 4px 8px; - margin-top: 0px; -} - -/* Preview/Program labels */ - -* [themeID="previewProgramLabels"] { - font-size: 14pt; - font-weight: bold; - color: rgb(210,210,210); - margin-bottom: 4px; -} - -/* Settings Icons */ - -OBSBasicSettings { - qproperty-generalIcon: url(theme:Dark/settings/general.svg); - qproperty-streamIcon: url(theme:Dark/settings/stream.svg); - qproperty-outputIcon: url(theme:Dark/settings/output.svg); - qproperty-audioIcon: url(theme:Dark/settings/audio.svg); - qproperty-videoIcon: url(theme:Dark/settings/video.svg); - qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg); - qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg); - qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg); -} - -/* Checkboxes */ -QCheckBox { - -} - -QCheckBox::indicator, -QGroupBox::indicator { - width: 18px; - height: 18px; -} - -QGroupBox::indicator { - margin-left: 2px; -} - -QCheckBox::indicator:unchecked, -QGroupBox::indicator:unchecked { - image: url(theme:Yami/checkbox_unchecked.svg); -} - -QCheckBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:hover { - border: none; - image: url(theme:Yami/checkbox_unchecked_focus.svg); -} - -QCheckBox::indicator:checked, -QGroupBox::indicator:checked { - image: url(theme:Yami/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QGroupBox::indicator:checked:hover { - border: none; - image: url(theme:Yami/checkbox_checked_focus.svg); -} - -QCheckBox::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled { - image: url(theme:Yami/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled { - image: url(theme:Yami/checkbox_unchecked_disabled.svg); -} - -/* Locked CheckBox */ - -QCheckBox[lockCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[lockCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[lockCheckBox=true]::indicator:checked, -QCheckBox[lockCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/locked.svg); -} - -QCheckBox[lockCheckBox=true]::indicator:unchecked, -QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/unlocked.svg); -} - -/* Visibility CheckBox */ - -QCheckBox[visibilityCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[visibilityCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[visibilityCheckBox=true]::indicator:checked, -QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/visible.svg); -} - -QCheckBox[visibilityCheckBox=true]::indicator:unchecked, -QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/invisible.svg); -} - -* [themeID="revertIcon"] { - qproperty-icon: url(theme:Dark/revert.svg); -} - -QPushButton#extraPanelDelete { - background-color: palette(mid); - margin: 0; - padding: 0; -} - -QPushButton#extraPanelDelete:hover { - background-color: rgb(68,75,110); -} - -QPushButton#extraPanelDelete:pressed { - background-color: palette(dark); -} - -/* Mute CheckBox */ - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator { - width: 16px; - height: 16px; -} - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:hover { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:focus { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:checked:hover { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:focus { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:disabled { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked:disabled { - image: url(theme:Dark/settings/audio.svg); -} - -#hotkeyFilterReset { - margin-top: 0px; -} - -OBSHotkeyWidget { - padding: 8px 0px; - margin: 2px 0px; -} - -OBSHotkeyLabel { - padding: 4px 0px; -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(240,98,146); -} - -OBSHotkeyWidget QPushButton { - min-width: 16px; - padding: 4px 4px; - margin-top: 0px; - margin-left: 4px; -} - - -/* Sources List Group Collapse Checkbox */ - -QCheckBox[sourceTreeSubItem=true] { - background: transparent; - outline: none; - padding: 0px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator { - width: 12px; - height: 12px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator:checked, -QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { - image: url(theme:Dark/expand.svg); -} - -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { - image: url(theme:Dark/collapse.svg); -} - -/* Source Icons */ - -OBSBasic { - qproperty-imageIcon: url(theme:Dark/sources/image.svg); - qproperty-colorIcon: url(theme:Dark/sources/brush.svg); - qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg); - qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg); - qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg); - qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg); - qproperty-windowCapIcon: url(theme:Dark/sources/window.svg); - qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg); - qproperty-cameraIcon: url(theme:Dark/sources/camera.svg); - qproperty-textIcon: url(theme:Dark/sources/text.svg); - qproperty-mediaIcon: url(theme:Dark/sources/media.svg); - qproperty-browserIcon: url(theme:Dark/sources/globe.svg); - qproperty-groupIcon: url(theme:Dark/sources/group.svg); - qproperty-sceneIcon: url(theme:Dark/sources/scene.svg); - qproperty-defaultIcon: url(theme:Dark/sources/default.svg); - qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg); -} - -/* Scene Tree Grid Mode */ - -SceneTree { - qproperty-gridItemWidth: 154; - qproperty-gridItemHeight: 31; -} - -*[gridMode="true"] SceneTree::item { - color: palette(text); - background-color: palette(button); - border-radius: 4px; - margin: 2px; -} - -*[gridMode="true"] SceneTree::item:selected { - background-color: rgb(145,76,103); -} - -*[gridMode="true"] SceneTree::item:checked { - background-color: rgb(145,76,103); -} - -*[gridMode="true"] SceneTree::item:hover { - background-color: rgb(145,76,103); -} - -*[gridMode="true"] SceneTree::item:selected:hover { - background-color: rgb(145,76,103); -} - -/* Save icon */ - -* [themeID="replayIconSmall"] { - qproperty-icon: url(theme:Dark/save.svg); -} - -/* Studio Mode T-Bar */ - -QSlider[themeID="tBarSlider"] { - height: 24px; -} - -QSlider::groove:horizontal[themeID="tBarSlider"] { - border: 1px solid #4c4c4c; - height: 5px; - background: palette(dark); -} - -QSlider::sub-page:horizontal[themeID="tBarSlider"] { - background: palette(dark); - border: 1px solid #4c4c4c; -} - -QSlider::handle:horizontal[themeID="tBarSlider"] { - background-color: #d2d2d2; - width: 12px; - height: 24px; - margin: -24px 0px; -} - -/* Media icons */ - -* [themeID="playIcon"] { - qproperty-icon: url(theme:Dark/media/media_play.svg); -} - -* [themeID="pauseIcon"] { - qproperty-icon: url(theme:Dark/media/media_pause.svg); -} - -* [themeID="restartIcon"] { - qproperty-icon: url(theme:Dark/media/media_restart.svg); -} - -* [themeID="stopIcon"] { - qproperty-icon: url(theme:Dark/media/media_stop.svg); -} - -* [themeID="nextIcon"] { - qproperty-icon: url(theme:Dark/media/media_next.svg); -} - -* [themeID="previousIcon"] { - qproperty-icon: url(theme:Dark/media/media_previous.svg); -} - -/* YouTube Integration */ -OBSYoutubeActions { - qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg); -} - -#ytEventList QLabel { - color: palette(text); - background-color: palette(base); - border: none; - border-radius: 4px; - padding: 4px 20px; -} - -#ytEventList QLabel:hover { - background-color: rgb(145,76,103); -} - -#ytEventList QLabel[isSelectedEvent=true] { - background-color: rgb(0,188,212); - border: none; -} - -#ytEventList QLabel[isSelectedEvent=true]:hover { - background-color: rgb(0,188,212); - color: palette(text); -} - -/* Calendar Widget */ -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 100%; -} - -QDateTimeEdit:on { - background-color: palette(mid); -} - -/* Calendar Top Bar */ -QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: palette(base); - padding: 4px 8px; -} - -/* Calendar Top Bar Buttons */ -QCalendarWidget QToolButton { - background-color: palette(button); - padding: 2px 16px; - border-radius: 4px; - margin: 2px; -} - -#qt_calendar_monthbutton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - padding-top: 2px; - padding-right: 6px; - height: 10px; - width: 10px; -} - -QCalendarWidget #qt_calendar_prevmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/left.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget #qt_calendar_nextmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/right.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget QToolButton:hover { - background-color: rgb(145,76,103); - border-radius: 4px; -} - -QCalendarWidget QToolButton:pressed { - background-color: rgb(240,98,146); -} - -/* Month Dropdown Menu */ -QCalendarWidget QMenu { - -} -/* Year spinbox */ -QCalendarWidget QSpinBox { - background-color: rgb(25,27,38); - border: none; - border-radius: 4px; - margin: 0px 3px 0px 0px; - padding: 4px 16px; -} - -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} -QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } -QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } - -/* Days of the Week Bar */ -QCalendarWidget QWidget { alternate-background-color: palette(mid); } - -QCalendarWidget QAbstractItemView:enabled { - background-color: palette(base); - color: palette(text); -} - -QCalendarWidget QAbstractItemView:disabled { - color: rgb(122,121,122); -} - -/* VirtualCam Plugin Fixes */ - -#VirtualProperties QWidget { - margin-top: 0; - margin-bottom: 0; -} - -/* Disable icons on QDialogButtonBox */ -QDialogButtonBox { - dialogbuttonbox-buttons-have-icons: 0; -} - -/* Stats dialog */ -OBSBasicStats { - background: palette(dark); -} - -/* Advanced audio dialog */ -OBSBasicAdvAudio #scrollAreaWidgetContents { - background: palette(dark); -} diff --git a/UI/data/themes/System.qss b/UI/data/themes/System.obt similarity index 96% rename from UI/data/themes/System.qss rename to UI/data/themes/System.obt index 7b361ada63f1df..6e34569ebeee49 100644 --- a/UI/data/themes/System.qss +++ b/UI/data/themes/System.obt @@ -8,8 +8,10 @@ /* Dark Theme is a good place to start if you need */ /* a template. */ -OBSThemeMeta { - dark: 'false'; +@OBSThemeMeta { + name: 'System'; + id: 'com.obsproject.System'; + author: 'Warchamp7'; } /* We need to set back the icons, or the preview wont stick. */ @@ -211,31 +213,31 @@ OBSBasicSettings QListWidget::item { /* Locked CheckBox */ -QCheckBox[lockCheckBox=true] { +LockedCheckBox { outline: none; background: transparent; } -QCheckBox[lockCheckBox=true]::indicator:checked { +LockedCheckBox::indicator:checked { image: url(:res/images/locked.svg); } -QCheckBox[lockCheckBox=true]::indicator:unchecked { +LockedCheckBox::indicator:unchecked { image: url(:res/images/unlocked.svg); } /* Visibility CheckBox */ -QCheckBox[visibilityCheckBox=true] { +VisibilityCheckBox { outline: none; background: transparent; } -QCheckBox[visibilityCheckBox=true]::indicator:checked { +VisibilityCheckBox::indicator:checked { image: url(:res/images/visible.svg); } -QCheckBox[visibilityCheckBox=true]::indicator:unchecked { +VisibilityCheckBox::indicator:unchecked { image: url(:res/images/invisible.svg); } diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt new file mode 100644 index 00000000000000..642cb203fd7c4f --- /dev/null +++ b/UI/data/themes/Yami.obt @@ -0,0 +1,1842 @@ +@OBSThemeMeta { + name: 'Yami'; + id: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'true'; +} + +@OBSThemeVars { + /* OBS Color Palette */ + --blue1: #718CDC; + --blue2: #476BD7; + --blue3: #284CB8; + --blue4: #213E97; + --blue5: #1A3278; + + --red1: #E85E75; + --red2: #E33B57; + --red3: #C01C37; + --red4: #A1172E; + --red5: #7D1224; + + --pink1: #E5619A; + --pink2: #E03E84; + --pink3: #C11F65; + --pink4: #9E1A53; + --pink5: #7B1441; + + --teal1: #3DBEF5; + --teal2: #16B1F3; + --teal3: #0981B4; + --teal4: #086F9B; + --teal5: #065374; + + --purple1: #997FDC; + --purple2: #805FD3; + --purple3: #5B34BF; + --purple4: #4D2CA0; + --purple5: #3D2380; + + --green1: #59D966; + --green2: #37D247; + --green3: #25A231; + --green4: #1E8528; + --green5: #17641E; + + --yellow1: #EABC48; + --yellow2: #E5AF24; + --yellow3: #B88A16; + --yellow4: #926E11; + --yellow5: #6E520D; + + --grey1: #5B6273; + --grey2: #4E5566; + --grey3: #464B59; + --grey4: #3C404D; + --grey5: #323540; + --grey6: #272A33; + --grey7: #1D1F26; + --grey8: #13141A; + + --white1: #FFFFFF; + --white2: #EBEBEB; + --white3: #D6D6D6; + --white4: #C2C2C2; + --white5: #ADADAD; + + --black1: #0A0A0A; + --black2: #1F1F1F; + --black3: #333333; + --black4: #474747; + --black5: #5C5C5C; + + /* Base Theme Colors */ + --bg_window: var(--grey7); + --bg_base: var(--grey6); + --bg_preview: var(--grey8); + + --primary: var(--blue3); + --primary_light: var(--blue2); + --primary_lighter: var(--blue1); + --primary_dark: var(--blue4); + --primary_darker: var(--blue5); + + --warning: var(--yellow3); + --danger: var(--red3); + + --text: var(--white1); + --text_light: rgb(214,214,214); + --text_muted: rgb(153,153,153); + + --text_disabled: var(--text_muted); + --text_inactive: rgb(255,254,255); + + /* Layout */ + /* Configurable Values */ + --font_base_value: 10; // TODO: Min 8, Max 12, Step 1 + --spacing_base_value: 4; // TODO: Min 2, Max 7, Step 1 + --padding_base_value: 4; // TODO: Min 0.25, Max 10, Step 2 + + --border_highlight: "transparent"; // TODO: Better Accessibility focus state + // TODO: Move Accessibilty Colors to Theme config system + + --font_base: calc(1pt * var(--font_base_value)); + --font_small: calc(0.9pt * var(--font_base_value)); + --font_large: calc(1.1pt * var(--font_base_value)); + --font_xlarge: calc(1.5pt * var(--font_base_value)); + + --font_heading: calc(2.5pt * var(--font_base_value)); + + --icon_base: calc(6px + var(--font_base_value)); + + --spacing_base: calc(0.5px * var(--spacing_base_value)); + --spacing_large: calc(1px * var(--spacing_base_value)); + --spacing_small: calc(0.25px * var(--spacing_base_value)); + --spacing_title: 4px; + + --padding_base: calc(0.5px * var(--padding_base_value)); + --padding_large: calc(1px * var(--padding_base_value)); + --padding_xlarge: calc(1.75px * var(--padding_base_value)); + --padding_small: calc(0.25px * var(--padding_base_value)); + + --padding_wide: calc(8px + calc(2 * var(--padding_base_value))); + --padding_menu: calc(4px + calc(2 * var(--padding_base_value))); + + --padding_base_border: calc(var(--padding_base) + 1px); + + --spinbox_button_height: calc(var(--input_height) - 2px); + + --scrollbar_size: 12px; + + /* Inputs / Controls */ + --border_color: var(--grey4); + + --border_radius: 4px; + --border_radius_small: 2px; + --border_radius_large: 6px; + + --input_font_scale: calc(var(--font_base_value) * 2.2); + --input_font_padding: calc(var(--padding_base_value) * 2); + + --input_height_base: calc(var(--input_font_scale) + var(--input_font_padding)); + --input_padding: var(--padding_large); + --input_height: calc(var(--input_height_base) - calc(var(--input_padding) * 2)); + --input_height_half: calc(var(--input_height_base) / 2); + + --input_bg: var(--grey4); + --input_bg_hover: var(--grey7); + --input_bg_focus: var(--grey7); + + --list_item_bg_selected: var(--primary); + --list_item_bg_hover: var(--primary_light); + + --input_border: var(--grey1); + --input_border_hover: var(--grey1); + --input_border_focus: var(--primary); + + --spacing_input: var(--spacing_base); + + --button_bg: var(--input_bg); + --button_bg_hover: var(--grey3); + --button_bg_down: var(--grey7); + --button_bg_disabled: var(--grey6); + + --button_border: var(--button_bg); + --button_border_hover: var(--grey1); + --button_border_focus: var(--grey1); + + --tab_bg: var(--input_bg); + --tab_bg_hover: var(--grey3); + --tab_bg_down: var(--grey7); + --tab_bg_disabled: var(--grey6); + + --tab_border: var(--grey1); + --tab_border_hover: var(--grey1); + --tab_border_focus: var(--grey1); + --tab_border_selected: var(--primary); + + --scrollbar: var(--grey4); + --scrollbar_hover: var(--grey3); + --scrollbar_down: var(--grey8); + --scrollbar_border: var(--grey2); + + --separator_hover: var(--white1); + + --highlight: rgb(42,130,218); + --highlight_inactive: rgb(25,28,34); + + /* Qt Palette variables can be set with the "palette_" prefix */ + --palette_window: var(--bg_window); + --palette_windowText: var(--text); + --palette_base: var(--bg_base); + + --palette_light: var(--grey2); + --palette_mid: var(--grey7); + --palette_dark: var(--grey6); + + --palette_highlight: var(--primary); + --palette_highlightedText: var(--text); + + --palette_text: var(--text); + --palette_link: var(--blue2); + --palette_linkVisited: var(--blue2); + + --palette_button: var(--button_bg); + --palette_buttonText: var(--text); + + /* They can be selectively set for palette groups by appending those as well */ + --palette_text_active: var(--text); + --palette_text_disabled: var(--text_disabled); + --palette_text_inactive: var(--text_inactive); + + /* + * Variables calculated at runtime (after all themes have been composed). + * + * Support standard add, sub, mul, div operations. + * Also supports nested calls (but keep it reasonable). + * + * Note: When using two operands that have a type (e.g. "px") the type must match! + * If only one operand has a type it'll be used for the result. + * Note 2: Cannot be !editable + * Note 3: Operands and operator MUST be separated by whitespace + */ +} + +/* Default widget style, we override only what is needed. */ + +QWidget { + alternate-background-color: var(--bg_base); + color: var(--text); + selection-background-color: var(--primary); + selection-color: var(--text); + font-size: var(--font_base); + font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif; +} + +QWidget:disabled { + color: var(--text_disabled); +} + +/* Container windows */ + +QDialog, +QMainWindow, +QStatusBar, +QMenuBar, +QMenu { + background-color: var(--bg_window); +} + +/* macOS Separator Fix */ + +QMainWindow::separator { + background: transparent; + width: var(--spacing_large); + height: var(--spacing_large); + margin: 0px; +} + +QMainWindow::separator:hover { + border: 1px solid var(--separator_hover); + margin: 1px; +} + +/* General Widgets */ + +QLabel, +QGroupBox, +QCheckBox { + background: transparent; +} + +QComboBox, +QCheckBox, +QPushButton, +QSpinBox, +QDoubleSpinBox { + margin-top: var(--spacing_input); + margin-bottom: var(--spacing_input); +} + +QListWidget QWidget, +SceneTree QWidget, +SourceTree QWidget { + margin-top: 0; + margin-bottom: 0; +} + +* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { + border: 1px solid var(--bg_base); +} + + +/* Misc */ + +QAbstractItemView, +QStackedWidget#stackedMixerArea QWidget { + background-color: var(--bg_base); +} + +QStackedWidget#stackedMixerArea QScrollBar { + background-color: var(--grey6); +} + +QToolTip { + background-color: var(--bg_base); + color: var(--text); + border: none; +} + +/* Context Menu */ + +QMenu::icon { + left: 4px; +} + +QMenu::separator { + background: var(--button_bg); + height: 1px; + margin: var(--spacing_base) var(--spacing_large); +} + +QMenu::item:disabled { + color: var(--text_disabled); + background: transparent; +} + +QMenu::right-arrow { + image: url(theme:Dark/expand.svg); +} + +/* Top Menu Bar Items */ +QMenuBar::item { + background-color: transparent; +} + +QMenuBar::item:selected { + background: var(--primary); +} + +/* Item Lists */ +QListWidget { + border-radius: var(--border_radius); +} + +QListWidget::item { + color: var(--text); +} + +QListWidget, +QMenu, +SceneTree, +SourceTree { + padding: var(--spacing_base); +} + +QListWidget::item, +SourceTreeItem, +SceneTree::item { + padding: var(--padding_large) var(--padding_large); +} + +QMenu::item { + padding: var(--padding_large) var(--padding_menu); +} + +QMenu::item { + padding-right: 20px; +} + +QListWidget::item, +SourceTreeItem, +QMenu::item, +SceneTree::item { + border-radius: var(--border_radius); + color: var(--text); + border: 1px solid transparent; +} + +SourceTree::item { + border-radius: var(--border_radius); + color: var(--text); +} + +QMenu::item:selected, +QListWidget::item:selected, +SceneTree::item:selected, +SourceTree::item:selected { + background-color: var(--primary); +} + +QMenu::item:hover, +QListWidget::item:hover, +SceneTree::item:hover, +SourceTree::item:hover, +QMenu::item:selected:hover, +QListWidget::item:selected:hover, +SceneTree::item:selected:hover, +SourceTree::item:selected:hover { + background-color: var(--primary_light); + color: var(--text); +} + +QMenu::item:focus, +QListWidget::item:focus, +SceneTree::item:focus, +SourceTree::item:focus, +QMenu::item:selected:focus, +QListWidget::item:selected:focus, +SceneTree::item:selected:focus, +SourceTree::item:selected:focus { + border: 1px solid var(--border_highlight); +} + +QListWidget::item:disabled, +QListWidget::item:disabled:hover, +SourceTree::item:disabled, +SourceTree::item:disabled:hover, +SceneTree::item:disabled, +SceneTree::item:disabled:hover { + background: transparent; + color: var(--text_disabled); +} + +QListWidget QLineEdit, +SceneTree QLineEdit, +SourceTree QLineEdit { + padding: 0; + padding-bottom: 1px; + margin: 0; + border: 1px solid var(--white1); + border-radius: var(--border_radius); +} + +QListWidget QLineEdit:focus, +SceneTree QLineEdit:focus, +SourceTree QLineEdit:focus { + border: 1px solid var(--grey1); +} + +/* Settings QList */ + +OBSBasicSettings QListWidget { + border-radius: var(--border_radius); + padding: var(--spacing_base); +} + +OBSBasicSettings QListWidget::item { + border-radius: var(--border_radius); + padding: var(--padding_large); +} + +/* Settings properties view */ +OBSBasicSettings #PropertiesContainer { + background-color: var(--bg_base); +} + +/* Dock Widget */ +OBSDock > QWidget { + background: var(--bg_base); + border-bottom-left-radius: var(--border_radius); + border-bottom-right-radius: var(--border_radius); + border: 1px solid var(--border_color); + border-top: none; +} + +#transitionsFrame { + padding: 4px 8px; +} + +OBSDock QLabel { + background: transparent; +} + +QDockWidget { + font-size: var(--font_base); + font-weight: bold; + + titlebar-close-icon: url(theme:Dark/close.svg); + titlebar-normal-icon: url(theme:Dark/popout.svg); +} + +QDockWidget::title { + text-align: left; + background-color: var(--button_bg); + padding: var(--padding_large); + border-top-left-radius: var(--border_radius); + border-top-right-radius: var(--border_radius); +} + +QDockWidget::close-button, +QDockWidget::float-button { + border: none; + border-radius: var(--border_radius); + background: transparent; + margin-right: 1px; +} + +QDockWidget::close-button:hover, +QDockWidget::float-button:hover { + background: var(--button_bg_hover); +} + +QDockWidget::close-button:pressed, +QDockWidget::float-button:pressed { + padding: 1px -1px -1px 1px; +} + +QScrollArea { + border-radius: var(--border_radius); +} + +/* Qt enforces a padding inside its status bar, so we + * oversize it and use margin to crunch it back down + */ +OBSBasicStatusBar { + margin-top: 4px; + border-top: 1px solid var(--border_color); + background: var(--bg_base); +} + +StatusBarWidget > QFrame { + margin-top: 1px; + border: 0px solid var(--border_color); + border-left-width: 1px; + padding: 0px 8px 2px; +} + +/* Group Box */ + +QGroupBox { + background: var(--bg_base); + border-radius: var(--border_radius); + padding-top: var(--input_height_base); + padding-bottom: var(--padding_large); + font-weight: bold; + margin-bottom: var(--spacing_large); +} + +QGroupBox::title { + subcontrol-origin: margin; + left: var(--spacing_title); + top: var(--spacing_title); +} + + +/* ScrollBars */ + +QScrollBar { + background-color: var(--grey6); + margin: 0px; + border-radius: var(--border_radius); +} + +::corner { + background-color: var(--bg_window); + border: none; +} + +QScrollBar:vertical { + width: var(--scrollbar_size); +} + +QScrollBar::add-line:vertical, +QScrollBar::sub-line:vertical { + border: none; + background: none; + height: 0px; +} + +QScrollBar::up-arrow:vertical, +QScrollBar::down-arrow:vertical, +QScrollBar::add-page:vertical, +QScrollBar::sub-page:vertical { + border: none; + background: none; + color: none; +} + +QScrollBar:horizontal { + height: var(--scrollbar_size); +} + +QScrollBar::add-line:horizontal, +QScrollBar::sub-line:horizontal { + border: none; + background: none; + width: 0px; +} + +QScrollBar::left-arrow:horizontal, +QScrollBar::right-arrow:horizontal, +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal { + border: none; + background: none; + color: none; +} + +QScrollBar::handle { + background-color: var(--scrollbar); + margin: 2px; + border-radius: var(--border_radius_small); + border: 1px solid var(--scrollbar); +} + +QScrollBar::handle:hover { + background-color: var(--scrollbar_hover); + border-color: var(--scrollbar_border); +} + +QScrollBar::handle:pressed { + background-color: var(--scrollbar_down); + border-color: var(--scrollbar_down); +} + +QScrollBar::handle:vertical { + min-height: 32px; +} + +QScrollBar::handle:horizontal { + min-width: 32px; +} + +/* Source Context Bar */ + +#contextContainer { + background-color: var(--bg_base); + margin-top: 4px; + border-radius: var(--border_radius); +} + +#contextContainer QPushButton { + padding-left: 12px; + padding-right: 12px; +} + +QPushButton#sourcePropertiesButton { + qproperty-icon: url(theme:Dark/settings/general.svg); + icon-size: var(--icon_base), var(--icon_base); +} + +QPushButton#sourceFiltersButton { + qproperty-icon: url(theme:Dark/filter.svg); + icon-size: var(--icon_base), var(--icon_base); +} + +/* Scenes and Sources toolbar */ + +QToolBar { + background-color: transparent; + border: none; + margin: var(--spacing_base) 0px; +} + +* [themeID="addIconSmall"] { + qproperty-icon: url(theme:Dark/plus.svg); +} + +* [themeID="removeIconSmall"] { + qproperty-icon: url(theme:Dark/trash.svg); +} + +* [themeID="clearIconSmall"] { + qproperty-icon: url(theme:Dark/entry-clear.svg); +} + +* [themeID="propertiesIconSmall"] { + qproperty-icon: url(theme:Dark/settings/general.svg); +} + +* [themeID="configIconSmall"] { + qproperty-icon: url(theme:Dark/settings/general.svg); +} + +* [themeID="menuIconSmall"] { + qproperty-icon: url(theme:Dark/dots-vert.svg); +} + +* [themeID="refreshIconSmall"] { + qproperty-icon: url(theme:Dark/refresh.svg); +} + +* [themeID="cogsIcon"] { + qproperty-icon: url(theme:Dark/cogs.svg); +} + +#sourceInteractButton { + qproperty-icon: url(theme:Dark/interact.svg); +} + +* [themeID="upArrowIconSmall"] { + qproperty-icon: url(theme:Dark/up.svg); +} + +* [themeID="downArrowIconSmall"] { + qproperty-icon: url(theme:Dark/down.svg); +} + +* [themeID="pauseIconSmall"] { + qproperty-icon: url(theme:Dark/media-pause.svg); +} + +* [themeID="filtersIcon"] { + qproperty-icon: url(theme:Dark/filter.svg); +} + +QToolBarExtension { + background: var(--button_bg); + min-width: 12px; + max-width: 12px; + padding: 4px 0px; + margin-left: 0px; + + qproperty-icon: url(theme:Dark/dots-vert.svg); +} + + +/* Tab Widget */ + +QTabWidget::pane { /* The tab widget frame */ + border-top: 4px solid var(--bg_base); +} + +QTabWidget::tab-bar { + alignment: left; +} + +QTabBar QToolButton { + background: var(--button_bg); + border: none; +} + +QTabBar::tab:top { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +QTabBar::tab:bottom { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +QTabBar::tab { + background: var(--tab_bg); + color: var(--text); + border: none; + padding: 8px 12px; + min-width: 50px; + margin: 1px 0px; + margin-right: 2px; + border: 1px solid var(--tab_border); +} + +QTabBar::tab:pressed { + background: var(--tab_bg_down); +} + +QTabBar::tab:hover { + background: var(--tab_bg_hover); + border-color: var(--tab_border_hover); + color: var(--text); +} + +QTabBar::tab:focus { + border-color: var(--tab_border_focus); +} + +QTabBar::tab:selected { + background: var(--tab_bg_down); + color: var(--text); +} + +QTabBar::tab:top { +} + +QTabBar::tab:top:selected { + border-bottom: 2px solid var(--tab_border_selected); +} + +QTabBar::tab:bottom { + +} + +QTabBar::tab:bottom:selected { + border-top: 2px solid var(--tab_border_selected); +} + +QTabBar QToolButton { + background: var(--button_bg); + min-width: 16px; + padding: 0px; +} + +/* ComboBox */ + +QComboBox, +QDateTimeEdit { + background-color: var(--input_bg); + border-style: solid; + border: 1px solid var(--input_bg); + border-radius: var(--border_radius); + padding: var(--padding_large) var(--padding_large); + padding-left: 10px; + max-height: var(--input_height); +} + +QComboBox QAbstractItemView::item:selected, +QComboBox QAbstractItemView::item:hover { + background-color: var(--list_item_bg_selected); +} + +QComboBox:hover, +QComboBox:focus, +QDateTimeEdit:hover, +QDateTimeEdit:selected { + border-color: var(--input_border_hover); +} + +QComboBox::drop-down, +QDateTimeEdit::drop-down { + border:none; + border-left: 1px solid var(--grey6); + width: 32px; +} + +QComboBox::down-arrow, +QDateTimeEdit::down-arrow { + qproperty-alignment: AlignTop; + image: url(theme:Dark/collapse.svg); + width: 100%; +} + +QComboBox:editable:hover { + background-color: var(--input_bg_hover); + border-color: var(--input_border_hover); +} + +QComboBox:on, +QDateTimeEdit:on, +QComboBox:editable:focus { + background-color: var(--input_bg_focus); + border-color: var(--input_border_focus); +} + +QComboBox::drop-down:editable, +QDateTimeEdit::drop-down:editable { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +QComboBox::down-arrow:editable, +QDateTimeEdit::down-arrow:editable { + qproperty-alignment: AlignTop; + image: url(theme:Dark/collapse.svg); + width: 100%; +} + +/* Textedits etc */ + +QLineEdit, +QTextEdit, +QPlainTextEdit { + background-color: var(--input_bg); + border: none; + border-radius: var(--border_radius); + padding: var(--input_padding) var(--padding_small) var(--input_padding) var(--input_padding); + border: 1px solid var(--input_bg); + height: var(--input_height); +} + +QLineEdit:hover, +QTextEdit:hover, +QPlainTextEdit:hover { + background-color: var(--input_bg_hover); + border-color: var(--input_border_hover); +} + +QLineEdit:focus, +QTextEdit:focus, +QPlainTextEdit:focus { + background-color: var(--input_bg_focus); + border-color: var(--input_border_focus); +} + +QTextEdit:!editable { + background-color: var(--input_bg); +} + +/* Spinbox and doubleSpinbox */ + +QSpinBox, +QDoubleSpinBox { + background-color: var(--input_bg); + border: 1px solid var(--input_bg); + border-radius: var(--border_radius); + margin-right: var(--spacing_base); + padding: var(--input_padding) 0px var(--input_padding) var(--input_padding); + height: var(--spinbox_button_height); + max-height: var(--spinbox_button_height); +} + +QSpinBox:hover, +QDoubleSpinBox:hover { + background-color: var(--input_bg_hover); + border-color: var(--input_border_hover); +} + +QSpinBox:focus, +QDoubleSpinBox:focus { + background-color: var(--input_bg_focus); + border-color: var(--input_border_focus); +} + +QSpinBox::up-button, +QDoubleSpinBox::up-button { + subcontrol-origin: padding; + subcontrol-position: top right; /* position at the top right corner */ + + width: 32px; + border-left: 1px solid var(--grey6); + border-bottom: 1px solid transparent; + border-radius: 0px; + margin-top: -1px; +} + +QSpinBox::down-button, +QDoubleSpinBox::down-button { + subcontrol-origin: padding; + subcontrol-position: bottom right; /* position at the top right corner */ + + width: 32px; + border-left: 1px solid var(--grey6); + border-top: 1px solid var(--grey6); + border-radius: 0px; + margin-bottom: -1px; +} + +QSpinBox::up-button:hover, +QSpinBox::down-button:hover, +QDoubleSpinBox::up-button:hover, +QDoubleSpinBox::down-button:hover { + background-color: var(--button_bg_hover); +} + +QSpinBox::up-button:pressed, +QSpinBox::down-button:pressed, +QDoubleSpinBox::up-button:pressed, +QDoubleSpinBox::down-button:pressed { + background-color: var(--button_bg_down); +} + +QSpinBox::up-button:disabled, +QSpinBox::up-button:off, +QSpinBox::down-button:disabled, +QSpinBox::down-button:off { + background-color: var(--button_bg_disabled); +} + +QDoubleSpinBox::up-button:disabled, +QDoubleSpinBox::up-button:off, +QDoubleSpinBox::down-button:disabled, +QDoubleSpinBox::down-button:off { + background-color: var(--button_bg_disabled); +} + +QSpinBox::up-arrow, +QDoubleSpinBox::up-arrow { + image: url(theme:Dark/up.svg); + width: 100%; + margin: 2px; +} + +QSpinBox::down-arrow, +QDoubleSpinBox::down-arrow { + image: url(theme:Dark/down.svg); + width: 100%; + padding: 2px; +} + +/* Controls Dock */ +#controlsFrame { + padding: var(--padding_large); +} + +#controlsFrame QPushButton { + margin: var(--spacing_base) var(--spacing_small); +} + +#streamButton, +#recordButton, +QPushButton[themeID="replayBufferButton"], +#broadcastButton { + padding: var(--padding_large); +} + +/* Primary Control Button Checked Coloring */ +#streamButton:!hover:!pressed:checked, +#recordButton:!hover:!pressed:checked, +QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, +QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, +#modeSwitch:!hover:!pressed:checked, +#broadcastButton:!hover:!pressed:checked { + background: var(--primary); +} + +/* Primary Control Button Hover Coloring */ +#streamButton:hover:!pressed:checked, +#recordButton:hover:!pressed:checked, +QPushButton[themeID="replayBufferButton"]:!pressed:checked, +QPushButton[themeID="vcamButton"]:!pressed:checked, +#modeSwitch:hover:!pressed:checked, +#broadcastButton:hover:!pressed:checked { + background: var(--primary_light); + color: var(--text); +} + + +/* Buttons */ + +QPushButton { + color: var(--text); + background-color: var(--button_bg); + border-radius: var(--border_radius); + height: var(--input_height); + max-height: var(--input_height); + padding: var(--input_padding) var(--padding_wide); + icon-size: var(--icon_base), var(--icon_base); +} + +QPushButton { + border: 1px solid var(--button_border); +} + +QToolButton { + border: 1px solid var(--button_border); +} + +QToolButton, +QPushButton[toolButton="true"] { + background-color: var(--button_bg); + padding: var(--padding_base) var(--padding_base); + margin: 0px var(--spacing_base); + border: 1px solid var(--button_border); + border-radius: var(--border_radius); + icon-size: var(--icon_base), var(--icon_base); +} + +QToolButton:last-child, +QPushButton[toolButton="true"]:last-child { + margin-right: 0px; +} + +QPushButton:hover, +QPushButton:focus { + border-color: var(--button_border_hover); +} + +QPushButton:hover { + background-color: var(--button_bg_hover); +} + +QToolButton:hover, +QToolButton:focus, +MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:focus { + border-color: var(--button_border); + background-color: var(--button_bg_hover); +} + +QPushButton::flat { + background-color: var(--button_bg); +} + +QPushButton:checked { + background-color: var(--primary); +} + +QPushButton:checked:hover, +QPushButton:checked:focus { + border-color: var(--primary_lighter); +} + +QPushButton:pressed, +QPushButton:pressed:hover { + background-color: var(--button_bg_down); + border-color: var(--button_border); +} + +QToolButton:pressed, +QToolButton:pressed:hover { + background-color: var(--button_bg_down); + border-color: var(--button_border); +} + +QPushButton:disabled { + background-color: var(--button_bg_disabled); + border-color: var(--button_border); +} + +QToolButton:disabled, +QPushButton[toolButton="true"]:disabled { + background-color: var(--button_bg_disabled); + border-color: transparent; +} + +QPushButton::menu-indicator { + image: url(theme:Dark/down.svg); + subcontrol-position: right; + subcontrol-origin: padding; + width: 25px; +} + +/* Sliders */ + +QSlider::groove { + background-color: var(--grey4); + border: none; + border-radius: 2px; +} + +QSlider::groove:horizontal { + height: 4px; +} + +QSlider::groove:vertical { + width: 4px; +} + +QSlider::sub-page:horizontal { + background-color: var(--blue2); + border-radius: 2px; +} + +QSlider::sub-page:horizontal:disabled { + background-color: var(--grey4); + border-radius: 2px; +} + +QSlider::add-page:horizontal:disabled { + background-color: var(--grey7); + border-radius: 2px; +} + +QSlider::add-page:vertical { + background-color: var(--blue2); + border-radius: 2px; +} + +QSlider::add-page:vertical:disabled { + background-color: var(--grey4); + border-radius: 2px; +} + +QSlider::sub-page:vertical:disabled { + background-color: var(--grey7); + border-radius: 2px; +} + +QSlider::handle { + background-color: var(--white1); + border-radius: var(--border_radius); +} + +QSlider::handle:horizontal { + height: 10px; + width: 20px; + margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ +} + +QSlider::handle:vertical { + width: 10px; + height: 20px; + margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ +} + +QSlider::handle:hover { + background-color: var(--white4); +} + +QSlider::handle:pressed { + background-color: var(--white5); +} + +QSlider::handle:disabled { + background-color: var(--white5); +} + +/* Volume Control */ + +#stackedMixerArea QPushButton { + background-color: var(--button_bg); + min-width: var(--icon_base); + padding: var(--padding_small) var(--padding_base); + margin: 0px var(--spacing_base); + border: 1px solid var(--button_border); + border-radius: var(--border_radius); + icon-size: var(--icon_base), var(--icon_base); +} + +/* This is an incredibly cursed but necessary fix */ +#stackedMixerArea QPushButton:!hover { + background-color: var(--button_bg); +} + +#stackedMixerArea QPushButton:hover { + background-color: var(--button_bg_hover); + border-color: var(--button_border_hover); +} + +#stackedMixerArea QPushButton:pressed { + background-color: var(--button_bg_down); +} + +VolumeMeter { + qproperty-backgroundNominalColor: var(--green5); + qproperty-backgroundWarningColor: var(--yellow5); + qproperty-backgroundErrorColor: var(--red5); + qproperty-foregroundNominalColor: var(--green2); + qproperty-foregroundWarningColor: var(--yellow2); + qproperty-foregroundErrorColor: var(--red2); + qproperty-magnitudeColor: rgb(0,0,0); + qproperty-majorTickColor: var(--white1); + qproperty-minorTickColor: var(--grey1); +} + +/* Status Bar */ + +QStatusBar::item { + border: none; +} + +/* Table View */ + +QTableView { + background: var(--bg_base); + gridline-color: var(--grey1); +} + +QTableView::item { + margin: 0px; + padding: 0px; +} + +QTableView QLineEdit { + background: var(--input_bg_focus); + padding: 0; + margin: 0; +} + +QTableView QPushButton, +QTableView QToolButton { + padding: 0px; + margin: -1px; + border_radius: 0px; +} + +QHeaderView::section { + background-color: var(--button_bg); + color: var(--text); + border: none; + border-left: 1px solid var(--bg_window); + border-right: 1px solid var(--bg_window); + padding: 3px 0px; + margin-bottom: 2px; +} + +OBSHotkeyLabel[hotkeyPairHover=true] { + color: var(--primary_light); +} + +/* Label warning/error */ + +QLabel#warningLabel { + color: var(--warning); + font-weight: bold; +} + +QLabel#errorLabel { + color: var(--danger); + font-weight: bold; +} + +* [themeID="warning"] { + color: var(--danger); + font-weight: bold; +} + +* [themeID="error"] { + color: var(--danger); + font-weight: bold; +} + +* [themeID="good"] { + color: var(--green3); + font-weight: bold; +} + +/* About dialog */ + +* [themeID="aboutName"] { + font-size: var(--font_heading); + font-weight: bold; +} + +* [themeID="aboutVersion"] { + font-size: var(--font_large); + margin-bottom: 20px; +} + +* [themeID="aboutInfo"] { + margin-bottom: 20px; +} + +* [themeID="aboutHLayout"] { + background-color: var(--bg_base); +} + +/* Canvas / Preview background color */ + +OBSQTDisplay { + qproperty-displayBackgroundColor: var(--bg_preview); +} + +/* Filters Window */ + +OBSBasicFilters QListWidget { + border-radius: var(--border_radius_large); + padding: var(--spacing_base); +} + +OBSBasicFilters QListWidget::item { + border-radius: var(--border_radius); + padding: var(--padding_base) var(--padding_large); +} + +OBSBasicFilters #widget, +OBSBasicFilters #widget_2 { + margin: 0px; + padding: 0px; + padding-bottom: var(--padding_base); +} + +OBSBasicFilters #widget QPushButton, +OBSBasicFilters #widget_2 QPushButton { + min-width: 16px; + padding: var(--padding_base) var(--padding_large); + margin-top: 0px; +} + +/* Preview/Program labels */ + +* [themeID="previewProgramLabels"] { + font-size: var(--font_xlarge); + font-weight: bold; + color: var(--text_light); + margin-bottom: 4px; +} + +/* Settings Icons */ + +OBSBasicSettings { + qproperty-generalIcon: url(theme:Dark/settings/general.svg); + qproperty-appearanceIcon: url(theme:Dark/settings/appearance.svg); + qproperty-streamIcon: url(theme:Dark/settings/stream.svg); + qproperty-outputIcon: url(theme:Dark/settings/output.svg); + qproperty-audioIcon: url(theme:Dark/settings/audio.svg); + qproperty-videoIcon: url(theme:Dark/settings/video.svg); + qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg); + qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg); + qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg); +} + +/* Checkboxes */ +QCheckBox { + +} + +QCheckBox::indicator, +QGroupBox::indicator { + width: var(--icon_base); + height: var(--icon_base); +} + +QGroupBox::indicator { + margin-left: 2px; +} + +QCheckBox::indicator:unchecked, +QGroupBox::indicator:unchecked { + image: url(theme:Yami/checkbox_unchecked.svg); +} + +QCheckBox::indicator:unchecked:hover, +QGroupBox::indicator:unchecked:hover { + border: none; + image: url(theme:Yami/checkbox_unchecked_focus.svg); +} + +QCheckBox::indicator:checked, +QGroupBox::indicator:checked { + image: url(theme:Yami/checkbox_checked.svg); +} + +QCheckBox::indicator:checked:hover, +QGroupBox::indicator:checked:hover { + image: url(theme:Yami/checkbox_checked_focus.svg); +} + +QCheckBox::indicator:checked:disabled, +QGroupBox::indicator:checked:disabled { + image: url(theme:Yami/checkbox_checked_disabled.svg); +} + +QCheckBox::indicator:unchecked:disabled, +QGroupBox::indicator:unchecked:disabled { + image: url(theme:Yami/checkbox_unchecked_disabled.svg); +} + +/* Locked CheckBox */ + +QCheckBox[lockCheckBox=true] { + outline: none; + background: transparent; +} + +QCheckBox[lockCheckBox=true]::indicator { + width: var(--icon_base); + height: var(--icon_base); + padding: 1px; + border: 1px solid transparent; + border-radius: 4px; +} + +QCheckBox[lockCheckBox=true]::indicator:checked, +QCheckBox[lockCheckBox=true]::indicator:checked:hover { + image: url(theme:Dark/locked.svg); +} + +QCheckBox[lockCheckBox=true]::indicator:unchecked, +QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { + image: url(:res/images/unlocked.svg); +} + +QCheckBox[lockCheckBox=true]::indicator:hover, +QCheckBox[lockCheckBox=true]::indicator:focus { + border: 1px solid var(--border_highlight); +} + +/* Visibility CheckBox */ + +QCheckBox[visibilityCheckBox=true] { + outline: none; + background: transparent; +} + +QCheckBox[visibilityCheckBox=true]::indicator { + width: var(--icon_base); + height: var(--icon_base); + padding: 1px; + border: 1px solid transparent; + border-radius: 4px; +} + +QCheckBox[visibilityCheckBox=true]::indicator:checked, +QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { + image: url(theme:Dark/visible.svg); +} + +QCheckBox[visibilityCheckBox=true]::indicator:unchecked, +QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { + image: url(:res/images/invisible.svg); +} + +QCheckBox[visibilityCheckBox=true]::indicator:hover, +QCheckBox[visibilityCheckBox=true]::indicator:focus { + border: 1px solid var(--border_highlight); +} + +* [themeID="revertIcon"] { + qproperty-icon: url(theme:Dark/revert.svg); +} + +/* Mute CheckBox */ + +MuteCheckBox { + outline: none; +} + +MuteCheckBox::indicator, +MuteCheckBox::indicator:unchecked { + width: var(--icon_base); + height: var(--icon_base); + background-color: var(--button_bg); + padding: var(--padding_base_border) var(--padding_base_border); + margin: 0px var(--spacing_base); + border: 1px solid var(--button_border); + border-radius: var(--border_radius); + icon-size: var(--icon_base), var(--icon_base); +} + +MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:unchecked:hover { + background-color: var(--button_bg_hover); + padding: var(--padding_base_border) var(--padding_base_border); + margin: 0px var(--spacing_base); + border: 1px solid var(--button_border_hover); + icon-size: var(--icon_base), var(--icon_base); +} + +MuteCheckBox::indicator:pressed, +MuteCheckBox::indicator:pressed:hover { + background-color: var(--button_bg_down); + border-color: var(--button_border); +} + +MuteCheckBox::indicator:checked { + image: url(theme:Dark/mute.svg); +} + +MuteCheckBox::indicator:unchecked { + image: url(theme:Dark/settings/audio.svg); +} + +MuteCheckBox::indicator:unchecked:hover { + image: url(theme:Dark/settings/audio.svg); +} + +MuteCheckBox::indicator:unchecked:focus { + image: url(theme:Dark/settings/audio.svg); +} + +MuteCheckBox::indicator:checked:hover { + image: url(theme:Dark/mute.svg); +} + +MuteCheckBox::indicator:checked:focus { + image: url(theme:Dark/mute.svg); +} + +MuteCheckBox::indicator:checked:disabled { + image: url(theme:Dark/mute.svg); +} + +MuteCheckBox::indicator:unchecked:disabled { + image: url(theme:Dark/settings/audio.svg); +} + +#hotkeyFilterReset { + margin-top: 0px; +} + +OBSHotkeyWidget { + padding: 8px 0px; + margin: 2px 0px; +} + +OBSHotkeyLabel { + padding: 4px 0px; +} + +OBSHotkeyLabel[hotkeyPairHover=true] { + color: var(--blue2); +} + +OBSHotkeyWidget QPushButton { + min-width: 16px; + padding: var(--padding_base); + margin-top: 0px; + margin-left: var(--spacing_base); +} + + +/* Sources List Group Collapse Checkbox */ + +QCheckBox[sourceTreeSubItem=true] { + background: transparent; + outline: none; + padding: 1px; + min-width: var(--icon_base); + min-height: var(--icon_base); +} + +QCheckBox[sourceTreeSubItem=true]::indicator { + width: var(--icon_base); + height: var(--icon_base); + padding: 0px; + border: 1px solid transparent; + border-radius: 4px; + margin-left: -1px; +} + +QCheckBox[sourceTreeSubItem=true]::indicator:checked, +QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { + image: url(theme:Dark/expand.svg); +} + +QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, +QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { + image: url(theme:Dark/collapse.svg); +} + +QCheckBox[sourceTreeSubItem=true]::indicator:hover, +QCheckBox[sourceTreeSubItem=true]::indicator:focus { + border: 1px solid var(--border_highlight); +} + +/* Source Icons */ + +OBSBasic { + qproperty-imageIcon: url(theme:Dark/sources/image.svg); + qproperty-colorIcon: url(theme:Dark/sources/brush.svg); + qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg); + qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg); + qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg); + qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg); + qproperty-windowCapIcon: url(theme:Dark/sources/window.svg); + qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg); + qproperty-cameraIcon: url(theme:Dark/sources/camera.svg); + qproperty-textIcon: url(theme:Dark/sources/text.svg); + qproperty-mediaIcon: url(theme:Dark/sources/media.svg); + qproperty-browserIcon: url(theme:Dark/sources/globe.svg); + qproperty-groupIcon: url(theme:Dark/sources/group.svg); + qproperty-sceneIcon: url(theme:Dark/sources/scene.svg); + qproperty-defaultIcon: url(theme:Dark/sources/default.svg); + qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg); +} + +/* Scene Tree Grid Mode */ + +SceneTree { + qproperty-gridItemWidth: 154; + qproperty-gridItemHeight: var(--input_height_base); +} + +*[gridMode="true"] SceneTree::item { + color: var(--text); + background-color: var(--button_bg); + border-radius: var(--border_radius); + margin: var(--spacing_base); +} + +*[gridMode="true"] SceneTree::item:selected { + background-color: var(--list_item_bg_selected); +} + +*[gridMode="true"] SceneTree::item:checked { + background-color: var(--primary); +} + +*[gridMode="true"] SceneTree::item:hover { + background-color: var(--list_item_bg_hover); +} + +*[gridMode="true"] SceneTree::item:selected:hover { + background-color: var(--list_item_bg_hover); +} + +/* Save icon */ + +* [themeID="replayIconSmall"] { + qproperty-icon: url(theme:Dark/save.svg); +} + +/* Studio Mode T-Bar */ + +QSlider[themeID="tBarSlider"] { + height: 24px; +} + +QSlider::groove:horizontal[themeID="tBarSlider"] { + height: 8px; +} + +QSlider::sub-page:horizontal[themeID="tBarSlider"] { + background: var(--blue2); +} + +QSlider::handle:horizontal[themeID="tBarSlider"] { + width: 12px; + height: 24px; + margin: -24px 0px; +} + +/* Media icons */ + +* [themeID="playIcon"] { + qproperty-icon: url(theme:Dark/media/media_play.svg); +} + +* [themeID="pauseIcon"] { + qproperty-icon: url(theme:Dark/media/media_pause.svg); +} + +* [themeID="restartIcon"] { + qproperty-icon: url(theme:Dark/media/media_restart.svg); +} + +* [themeID="stopIcon"] { + qproperty-icon: url(theme:Dark/media/media_stop.svg); +} + +* [themeID="nextIcon"] { + qproperty-icon: url(theme:Dark/media/media_next.svg); +} + +* [themeID="previousIcon"] { + qproperty-icon: url(theme:Dark/media/media_previous.svg); +} + +/* YouTube Integration */ +OBSYoutubeActions { + qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg); +} + +#ytEventList QLabel { + color: var(--text); + background-color: var(--button_bg); + border: none; + border-radius: var(--border_radius); + padding: 4px 20px; +} + +#ytEventList QLabel:hover { + background-color: var(--button_bg_hover); +} + +#ytEventList QLabel[isSelectedEvent=true] { + background-color: var(--primary); + border: none; +} + +#ytEventList QLabel[isSelectedEvent=true]:hover { + background-color: var(--primary_light); + color: var(--text); +} + +/* Calendar Widget */ +QDateTimeEdit::down-arrow { + qproperty-alignment: AlignTop; + image: url(theme:Dark/down.svg); + width: 100%; +} + +QDateTimeEdit:on { + background-color: var(--grey7); +} + +/* Calendar Top Bar */ +QCalendarWidget QWidget#qt_calendar_navigationbar { + background-color: var(--bg_base); + padding: var(--padding_base) var(--padding_large); +} + +/* Calendar Top Bar Buttons */ +QCalendarWidget QToolButton { + background-color: var(--button_bg); + padding: 2px 16px; + border-radius: var(--border_radius); + margin: var(--spacing_base); +} + +#qt_calendar_monthbutton::menu-indicator { + image: url(theme:Dark/down.svg); + subcontrol-position: right; + padding-top: var(--padding_small); + padding-right: var(--padding_base); + height: 10px; + width: 10px; +} + +QCalendarWidget #qt_calendar_prevmonth { + padding: var(--padding_small); + qproperty-icon: url(theme:Dark/left.svg); + icon-size: var(--icon_base), var(--icon_base); +} + +QCalendarWidget #qt_calendar_nextmonth { + padding: var(--padding_small); + qproperty-icon: url(theme:Dark/right.svg); + icon-size: var(--icon_base), var(--icon_base); +} + +QCalendarWidget QToolButton:hover { + background-color: var(--button_bg_hover); + border-radius: var(--border_radius); +} + +QCalendarWidget QToolButton:pressed { + background-color: var(--button_bg_down); +} + +/* Month Dropdown Menu */ +QCalendarWidget QMenu { + +} +/* Year spinbox */ +QCalendarWidget QSpinBox { + background-color: var(--input_bg); + border: none; + border-radius: var(--border_radius); + margin: 0px var(--spacing_base) 0px 0px; + padding: var(--padding_base) 16px; +} + +QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } +QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} +QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } +QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } + +/* Days of the Week Bar */ +QCalendarWidget QWidget { alternate-background-color: var(--grey7); } + +QCalendarWidget QAbstractItemView:enabled { + background-color: var(--bg_base); + color: var(--text); +} + +QCalendarWidget QAbstractItemView:disabled { + color: var(--text_disabled); +} + +/* VirtualCam Plugin Fixes */ + +#VirtualProperties QWidget { + margin-top: 0; + margin-bottom: 0; +} + +/* Disable icons on QDialogButtonBox */ +QDialogButtonBox { + dialogbuttonbox-buttons-have-icons: 0; +} + +/* Stats dialog */ +OBSBasicStats { + background: var(--bg_base); +} + +/* Advanced audio dialog */ +OBSBasicAdvAudio #scrollAreaWidgetContents { + background: var(--bg_base); +} diff --git a/UI/data/themes/Yami.qss b/UI/data/themes/Yami.qss deleted file mode 100644 index 7fb9c9dc713523..00000000000000 --- a/UI/data/themes/Yami.qss +++ /dev/null @@ -1,1540 +0,0 @@ -/******************************************************************************/ -/* Copyright (C) 2014-2015 by Philippe Groarke */ -/* */ -/* */ -/* This program is free software: you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation, either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program. If not, see . */ -/******************************************************************************/ - -/* Colors */ - -OBSThemeMeta { - dark: 'true'; - author: 'Warchamp7'; -} - -/* Custom theme information. This will set the application's QPalette, as - * well as pass to QML via the OBSTheme object. - * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. - * Using it without will set all three (making 'active' a bit redundant) */ -OBSTheme { - window: rgb(31,33,42); - windowText: rgb(255,254,255); - - base: rgb(43,46,56); - alternateBase: rgb(11,10,11); - - text: rgb(255,254,255); - - button: rgb(60,64,75); - buttonText: rgb(255,254,255); - - brightText: rgb(255,254,255); - - light: rgb(88,87,88); - mid: rgb(31,33,42); - dark: rgb(43,46,56); - shadow: rgb(11,10,11); - - primary: rgb(40,76,184); - primaryLight: rgb(54,92,192); - primaryDark: rgb(25,27,38); - - highlight: rgb(42,130,218); - highlightText: rgb(255,254,255); - - link: rgb(77,166,255); - linkVisited: rgb(77,166,255); -} - -OBSTheme::disabled { - windowText: rgb(153,153,153); - text: rgb(153,153,153); - button: rgb(27,29,34); - - buttonText: rgb(43,46,56); - brightText: rgb(43,46,56); -} - -OBSTheme::inactive { - text: rgb(255,254,255); - - highlight: rgb(25,28,34); - highlightText: rgb(255,255,255); -} - -/* Default widget style, we override only what is needed. */ - -QWidget { - alternate-background-color: palette(base); - color: palette(text); - selection-background-color: rgb(40,76,184); - selection-color: palette(text); - font-size: 10pt; - font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif; -} - -QWidget:disabled { - color: rgb(153,153,153); -} - -/* Container windows */ - -QDialog, -QMainWindow, -QStatusBar, -QMenuBar, -QMenu { - background-color: palette(window); -} - -/* macOS Separator Fix */ - -QMainWindow::separator { - background: transparent; - width: 4px; - height: 4px; -} - -/* General Widgets */ - -QLabel, -QGroupBox, -QCheckBox { - background: transparent; -} - -QComboBox, -QCheckBox, -QPushButton, -QSpinBox, -QDoubleSpinBox { - margin-top: 3px; - margin-bottom: 3px; -} - -QListWidget QWidget, -SceneTree QWidget, -SourceTree QWidget { - margin-top: 0; - margin-bottom: 0; -} - -* [frameShape="1"], * [frameShape="2"], * [frameShape="3"], * [frameShape="4"], * [frameShape="5"], * [frameShape="6"] { - border: 1px solid palette(dark); -} - - -/* Misc */ - -QAbstractItemView, QStackedWidget#stackedMixerArea QWidget { - background-color: palette(base); -} - -QToolTip { - background-color: palette(base); - color: palette(text); - border: none; -} - -/* Context Menu */ - -QMenu::icon { - left: 4px; -} - -QMenu::separator { - background: palette(button); - height: 1px; - margin: 3px 6px; -} - -QMenu::item:disabled { - color: rgb(153,153,153); - background: transparent; -} - -QMenu::right-arrow { - image: url(theme:Dark/expand.svg); -} - -/* Top Menu Bar Items */ -QMenuBar::item { - background-color: transparent; -} - -QMenuBar::item:selected { - background: rgb(40,76,184); -} - -/* Item Lists */ -QListWidget { - border-radius: 4px; -} - -QListWidget::item { - color: palette(text); -} - -QListWidget, -QMenu, -SceneTree, -SourceTree { - padding: 3px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item { - padding: 6px; -} - -QMenu::item { - padding-right: 20px; -} - -QListWidget::item, -SourceTreeItem, -QMenu::item, -SceneTree::item, -SourceTree::item { - border-radius: 4px; - color: palette(text); - border: 0px solid transparent; -} - -QMenu::item:selected, -QListWidget::item:selected, -SceneTree::item:selected, -SourceTree::item:selected { - background-color: rgb(40,76,184); -} - -QMenu::item:hover, -QListWidget::item:hover, -SceneTree::item:hover, -SourceTree::item:hover, -QMenu::item:selected:hover, -QListWidget::item:selected:hover, -SceneTree::item:selected:hover, -SourceTree::item:selected:hover { - background-color: rgb(54,92,192); - color: palette(text); -} - -QListWidget::item:disabled, -QListWidget::item:disabled:hover, -SourceTree::item:disabled, -SourceTree::item:disabled:hover, -SceneTree::item:disabled, -SceneTree::item:disabled:hover { - background: transparent; - color: rgb(153,153,153); -} - -QListWidget QLineEdit, -SceneTree QLineEdit, -SourceTree QLineEdit { - padding: 0px; - padding-bottom: 2px; - margin: 0px; - border: 1px solid #FFF; - border-radius: 4px; -} - -QListWidget QLineEdit:focus, -SceneTree QLineEdit:focus, -SourceTree QLineEdit:focus { - border: 1px solid #FFF; -} - -/* Settings QList */ - -OBSBasicSettings QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicSettings QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -/* Settings properties view */ -OBSBasicSettings #PropertiesContainer { - background-color: palette(dark); -} - -#PropertiesContainer QListWidget { - background-color: palette(button); -} - -/* Dock Widget */ -OBSDock > QWidget { - background: palette(dark); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -OBSDock QLabel { - background: transparent; -} - -#transitionsFrame { - padding: 4px 8px; -} - -QDockWidget { - font-size: 10.5pt; - font-weight: bold; - - titlebar-close-icon: url(theme:Dark/close.svg); - titlebar-normal-icon: url(theme:Dark/popout.svg); -} - -QDockWidget::title { - text-align: left; - background-color: palette(button); - padding: 6px 8px; - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QDockWidget::close-button, QDockWidget::float-button { - border: 0px solid transparent; - border-radius: 4px; - background: transparent; - margin-right: 1px; - opacity: .5; -} - -QDockWidget::close-button:hover, QDockWidget::float-button:hover { - background: rgb(79,83,94); - opacity: 1; -} - -QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { - padding: 1px -1px -1px 1px; -} - -QScrollArea { - border-radius: 4px; -} - -/* Qt enforces a padding inside its status bar, so we - * oversize it and use margin to crunch it back down - */ -OBSBasicStatusBar { - margin-top: 4px; - border-top: 1px solid #3c404b; - background: palette(dark); -} - -StatusBarWidget > QFrame { - margin-top: 1px; - border: 0px solid #3c404b; - border-left-width: 1px; - padding: 0px 12px 2px; -} - -/* Group Box */ - -QGroupBox { - background: palette(dark); - border-radius: 4px; - padding-top: 32px; - padding-bottom: 8px; - font-weight: bold; - margin-bottom: 6px; -} - -QGroupBox::title { - subcontrol-origin: margin; - left: 8px; - top: 8px; -} - - -/* ScrollBars */ - -::corner { - background-color: palette(window); - border: none; -} - -QScrollBar:vertical { - background-color: #1F212A; - width: 14px; - margin: 0px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - border: none; - background: none; - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical, QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - border: none; - background: none; - color: none; -} - -QScrollBar:horizontal { - background: #1F212A; - height: 14px; - margin: 0px; -} - -QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { - border: none; - background: none; - width: 0px; -} - -QScrollBar::left-arrow:horizontal, QScrollBar::right-arrow:horizontal, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { - border: none; - background: none; - color: none; -} - -QScrollBar::handle { - background-color: palette(button); - margin: 2px; - border-radius: 2px; - border: 1px solid palette(button); -} - -QScrollBar::handle:hover { - background-color: rgb(79,83,94); - border-color: rgb(79,83,94); -} - -QScrollBar::handle:pressed { - background-color: rgb(40,76,184); - border-color: rgb(40,76,184); -} - -QScrollBar::handle:vertical { - min-height: 20px; -} - -QScrollBar::handle:horizontal { - min-width: 20px; -} - -/* Source Context Bar */ - -#contextContainer { - background-color: palette(dark); - margin-top: 4px; - border-radius: 4px; -} - -#contextContainer QPushButton { - padding-left: 12px; - padding-right: 12px; -} - -QPushButton#sourcePropertiesButton { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -QPushButton#sourceFiltersButton { - qproperty-icon: url(theme:Dark/filter.svg); -} - -/* Scenes and Sources toolbar */ - -QToolBar { - background-color: palette(dark); - border: none; - padding: 0px; - margin: 4px 0px; -} - -QPushButton[toolButton="true"], -QToolButton { - background-color: rgb(60,64,75); - padding: 4px 6px; - margin: 0px 2px; - border-radius: 4px; -} - -QPushButton[toolButton="true"]:last-child, -QToolButton:last-child { - margin-right: 0px; -} - -QToolButton:hover { - background-color: rgb(79,83,94); -} - -QToolButton:pressed { - background-color: rgb(25,27,38); -} - -* [themeID="addIconSmall"] { - qproperty-icon: url(theme:Dark/plus.svg); -} - -* [themeID="removeIconSmall"] { - qproperty-icon: url(theme:Dark/trash.svg); -} - -* [themeID="clearIconSmall"] { - qproperty-icon: url(theme:Dark/entry-clear.svg); -} - -* [themeID="propertiesIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="configIconSmall"] { - qproperty-icon: url(theme:Dark/settings/general.svg); -} - -* [themeID="menuIconSmall"] { - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - -* [themeID="refreshIconSmall"] { - qproperty-icon: url(theme:Dark/refresh.svg); -} - -* [themeID="cogsIcon"] { - qproperty-icon: url(theme:Dark/cogs.svg); -} - -#sourceInteractButton { - qproperty-icon: url(theme:Dark/interact.svg); -} - -* [themeID="upArrowIconSmall"] { - qproperty-icon: url(theme:Dark/up.svg); -} - -* [themeID="downArrowIconSmall"] { - qproperty-icon: url(theme:Dark/down.svg); -} - -* [themeID="pauseIconSmall"] { - qproperty-icon: url(theme:Dark/media-pause.svg); -} - -* [themeID="filtersIcon"] { - qproperty-icon: url(theme:Dark/filter.svg); -} - -QToolBarExtension { - background: palette(button); - min-width: 12px; - max-width: 12px; - padding: 4px 0px; - margin-left: 0px; - - qproperty-icon: url(theme:Dark/dots-vert.svg); -} - - -/* Tab Widget */ - -QTabWidget::pane { /* The tab widget frame */ - border-top: 4px solid palette(base); -} - -QTabWidget::tab-bar { - alignment: left; -} - -QTabBar QToolButton { - background: rgb(44,46,53); - border: none; -} - -QTabBar::tab:top { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} - -QTabBar::tab:bottom { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; -} - -QTabBar::tab { - background: palette(dark); - color: palette(text); - border: none; - padding: 8px 12px; - min-width: 50px; - margin: 1px 2px; -} - -QTabBar::tab:pressed { - background: rgb(25,27,38); -} - -QTabBar::tab:hover { - background: rgb(79,83,94); - color: palette(text); -} - -QTabBar::tab:selected { - background: rgb(60,64,75); - color: palette(text); -} - -QTabBar::tab:top:selected { - border-bottom: 2px solid rgb(250,250,250); -} - -QTabBar::tab:bottom:selected { - border-top: 2px solid rgb(250,250,250); -} - -QTabBar QToolButton { - background: palette(button); - min-width: 16px; - padding: 0px; -} - -/* ComboBox */ - -QComboBox, -QDateTimeEdit { - background-color: rgb(60,64,75); - border-style: solid; - border: 1px; - border-radius: 4px; - border-color: rgb(60,64,75); - padding: 4px; - padding-left: 10px; -} - -QComboBox:hover, -QComboBox:selected, -QDateTimeEdit:hover, -QDateTimeEdit:selected { - background-color: rgb(79,83,94); -} - -QComboBox::drop-down, -QDateTimeEdit::drop-down { - border:none; - border-left: 1px solid rgb(25,28,34); - width: 20px; -} - -QComboBox::down-arrow, -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/updown.svg); - width: 100%; -} - -QComboBox:on, -QDateTimeEdit:on { - background-color: rgb(25,27,38); -} - -QComboBox:editable:hover { - -} - -QComboBox::drop-down:editable, -QDateTimeEdit::drop-down:editable { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} - -QComboBox::down-arrow:editable, -QDateTimeEdit::down-arrow:editable { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 8%; -} - -/* Textedits etc */ - -QLineEdit, QTextEdit, QPlainTextEdit { - background-color: palette(button); - border: none; - border-radius: 4px; - padding: 5px 2px 5px 7px; - border: 2px solid transparent; -} - -QLineEdit:hover, -QTextEdit:hover, -QPlainTextEdit:hover { - border: 2px solid rgb(99,102,111); -} - -QLineEdit:focus, -QTextEdit:focus, -QPlainTextEdit:focus { - background-color: palette(mid); - border: 2px solid rgb(40,76,184); -} - -/* Spinbox and doubleSpinbox */ - -QSpinBox, -QDoubleSpinBox { - background-color: palette(button); - border: 2px solid palette(button); - border-radius: 4px; - margin-right: 3px; - padding: 3px 0px 4px 5px; -} - -QSpinBox:hover, -QDoubleSpinBox:hover { - border: 2px solid rgb(99,102,111); -} - -QSpinBox:focus, -QDoubleSpinBox:focus { - background-color: palette(mid); - border: 2px solid rgb(40,76,184); -} - -QSpinBox::up-button, QDoubleSpinBox::up-button { - subcontrol-origin: padding; - subcontrol-position: top right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-bottom-width: 0; -} - -QSpinBox::down-button, QDoubleSpinBox::down-button { - subcontrol-origin: padding; - subcontrol-position: bottom right; /* position at the top right corner */ - right: 2px; - border-radius: 3px; - border-width: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; - border-top-width: 0; -} - -QSpinBox::up-button:hover, QSpinBox::down-button:hover, QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { - background-color: rgb(79,83,94); -} - -QSpinBox::up-button:pressed, QSpinBox::down-button:pressed, QDoubleSpinBox::up-button:pressed, QDoubleSpinBox::down-button:pressed { - background-color: rgb(25,27,38); -} - -QSpinBox::up-button:disabled, QSpinBox::up-button:off, QSpinBox::down-button:disabled, QSpinBox::down-button:off { - background-color: rgb(25,27,38); -} - -QDoubleSpinBox::up-button:disabled, QDoubleSpinBox::up-button:off, QDoubleSpinBox::down-button:disabled, QDoubleSpinBox::down-button:off { - background-color: rgb(25,27,38); -} - -QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { - image: url(theme:Dark/up.svg); - width: 100%; - margin: 2px; -} - -QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { - image: url(theme:Dark/down.svg); - width: 100%; - padding: 2px; -} - -/* Controls Dock */ -#controlsFrame { - padding: 4px 3px; -} - -#controlsFrame QPushButton { - margin: 2px 1px; -} - -#streamButton, -#recordButton, -QPushButton[themeID="replayBufferButton"], -#broadcastButton { - padding: 10px; -} - -/* Primary Control Button Checked Coloring */ -#streamButton:!hover:!pressed:checked, -#recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, -#modeSwitch:!hover:!pressed:checked, -#broadcastButton:!hover:!pressed:checked { - background: rgb(40,76,184); -} - -/* Primary Control Button Hover Coloring */ -#streamButton:hover:!pressed:checked, -#recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, -#modeSwitch:hover:!pressed:checked, -#broadcastButton:hover:!pressed:checked { - background: rgb(54,92,192); - color: palette(text); -} - - -/* Buttons */ - -QPushButton { - color: palette(text); - background-color: palette(button); - min-height: 18px; - border: none; - border-radius: 4px; - padding: 6px 16px; -} - -QPushButton::flat { - background-color: rgb(60,64,75); -} - -QPushButton:checked { - background-color: rgb(40,76,184); -} - -QPushButton:hover { - background-color: rgb(79,83,94); -} - -QPushButton:pressed { - background-color: rgb(25,27,38); -} - -QPushButton:disabled, QToolButton:disabled { - background-color: rgb(25,27,38); -} - -QPushButton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - subcontrol-origin: padding; - width: 25px; -} - -/* Sliders */ - -QSlider::groove:horizontal { - background-color: rgb(60,64,75); - height: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:horizontal { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - height: 10px; - width: 18px; - margin: -3px 0; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:horizontal:pressed { - background-color: palette(text); -} - -QSlider::sub-page:horizontal { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::sub-page:horizontal:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::groove:vertical { - background-color: rgb(60,64,75); - width: 4px; - border: none; - border-radius: 2px; -} - -QSlider::handle:vertical { - background-color: palette(text); - border: 1px solid palette(mid); - border-radius: 3px; - width: 10px; - height: 18px; - margin: 0 -3px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ -} - -QSlider::handle:vertical:pressed { - background-color: palette(text); -} - -QSlider::add-page:vertical { - background-color: palette(highlight); - border-radius: 2px; -} - -QSlider::add-page:vertical:disabled { - background-color: palette(window); - border-radius: 2px; -} - -QSlider::handle:hover { - background-color: rgb(200,199,200); -} - -QSlider::handle:disabled { - background-color: rgb(68,75,110); -} - -/* Volume Control */ -#stackedMixerArea QPushButton { - min-width: 16px; - padding: 4px 8px; -} - -/* This is an incredibly cursed but necessary fix */ -#stackedMixerArea QPushButton:!hover { - background-color: palette(button); -} - -#stackedMixerArea QPushButton:hover { - background-color: rgb(79,83,94); -} - -#stackedMixerArea QPushButton:pressed { - background-color: rgb(25,27,38); -} - -VolumeMeter { - qproperty-backgroundNominalColor: rgb(66,112,24); - qproperty-backgroundWarningColor: rgb(112,91,28); - qproperty-backgroundErrorColor: rgb(112,39,53); - qproperty-foregroundNominalColor: rgb(115,189,49); - qproperty-foregroundWarningColor: rgb(189,144,9); - qproperty-foregroundErrorColor: rgb(189,47,73); - qproperty-magnitudeColor: rgb(0,0,0); - qproperty-majorTickColor: palette(text); - qproperty-minorTickColor: palette(light); -} - -/* Status Bar */ - -QStatusBar::item { - border: none; -} - -/* Table View */ - -QTableView { - background: palette(base); - gridline-color: palette(light); -} - -QTableView::item { - margin: 0px; - padding: 0px; -} - -QTableView QLineEdit { - background: palette(mid); - padding: 0; - margin: 0; -} - -QTableView QPushButton, -QTableView QToolButton { - margin: 1px 1px 2px; -} - -QHeaderView::section { - background-color: palette(button); - color: palette(text); - border: none; - border-left: 1px solid palette(window); - border-right: 1px solid palette(window); - padding: 2px 4px; - margin-bottom: 2px; -} - -/* Mute CheckBox */ - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:indeterminate { - image: url(theme:Dark/unassigned.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -/* Label warning/error */ - -QLabel#warningLabel { - color: rgb(192,128,0); - font-weight: bold; -} - -QLabel#errorLabel { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="warning"] { - color: rgb(192,128,0); - font-weight: bold; -} - -* [themeID="error"] { - color: rgb(192,0,0); - font-weight: bold; -} - -* [themeID="good"] { - color: rgb(0,192,0); - font-weight: bold; -} - -/* About dialog */ - -* [themeID="aboutName"] { - font-size: 26pt; - font-weight: bold; -} - -* [themeID="aboutVersion"] { - font-size: 12pt; - margin-bottom: 20px; -} - -* [themeID="aboutInfo"] { - margin-bottom: 20px; -} - -* [themeID="aboutHLayout"] { - background-color: palette(base); -} - -/* Canvas / Preview background color */ - -OBSQTDisplay { - qproperty-displayBackgroundColor: rgb(25,27,38); - border-radius: 10px; -} - -/* Filters Window */ - -OBSBasicFilters QListWidget { - border-radius: 4px; - padding: 3px; -} - -OBSBasicFilters QListWidget::item { - border-radius: 4px; - padding: 6px; -} - -OBSBasicFilters #widget, -OBSBasicFilters #widget_2 { - margin: 0px; - padding: 0px; - padding-bottom: 4px; -} - -OBSBasicFilters #widget QPushButton, -OBSBasicFilters #widget_2 QPushButton { - min-width: 16px; - padding: 4px 8px; - margin-top: 0px; -} - -/* Preview/Program labels */ - -* [themeID="previewProgramLabels"] { - font-size: 14pt; - font-weight: bold; - color: rgb(210,210,210); - margin-bottom: 4px; -} - -/* Settings Icons */ - -OBSBasicSettings { - qproperty-generalIcon: url(theme:Dark/settings/general.svg); - qproperty-streamIcon: url(theme:Dark/settings/stream.svg); - qproperty-outputIcon: url(theme:Dark/settings/output.svg); - qproperty-audioIcon: url(theme:Dark/settings/audio.svg); - qproperty-videoIcon: url(theme:Dark/settings/video.svg); - qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg); - qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg); - qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg); -} - -/* Checkboxes */ -QCheckBox { - -} - -QCheckBox::indicator, -QGroupBox::indicator { - width: 18px; - height: 18px; -} - -QGroupBox::indicator { - margin-left: 2px; -} - -QCheckBox::indicator:unchecked, -QGroupBox::indicator:unchecked { - image: url(theme:Yami/checkbox_unchecked.svg); -} - -QCheckBox::indicator:unchecked:hover, -QGroupBox::indicator:unchecked:hover { - border: none; - image: url(theme:Yami/checkbox_unchecked_focus.svg); -} - -QCheckBox::indicator:checked, -QGroupBox::indicator:checked { - image: url(theme:Yami/checkbox_checked.svg); -} - -QCheckBox::indicator:checked:hover, -QGroupBox::indicator:checked:hover { - border: none; - image: url(theme:Yami/checkbox_checked_focus.svg); -} - -QCheckBox::indicator:checked:disabled, -QGroupBox::indicator:checked:disabled { - image: url(theme:Yami/checkbox_checked_disabled.svg); -} - -QCheckBox::indicator:unchecked:disabled, -QGroupBox::indicator:unchecked:disabled { - image: url(theme:Yami/checkbox_unchecked_disabled.svg); -} - -/* Locked CheckBox */ - -QCheckBox[lockCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[lockCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[lockCheckBox=true]::indicator:checked, -QCheckBox[lockCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/locked.svg); -} - -QCheckBox[lockCheckBox=true]::indicator:unchecked, -QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/unlocked.svg); -} - -/* Visibility CheckBox */ - -QCheckBox[visibilityCheckBox=true] { - outline: none; - background: transparent; -} - -QCheckBox[visibilityCheckBox=true]::indicator { - width: 16px; - height: 16px; -} - -QCheckBox[visibilityCheckBox=true]::indicator:checked, -QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { - image: url(theme:Dark/visible.svg); -} - -QCheckBox[visibilityCheckBox=true]::indicator:unchecked, -QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { - image: url(:res/images/invisible.svg); -} - -* [themeID="revertIcon"] { - qproperty-icon: url(theme:Dark/revert.svg); -} - -QPushButton#extraPanelDelete { - background-color: palette(mid); - margin: 0; - padding: 0; -} - -QPushButton#extraPanelDelete:hover { - background-color: rgb(68,75,110); -} - -QPushButton#extraPanelDelete:pressed { - background-color: palette(dark); -} - -/* Mute CheckBox */ - -MuteCheckBox { - outline: none; -} - -MuteCheckBox::indicator { - width: 16px; - height: 16px; -} - -MuteCheckBox::indicator:checked { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:hover { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:unchecked:focus { - image: url(theme:Dark/settings/audio.svg); -} - -MuteCheckBox::indicator:checked:hover { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:focus { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:checked:disabled { - image: url(theme:Dark/mute.svg); -} - -MuteCheckBox::indicator:unchecked:disabled { - image: url(theme:Dark/settings/audio.svg); -} - -#hotkeyFilterReset { - margin-top: 0px; -} - -OBSHotkeyWidget { - padding: 8px 0px; - margin: 2px 0px; -} - -OBSHotkeyLabel { - padding: 4px 0px; -} - -OBSHotkeyLabel[hotkeyPairHover=true] { - color: rgb(53,82,222); -} - -OBSHotkeyWidget QPushButton { - min-width: 16px; - padding: 4px 4px; - margin-top: 0px; - margin-left: 4px; -} - - -/* Sources List Group Collapse Checkbox */ - -QCheckBox[sourceTreeSubItem=true] { - background: transparent; - outline: none; - padding: 0px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator { - width: 12px; - height: 12px; -} - -QCheckBox[sourceTreeSubItem=true]::indicator:checked, -QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { - image: url(theme:Dark/expand.svg); -} - -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, -QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { - image: url(theme:Dark/collapse.svg); -} - -/* Source Icons */ - -OBSBasic { - qproperty-imageIcon: url(theme:Dark/sources/image.svg); - qproperty-colorIcon: url(theme:Dark/sources/brush.svg); - qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg); - qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg); - qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg); - qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg); - qproperty-windowCapIcon: url(theme:Dark/sources/window.svg); - qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg); - qproperty-cameraIcon: url(theme:Dark/sources/camera.svg); - qproperty-textIcon: url(theme:Dark/sources/text.svg); - qproperty-mediaIcon: url(theme:Dark/sources/media.svg); - qproperty-browserIcon: url(theme:Dark/sources/globe.svg); - qproperty-groupIcon: url(theme:Dark/sources/group.svg); - qproperty-sceneIcon: url(theme:Dark/sources/scene.svg); - qproperty-defaultIcon: url(theme:Dark/sources/default.svg); - qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg); -} - -/* Scene Tree Grid Mode */ - -SceneTree { - qproperty-gridItemWidth: 154; - qproperty-gridItemHeight: 31; -} - -*[gridMode="true"] SceneTree::item { - color: palette(text); - background-color: palette(button); - border-radius: 4px; - margin: 2px; -} - -*[gridMode="true"] SceneTree::item:selected { - background-color: rgb(40,76,184); -} - -*[gridMode="true"] SceneTree::item:checked { - background-color: rgb(40,76,184); -} - -*[gridMode="true"] SceneTree::item:hover { - background-color: rgb(79,83,94); -} - -*[gridMode="true"] SceneTree::item:selected:hover { - background-color: rgb(54,92,192); -} - -/* Save icon */ - -* [themeID="replayIconSmall"] { - qproperty-icon: url(theme:Dark/save.svg); -} - -/* Studio Mode T-Bar */ - -QSlider[themeID="tBarSlider"] { - height: 24px; -} - -QSlider::groove:horizontal[themeID="tBarSlider"] { - border: 1px solid #4c4c4c; - height: 5px; - background: palette(dark); -} - -QSlider::sub-page:horizontal[themeID="tBarSlider"] { - background: palette(dark); - border: 1px solid #4c4c4c; -} - -QSlider::handle:horizontal[themeID="tBarSlider"] { - background-color: #d2d2d2; - width: 12px; - height: 24px; - margin: -24px 0px; -} - -/* Media icons */ - -* [themeID="playIcon"] { - qproperty-icon: url(theme:Dark/media/media_play.svg); -} - -* [themeID="pauseIcon"] { - qproperty-icon: url(theme:Dark/media/media_pause.svg); -} - -* [themeID="restartIcon"] { - qproperty-icon: url(theme:Dark/media/media_restart.svg); -} - -* [themeID="stopIcon"] { - qproperty-icon: url(theme:Dark/media/media_stop.svg); -} - -* [themeID="nextIcon"] { - qproperty-icon: url(theme:Dark/media/media_next.svg); -} - -* [themeID="previousIcon"] { - qproperty-icon: url(theme:Dark/media/media_previous.svg); -} - -/* YouTube Integration */ -OBSYoutubeActions { - qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg); -} - -#ytEventList QLabel { - color: palette(text); - background-color: palette(button); - border: none; - border-radius: 4px; - padding: 4px 20px; -} - -#ytEventList QLabel:hover { - background-color: rgb(79,83,94); -} - -#ytEventList QLabel[isSelectedEvent=true] { - background-color: rgb(40,76,184); - border: none; -} - -#ytEventList QLabel[isSelectedEvent=true]:hover { - background-color: rgb(54,92,192); - color: palette(text); -} - -/* Calendar Widget */ -QDateTimeEdit::down-arrow { - qproperty-alignment: AlignTop; - image: url(theme:Dark/down.svg); - width: 100%; -} - -QDateTimeEdit:on { - background-color: palette(mid); -} - -/* Calendar Top Bar */ -QCalendarWidget QWidget#qt_calendar_navigationbar { - background-color: palette(base); - padding: 4px 8px; -} - -/* Calendar Top Bar Buttons */ -QCalendarWidget QToolButton { - background-color: palette(button); - padding: 2px 16px; - border-radius: 4px; - margin: 2px; -} - -#qt_calendar_monthbutton::menu-indicator { - image: url(theme:Dark/down.svg); - subcontrol-position: right; - padding-top: 2px; - padding-right: 6px; - height: 10px; - width: 10px; -} - -QCalendarWidget #qt_calendar_prevmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/left.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget #qt_calendar_nextmonth { - padding: 2px; - qproperty-icon: url(theme:Dark/right.svg); - icon-size: 16px, 16px; -} - -QCalendarWidget QToolButton:hover { - background-color: rgb(79,83,94); - border-radius: 4px; -} - -QCalendarWidget QToolButton:pressed { - background-color: rgb(25,27,38); -} - -/* Month Dropdown Menu */ -QCalendarWidget QMenu { - -} -/* Year spinbox */ -QCalendarWidget QSpinBox { - background-color: rgb(25,27,38); - border: none; - border-radius: 4px; - margin: 0px 3px 0px 0px; - padding: 4px 16px; -} - -QCalendarWidget QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; } -QCalendarWidget QSpinBox::down-button {subcontrol-origin: border; subcontrol-position: bottom right; width: 16px;} -QCalendarWidget QSpinBox::up-arrow { width: 10px; height: 10px; } -QCalendarWidget QSpinBox::down-arrow { width: 10px; height: 10px; } - -/* Days of the Week Bar */ -QCalendarWidget QWidget { alternate-background-color: palette(mid); } - -QCalendarWidget QAbstractItemView:enabled { - background-color: palette(base); - color: palette(text); -} - -QCalendarWidget QAbstractItemView:disabled { - color: rgb(122,121,122); -} - -/* VirtualCam Plugin Fixes */ - -#VirtualProperties QWidget { - margin-top: 0; - margin-bottom: 0; -} - -/* Disable icons on QDialogButtonBox */ -QDialogButtonBox { - dialogbuttonbox-buttons-have-icons: 0; -} - -/* Stats dialog */ -OBSBasicStats { - background: palette(dark); -} - -/* Advanced audio dialog */ -OBSBasicAdvAudio #scrollAreaWidgetContents { - background: palette(dark); -} diff --git a/UI/data/themes/Yami_Acri.ovt b/UI/data/themes/Yami_Acri.ovt new file mode 100644 index 00000000000000..2a847e6325c19b --- /dev/null +++ b/UI/data/themes/Yami_Acri.ovt @@ -0,0 +1,232 @@ +@OBSThemeMeta { + name: 'Acri'; + id: 'com.obsproject.Yami.Acri'; + extends: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'true'; +} + +@OBSThemeVars { + --grey1: rgb(97,97,97); + --grey2: rgb(87,87,87); + --grey3: rgb(61,61,63); + --grey4: rgb(22,36,88); + --grey5: rgb(40,40,42); + --grey6: rgb(24,24,25); + --grey7: rgb(16,16,16); + --grey8: rgb(9,9,9); + + --primary: rgb(19,26,48); + --primary_light: rgb(42,58,117); + --primary_dark: rgb(22,31,65); + + --border_color: var(--grey5); + + --input_bg: var(--grey5); + --input_bg_hover: var(--grey7); + --input_bg_focus: var(--grey7); + + --list_item_bg_selected: var(--primary); + --list_item_bg_hover: var(--primary_light); + + --input_border: var(--grey1); + --input_border_hover: var(--grey1); + --input_border_focus: var(--primary); + + --spacing_input: var(--spacing_base); + + --button_bg: #162458; + --button_bg_hover: var(--primary_light); + --button_bg_down: var(--grey7); + --button_bg_disabled: var(--grey6); + + --button_bg_red: rgb(88,22,36); + --button_bg_red_hover: rgb(116,32,49); + --button_bg_red_down: rgb(63,21,30); + + --button_border: #162458; + --button_border_hover: var(--button_bg_hover); + --button_border_focus: var(--button_bg_hover); + + --tab_bg: var(--input_bg); + --tab_bg_hover: var(--grey3); + --tab_bg_down: var(--grey7); + --tab_bg_disabled: var(--grey6); + + --tab_border: var(--grey3); + --tab_border_hover: var(--grey1); + --tab_border_focus: var(--grey1); + --tab_border_selected: var(--primary_light); + + --scrollbar: var(--grey5); + --scrollbar_hover: var(--primary_light); + --scrollbar_down: var(--grey5); + --scrollbar_border: var(--grey5); + + --toolbutton_bg: var(--grey5); + --toolbutton_bg_hover: var(--primary_light); + --toolbutton_bg_down: var(--primary_dark); +} + +QMenu::separator { + background-color: var(--grey5); +} + +QDockWidget::title { + background-color: var(--grey6); +} + +QDockWidget::close-button:hover, +QDockWidget::float-button:hover { + background: var(--grey3); +} + +QScrollBar::handle:hover { + border-color: var(--scrollbar_hover); +} + +QScrollBar::handle:pressed { + border-color: var(--scrollbar_down); +} + +QToolButton, +QPushButton[toolButton="true"] { + background-color: var(--toolbutton_bg); + border-color: var(--toolbutton_bg); +} + +QToolButton:hover, +QToolButton:focus { + border-color: var(--primary_light); + background-color: var(--primary_light); +} + +QToolButton:pressed, +QToolButton:pressed:hover { + background-color: var(--toolbutton_bg_down); + border-color: var(--toolbutton_bg_down); +} + +MuteCheckBox::indicator, +MuteCheckBox::indicator:unchecked, +MuteCheckBox::indicator:focus { + background-color: var(--toolbutton_bg); + border: 1px solid var(--toolbutton_bg); +} + +MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:unchecked:hover { + background-color: var(--toolbutton_bg_hover); + border: 1px solid var(--toolbutton_bg_hover); +} + +MuteCheckBox::indicator:pressed, +MuteCheckBox::indicator:pressed:hover { + background-color: var(--toolbutton_bg_down); + border-color: var(--toolbutton_bg_down); +} + +#stackedMixerArea QPushButton, +#stackedMixerArea QPushButton:!hover { + background-color: var(--toolbutton_bg); + border-color: var(--toolbutton_bg); +} + +#stackedMixerArea QPushButton:hover { + background-color: var(--toolbutton_bg_hover); +} + +#stackedMixerArea QPushButton:pressed { + background-color: var(--toolbutton_bg_down); +} + +QTabBar::tab { + background-color: var(--grey5); +} + +QTabBar QToolButton { + background-color: var(--grey5); +} + +/* Primary Control Button Checked Coloring */ +#streamButton:!hover:!pressed:checked, +#recordButton:!hover:!pressed:checked, +QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, +QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, +#modeSwitch:!hover:!pressed:checked, +#broadcastButton:!hover:!pressed:checked { + background: var(--button_bg_red); + border-color: var(--button_bg_red); +} + +/* Primary Control Button Hover Coloring */ +#streamButton:hover:!pressed:checked, +#recordButton:hover:!pressed:checked, +QPushButton[themeID="replayBufferButton"]:!pressed:checked, +QPushButton[themeID="vcamButton"]:!pressed:checked, +#modeSwitch:hover:!pressed:checked, +#broadcastButton:hover:!pressed:checked { + background: var(--button_bg_red_hover); +} + +/* Primary Control Button Checked + Pressed Coloring */ +#streamButton:pressed:checked, +#recordButton:pressed:checked, +QPushButton[themeID="replayBufferButton"]:pressed:checked, +QPushButton[themeID="vcamButton"]:pressed:checked, +#modeSwitch:pressed:checked, +#broadcastButton:pressed:checked { + background: var(--button_bg_red_down); +} + +QPushButton:disabled { + border-color: var(--grey5); +} + +QHeaderView::section { + background-color: var(--grey5); +} + +OBSQTDisplay { + qproperty-displayBackgroundColor: var(--grey5); +} + +*[gridMode="true"] SceneTree::item:selected { + background-color: var(--button_bg_red); +} + +*[gridMode="true"] SceneTree::item:checked { + background-color: var(--button_bg_red); +} + +*[gridMode="true"] SceneTree::item:hover { + background-color: var(--button_bg_red_down); +} + +*[gridMode="true"] SceneTree::item:selected:hover { + background-color: var(--button_bg_red_hover); +} + +#ytEventList QLabel { + background-color: var(--grey5); +} + +#ytEventList QLabel:hover { + background-color: var(--grey3); +} + +QCalendarWidget QToolButton { + background-color: var(--grey6); +} + +QCalendarWidget QToolButton:hover { + background-color: var(--primary_light); +} + +QCalendarWidget QToolButton:pressed { + background-color: var(--primary_dark); +} + +QCalendarWidget QSpinBox { + background-color: var(--primary_dark); +} diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt new file mode 100644 index 00000000000000..c365f1c3e4c516 --- /dev/null +++ b/UI/data/themes/Yami_Classic.ovt @@ -0,0 +1,228 @@ +@OBSThemeMeta { + name: 'Classic'; + id: 'com.obsproject.Yami.Classic'; + extends: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'true'; +} + +@OBSThemeVars { + --grey1: rgb(97,97,97); + --grey2: rgb(134,135,134); + --grey3: rgb(122,121,122); + --grey4: rgb(76,76,76); + --grey5: rgb(88,87,88); + --grey6: rgb(31,30,31); + --grey7: rgb(58,57,58); + --grey8: rgb(46,45,46); + + --bg_window: var(--grey7); + --bg_base: var(--grey6); + --bg_preview: rgb(76,76,76); + + --primary: rgb(25,52,76); + --primary_light: rgb(33,71,109); + + /* Layout */ + --font_base_value: 9; + --spacing_base_value: 2; + --padding_base_value: 0.25; + + --icon_base: calc(6px + var(--font_base_value)); + + --padding_wide: calc(18px + calc(0.25 * var(--padding_base_value))); + --padding_menu: calc(8px + calc(1 * var(--padding_base_value))); + + --input_height_base: calc(1px + calc(var(--input_font_scale) + var(--input_font_padding))); + + --border_color: var(--grey6); + + --border_radius: 2px; + --border_radius_small: 1px; + --border_radius_large: 2px; + + --input_bg: var(--grey4); + --input_bg_hover: var(--grey5); + --input_bg_focus: var(--grey6); + + --list_item_bg_selected: var(--primary); + --list_item_bg_hover: var(--primary_light); + + --input_border: var(--grey4); + --input_border_hover: var(--grey5); + --input_border_focus: var(--grey6); + + --spacing_input: var(--spacing_base); + + --button_bg: var(--input_bg); + --button_bg_hover: var(--grey3); + --button_bg_down: var(--grey7); + --button_bg_disabled: var(--grey8); + + --button_border: var(--button_bg); + --button_border_hover: var(--grey1); + --button_border_focus: var(--grey1); + + --tab_bg: var(--input_bg); + --tab_bg_hover: var(--grey3); + --tab_bg_down: var(--grey7); + --tab_bg_disabled: var(--grey8); + + --tab_border: var(--grey1); + --tab_border_hover: var(--grey1); + --tab_border_focus: var(--grey1); + --tab_border_selected: var(--primary); + + --scrollbar: var(--grey4); + --scrollbar_hover: var(--grey3); + --scrollbar_down: var(--grey8); + --scrollbar_border: var(--grey2); + + /* Variant Extras */ + --dock_title_padding: calc(2px + var(--padding_base)); + --icon_base_mixer: calc(2px + var(--icon_base)); + --padding_menu_y: calc(3px + calc(1 * var(--padding_base_value))); +} + +QStatusBar { + background-color: var(--bg_window); +} + +QDockWidget { + font-weight: normal; +} + +QDockWidget::title { + padding: var(--dock_title_padding); + text-align: center; +} + +QDockWidget > QWidget { + background: var(--bg_window); +} + +SceneTree::item, +SourceTreeItem { + border-width: 0px; +} + +QMenu::item { + padding: var(--padding_menu_y) var(--padding_menu); +} + +QGroupBox { + background: var(--bg_window); + border: 1px solid var(--border_color); +} + +QTabWidget::pane { + border-width: 1px; +} + +QTabBar::tab { + border-color: var(--tab_bg); + padding: 3px 6px; +} + +QComboBox:hover, +QComboBox:focus, +QDateTimeEdit:hover, +QDateTimeEdit:selected { + background-color: var(--input_bg_hover); +} + +QComboBox:on, +QDateTimeEdit:on, +QComboBox:editable:focus { + background-color: var(--input_bg_focus); +} + +QLineEdit, +QTextEdit, +QPlainTextEdit { + background-color: var(--input_bg_focus); + padding-left: 8px; +} + +QLineEdit:hover, +QTextEdit:hover, +QPlainTextEdit:hover { + background-color: var(--input_bg_focus); +} + +QSpinBox, +QDoubleSpinBox { + background-color: var(--input_bg_focus); + border-width: 1px; + padding-left: 8px; + height: var(--input_height); + max-height: var(--input_height); +} + +QSpinBox:hover, +QDoubleSpinBox:hover { + background-color: var(--input_bg_focus); +} + +QSpinBox::up-button:disabled, +QSpinBox::up-button:off, +QDoubleSpinBox::up-button:disabled, +QDoubleSpinBox::up-button:off { + margin-top: 0px; +} + +QSpinBox::down-button:disabled, +QSpinBox::down-button:off, +QDoubleSpinBox::down-button:disabled, +QDoubleSpinBox::down-button:off { + margin-bottom: 0px; +} + +OBSBasicSettings #PropertiesContainer { + background-color: var(--bg_window); +} + +OBSBasicSettings QListWidget::item { + padding: 4px; +} + +QToolButton, +QPushButton[toolButton="true"] { + background-color: var(--bg_window); + border-color: var(--bg_window); +} + +#stackedMixerArea QPushButton { + min-width: var(--icon_base); + padding: var(--padding_large) var(--padding_large); + icon-size: var(--icon_base), var(--icon_base); +} + +MuteCheckBox::indicator, +MuteCheckBox::indicator:unchecked { + width: var(--icon_base_mixer); + height: var(--icon_base_mixer); + icon-size: var(--icon_base_mixer), var(--icon_base_mixer); +} + +MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:unchecked:hover { + icon-size: var(--icon_base_mixer), var(--icon_base_mixer); +} + +#contextContainer { + background-color: var(--bg_window); +} + +VolumeMeter { + qproperty-backgroundNominalColor: rgb(38,127,38); + qproperty-backgroundWarningColor: rgb(127,127,38); + qproperty-backgroundErrorColor: rgb(127,38,38); + qproperty-foregroundNominalColor: rgb(76,255,76); + qproperty-foregroundWarningColor: rgb(255,255,76); + qproperty-foregroundErrorColor: rgb(255,76,76); + qproperty-magnitudeColor: rgb(0,0,0); + qproperty-majorTickColor: var(--text); + qproperty-minorTickColor: rgb(122,121,122); /* light */ + qproperty-meterThickness: 3; +} diff --git a/UI/data/themes/Yami_Default.ovt b/UI/data/themes/Yami_Default.ovt new file mode 100644 index 00000000000000..c248cec93acf61 --- /dev/null +++ b/UI/data/themes/Yami_Default.ovt @@ -0,0 +1,7 @@ +@OBSThemeMeta { + name: 'Default'; + id: 'com.obsproject.Yami.Original'; + extends: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'true'; +} diff --git a/UI/data/themes/Yami_Grey.ovt b/UI/data/themes/Yami_Grey.ovt new file mode 100644 index 00000000000000..645b51b90619e1 --- /dev/null +++ b/UI/data/themes/Yami_Grey.ovt @@ -0,0 +1,18 @@ +@OBSThemeMeta { + name: 'Grey'; + id: 'com.obsproject.Yami.Grey'; + extends: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'true'; +} + +@OBSThemeVars { + --grey1: rgb(97,97,97); + --grey2: rgb(87,87,87); + --grey3: rgb(77,77,77); + --grey4: rgb(67,67,67); + --grey5: rgb(57,57,57); + --grey6: rgb(47,47,47); + --grey7: rgb(33,33,33); + --grey8: rgb(21,21,21); +} diff --git a/UI/data/themes/Yami_Light.ovt b/UI/data/themes/Yami_Light.ovt new file mode 100644 index 00000000000000..a2fd922010ecf2 --- /dev/null +++ b/UI/data/themes/Yami_Light.ovt @@ -0,0 +1,313 @@ +@OBSThemeMeta { + name: 'Light'; + id: 'com.obsproject.Yami.Light'; + extends: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'false'; +} + +@OBSThemeVars { + --grey1: rgb(140,140,140); + --grey2: rgb(254,254,254); + --grey3: rgb(254,254,254); + --grey4: rgb(243,243,243); + --grey5: rgb(236,236,236); + --grey6: rgb(229,229,229); + --grey7: rgb(211,211,211); + --grey8: rgb(193,193,193); + + --primary: rgb(140,181,255); + --primary_light: rgb(178,207,255); + --primary_dark: rgb(22,31,65); + + --bg_window: var(--grey7); + --bg_base: var(--grey6); + --bg_preview: var(--grey8); + + --text: var(--black1); + --text_light: var(--black3); + --text_muted: var(--black4); + + --text_disabled: var(--text_muted); + --text_inactive: var(--text_light); + + --input_bg_hover: var(--grey3); + --input_bg_focus: var(--grey3); + + --separator_hover: var(--black1); +} + +VolumeMeter { + qproperty-backgroundNominalColor: rgb(66,112,24); + qproperty-backgroundWarningColor: rgb(112,91,28); + qproperty-backgroundErrorColor: rgb(112,39,53); + qproperty-foregroundNominalColor: rgb(115,189,49); + qproperty-foregroundWarningColor: rgb(189,144,9); + qproperty-foregroundErrorColor: rgb(189,47,73); + qproperty-magnitudeColor: rgb(0,0,0); + qproperty-majorTickColor: palette(text); + qproperty-minorTickColor: palette(light); +} + +QMenu::right-arrow { + image: url(theme:Light/expand.svg); +} + +QDockWidget { + titlebar-close-icon: url(theme:Light/close.svg); + titlebar-normal-icon: url(theme:Light/popout.svg); +} + +QPushButton#sourcePropertiesButton { + qproperty-icon: url(theme:Light/settings/general.svg); +} + +QPushButton#sourceFiltersButton { + qproperty-icon: url(theme:Light/filter.svg); +} + +* [themeID="addIconSmall"] { + qproperty-icon: url(theme:Light/plus.svg); +} + +* [themeID="removeIconSmall"] { + qproperty-icon: url(theme:Light/trash.svg); +} + +* [themeID="clearIconSmall"] { + qproperty-icon: url(theme:Light/entry-clear.svg); +} + +* [themeID="propertiesIconSmall"] { + qproperty-icon: url(theme:Light/settings/general.svg); +} + +* [themeID="configIconSmall"] { + qproperty-icon: url(theme:Light/settings/general.svg); +} + +* [themeID="menuIconSmall"] { + qproperty-icon: url(theme:Light/dots-vert.svg); +} + +* [themeID="refreshIconSmall"] { + qproperty-icon: url(theme:Light/refresh.svg); +} + +* [themeID="cogsIcon"] { + qproperty-icon: url(theme:Light/cogs.svg); +} + +#sourceInteractButton { + qproperty-icon: url(theme:Light/interact.svg); +} + +* [themeID="upArrowIconSmall"] { + qproperty-icon: url(theme:Light/up.svg); +} + +* [themeID="downArrowIconSmall"] { + qproperty-icon: url(theme:Light/down.svg); +} + +* [themeID="pauseIconSmall"] { + qproperty-icon: url(theme:Light/media-pause.svg); +} + +* [themeID="filtersIcon"] { + qproperty-icon: url(theme:Light/filter.svg); +} + +QToolBarExtension { + qproperty-icon: url(theme:Light/dots-vert.svg); +} + +QComboBox::down-arrow, +QDateTimeEdit::down-arrow { + image: url(theme:Light/collapse.svg); +} + +QComboBox::down-arrow:editable, +QDateTimeEdit::down-arrow:editable { + image: url(theme:Light/collapse.svg); +} + +QSpinBox::up-arrow, QDoubleSpinBox::up-arrow { + image: url(theme:Light/up.svg); +} + +QSpinBox::down-arrow, QDoubleSpinBox::down-arrow { + image: url(theme:Light/down.svg); +} + +QPushButton::menu-indicator { + image: url(theme:Light/down.svg); +} + +OBSBasicSettings { + qproperty-generalIcon: url(theme:Light/settings/general.svg); + qproperty-appearanceIcon: url(theme:Light/settings/appearance.svg); + qproperty-streamIcon: url(theme:Light/settings/stream.svg); + qproperty-outputIcon: url(theme:Light/settings/output.svg); + qproperty-audioIcon: url(theme:Light/settings/audio.svg); + qproperty-videoIcon: url(theme:Light/settings/video.svg); + qproperty-hotkeysIcon: url(theme:Light/settings/hotkeys.svg); + qproperty-accessibilityIcon: url(theme:Light/settings/accessibility.svg); + qproperty-advancedIcon: url(theme:Light/settings/advanced.svg); +} + +QCheckBox::indicator:unchecked, +QGroupBox::indicator:unchecked { + image: url(theme:Light/checkbox_unchecked.svg); +} + +QCheckBox::indicator:unchecked:hover, +QGroupBox::indicator:unchecked:hover { + image: url(theme:Light/checkbox_unchecked_focus.svg); +} + +QCheckBox::indicator:checked, +QGroupBox::indicator:checked { + image: url(theme:Light/checkbox_checked.svg); +} + +QCheckBox::indicator:checked:hover, +QGroupBox::indicator:checked:hover { + image: url(theme:Light/checkbox_checked_focus.svg); +} + +QCheckBox::indicator:checked:disabled, +QGroupBox::indicator:checked:disabled { + image: url(theme:Light/checkbox_checked_disabled.svg); +} + +QCheckBox::indicator:unchecked:disabled, +QGroupBox::indicator:unchecked:disabled { + image: url(theme:Light/checkbox_unchecked_disabled.svg); +} + +QCheckBox[lockCheckBox=true]::indicator:checked, +QCheckBox[lockCheckBox=true]::indicator:checked:hover { + image: url(theme:Light/locked.svg); +} + +QCheckBox[visibilityCheckBox=true]::indicator:checked, +QCheckBox[visibilityCheckBox=true]::indicator:checked:hover { + image: url(theme:Light/visible.svg); +} + +* [themeID="revertIcon"] { + qproperty-icon: url(theme:Light/revert.svg); +} + +MuteCheckBox::indicator:checked { + image: url(theme:Light/mute.svg); +} + +MuteCheckBox::indicator:unchecked { + image: url(theme:Light/settings/audio.svg); +} + +MuteCheckBox::indicator:unchecked:hover { + image: url(theme:Light/settings/audio.svg); +} + +MuteCheckBox::indicator:unchecked:focus { + image: url(theme:Light/settings/audio.svg); +} + +MuteCheckBox::indicator:checked:hover { + image: url(theme:Light/mute.svg); +} + +MuteCheckBox::indicator:checked:focus { + image: url(theme:Light/mute.svg); +} + +MuteCheckBox::indicator:checked:disabled { + image: url(theme:Light/mute.svg); +} + +MuteCheckBox::indicator:unchecked:disabled { + image: url(theme:Light/settings/audio.svg); +} + +QCheckBox[sourceTreeSubItem=true]::indicator:checked, +QCheckBox[sourceTreeSubItem=true]::indicator:checked:hover { + image: url(theme:Light/expand.svg); +} + +QCheckBox[sourceTreeSubItem=true]::indicator:unchecked, +QCheckBox[sourceTreeSubItem=true]::indicator:unchecked:hover { + image: url(theme:Light/collapse.svg); +} + +/* Source Icons */ +OBSBasic { + qproperty-imageIcon: url(theme:Light/sources/image.svg); + qproperty-colorIcon: url(theme:Light/sources/brush.svg); + qproperty-slideshowIcon: url(theme:Light/sources/slideshow.svg); + qproperty-audioInputIcon: url(theme:Light/sources/microphone.svg); + qproperty-audioOutputIcon: url(theme:Light/settings/audio.svg); + qproperty-desktopCapIcon: url(theme:Light/settings/video.svg); + qproperty-windowCapIcon: url(theme:Light/sources/window.svg); + qproperty-gameCapIcon: url(theme:Light/sources/gamepad.svg); + qproperty-cameraIcon: url(theme:Light/sources/camera.svg); + qproperty-textIcon: url(theme:Light/sources/text.svg); + qproperty-mediaIcon: url(theme:Light/sources/media.svg); + qproperty-browserIcon: url(theme:Light/sources/globe.svg); + qproperty-groupIcon: url(theme:Light/sources/group.svg); + qproperty-sceneIcon: url(theme:Light/sources/scene.svg); + qproperty-defaultIcon: url(theme:Light/sources/default.svg); + qproperty-audioProcessOutputIcon: url(theme:Light/sources/windowaudio.svg); +} + +* [themeID="replayIconSmall"] { + qproperty-icon: url(theme:Light/save.svg); +} + +/* Media icons */ +* [themeID="playIcon"] { + qproperty-icon: url(theme:Light/media/media_play.svg); +} + +* [themeID="pauseIcon"] { + qproperty-icon: url(theme:Light/media/media_pause.svg); +} + +* [themeID="restartIcon"] { + qproperty-icon: url(theme:Light/media/media_restart.svg); +} + +* [themeID="stopIcon"] { + qproperty-icon: url(theme:Light/media/media_stop.svg); +} + +* [themeID="nextIcon"] { + qproperty-icon: url(theme:Light/media/media_next.svg); +} + +* [themeID="previousIcon"] { + qproperty-icon: url(theme:Light/media/media_previous.svg); +} + +/* YouTube Integration */ +OBSYoutubeActions { + qproperty-thumbPlaceholder: url(theme:Light/sources/image.svg); +} + +QDateTimeEdit::down-arrow { + image: url(theme:Light/down.svg); +} + +#qt_calendar_monthbutton::menu-indicator { + image: url(theme:Light/down.svg); +} + +QCalendarWidget #qt_calendar_prevmonth { + qproperty-icon: url(theme:Light/left.svg); +} + +QCalendarWidget #qt_calendar_nextmonth { + qproperty-icon: url(theme:Light/right.svg); +} diff --git a/UI/data/themes/Yami_Rachni.ovt b/UI/data/themes/Yami_Rachni.ovt new file mode 100644 index 00000000000000..444cdbe7ce1764 --- /dev/null +++ b/UI/data/themes/Yami_Rachni.ovt @@ -0,0 +1,221 @@ +@OBSThemeMeta { + name: 'Rachni'; + id: 'com.obsproject.Yami.Rachni'; + extends: 'com.obsproject.Yami'; + author: 'Warchamp7'; + dark: 'true'; +} + +@OBSThemeVars { + --grey1: rgb(118,121,124); + --grey2: rgb(110,113,114); + --grey3: rgb(59,65,71); + --grey4: rgb(49,54,59); + --grey5: rgb(42,46,50); + --grey6: rgb(35,38,41); + --grey7: rgb(25,27,38); + --grey8: rgb(21,22,23); + + --primary: rgb(0,188,212); + --primary_light: rgb(145,76,103); + --primary_lighter: rgb(240,96,146); + --primary_dark: rgb(89,66,79); + --primary_darker: rgb(25,27,38); + + --bg_base: var(--grey6); + --bg_window: var(--grey4); + --bg_preview: rgb(34,37,40); + + --border_color: var(--grey5); + + --input_bg: var(--grey6); + --input_bg_hover: var(--grey7); + --input_bg_focus: var(--grey7); + + --list_item_bg_selected: var(--primary); + --list_item_bg_hover: var(--primary_light); + + --input_border: var(--grey1); + --input_border_hover: var(--primary); + --input_border_focus: var(--primary); + + --spacing_input: var(--spacing_base); + + --button_bg: var(--primary); + --button_bg_hover: var(--primary_light); + --button_bg_down: var(--primary_dark); + --button_bg_disabled: var(--grey6); + + --button_bg_red: rgb(88,22,36); + --button_bg_red_hover: rgb(116,32,49); + --button_bg_red_down: rgb(63,21,30); + + --button_border: var(--primary); + --button_border_hover: var(--button_bg_hover); + --button_border_focus: var(--button_bg_hover); + + --tab_bg: var(--input_bg); + --tab_bg_hover: var(--grey3); + --tab_bg_down: var(--grey7); + --tab_bg_disabled: var(--grey6); + + --tab_border: var(--grey3); + --tab_border_hover: var(--grey1); + --tab_border_focus: var(--grey1); + --tab_border_selected: var(--primary_light); + + --scrollbar: var(--grey2); + --scrollbar_hover: var(--primary_light); + --scrollbar_down: var(--grey3); + --scrollbar_border: var(--primary_light); + + --toolbutton_bg: var(--grey3); + --toolbutton_bg_hover: var(--primary_light); + --toolbutton_bg_down: var(--primary_dark); + --toolbutton_bg_disabled: var(--grey4); +} + +QDockWidget::title { + background-color: var(--bg_base); +} + +QDockWidget::close-button:hover, +QDockWidget::float-button:hover { + background: var(--grey3); +} + +QComboBox, +QDateTimeEdit, +QLineEdit, +QPlainTextEdit, +QTextEdit, +QSpinBox, +QDoubleSpinBox { + border-width: 2px; + border-color: var(--input_border); +} + +QMenu::item:selected, +QListWidget::item:selected, +SceneTree::item:selected, +SourceTree::item:selected { + background-color: var(--primary_dark); +} + +QMenu::item:hover, +QListWidget::item:hover, +SceneTree::item:hover, +SourceTree::item:hover, +QMenu::item:selected:hover, +QListWidget::item:selected:hover, +SceneTree::item:selected:hover, +SourceTree::item:selected:hover { + background-color: var(--primary); +} + +QToolBar { + background-color: var(--grey4); + padding: var(--padding_large); + margin: 0; +} + +QPushButton:disabled { + border-color: var(--grey3); +} + +QToolButton, +QPushButton[toolButton="true"] { + background-color: var(--toolbutton_bg); + border-color: var(--toolbutton_bg); +} + +QToolButton:hover, +QToolButton:focus { + border-color: var(--primary_light); + background-color: var(--primary_light); +} + +QToolButton:pressed, +QToolButton:pressed:hover { + background-color: var(--toolbutton_bg_down); + border-color: var(--toolbutton_bg_down); +} + +MuteCheckBox::indicator, +MuteCheckBox::indicator:unchecked, +MuteCheckBox::indicator:focus { + background-color: var(--toolbutton_bg); + border: 1px solid var(--toolbutton_bg); +} + +MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:unchecked:hover { + background-color: var(--toolbutton_bg_hover); + border: 1px solid var(--toolbutton_bg_hover); +} + +MuteCheckBox::indicator:pressed, +MuteCheckBox::indicator:pressed:hover { + background-color: var(--toolbutton_bg_down); + border-color: var(--toolbutton_bg_down); +} + +#stackedMixerArea QPushButton, +#stackedMixerArea QPushButton:!hover { + background-color: var(--toolbutton_bg); + border-color: var(--toolbutton_bg); +} + +#stackedMixerArea QPushButton:hover { + background-color: var(--toolbutton_bg_hover); +} + +#stackedMixerArea QPushButton:pressed { + background-color: var(--toolbutton_bg_down); +} + +QToolButton:disabled, +QPushButton[toolButton="true"]:disabled { + background-color: var(--toolbutton_bg_disabled); + border-color: transparent; +} + +QTabBar::tab { + background-color: var(--grey5); +} + +QTabBar::tab:top:selected { + border-bottom: 2px solid var(--primary_light); +} + +QTabBar QToolButton { + background-color: var(--grey5); +} + +QGroupBox { + background-color: var(--grey3); +} + +OBSBasicSettings #PropertiesContainer { + background-color: var(--grey3); +} + +QGroupBox::title { + color: var(--primary_lighter); +} + +QSlider::groove { + background-color: var(--grey2); +} + +VolumeMeter { + qproperty-backgroundNominalColor: rgb(0,128,79); + qproperty-backgroundWarningColor: rgb(128,57,0); + qproperty-backgroundErrorColor: rgb(128,9,0); + qproperty-foregroundNominalColor: rgb(119,255,143); + qproperty-foregroundWarningColor: rgb(255,157,76); + qproperty-foregroundErrorColor: rgb(255,89,76); + qproperty-magnitudeColor: palette(window); + qproperty-majorTickColor: palette(window-text); + qproperty-minorTickColor: palette(mid); +} From cfb10ece79383eaa97608c6deb62ab9d67810faf Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Sun, 14 Apr 2024 20:13:56 -0700 Subject: [PATCH 0032/1073] text-freetype2: Use a shader uniform instead of vertex attributes We used to swap in a buffer with (0,0,0,1) for all vertex colors for drop shadows and outlines. However, this vertex buffer couldn't be uploaded separately from the vertex data in OBS, so we were reuploading the text vertices every frame. Instead, let's use a uniform for this uniform data and save 500us (or more when handles are visible), a significant portion of my test scenes render time. --- .../text-freetype2/data/text_default.effect | 3 +- plugins/text-freetype2/obs-convenience.c | 7 ++-- plugins/text-freetype2/obs-convenience.h | 2 +- plugins/text-freetype2/text-freetype2.c | 4 +-- plugins/text-freetype2/text-freetype2.h | 1 - plugins/text-freetype2/text-functionality.c | 35 ++----------------- 6 files changed, 12 insertions(+), 40 deletions(-) diff --git a/plugins/text-freetype2/data/text_default.effect b/plugins/text-freetype2/data/text_default.effect index 39b82e10487e84..177e103f4af2ed 100644 --- a/plugins/text-freetype2/data/text_default.effect +++ b/plugins/text-freetype2/data/text_default.effect @@ -1,5 +1,6 @@ uniform float4x4 ViewProj; uniform texture2d image; +uniform bool use_color; sampler_state def_sampler { Filter = Linear; @@ -18,7 +19,7 @@ VertInOut VSDefault(VertInOut vert_in) VertInOut vert_out; vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj); vert_out.uv = vert_in.uv; - vert_out.col = vert_in.col; + vert_out.col = use_color ? vert_in.col : float4(0.0, 0.0, 0.0, 1.0); return vert_out; } diff --git a/plugins/text-freetype2/obs-convenience.c b/plugins/text-freetype2/obs-convenience.c index 3607a9a81f787e..38a842d7613ef1 100644 --- a/plugins/text-freetype2/obs-convenience.c +++ b/plugins/text-freetype2/obs-convenience.c @@ -57,7 +57,7 @@ gs_vertbuffer_t *create_uv_vbuffer(uint32_t num_verts, bool add_color) } void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex, - gs_effect_t *effect, uint32_t num_verts) + gs_effect_t *effect, uint32_t num_verts, bool use_color) { gs_texture_t *texture = tex; gs_technique_t *tech = gs_effect_get_technique(effect, "Draw"); @@ -72,7 +72,6 @@ void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex, const bool previous = gs_framebuffer_srgb_enabled(); gs_enable_framebuffer_srgb(linear_srgb); - gs_vertexbuffer_flush(vbuf); gs_load_vertexbuffer(vbuf); gs_load_indexbuffer(NULL); @@ -85,6 +84,10 @@ void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex, else gs_effect_set_texture(image, texture); + gs_effect_set_bool(gs_effect_get_param_by_name( + effect, "use_color"), + use_color); + gs_draw(GS_TRIS, 0, num_verts); gs_technique_end_pass(tech); diff --git a/plugins/text-freetype2/obs-convenience.h b/plugins/text-freetype2/obs-convenience.h index f004686c38279f..ebcffafc07b7d3 100644 --- a/plugins/text-freetype2/obs-convenience.h +++ b/plugins/text-freetype2/obs-convenience.h @@ -21,7 +21,7 @@ along with this program. If not, see . gs_vertbuffer_t *create_uv_vbuffer(uint32_t num_verts, bool add_color); void draw_uv_vbuffer(gs_vertbuffer_t *vbuf, gs_texture_t *tex, - gs_effect_t *effect, uint32_t num_verts); + gs_effect_t *effect, uint32_t num_verts, bool use_color); #define set_v3_rect(a, x, y, w, h) \ vec3_set(a, x, y, 0.0f); \ diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index 1639a2fe881cd5..3de3dc4c68f1cf 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -240,8 +240,6 @@ static void ft2_source_destroy(void *data) bfree(srcdata->text); if (srcdata->texbuf != NULL) bfree(srcdata->texbuf); - if (srcdata->colorbuf != NULL) - bfree(srcdata->colorbuf); if (srcdata->text_file != NULL) bfree(srcdata->text_file); @@ -283,7 +281,7 @@ static void ft2_source_render(void *data, gs_effect_t *effect) draw_drop_shadow(srcdata); draw_uv_vbuffer(srcdata->vbuf, srcdata->tex, srcdata->draw_effect, - (uint32_t)wcslen(srcdata->text) * 6); + (uint32_t)wcslen(srcdata->text) * 6, true); UNUSED_PARAMETER(effect); } diff --git a/plugins/text-freetype2/text-freetype2.h b/plugins/text-freetype2/text-freetype2.h index 120c36a22b6de1..98e404f842d944 100644 --- a/plugins/text-freetype2/text-freetype2.h +++ b/plugins/text-freetype2/text-freetype2.h @@ -48,7 +48,6 @@ struct ft2_source { uint32_t outline_width; uint32_t texbuf_x, texbuf_y; uint32_t color[2]; - uint32_t *colorbuf; int32_t cur_scroll, scroll_speed; diff --git a/plugins/text-freetype2/text-functionality.c b/plugins/text-freetype2/text-functionality.c index e20176a8f7539f..08eb96e72a66ad 100644 --- a/plugins/text-freetype2/text-functionality.c +++ b/plugins/text-freetype2/text-functionality.c @@ -30,52 +30,32 @@ extern uint32_t texbuf_w, texbuf_h; void draw_outlines(struct ft2_source *srcdata) { - // Horrible (hopefully temporary) solution for outlines. - uint32_t *tmp; - - struct gs_vb_data *vdata = gs_vertexbuffer_get_data(srcdata->vbuf); - if (!srcdata->text) return; - tmp = vdata->colors; - vdata->colors = srcdata->colorbuf; - gs_matrix_push(); for (int32_t i = 0; i < 8; i++) { gs_matrix_translate3f(offsets[i * 2], offsets[(i * 2) + 1], 0.0f); draw_uv_vbuffer(srcdata->vbuf, srcdata->tex, srcdata->draw_effect, - (uint32_t)wcslen(srcdata->text) * 6); + (uint32_t)wcslen(srcdata->text) * 6, false); } gs_matrix_identity(); gs_matrix_pop(); - - vdata->colors = tmp; } void draw_drop_shadow(struct ft2_source *srcdata) { - // Horrible (hopefully temporary) solution for drop shadow. - uint32_t *tmp; - - struct gs_vb_data *vdata = gs_vertexbuffer_get_data(srcdata->vbuf); - if (!srcdata->text) return; - tmp = vdata->colors; - vdata->colors = srcdata->colorbuf; - gs_matrix_push(); gs_matrix_translate3f(4.0f, 4.0f, 0.0f); draw_uv_vbuffer(srcdata->vbuf, srcdata->tex, srcdata->draw_effect, - (uint32_t)wcslen(srcdata->text) * 6); + (uint32_t)wcslen(srcdata->text) * 6, false); gs_matrix_identity(); gs_matrix_pop(); - - vdata->colors = tmp; } void set_up_vertex_buffer(struct ft2_source *srcdata) @@ -147,6 +127,7 @@ void set_up_vertex_buffer(struct ft2_source *srcdata) skip_word_wrap:; fill_vertex_buffer(srcdata); + gs_vertexbuffer_flush(srcdata->vbuf); obs_leave_graphics(); } @@ -171,16 +152,6 @@ void fill_vertex_buffer(struct ft2_source *srcdata) dx = offset; } - if (srcdata->colorbuf != NULL) { - bfree(srcdata->colorbuf); - srcdata->colorbuf = NULL; - } - srcdata->colorbuf = - bzalloc(sizeof(uint32_t) * wcslen(srcdata->text) * 6); - for (size_t i = 0; i < len * 6; i++) { - srcdata->colorbuf[i] = 0xFF000000; - } - for (size_t i = 0; i < len; i++) { add_linebreak:; if (srcdata->text[i] != L'\n') From 065d4533ee4ca3a63900598cba4c875f98aa5489 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 12 Dec 2023 21:36:27 -0800 Subject: [PATCH 0033/1073] obs-webrtc: Add support for audio-only and video-only outputs Adds the `whip_output_audio` and `whip_output_video` output kinds, which selectively advertise only video or only audio support. To use these types, it is effectively the same as with the AV version. Just create the output, assign your video or audio encoder, and you're good. libobs does not have support for "optional" outputs. With an AV output, if you only assign a video or audio encoder, start will fail. --- plugins/obs-webrtc/whip-output.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index 4d8606862e52fc..b236111e55a6b3 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -83,12 +83,12 @@ void WHIPOutput::Data(struct encoder_packet *packet) return; } - if (packet->type == OBS_ENCODER_AUDIO) { + if (audio_track && packet->type == OBS_ENCODER_AUDIO) { int64_t duration = packet->dts_usec - last_audio_timestamp; Send(packet->data, packet->size, duration, audio_track, audio_sr_reporter); last_audio_timestamp = packet->dts_usec; - } else if (packet->type == OBS_ENCODER_VIDEO) { + } else if (video_track && packet->type == OBS_ENCODER_VIDEO) { int64_t duration = packet->dts_usec - last_video_timestamp; Send(packet->data, packet->size, duration, video_track, video_sr_reporter); @@ -99,6 +99,12 @@ void WHIPOutput::Data(struct encoder_packet *packet) void WHIPOutput::ConfigureAudioTrack(std::string media_stream_id, std::string cname) { + if (!obs_output_get_audio_encoder(output, 0)) { + do_log(LOG_DEBUG, + "Not configuring audio track: Audio encoder not assigned"); + return; + } + auto media_stream_track_id = std::string(media_stream_id + "-audio"); uint32_t ssrc = base_ssrc; @@ -125,6 +131,12 @@ void WHIPOutput::ConfigureAudioTrack(std::string media_stream_id, void WHIPOutput::ConfigureVideoTrack(std::string media_stream_id, std::string cname) { + if (!obs_output_get_video_encoder(output)) { + do_log(LOG_DEBUG, + "Not configuring video track: Video encoder not assigned"); + return; + } + auto media_stream_track_id = std::string(media_stream_id + "-video"); std::shared_ptr packetizer; @@ -590,10 +602,11 @@ void WHIPOutput::Send(void *data, uintptr_t size, uint64_t duration, void register_whip_output() { - struct obs_output_info info = {}; + const uint32_t base_flags = OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE; + struct obs_output_info info = {}; info.id = "whip_output"; - info.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE; + info.flags = OBS_OUTPUT_AV | base_flags; info.get_name = [](void *) -> const char * { return obs_module_text("Output.Name"); }; @@ -630,4 +643,12 @@ void register_whip_output() info.protocols = "WHIP"; obs_register_output(&info); + + info.id = "whip_output_video"; + info.flags = OBS_OUTPUT_VIDEO | base_flags; + obs_register_output(&info); + + info.id = "whip_output_audio"; + info.flags = OBS_OUTPUT_AUDIO | base_flags; + obs_register_output(&info); } From 93bedd21c8394df85cd62c73af7e15b4d9119b0a Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Wed, 10 Apr 2024 17:28:26 -0400 Subject: [PATCH 0034/1073] UI: Reset duration when removing show/hide transition --- UI/window-basic-main-transitions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 936d01474c8363..edf3baf9ffa08e 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -1184,6 +1184,8 @@ QMenu *OBSBasic::CreateVisibilityTransitionMenu(bool visible) if (id.isNull() || id.isEmpty()) { obs_sceneitem_set_transition(sceneItem, visible, nullptr); + obs_sceneitem_set_transition_duration(sceneItem, + visible, 0); } else { OBSSource tr = obs_sceneitem_get_transition(sceneItem, visible); From 65462fdc1dfa747d232c4d580f4c3c83c89ef8df Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 18 Apr 2024 19:05:23 -0400 Subject: [PATCH 0035/1073] CI: Update softprops/action-gh-release to v2.0.4 This is one of the few remaining actions in this repo that was still using node16. Updating will remove the associated warnings. --- .github/workflows/push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 1bcd71fc670469..77402ece7efc11 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -298,7 +298,7 @@ jobs: - name: Create Release 🛫 if: fromJSON(steps.check.outputs.validTag) id: create_release - uses: softprops/action-gh-release@d4e8205d7e959a9107da6396278b2f1f07af0f9b + uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 with: draft: true prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }} From cfe91925726e8c24a77441df954c47ed880b8c58 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 18 Apr 2024 19:40:56 -0400 Subject: [PATCH 0036/1073] CI: Update yuzutech/annotations-action to v0.5.0 This is one of the few remaining actions in this repo that was still using node16. Updating will remove the associated warnings. Also, pin to the v0.5.0 commit. --- .github/actions/compatibility-validator/action.yaml | 2 +- .github/actions/services-validator/action.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/compatibility-validator/action.yaml b/.github/actions/compatibility-validator/action.yaml index a133e70a1f592a..da0bd62ef715bc 100644 --- a/.github/actions/compatibility-validator/action.yaml +++ b/.github/actions/compatibility-validator/action.yaml @@ -55,7 +55,7 @@ runs: echo ::endgroup:: - name: Annotate Schema Validation Errors 🏷️ - uses: yuzutech/annotations-action@v0.4.0 + uses: yuzutech/annotations-action@0e061a6e3ac848299310b6429b60d67cafd4e7f8 if: failure() with: repo-token: ${{ inputs.repositorySecret }} diff --git a/.github/actions/services-validator/action.yaml b/.github/actions/services-validator/action.yaml index 9a691acb37e841..b82a036317c6cb 100644 --- a/.github/actions/services-validator/action.yaml +++ b/.github/actions/services-validator/action.yaml @@ -80,7 +80,7 @@ runs: - name: Annotate schema validation errors 🏷️ if: fromJSON(inputs.runSchemaChecks) && failure() - uses: yuzutech/annotations-action@v0.4.0 + uses: yuzutech/annotations-action@0e061a6e3ac848299310b6429b60d67cafd4e7f8 with: repo-token: ${{ inputs.repositorySecret }} title: Service JSON Errors From 14f588f318440df3d77d4666fa0c503a70be7ddb Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 21 Apr 2024 18:11:27 +0200 Subject: [PATCH 0037/1073] obs-ffmpeg: Fix output path logging --- plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index b1b46f88fb4325..111b48336b6242 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -306,6 +306,7 @@ static void build_command_line(struct ffmpeg_muxer *stream, *args = os_process_args_create(exe); bfree(exe); + dstr_copy(&stream->path, path); os_process_args_add_arg(*args, path); os_process_args_add_argf(*args, "%d", vencoder ? 1 : 0); os_process_args_add_argf(*args, "%d", num_tracks); From 402ba09a77a236332b2cd4cda42ca4fbc224c28a Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 24 Apr 2024 21:36:36 -0300 Subject: [PATCH 0038/1073] UI: Update filters icon - Add rounder borders - Adjust fills --- UI/data/themes/Dark/filter.svg | 2 +- UI/data/themes/Light/filter.svg | 2 +- UI/forms/images/filter.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/data/themes/Dark/filter.svg b/UI/data/themes/Dark/filter.svg index 0038a7618e6106..4054df0e29f80a 100644 --- a/UI/data/themes/Dark/filter.svg +++ b/UI/data/themes/Dark/filter.svg @@ -1,2 +1,2 @@ - + diff --git a/UI/data/themes/Light/filter.svg b/UI/data/themes/Light/filter.svg index ed6de819d78f94..730642554e2568 100644 --- a/UI/data/themes/Light/filter.svg +++ b/UI/data/themes/Light/filter.svg @@ -1,2 +1,2 @@ - + diff --git a/UI/forms/images/filter.svg b/UI/forms/images/filter.svg index 57a79c8e714407..a9f2de2dba779a 100644 --- a/UI/forms/images/filter.svg +++ b/UI/forms/images/filter.svg @@ -1,2 +1,2 @@ - + From df1ec719d13c837089647b57b44899cb9d4a5df5 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Wed, 24 Apr 2024 21:39:57 -0300 Subject: [PATCH 0039/1073] UI: Update appearance icon - Use the monitor with brush symbolic --- UI/data/themes/Dark/settings/appearance.svg | 2 +- UI/data/themes/Light/settings/appearance.svg | 2 +- UI/forms/images/settings/appearance.svg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/data/themes/Dark/settings/appearance.svg b/UI/data/themes/Dark/settings/appearance.svg index 0038a7618e6106..c27952b450d4f1 100644 --- a/UI/data/themes/Dark/settings/appearance.svg +++ b/UI/data/themes/Dark/settings/appearance.svg @@ -1,2 +1,2 @@ - + diff --git a/UI/data/themes/Light/settings/appearance.svg b/UI/data/themes/Light/settings/appearance.svg index ed6de819d78f94..2ef0496b4162d0 100644 --- a/UI/data/themes/Light/settings/appearance.svg +++ b/UI/data/themes/Light/settings/appearance.svg @@ -1,2 +1,2 @@ - + diff --git a/UI/forms/images/settings/appearance.svg b/UI/forms/images/settings/appearance.svg index 57a79c8e714407..ac9070b266cecd 100644 --- a/UI/forms/images/settings/appearance.svg +++ b/UI/forms/images/settings/appearance.svg @@ -1,2 +1,2 @@ - + From 1ba54adb85edbb9cacc6f0bae0dcca68a8ecf1e2 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sun, 21 Apr 2024 05:26:22 -0400 Subject: [PATCH 0040/1073] UI: Clean up theme file formatting --- UI/data/themes/Yami.obt | 23 +++++++++++------------ UI/data/themes/Yami_Acri.ovt | 2 +- UI/data/themes/Yami_Classic.ovt | 2 +- UI/data/themes/Yami_Rachni.ovt | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 642cb203fd7c4f..f35137a97f8354 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -93,12 +93,12 @@ /* Layout */ /* Configurable Values */ - --font_base_value: 10; // TODO: Min 8, Max 12, Step 1 - --spacing_base_value: 4; // TODO: Min 2, Max 7, Step 1 - --padding_base_value: 4; // TODO: Min 0.25, Max 10, Step 2 + --font_base_value: 10; /* TODO: Min 8, Max 12, Step 1 */ + --spacing_base_value: 4; /* TODO: Min 2, Max 7, Step 1 */ + --padding_base_value: 4; /* TODO: Min 0.25, Max 10, Step 2 */ - --border_highlight: "transparent"; // TODO: Better Accessibility focus state - // TODO: Move Accessibilty Colors to Theme config system + --border_highlight: "transparent"; /* TODO: Better Accessibility focus state */ + /* TODO: Move Accessibilty Colors to Theme config system */ --font_base: calc(1pt * var(--font_base_value)); --font_small: calc(0.9pt * var(--font_base_value)); @@ -184,12 +184,12 @@ --highlight: rgb(42,130,218); --highlight_inactive: rgb(25,28,34); - + /* Qt Palette variables can be set with the "palette_" prefix */ --palette_window: var(--bg_window); --palette_windowText: var(--text); --palette_base: var(--bg_base); - + --palette_light: var(--grey2); --palette_mid: var(--grey7); --palette_dark: var(--grey6); @@ -208,7 +208,7 @@ --palette_text_active: var(--text); --palette_text_disabled: var(--text_disabled); --palette_text_inactive: var(--text_inactive); - + /* * Variables calculated at runtime (after all themes have been composed). * @@ -778,7 +778,6 @@ QTabBar::tab:top:selected { } QTabBar::tab:bottom { - } QTabBar::tab:bottom:selected { @@ -915,7 +914,7 @@ QSpinBox::up-button, QDoubleSpinBox::up-button { subcontrol-origin: padding; subcontrol-position: top right; /* position at the top right corner */ - + width: 32px; border-left: 1px solid var(--grey6); border-bottom: 1px solid transparent; @@ -927,7 +926,7 @@ QSpinBox::down-button, QDoubleSpinBox::down-button { subcontrol-origin: padding; subcontrol-position: bottom right; /* position at the top right corner */ - + width: 32px; border-left: 1px solid var(--grey6); border-top: 1px solid var(--grey6); @@ -1498,7 +1497,7 @@ MuteCheckBox::indicator:unchecked { icon-size: var(--icon_base), var(--icon_base); } -MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { background-color: var(--button_bg_hover); padding: var(--padding_base_border) var(--padding_base_border); diff --git a/UI/data/themes/Yami_Acri.ovt b/UI/data/themes/Yami_Acri.ovt index 2a847e6325c19b..e375704801a824 100644 --- a/UI/data/themes/Yami_Acri.ovt +++ b/UI/data/themes/Yami_Acri.ovt @@ -114,7 +114,7 @@ MuteCheckBox::indicator:focus { border: 1px solid var(--toolbutton_bg); } -MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { background-color: var(--toolbutton_bg_hover); border: 1px solid var(--toolbutton_bg_hover); diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index c365f1c3e4c516..ffc6a863b84214 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -205,7 +205,7 @@ MuteCheckBox::indicator:unchecked { icon-size: var(--icon_base_mixer), var(--icon_base_mixer); } -MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { icon-size: var(--icon_base_mixer), var(--icon_base_mixer); } diff --git a/UI/data/themes/Yami_Rachni.ovt b/UI/data/themes/Yami_Rachni.ovt index 444cdbe7ce1764..7f6fa4ae078563 100644 --- a/UI/data/themes/Yami_Rachni.ovt +++ b/UI/data/themes/Yami_Rachni.ovt @@ -148,7 +148,7 @@ MuteCheckBox::indicator:focus { border: 1px solid var(--toolbutton_bg); } -MuteCheckBox::indicator:hover, +MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { background-color: var(--toolbutton_bg_hover); border: 1px solid var(--toolbutton_bg_hover); From 7d8a78474c36c34b806031fb9165482b5c0def5c Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 21 Apr 2024 12:16:02 +0200 Subject: [PATCH 0041/1073] Add composable theme files spacing in editorconfig --- .editorconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index 91b004870397c9..5c9704f411edb1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -57,3 +57,7 @@ indent_size = 2 [*.ui] indent_style = space indent_size = 1 + +[{*.obt,*.oha,*.ovt}] +indent_style = space +indent_size = 4 From 2fc13540f0a3449b62f5de9dfd53343ea3f341ef Mon Sep 17 00:00:00 2001 From: tt2468 Date: Thu, 25 Jan 2024 05:22:47 +0000 Subject: [PATCH 0042/1073] libobs: Rewrite video-frame Rewrites the `struct video_frame` implementation, resolving a few hidden bugs, and improving memory alignment reliability. Should also make it much easier to implement and maintain texture formats in this part of the codebase. --- libobs/media-io/video-frame.c | 514 ++++++++++++---------------------- 1 file changed, 185 insertions(+), 329 deletions(-) diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index 5dc761d8d9f44a..0a601f9e57ae99 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -14,362 +14,136 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ - +#include #include "video-frame.h" -#define ALIGN_SIZE(size, align) size = (((size) + (align - 1)) & (~(align - 1))) +#define HALF(size) ((size + 1) / 2) +#define ALIGN(size, alignment) \ + *size = (*size + alignment - 1) & (~(alignment - 1)); -/* messy code alarm */ -void video_frame_init(struct video_frame *frame, enum video_format format, - uint32_t width, uint32_t height) +static inline void align_size(size_t *size, size_t alignment) { - size_t size; - size_t offsets[MAX_AV_PLANES]; - int alignment = base_get_alignment(); - - if (!frame) - return; + ALIGN(size, alignment); +} - memset(frame, 0, sizeof(struct video_frame)); - memset(offsets, 0, sizeof(offsets)); +static inline void align_uint32(uint32_t *size, size_t alignment) +{ + ALIGN(size, (uint32_t)alignment); +} +/* assumes already-zeroed array */ +void video_frame_get_linesizes(uint32_t linesize[MAX_AV_PLANES], + enum video_format format, uint32_t width) +{ switch (format) { + default: case VIDEO_FORMAT_NONE: - return; - - case VIDEO_FORMAT_I420: { - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t half_width = (width + 1) / 2; - const uint32_t half_height = (height + 1) / 2; - const uint32_t quarter_area = half_width * half_height; - size += quarter_area; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += quarter_area; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->linesize[0] = width; - frame->linesize[1] = half_width; - frame->linesize[2] = half_width; - break; - } - - case VIDEO_FORMAT_NV12: { - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t cbcr_width = (width + 1) & (UINT32_MAX - 1); - size += cbcr_width * ((height + 1) / 2); - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->linesize[0] = width; - frame->linesize[1] = cbcr_width; break; - } - - case VIDEO_FORMAT_Y800: - size = width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = width; - break; - - case VIDEO_FORMAT_YVYU: - case VIDEO_FORMAT_YUY2: - case VIDEO_FORMAT_UYVY: { - const uint32_t double_width = - ((width + 1) & (UINT32_MAX - 1)) * 2; - size = double_width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = double_width; + case VIDEO_FORMAT_BGR3: /* one plane: triple width */ + linesize[0] = width * 3; break; - } - - case VIDEO_FORMAT_RGBA: + case VIDEO_FORMAT_RGBA: /* one plane: quadruple width */ case VIDEO_FORMAT_BGRA: case VIDEO_FORMAT_BGRX: case VIDEO_FORMAT_AYUV: - size = width * height * 4; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = width * 4; - break; - - case VIDEO_FORMAT_I444: - size = width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size * 3); - frame->data[1] = (uint8_t *)frame->data[0] + size; - frame->data[2] = (uint8_t *)frame->data[1] + size; - frame->linesize[0] = width; - frame->linesize[1] = width; - frame->linesize[2] = width; + case VIDEO_FORMAT_R10L: + linesize[0] = width * 4; break; - - case VIDEO_FORMAT_I412: - size = width * height * 2; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size * 3); - frame->data[1] = (uint8_t *)frame->data[0] + size; - frame->data[2] = (uint8_t *)frame->data[1] + size; - frame->linesize[0] = width * 2; - frame->linesize[1] = width * 2; - frame->linesize[2] = width * 2; + case VIDEO_FORMAT_P416: /* two planes: double width, quadruple width */ + linesize[0] = width * 2; + linesize[1] = width * 4; break; - - case VIDEO_FORMAT_BGR3: - size = width * height * 3; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = width * 3; + case VIDEO_FORMAT_I420: /* three planes: full width, half width, half width */ + case VIDEO_FORMAT_I422: + linesize[0] = width; + linesize[1] = HALF(width); + linesize[2] = HALF(width); break; - - case VIDEO_FORMAT_I422: { - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t half_width = (width + 1) / 2; - const uint32_t half_area = half_width * height; - size += half_area; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += half_area; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->linesize[0] = width; - frame->linesize[1] = half_width; - frame->linesize[2] = half_width; + case VIDEO_FORMAT_I210: /* three planes: double width, full width, full width */ + case VIDEO_FORMAT_I010: + linesize[0] = width * 2; + linesize[1] = width; + linesize[2] = width; break; - } - - case VIDEO_FORMAT_I210: { - size = width * height * 2; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t half_width = (width + 1) / 2; - const uint32_t half_area = half_width * height; - const uint32_t half_area_size = 2 * half_area; - size += half_area_size; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += half_area_size; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->linesize[0] = width * 2; - frame->linesize[1] = half_width * 2; - frame->linesize[2] = half_width * 2; + case VIDEO_FORMAT_I40A: /* four planes: full width, half width, half width, full width */ + case VIDEO_FORMAT_I42A: + linesize[0] = width; + linesize[1] = HALF(width); + linesize[2] = HALF(width); + linesize[3] = width; break; - } - case VIDEO_FORMAT_I40A: { - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t half_width = (width + 1) / 2; - const uint32_t half_height = (height + 1) / 2; - const uint32_t quarter_area = half_width * half_height; - size += quarter_area; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += quarter_area; - ALIGN_SIZE(size, alignment); - offsets[2] = size; - size += width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->data[3] = (uint8_t *)frame->data[0] + offsets[2]; - frame->linesize[0] = width; - frame->linesize[1] = half_width; - frame->linesize[2] = half_width; - frame->linesize[3] = width; + case VIDEO_FORMAT_YVYU: /* one plane: double width */ + case VIDEO_FORMAT_YUY2: + case VIDEO_FORMAT_UYVY: + linesize[0] = width * 2; break; - } - - case VIDEO_FORMAT_I42A: { - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t half_width = (width + 1) / 2; - const uint32_t half_area = half_width * height; - size += half_area; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += half_area; - ALIGN_SIZE(size, alignment); - offsets[2] = size; - size += width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->data[3] = (uint8_t *)frame->data[0] + offsets[2]; - frame->linesize[0] = width; - frame->linesize[1] = half_width; - frame->linesize[2] = half_width; - frame->linesize[3] = width; + case VIDEO_FORMAT_P010: /* two planes: all double width */ + case VIDEO_FORMAT_P216: + linesize[0] = width * 2; + linesize[1] = width * 2; break; - } - - case VIDEO_FORMAT_YUVA: - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - size += width * height; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += width * height; - ALIGN_SIZE(size, alignment); - offsets[2] = size; - size += width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->data[3] = (uint8_t *)frame->data[0] + offsets[2]; - frame->linesize[0] = width; - frame->linesize[1] = width; - frame->linesize[2] = width; - frame->linesize[3] = width; + case VIDEO_FORMAT_I412: /* three planes: all double width */ + linesize[0] = width * 2; + linesize[1] = width * 2; + linesize[2] = width * 2; break; - - case VIDEO_FORMAT_YA2L: { - const uint32_t linesize = width * 2; - const uint32_t plane_size = linesize * height; - size = plane_size; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - size += plane_size; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += plane_size; - ALIGN_SIZE(size, alignment); - offsets[2] = size; - size += plane_size; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->data[3] = (uint8_t *)frame->data[0] + offsets[2]; - frame->linesize[0] = linesize; - frame->linesize[1] = linesize; - frame->linesize[2] = linesize; - frame->linesize[3] = linesize; + case VIDEO_FORMAT_YA2L: /* four planes: all double width */ + linesize[0] = width * 2; + linesize[1] = width * 2; + linesize[2] = width * 2; + linesize[3] = width * 2; break; - } - case VIDEO_FORMAT_I010: { - size = width * height * 2; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t half_width = (width + 1) / 2; - const uint32_t half_height = (height + 1) / 2; - const uint32_t quarter_area = half_width * half_height; - size += quarter_area * 2; - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += quarter_area * 2; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t *)frame->data[0] + offsets[1]; - frame->linesize[0] = width * 2; - frame->linesize[1] = half_width * 2; - frame->linesize[2] = half_width * 2; + case VIDEO_FORMAT_Y800: /* one plane: full width */ + linesize[0] = width; break; - } - - case VIDEO_FORMAT_P010: { - size = width * height * 2; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t cbcr_width = (width + 1) & (UINT32_MAX - 1); - size += cbcr_width * ((height + 1) / 2) * 2; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->linesize[0] = width * 2; - frame->linesize[1] = cbcr_width * 2; + case VIDEO_FORMAT_NV12: /* two planes: all full width */ + linesize[0] = width; + linesize[1] = width; break; - } - - case VIDEO_FORMAT_P216: { - size = width * height * 2; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - const uint32_t cbcr_width = (width + 1) & (UINT32_MAX - 1); - size += cbcr_width * height * 2; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->linesize[0] = width * 2; - frame->linesize[1] = cbcr_width * 2; + case VIDEO_FORMAT_I444: /* three planes: all full width */ + linesize[0] = width; + linesize[1] = width; + linesize[2] = width; break; - } - - case VIDEO_FORMAT_P416: { - size = width * height * 2; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - size += width * height * 4; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t *)frame->data[0] + offsets[0]; - frame->linesize[0] = width * 2; - frame->linesize[1] = width * 4; + case VIDEO_FORMAT_YUVA: /* four planes: all full width */ + linesize[0] = width; + linesize[1] = width; + linesize[2] = width; + linesize[3] = width; break; - } - case VIDEO_FORMAT_V210: { - const uint32_t adjusted_width = ((width + 5) / 6) * 16; - size = adjusted_width * height; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = adjusted_width; + case VIDEO_FORMAT_V210: { /* one plane: bruh (Little Endian Compressed) */ + align_uint32(&width, 48); + linesize[0] = ((width + 5) / 6) * 16; break; } - - case VIDEO_FORMAT_R10L: - size = width * height * 4; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = width * 4; - break; } } -void video_frame_copy(struct video_frame *dst, const struct video_frame *src, - enum video_format format, uint32_t cy) +void video_frame_get_plane_heights(uint32_t heights[MAX_AV_PLANES], + enum video_format format, uint32_t height) { switch (format) { + default: case VIDEO_FORMAT_NONE: return; - case VIDEO_FORMAT_I420: + case VIDEO_FORMAT_I420: /* three planes: full height, half height, half height */ case VIDEO_FORMAT_I010: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); - memcpy(dst->data[1], src->data[1], src->linesize[1] * cy / 2); - memcpy(dst->data[2], src->data[2], src->linesize[2] * cy / 2); + heights[0] = height; + heights[1] = height / 2; + heights[2] = height / 2; break; - case VIDEO_FORMAT_NV12: + case VIDEO_FORMAT_NV12: /* two planes: full height, half height */ case VIDEO_FORMAT_P010: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); - memcpy(dst->data[1], src->data[1], src->linesize[1] * cy / 2); + heights[0] = height; + heights[1] = height / 2; break; - case VIDEO_FORMAT_Y800: + case VIDEO_FORMAT_Y800: /* one plane: full height */ case VIDEO_FORMAT_YVYU: case VIDEO_FORMAT_YUY2: case VIDEO_FORMAT_UYVY: @@ -380,38 +154,120 @@ void video_frame_copy(struct video_frame *dst, const struct video_frame *src, case VIDEO_FORMAT_AYUV: case VIDEO_FORMAT_V210: case VIDEO_FORMAT_R10L: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); + heights[0] = height; break; - case VIDEO_FORMAT_I444: + case VIDEO_FORMAT_I444: /* three planes: all full height */ case VIDEO_FORMAT_I422: case VIDEO_FORMAT_I210: case VIDEO_FORMAT_I412: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); - memcpy(dst->data[1], src->data[1], src->linesize[1] * cy); - memcpy(dst->data[2], src->data[2], src->linesize[2] * cy); + heights[0] = height; + heights[1] = height; + heights[2] = height; break; - case VIDEO_FORMAT_I40A: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); - memcpy(dst->data[1], src->data[1], src->linesize[1] * cy / 2); - memcpy(dst->data[2], src->data[2], src->linesize[2] * cy / 2); - memcpy(dst->data[3], src->data[3], src->linesize[3] * cy); + case VIDEO_FORMAT_I40A: /* four planes: full height, half height, half height, full height */ + heights[0] = height; + heights[1] = height / 2; + heights[2] = height / 2; + heights[0] = height; break; - case VIDEO_FORMAT_I42A: + case VIDEO_FORMAT_I42A: /* four planes: all full height */ case VIDEO_FORMAT_YUVA: case VIDEO_FORMAT_YA2L: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); - memcpy(dst->data[1], src->data[1], src->linesize[1] * cy); - memcpy(dst->data[2], src->data[2], src->linesize[2] * cy); - memcpy(dst->data[3], src->data[3], src->linesize[3] * cy); + heights[0] = height; + heights[1] = height; + heights[2] = height; + heights[3] = height; break; - case VIDEO_FORMAT_P216: + case VIDEO_FORMAT_P216: /* two planes: all full height */ case VIDEO_FORMAT_P416: - memcpy(dst->data[0], src->data[0], src->linesize[0] * cy); - memcpy(dst->data[1], src->data[1], src->linesize[1] * cy); + heights[0] = height; + heights[1] = height; break; } } + +void video_frame_init(struct video_frame *frame, enum video_format format, + uint32_t width, uint32_t height) +{ + size_t size = 0; + uint32_t linesizes[MAX_AV_PLANES]; + uint32_t heights[MAX_AV_PLANES]; + size_t offsets[MAX_AV_PLANES - 1]; + int alignment = base_get_alignment(); + + if (!frame) + return; + + memset(frame, 0, sizeof(struct video_frame)); + memset(linesizes, 0, sizeof(linesizes)); + memset(heights, 0, sizeof(heights)); + memset(offsets, 0, sizeof(offsets)); + + /* determine linesizes for each plane */ + video_frame_get_linesizes(linesizes, format, width); + + /* determine line count for each plane */ + video_frame_get_plane_heights(heights, format, height); + + /* calculate total buffer required */ + for (uint32_t i = 0; i < MAX_AV_PLANES; i++) { + if (!linesizes[i] || !heights[i]) + continue; + size_t plane_size = (size_t)linesizes[i] * (size_t)heights[i]; + align_size(&plane_size, alignment); + size += plane_size; + offsets[i] = size; + } + + /* allocate memory */ + frame->data[0] = bmalloc(size); + frame->linesize[0] = linesizes[0]; + + /* apply plane data pointers according to offsets */ + for (uint32_t i = 1; i < MAX_AV_PLANES; i++) { + if (!linesizes[i] || !heights[i]) + continue; + frame->data[i] = frame->data[0] + offsets[i - 1]; + frame->linesize[i] = linesizes[i]; + } +} + +void video_frame_copy(struct video_frame *dst, const struct video_frame *src, + enum video_format format, uint32_t cy) +{ + uint32_t heights[MAX_AV_PLANES]; + + memset(heights, 0, sizeof(heights)); + + /* determine line count for each plane */ + video_frame_get_plane_heights(heights, format, cy); + + /* copy each plane */ + for (uint32_t i = 0; i < MAX_AV_PLANES; i++) { + if (!heights[i]) + continue; + + if (src->linesize[i] == dst->linesize[i]) { + memcpy(dst->data[i], src->data[i], + src->linesize[i] * heights[i]); + } else { /* linesizes which do not match must be copied line-by-line */ + size_t src_linesize = src->linesize[i]; + size_t dst_linesize = dst->linesize[i]; + /* determine how much we can write (frames with different line sizes require more )*/ + size_t linesize = src_linesize < dst_linesize + ? src_linesize + : dst_linesize; + for (uint32_t i = 0; i < heights[i]; i++) { + uint8_t *src_pos = + src->data[i] + (src_linesize * i); + uint8_t *dst_pos = + dst->data[i] + (dst_linesize * i); + memcpy(dst_pos, src_pos, linesize); + } + } + } +} From 849c1180acaea3552cebd4ca78bb1b0554579f65 Mon Sep 17 00:00:00 2001 From: John Bowers Date: Mon, 11 Mar 2024 12:53:04 -0600 Subject: [PATCH 0043/1073] libobs: Insert captions on all video tracks We will now keep a per track list of caption data. When caption insertion is triggered we will add that to the list for each actively encoding video track. When doing interleaved send, we pull the data from the caption data for the corresponding video. This ensures that captions get copied to all video tracks. --- libobs/obs-internal.h | 17 +++-- libobs/obs-output.c | 158 ++++++++++++++++++++++++++++++------------ 2 files changed, 124 insertions(+), 51 deletions(-) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 91a53d7f2dc659..25108083b1d2c2 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -1080,6 +1080,15 @@ struct caption_text { struct caption_text *next; }; +struct caption_track_data { + struct caption_text *caption_head; + struct caption_text *caption_tail; + pthread_mutex_t caption_mutex; + double caption_timestamp; + double last_caption_timestamp; + struct deque caption_data; +}; + struct pause_data { pthread_mutex_t mutex; uint64_t last_video_ts; @@ -1171,12 +1180,8 @@ struct obs_output { struct video_scale_info video_conversion; struct audio_convert_info audio_conversion; - pthread_mutex_t caption_mutex; - double caption_timestamp; - struct caption_text *caption_head; - struct caption_text *caption_tail; - - struct deque caption_data; + // captions are output per track + struct caption_track_data *caption_tracks[MAX_OUTPUT_VIDEO_ENCODERS]; bool valid; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 566432eb075baa..48e7c3e004a637 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -175,15 +175,12 @@ obs_output_t *obs_output_create(const char *id, const char *name, output = bzalloc(sizeof(struct obs_output)); pthread_mutex_init_value(&output->interleaved_mutex); pthread_mutex_init_value(&output->delay_mutex); - pthread_mutex_init_value(&output->caption_mutex); pthread_mutex_init_value(&output->pause.mutex); if (pthread_mutex_init(&output->interleaved_mutex, NULL) != 0) goto fail; if (pthread_mutex_init(&output->delay_mutex, NULL) != 0) goto fail; - if (pthread_mutex_init(&output->caption_mutex, NULL) != 0) - goto fail; if (pthread_mutex_init(&output->pause.mutex, NULL) != 0) goto fail; if (os_event_init(&output->stopping_event, OS_EVENT_TYPE_MANUAL) != 0) @@ -255,6 +252,18 @@ static inline void clear_raw_audio_buffers(obs_output_t *output) } } +static void destroy_caption_track(struct caption_track_data **ctrack_ptr) +{ + if (!ctrack_ptr || !*ctrack_ptr) { + return; + } + struct caption_track_data *ctrack = *ctrack_ptr; + pthread_mutex_destroy(&ctrack->caption_mutex); + deque_free(&ctrack->caption_data); + bfree(ctrack); + *ctrack_ptr = NULL; +} + void obs_output_destroy(obs_output_t *output) { if (output) { @@ -281,6 +290,10 @@ void obs_output_destroy(obs_output_t *output) obs_encoder_remove_output( output->video_encoders[i], output); } + if (output->caption_tracks[i]) { + destroy_caption_track( + &output->caption_tracks[i]); + } } for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { @@ -296,13 +309,11 @@ void obs_output_destroy(obs_output_t *output) os_event_destroy(output->stopping_event); pthread_mutex_destroy(&output->pause.mutex); - pthread_mutex_destroy(&output->caption_mutex); pthread_mutex_destroy(&output->interleaved_mutex); pthread_mutex_destroy(&output->delay_mutex); os_event_destroy(output->reconnect_stop_event); obs_context_data_free(&output->context); deque_free(&output->delay_data); - deque_free(&output->caption_data); if (output->owns_info_id) bfree((void *)output->info.id); if (output->last_error_message) @@ -340,11 +351,17 @@ bool obs_output_actual_start(obs_output_t *output) if (os_atomic_load_long(&output->delay_restart_refs)) os_atomic_dec_long(&output->delay_restart_refs); - output->caption_timestamp = 0; - - deque_free(&output->caption_data); - deque_init(&output->caption_data); - + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + struct caption_track_data *ctrack = output->caption_tracks[i]; + if (!ctrack) { + continue; + } + pthread_mutex_lock(&ctrack->caption_mutex); + ctrack->caption_timestamp = 0; + deque_free(&ctrack->caption_data); + deque_init(&ctrack->caption_data); + pthread_mutex_unlock(&ctrack->caption_mutex); + } return success; } @@ -472,10 +489,16 @@ void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts) os_event_signal(output->stopping_event); } - while (output->caption_head) { - output->caption_tail = output->caption_head->next; - bfree(output->caption_head); - output->caption_head = output->caption_tail; + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + struct caption_track_data *ctrack = output->caption_tracks[i]; + if (!ctrack) { + continue; + } + while (ctrack->caption_head) { + ctrack->caption_tail = ctrack->caption_head->next; + bfree(ctrack->caption_head); + ctrack->caption_head = ctrack->caption_tail; + } } da_clear(output->keyframe_group_tracking); @@ -969,6 +992,19 @@ void obs_output_remove_encoder(struct obs_output *output, obs_output_remove_encoder_internal(output, encoder); } +static struct caption_track_data *create_caption_track() +{ + struct caption_track_data *rval = + bzalloc(sizeof(struct caption_track_data)); + pthread_mutex_init_value(&rval->caption_mutex); + + if (pthread_mutex_init(&rval->caption_mutex, NULL) != 0) { + bfree(rval); + rval = NULL; + } + return rval; +} + void obs_output_set_video_encoder2(obs_output_t *output, obs_encoder_t *encoder, size_t idx) { @@ -1007,6 +1043,13 @@ void obs_output_set_video_encoder2(obs_output_t *output, obs_encoder_t *encoder, obs_encoder_add_output(encoder, output); output->video_encoders[idx] = encoder; + destroy_caption_track(&output->caption_tracks[idx]); + if (encoder != NULL) { + output->caption_tracks[idx] = create_caption_track(); + } else { + output->caption_tracks[idx] = NULL; + } + // Set preferred resolution on the default index to preserve old behavior if (idx == 0) { /* set the preferred resolution on the encoder */ @@ -1482,20 +1525,29 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) if (out->priority > 1) return false; + struct caption_track_data *ctrack = + output->caption_tracks[out->track_idx]; + if (!ctrack) { + blog(LOG_DEBUG, + "Caption track for index: %lu has not been initialized", + out->track_idx); + return false; + } + sei_init(&sei, 0.0); da_init(out_data); da_push_back_array(out_data, (uint8_t *)&ref, sizeof(ref)); da_push_back_array(out_data, out->data, out->size); - if (output->caption_data.size > 0) { + if (ctrack->caption_data.size > 0) { cea708_t cea708; cea708_init(&cea708, 0); // set up a new popon frame void *caption_buf = bzalloc(3 * sizeof(uint8_t)); - while (output->caption_data.size > 0) { - deque_pop_front(&output->caption_data, caption_buf, + while (ctrack->caption_data.size > 0) { + deque_pop_front(&ctrack->caption_data, caption_buf, 3 * sizeof(uint8_t)); if ((((uint8_t *)caption_buf)[0] & 0x3) != 0) { @@ -1533,16 +1585,16 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) msg->size = cea708_render(&cea708, sei_message_data(msg), sei_message_size(msg)); sei_message_append(&sei, msg); - } else if (output->caption_head) { + } else if (ctrack->caption_head) { caption_frame_t cf; caption_frame_init(&cf); - caption_frame_from_text(&cf, &output->caption_head->text[0]); + caption_frame_from_text(&cf, &ctrack->caption_head->text[0]); sei_from_caption_frame(&sei, &cf); - struct caption_text *next = output->caption_head->next; - bfree(output->caption_head); - output->caption_head = next; + struct caption_text *next = ctrack->caption_head->next; + bfree(ctrack->caption_head); + ctrack->caption_head = next; } data = malloc(sei_render_size(&sei)); @@ -1563,8 +1615,6 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) return true; } -double last_caption_timestamp = 0; - static inline void send_interleaved(struct obs_output *output) { struct encoder_packet out = output->interleaved_packets.array[0]; @@ -1580,33 +1630,37 @@ static inline void send_interleaved(struct obs_output *output) if (out.type == OBS_ENCODER_VIDEO) { output->total_frames++; - pthread_mutex_lock(&output->caption_mutex); + pthread_mutex_lock( + &output->caption_tracks[out.track_idx]->caption_mutex); double frame_timestamp = (out.pts * out.timebase_num) / (double)out.timebase_den; - if (output->caption_head && - output->caption_timestamp <= frame_timestamp) { + struct caption_track_data *ctrack = + output->caption_tracks[out.track_idx]; + + if (ctrack->caption_head && + ctrack->caption_timestamp <= frame_timestamp) { blog(LOG_DEBUG, "Sending caption: %f \"%s\"", - frame_timestamp, &output->caption_head->text[0]); + frame_timestamp, &ctrack->caption_head->text[0]); double display_duration = - output->caption_head->display_duration; + ctrack->caption_head->display_duration; if (add_caption(output, &out)) { - output->caption_timestamp = + ctrack->caption_timestamp = frame_timestamp + display_duration; } } - if (output->caption_data.size > 0) { - if (last_caption_timestamp < frame_timestamp) { - last_caption_timestamp = frame_timestamp; + if (ctrack->caption_data.size > 0) { + if (ctrack->last_caption_timestamp < frame_timestamp) { + ctrack->last_caption_timestamp = + frame_timestamp; add_caption(output, &out); } } - - pthread_mutex_unlock(&output->caption_mutex); + pthread_mutex_unlock(&ctrack->caption_mutex); } output->info.encoded_packet(output->context.data, &out); @@ -2937,12 +2991,19 @@ const char *obs_output_get_id(const obs_output_t *output) void obs_output_caption(obs_output_t *output, const struct obs_source_cea_708 *captions) { - pthread_mutex_lock(&output->caption_mutex); - for (size_t i = 0; i < captions->packets; i++) { - deque_push_back(&output->caption_data, captions->data + (i * 3), - 3 * sizeof(uint8_t)); + for (int i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + struct caption_track_data *ctrack = output->caption_tracks[i]; + if (!ctrack) { + continue; + } + pthread_mutex_lock(&ctrack->caption_mutex); + for (size_t i = 0; i < captions->packets; i++) { + deque_push_back(&ctrack->caption_data, + captions->data + (i * 3), + 3 * sizeof(uint8_t)); + } + pthread_mutex_unlock(&ctrack->caption_mutex); } - pthread_mutex_unlock(&output->caption_mutex); } static struct caption_text *caption_text_new(const char *text, size_t bytes, @@ -2983,13 +3044,20 @@ void obs_output_output_caption_text2(obs_output_t *output, const char *text, int size = (int)strlen(text); blog(LOG_DEBUG, "Caption text: %s", text); - pthread_mutex_lock(&output->caption_mutex); + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + struct caption_track_data *ctrack = output->caption_tracks[i]; + if (!ctrack) { + continue; + } + pthread_mutex_lock(&ctrack->caption_mutex); - output->caption_tail = - caption_text_new(text, size, output->caption_tail, - &output->caption_head, display_duration); + ctrack->caption_tail = caption_text_new(text, size, + ctrack->caption_tail, + &ctrack->caption_head, + display_duration); - pthread_mutex_unlock(&output->caption_mutex); + pthread_mutex_unlock(&ctrack->caption_mutex); + } } float obs_output_get_congestion(obs_output_t *output) From f9bc9a8b12b96241089c01578d2c6baf99a0ab4c Mon Sep 17 00:00:00 2001 From: John Bowers Date: Mon, 11 Mar 2024 13:21:01 -0600 Subject: [PATCH 0044/1073] libobs: Avoid adding captions on non H.264 streams The existing caption insertion implementation is to add H.264 specific SEI NALs, because of this we will skip caption insertion unless the video stream is H.264. This prevents corruption of AV1/HEVC/etc. --- libobs/obs-output.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 48e7c3e004a637..551699b3808410 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -1519,6 +1519,15 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) uint8_t *data; size_t size; long ref = 1; + bool avc = false; + + /* Instead of exiting early for unsupported codecs, we will continue + * processing to allow the freeing of caption data even if the captions + * will not be included in the bitstream due to being unimplemented in + * the given codec. */ + if (strcmp(out->encoder->info.codec, "h264") == 0) { + avc = true; + } DARRAY(uint8_t) out_data; @@ -1597,22 +1606,26 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) ctrack->caption_head = next; } - data = malloc(sei_render_size(&sei)); - size = sei_render(&sei, data); - /* TODO SEI should come after AUD/SPS/PPS, but before any VCL */ - da_push_back_array(out_data, nal_start, 4); - da_push_back_array(out_data, data, size); - free(data); + if (avc) { + data = malloc(sei_render_size(&sei)); + size = sei_render(&sei, data); + /* TODO: SEI should come after AUD/SPS/PPS, + * but before any VCL */ + da_push_back_array(out_data, nal_start, 4); + da_push_back_array(out_data, data, size); + free(data); + } obs_encoder_packet_release(out); *out = backup; out->data = (uint8_t *)out_data.array + sizeof(ref); out->size = out_data.num - sizeof(ref); - sei_free(&sei); - - return true; + if (avc) { + return true; + } + return false; } static inline void send_interleaved(struct obs_output *output) From 33eb95c2747f48cf420f9a60e9e7d02a813cec3e Mon Sep 17 00:00:00 2001 From: JoHn BoWeRs Date: Wed, 13 Mar 2024 13:55:00 -0600 Subject: [PATCH 0045/1073] libobs: Add caption support for HEVC Add embedded caption support for HEVC by slightly modifying the H.264 SEI to be an HEVC SEI (2 byte SEI NAL header). --- libobs/obs-output.c | 69 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 551699b3808410..98bb2f014ad084 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -1520,6 +1520,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) size_t size; long ref = 1; bool avc = false; + bool hevc = false; /* Instead of exiting early for unsupported codecs, we will continue * processing to allow the freeing of caption data even if the captions @@ -1527,6 +1528,10 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) * the given codec. */ if (strcmp(out->encoder->info.codec, "h264") == 0) { avc = true; +#ifdef ENABLE_HEVC + } else if (strcmp(out->encoder->info.codec, "hevc") == 0) { + hevc = true; +#endif } DARRAY(uint8_t) out_data; @@ -1543,6 +1548,31 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) return false; } +#ifdef ENABLE_HEVC + uint8_t hevc_nal_header[2]; + if (hevc) { + size_t nal_header_index_start = 4; + // Skip past the annex-b start code + if (memcmp(out->data, nal_start + 1, 3) == 0) { + nal_header_index_start = 3; + } else if (memcmp(out->data, nal_start, 4) == 0) { + nal_header_index_start = 4; + + } else { + /* We shouldn't ever see this unless we start getting + * packets without annex-b start codes. */ + blog(LOG_DEBUG, + "Annex-B start code not found. We may not " + "generate a valid HEVC NAL unit header " + "for our caption"); + return false; + } + /* We will use the same 2 byte NAL unit header for the CC SEI, + * but swap the NAL types out. */ + hevc_nal_header[0] = out->data[nal_header_index_start]; + hevc_nal_header[1] = out->data[nal_header_index_start + 1]; + } +#endif sei_init(&sei, 0.0); da_init(out_data); @@ -1606,13 +1636,40 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) ctrack->caption_head = next; } - if (avc) { + if (avc || hevc) { data = malloc(sei_render_size(&sei)); size = sei_render(&sei, data); - /* TODO: SEI should come after AUD/SPS/PPS, - * but before any VCL */ - da_push_back_array(out_data, nal_start, 4); - da_push_back_array(out_data, data, size); + if (avc) { + /* TODO: SEI should come after AUD/SPS/PPS, + * but before any VCL */ + da_push_back_array(out_data, nal_start, 4); + da_push_back_array(out_data, data, size); +#ifdef ENABLE_HEVC + } else if (hevc) { + /* Only first NAL, VPS/PPS/SPS should use the 4 byte + start code, SEIs use 3 byte version */ + da_push_back_array(out_data, nal_start + 1, 3); + /* nal_unit_header( ) { + * forbidden_zero_bit f(1) + * nal_unit_type u(6) + * nuh_layer_id u(6) + * nuh_temporal_id_plus1 u(3) + * } + */ + const uint8_t prefix_sei_nal_type = 39; + /* The first bit is always 0, so we just need to + * save the last bit off the original header and + * add the SEI NAL type. */ + uint8_t first_byte = (prefix_sei_nal_type << 1) | + (0x01 & hevc_nal_header[0]); + hevc_nal_header[0] = first_byte; + /* The HEVC NAL unit header is 2 byte instead of + * one, otherwise everything else is the + * same. */ + da_push_back_array(out_data, hevc_nal_header, 2); + da_push_back_array(out_data, &data[1], size - 1); +#endif + } free(data); } @@ -1622,7 +1679,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) out->data = (uint8_t *)out_data.array + sizeof(ref); out->size = out_data.num - sizeof(ref); sei_free(&sei); - if (avc) { + if (avc || hevc) { return true; } return false; From 5c8be1de2235ff527b1db39ddd169228e2d5dbab Mon Sep 17 00:00:00 2001 From: John Bowers Date: Mon, 11 Mar 2024 15:06:33 -0600 Subject: [PATCH 0046/1073] libobs: Add caption support for AV1 Extract the itut_t35 caption data buffer from existing caption data and repackage in a metadata OBU instead of an AVC/HEVC SEI. --- libobs/obs-av1.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ libobs/obs-av1.h | 4 ++ libobs/obs-output.c | 74 ++++++++++++++++++++++++++++--------- 3 files changed, 149 insertions(+), 18 deletions(-) diff --git a/libobs/obs-av1.c b/libobs/obs-av1.c index a015130537cf2b..9a8f5fa49c9ca3 100644 --- a/libobs/obs-av1.c +++ b/libobs/obs-av1.c @@ -63,6 +63,95 @@ static void parse_obu_header(const uint8_t *buf, size_t size, size_t *obu_start, *obu_start += size_len; } +// Pass a static 10 byte buffer in. The max size for a leb128. +static inline void encode_uleb128(uint64_t val, uint8_t *out_buf, + size_t *len_out) +{ + size_t num_bytes = 0; + uint8_t b = val & 0x7f; + val >>= 7; + while (val > 0) { + out_buf[num_bytes] = b | 0x80; + ++num_bytes; + b = val & 0x7f; + val >>= 7; + } + out_buf[num_bytes] = b; + ++num_bytes; + *len_out = num_bytes; +} + +static const uint8_t METADATA_TYPE_ITUT_T35 = 4; +static const uint8_t OBU_METADATA = 5; + +// Create a metadata OBU to carry caption information. +void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, + uint8_t **out_buffer, size_t *outbuf_size) +{ + /* From the AV1 spec: 5.3.2 OBU Header Syntax + * ------------- + * obu_forbidden_bit (1) + * obu_type (4) // In this case OBS_OBU_METADATA + * obu_extension_flag (1) + * obu_has_size_field (1) // Must be set, size of OBU is variable + * obu_reserved_1bit (1) + * if(obu_extension_flag == 1) + * // skip, because we aren't setting this + */ + + uint8_t obu_header_byte = (OBU_METADATA << 3) | (1 << 1); + + /* From the AV1 spec: 5.3.1 General OBU Syntax + * if (obu_has_size_field) + * obu_size leb128() + * else + * obu_size = sz - 1 - obu_extension_flag + * + * // Skipping portions unrelated to this OBU type + * + * if (obu_type == OBU_METADATA) + * metdata_obu() + * 5.8.1 General metadata OBU Syntax + * // leb128(metadatatype) should always be 1 byte +1 for trailing bits + * metadata_type leb128() + * 5.8.2 Metadata ITUT T35 syntax + * if (metadata_type == METADATA_TYPE_ITUT_T35) + * // add ITUT T35 payload + * 5.8.1 General metadata OBU Syntax + * // trailing bits will always be 0x80 because + * // everything in here is byte aligned + * trailing_bits( obu_size * 8 - payloadBits ) + */ + + int64_t size_field = 1 + itut_bufsize + 1; + uint8_t size_buf[10]; + size_t size_buf_size = 0; + encode_uleb128(size_field, size_buf, &size_buf_size); + // header + obu_size + metadata_type + metadata_payload + trailing_bits + *outbuf_size = 1 + size_buf_size + 1 + itut_bufsize + 1; + *out_buffer = bzalloc(*outbuf_size); + size_t offset = 0; + (*out_buffer)[0] = obu_header_byte; + ++offset; + memcpy((*out_buffer) + offset, size_buf, size_buf_size); + offset += size_buf_size; + (*out_buffer)[offset] = METADATA_TYPE_ITUT_T35; + ++offset; + memcpy((*out_buffer) + offset, itut_t35_buffer, itut_bufsize); + offset += itut_bufsize; + + /* From AV1 spec: 6.2.1 General OBU semantics + * ... Trailing bits are always present, unless the OBU consists of only + * the header. Trailing bits achieve byte alignment when the payload of + * an OBU is not byte aligned. The trailing bits may also used for + * additional byte padding, and if used are taken into account in the + * sz value. In all cases, the pattern used for the trailing bits + * guarantees that all OBUs (except header-only OBUs) end with the same + * pattern: one bit set to one, optionally followed by zeros. */ + + (*out_buffer)[offset] = 0x80; +} + bool obs_av1_keyframe(const uint8_t *data, size_t size) { const uint8_t *start = data, *end = data + size; diff --git a/libobs/obs-av1.h b/libobs/obs-av1.h index 031299da0a4155..a71453f8bad0e0 100644 --- a/libobs/obs-av1.h +++ b/libobs/obs-av1.h @@ -30,6 +30,10 @@ EXPORT void obs_extract_av1_headers(const uint8_t *packet, size_t size, size_t *new_packet_size, uint8_t **header_data, size_t *header_size); +EXPORT void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, + size_t itut_bufsize, uint8_t **out_buffer, + size_t *outbuf_size); + #ifdef __cplusplus } #endif diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 98bb2f014ad084..8cf611339d26a2 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -21,6 +21,7 @@ #include "graphics/math-extra.h" #include "obs.h" #include "obs-internal.h" +#include "obs-av1.h" #include #include @@ -1510,17 +1511,33 @@ static inline bool has_higher_opposing_ts(struct obs_output *output, output->highest_audio_ts > packet->dts_usec); } -static const uint8_t nal_start[4] = {0, 0, 0, 1}; +static size_t extract_itut_t35_buffer_from_sei(sei_t *sei, uint8_t **data_out) +{ + if (!sei || !sei->head) { + return 0; + } + /* We should only need to get one payload, because the SEI that was + * generated should only have one message, so no need to iterate. If + * we did iterate, we would need to generate multiple OBUs. */ + sei_message_t *msg = sei_message_head(sei); + int payload_size = (int)sei_message_size(msg); + uint8_t *payload_data = sei_message_data(msg); + *data_out = malloc(payload_size); + memcpy(*data_out, payload_data, payload_size); + return payload_size; +} +static const uint8_t nal_start[4] = {0, 0, 0, 1}; static bool add_caption(struct obs_output *output, struct encoder_packet *out) { struct encoder_packet backup = *out; sei_t sei; - uint8_t *data; + uint8_t *data = NULL; size_t size; long ref = 1; bool avc = false; bool hevc = false; + bool av1 = false; /* Instead of exiting early for unsupported codecs, we will continue * processing to allow the freeing of caption data even if the captions @@ -1528,6 +1545,8 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) * the given codec. */ if (strcmp(out->encoder->info.codec, "h264") == 0) { avc = true; + } else if (strcmp(out->encoder->info.codec, "av1") == 0) { + av1 = true; #ifdef ENABLE_HEVC } else if (strcmp(out->encoder->info.codec, "hevc") == 0) { hevc = true; @@ -1636,9 +1655,19 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) ctrack->caption_head = next; } - if (avc || hevc) { - data = malloc(sei_render_size(&sei)); - size = sei_render(&sei, data); + if (avc || hevc || av1) { + if (avc || hevc) { + data = malloc(sei_render_size(&sei)); + size = sei_render(&sei, data); + } + /* In each of these specs there is an identical structure that + * carries caption information. It is named slightly differently + * in each one. The metadata_itut_t35 in AV1 or the + * user_data_registered_itu_t_t35 in HEVC/AVC. We have an AVC + * SEI wrapped version of that here. We will strip it out and + * repackage it slightly to fit the different codec carrying + * mechanisms. A slightly modified SEI for HEVC and a metadata + * OBU for AV1. */ if (avc) { /* TODO: SEI should come after AUD/SPS/PPS, * but before any VCL */ @@ -1646,8 +1675,8 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) da_push_back_array(out_data, data, size); #ifdef ENABLE_HEVC } else if (hevc) { - /* Only first NAL, VPS/PPS/SPS should use the 4 byte - start code, SEIs use 3 byte version */ + /* Only first NAL (VPS/PPS/SPS) should use the 4 byte + * start code. SEIs use 3 byte version */ da_push_back_array(out_data, nal_start + 1, 3); /* nal_unit_header( ) { * forbidden_zero_bit f(1) @@ -1669,20 +1698,29 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) da_push_back_array(out_data, hevc_nal_header, 2); da_push_back_array(out_data, &data[1], size - 1); #endif + } else if (av1) { + uint8_t *obu_buffer = NULL; + size_t obu_buffer_size = 0; + size = extract_itut_t35_buffer_from_sei(&sei, &data); + metadata_obu_itu_t35(data, size, &obu_buffer, + &obu_buffer_size); + if (obu_buffer) { + da_push_back_array(out_data, obu_buffer, + obu_buffer_size); + bfree(obu_buffer); + } } - free(data); - } - - obs_encoder_packet_release(out); + if (data) { + free(data); + } + obs_encoder_packet_release(out); - *out = backup; - out->data = (uint8_t *)out_data.array + sizeof(ref); - out->size = out_data.num - sizeof(ref); - sei_free(&sei); - if (avc || hevc) { - return true; + *out = backup; + out->data = (uint8_t *)out_data.array + sizeof(ref); + out->size = out_data.num - sizeof(ref); } - return false; + sei_free(&sei); + return avc || hevc || av1; } static inline void send_interleaved(struct obs_output *output) From 3ab71a9f902ba4a047e6c4b30562aa7d40da2e18 Mon Sep 17 00:00:00 2001 From: JoHn BoWeRs Date: Mon, 22 Apr 2024 10:57:02 -0600 Subject: [PATCH 0047/1073] libobs: Use bmalloc/bfree for caption data --- libobs/obs-output.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 8cf611339d26a2..8528fcb011a62f 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -1522,7 +1522,7 @@ static size_t extract_itut_t35_buffer_from_sei(sei_t *sei, uint8_t **data_out) sei_message_t *msg = sei_message_head(sei); int payload_size = (int)sei_message_size(msg); uint8_t *payload_data = sei_message_data(msg); - *data_out = malloc(payload_size); + *data_out = bmalloc(payload_size); memcpy(*data_out, payload_data, payload_size); return payload_size; } @@ -1657,7 +1657,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) if (avc || hevc || av1) { if (avc || hevc) { - data = malloc(sei_render_size(&sei)); + data = bmalloc(sei_render_size(&sei)); size = sei_render(&sei, data); } /* In each of these specs there is an identical structure that @@ -1711,7 +1711,7 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) } } if (data) { - free(data); + bfree(data); } obs_encoder_packet_release(out); From d81dd24f38506c1881a4299d95c0f8af2269049a Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sun, 28 Apr 2024 22:27:24 +0900 Subject: [PATCH 0048/1073] libobs/media-io: Fix copying different line-size video frame The commit 2fc13540f implemented to copy a video frame into a different line-size video frame. However, when the line-size was different, the frame was not correctly copied. --- libobs/media-io/video-frame.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index 0a601f9e57ae99..5662812feaa91a 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -261,11 +261,11 @@ void video_frame_copy(struct video_frame *dst, const struct video_frame *src, size_t linesize = src_linesize < dst_linesize ? src_linesize : dst_linesize; - for (uint32_t i = 0; i < heights[i]; i++) { + for (uint32_t y = 0; y < heights[i]; y++) { uint8_t *src_pos = - src->data[i] + (src_linesize * i); + src->data[i] + (src_linesize * y); uint8_t *dst_pos = - dst->data[i] + (dst_linesize * i); + dst->data[i] + (dst_linesize * y); memcpy(dst_pos, src_pos, linesize); } } From ee72927ad203828d7b0ebd5564e6271a31202012 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Mon, 29 Apr 2024 02:59:22 -0500 Subject: [PATCH 0049/1073] UI: Fix styling of appearance tab This makes the appearance tab look the same as other settings tabs. --- UI/forms/OBSBasicSettings.ui | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 13772c172b3307..da5591e1e6dc09 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -936,6 +936,15 @@ + + 0 + + + 0 + + + 0 + @@ -945,6 +954,12 @@ false + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + @@ -974,6 +989,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + From dfd5798e6ca8dc72532e96239b8229f032d37d4f Mon Sep 17 00:00:00 2001 From: cg2121 Date: Mon, 29 Apr 2024 02:25:06 -0500 Subject: [PATCH 0050/1073] UI: Fix volume control button styling The config button and mute checkbox were different sizes. --- UI/data/themes/Yami.obt | 5 +++-- UI/volume-control.cpp | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index f35137a97f8354..bebca101ea5b6b 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -1187,9 +1187,10 @@ QSlider::handle:disabled { /* Volume Control */ #stackedMixerArea QPushButton { + width: var(--icon_base); + height: var(--icon_base); background-color: var(--button_bg); - min-width: var(--icon_base); - padding: var(--padding_small) var(--padding_base); + padding: var(--padding_base_border) var(--padding_base_border); margin: 0px var(--spacing_base); border: 1px solid var(--button_border); border-radius: var(--border_radius); diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 3d33edb0a769d1..f8ebff1dc35c6c 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -263,9 +263,6 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) if (showConfig) { config = new QPushButton(this); config->setProperty("themeID", "menuIconSmall"); - config->setSizePolicy(QSizePolicy::Maximum, - QSizePolicy::Maximum); - config->setMaximumSize(22, 22); config->setAutoDefault(false); config->setAccessibleName( From e693f644aebf53df8979edfcc34fc6cb14ba233b Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 26 Apr 2024 16:00:13 -0400 Subject: [PATCH 0051/1073] win-dshow: Fix compiling with FFmpeg 7.0 --- plugins/win-dshow/ffmpeg-decode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/win-dshow/ffmpeg-decode.c b/plugins/win-dshow/ffmpeg-decode.c index 541f59faef1491..e5ba5ea4fa9ebc 100644 --- a/plugins/win-dshow/ffmpeg-decode.c +++ b/plugins/win-dshow/ffmpeg-decode.c @@ -265,8 +265,13 @@ bool ffmpeg_decode_audio(struct ffmpeg_decode *decode, uint8_t *data, audio->samples_per_sec = decode->frame->sample_rate; audio->format = convert_sample_format(decode->frame->format); +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) audio->speakers = convert_speaker_layout((uint8_t)decode->decoder->channels); +#else + audio->speakers = convert_speaker_layout( + (uint8_t)decode->decoder->ch_layout.nb_channels); +#endif audio->frames = decode->frame->nb_samples; From 649c62cfac96dda58b8c1610a5b255cffc9c15f2 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 26 Apr 2024 14:59:26 -0400 Subject: [PATCH 0052/1073] cmake: Remove more Qt 5 code Qt Gui virtualkeyboard plugin was removed in Qt 6.x. Qt Network Bearer Management was removed in Qt 6.0. Qt Multimedia mediaservice and audio plugins were removed in Qt 6.x. --- cmake/common/helpers_common.cmake | 4 +--- cmake/macos/helpers.cmake | 2 +- cmake/windows/helpers.cmake | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index c73c3190a51efa..be7339923c627d 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -255,10 +255,8 @@ function(find_qt_plugins) # cmake-format: off list(APPEND qt_plugins_Core platforms printsupport styles imageformats iconengines) # cmake-format: on - list(APPEND qt_plugins_Gui platforminputcontexts virtualkeyboard) - list(APPEND qt_plugins_Network bearer) + list(APPEND qt_plugins_Gui platforminputcontexts) list(APPEND qt_plugins_Sql sqldrivers) - list(APPEND qt_plugins_Multimedia mediaservice audio) list(APPEND qt_plugins_3dRender sceneparsers geometryloaders) list(APPEND qt_plugins_3dQuickRender renderplugins) list(APPEND qt_plugins_Positioning position) diff --git a/cmake/macos/helpers.cmake b/cmake/macos/helpers.cmake index 954ee0c5ea2712..da731ccdace2fa 100644 --- a/cmake/macos/helpers.cmake +++ b/cmake/macos/helpers.cmake @@ -450,7 +450,7 @@ function(_bundle_dependencies target) continue() endif() - if(library MATCHES "Qt[56]?::.+") + if(library MATCHES "Qt6?::.+") find_qt_plugins(COMPONENT ${library} TARGET ${target} FOUND_VAR plugins_list) endif() list(APPEND library_paths ${library_location}) diff --git a/cmake/windows/helpers.cmake b/cmake/windows/helpers.cmake index a62576b1df8686..a8cb6b41c3383f 100644 --- a/cmake/windows/helpers.cmake +++ b/cmake/windows/helpers.cmake @@ -402,7 +402,7 @@ function(_bundle_dependencies target) endif() endforeach() - if(library MATCHES "Qt[56]?::.+") + if(library MATCHES "Qt6?::.+") find_qt_plugins(COMPONENT ${library} TARGET ${target} FOUND_VAR plugins_list) endif() endif() From 182410cf6cf45ac02b2c871e9fd67c856d67e25d Mon Sep 17 00:00:00 2001 From: Paul Hindt Date: Wed, 11 Oct 2023 21:57:44 -0700 Subject: [PATCH 0053/1073] aja: Prepare plugins for new libajantv2 repo Support building AJA plugins with either the new libajantv2 library, or the deprecated ntv2 library. Finder scripts updated to search for libajantv2 and fall back to ntv2 if not found. This allows this PR to be merged without requiring a corresponding update to the pre-built obs-deps packages. --- .../aja-output-ui/CMakeLists.txt | 8 --- cmake/Modules/FindLibAJANTV2.cmake | 45 +++++++++++++- cmake/finders/FindLibAJANTV2.cmake | 59 ++++++++++++++++--- plugins/aja/CMakeLists.txt | 14 +---- plugins/aja/aja-widget-io.cpp | 17 +++--- 5 files changed, 102 insertions(+), 41 deletions(-) diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 3bed6ee56a3a22..56c4be21e49d37 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -43,14 +43,6 @@ target_link_libraries( OBS::ui-support Qt::Widgets AJA::LibAJANTV2 - $<$:ws2_32.lib> - $<$:setupapi.lib> - $<$:Winmm.lib> - $<$:netapi32.lib> - $<$:Shlwapi.lib> - "$<$:$>" - "$<$:$>" - "$<$:$>" $<$:X11::X11> $<$:Qt::GuiPrivate>) diff --git a/cmake/Modules/FindLibAJANTV2.cmake b/cmake/Modules/FindLibAJANTV2.cmake index 3ce91deedff7a2..2ae1801ffdd5f8 100644 --- a/cmake/Modules/FindLibAJANTV2.cmake +++ b/cmake/Modules/FindLibAJANTV2.cmake @@ -14,8 +14,8 @@ else() endif() find_path( - AJA_LIBRARIES_INCLUDE_DIR - NAMES ajalibraries + _LIBAJANTV2_NEW_INCLUDE_DIR + NAMES libajantv2 HINTS ENV AJASDKPath${_lib_suffix} ENV @@ -32,6 +32,38 @@ find_path( PATHS /usr/include /usr/local/include /opt/local/include /sw/include PATH_SUFFIXES include) +if(${_LIBAJANTV2_NEW_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_NEW_INCLUDE_DIR-NOTFOUND") + find_path( + _LIBAJANTV2_OLD_INCLUDE_DIR + NAMES libajantv2 + HINTS ENV + AJASDKPath${_lib_suffix} + ENV + AJASDKPath + ENV + DepsPath${_lib_suffix} + ENV + DepsPath + ${AJASDKPath${_lib_suffix}} + ${AJASDKPath} + ${DepsPath${_lib_suffix}} + ${DepsPath} + ${_AJA_NTV2_INCLUDE_DIRS} + PATHS /usr/include /usr/local/include /opt/local/include /sw/include + PATH_SUFFIXES include) + if(${_LIBAJANTV2_OLD_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_OLD_INCLUDE_DIR-NOTFOUND") + set(AJA_LIBRARIES_INCLUDE_DIR ${_LIBAJANTV2_OLD_INCLUDE_DIR}/ajalibraries) + if(NOT LibAJANTV2_FIND_QUIETLY) + message(DEPRECATION "aja: Using old ntv2 library") + endif() + endif() +else() + set(AJA_LIBRARIES_INCLUDE_DIR ${_LIBAJANTV2_NEW_INCLUDE_DIR}/libajantv2) + if(NOT LibAJANTV2_FIND_QUIETLY) + message(STATUS "aja: Using new libajantv2 library") + endif() +endif() + find_library( AJA_NTV2_LIB NAMES ${_AJA_NTV2_LIBRARIES} ajantv2 libajantv2 @@ -99,10 +131,17 @@ find_package_handle_standard_args(LibAJANTV2 DEFAULT_MSG AJA_LIBRARIES_INCLUDE_D mark_as_advanced(AJA_LIBRARIES_INCLUDE_DIR AJA_NTV2_LIB) if(LIBAJANTV2_FOUND) - set(AJA_LIBRARIES_INCLUDE_DIR ${AJA_LIBRARIES_INCLUDE_DIR}/ajalibraries) + set(AJA_LIBRARIES_INCLUDE_DIR ${AJA_LIBRARIES_INCLUDE_DIR}) set(AJA_LIBRARIES_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR} ${AJA_LIBRARIES_INCLUDE_DIR}/ajaanc ${AJA_LIBRARIES_INCLUDE_DIR}/ajabase ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2 ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/includes) + if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") + list(APPEND LibAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/win) + elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") + list(APPEND LibAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/mac) + elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux") + list(APPEND LibAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/lin) + endif() set(LIBAJANTV2_LIBRARIES ${AJA_NTV2_LIB}) if(AJA_NTV2_DEBUG_LIB STREQUAL "AJA_NTV2_DEBUG_LIB-NOTFOUND") diff --git a/cmake/finders/FindLibAJANTV2.cmake b/cmake/finders/FindLibAJANTV2.cmake index 333ecb6b98d5af..8a667a5d7e8119 100644 --- a/cmake/finders/FindLibAJANTV2.cmake +++ b/cmake/finders/FindLibAJANTV2.cmake @@ -56,11 +56,30 @@ if(PKG_CONFIG_FOUND) endif() find_path( - LibAJANTV2_INCLUDE_DIR - NAMES ajalibraries + _LIBAJANTV2_NEW_INCLUDE_DIR + NAMES libajantv2 HINTS ${PC_LibAJANTV2_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "LibAJANTV2 include directory") + DOC "LibAJANTV2 (new) include directory") +if(${_LIBAJANTV2_NEW_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_NEW_INCLUDE_DIR-NOTFOUND") + find_path( + _LIBAJANTV2_OLD_INCLUDE_DIR + NAMES ajalibraries + HINTS ${PC_LibAJANTV2_INCLUDE_DIRS} + PATHS /usr/include /usr/local/include + DOC "LibAJANTV2 (old) include directory") + if(NOT ${_LIBAJANTV2_OLD_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_OLD_INCLUDE_DIR-NOTFOUND") + set(LibAJANTV2_INCLUDE_DIR ${_LIBAJANTV2_OLD_INCLUDE_DIR}/ajalibraries) + if(NOT LibAJANTV2_FIND_QUIETLY) + message(DEPRECATION "aja: Using old ntv2 library") + endif() + endif() +else() + set(LibAJANTV2_INCLUDE_DIR ${_LIBAJANTV2_NEW_INCLUDE_DIR}/libajantv2) + if(NOT LibAJANTV2_FIND_QUIETLY) + message(STATUS "aja: Using new libajantv2 library") + endif() +endif() find_library( LibAJANTV2_LIBRARY_RELEASE @@ -97,7 +116,7 @@ endif() find_package_handle_standard_args( LibAJANTV2 REQUIRED_VARS LibAJANTV2_LIBRARY LibAJANTV2_INCLUDE_DIR - VERSION_VAR LibAJANTV2_VERSION REASON_FAILURE_MESSAGE LibAJANTV2_ERROR_REASON) + VERSION_VAR LibAJANTV2_VERSION REASON_FAILURE_MESSAGE ${LibAJANTV2_ERROR_REASON}) mark_as_advanced(LibAJANTV2_LIBRARY LibAJANTV2_INCLUDE_DIR) unset(LibAJANTV2_ERROR_REASON) @@ -105,11 +124,19 @@ if(LibAJANTV2_FOUND) list( APPEND LibAJANTV2_INCLUDE_DIRS - ${LibAJANTV2_INCLUDE_DIR}/ajalibraries - ${LibAJANTV2_INCLUDE_DIR}/ajalibraries/ajaanc - ${LibAJANTV2_INCLUDE_DIR}/ajalibraries/ajabase - ${LibAJANTV2_INCLUDE_DIR}/ajalibraries/ajantv2 - ${LibAJANTV2_INCLUDE_DIR}/ajalibraries/ajantv2/includes) + ${LibAJANTV2_INCLUDE_DIR} + ${LibAJANTV2_INCLUDE_DIR}/ajaanc + ${LibAJANTV2_INCLUDE_DIR}/ajabase + ${LibAJANTV2_INCLUDE_DIR}/ajantv2 + ${LibAJANTV2_INCLUDE_DIR}/ajantv2/includes) + if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") + list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/win) + elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") + list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/mac) + elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux") + list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/lin) + endif() + set(LibAJANTV2_LIBRARIES ${LibAJANTV2_LIBRARY}) mark_as_advanced(LibAJANTV2_INCLUDE_DIR LibAJANTV2_LIBRARY) @@ -142,6 +169,20 @@ if(LibAJANTV2_FOUND) endif() set_target_properties(AJA::LibAJANTV2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LibAJANTV2_INCLUDE_DIRS}") + set_target_properties(AJA::LibAJANTV2 PROPERTIES INTERFACE_LINK_OPTIONS $<$:/IGNORE:4099>) + set_property( + TARGET AJA::LibAJANTV2 + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES + $<$:netapi32.lib> + $<$:setupapi.lib> + $<$:shlwapi.lib> + $<$:wbemuuid.lib> + $<$:winmm.lib> + $<$:ws2_32.lib> + "$<$:$>" + "$<$:$>" + "$<$:$>") set_property( TARGET AJA::LibAJANTV2 APPEND diff --git a/plugins/aja/CMakeLists.txt b/plugins/aja/CMakeLists.txt index ffdfdf8e06b8f0..ae170e6f5fc92e 100644 --- a/plugins/aja/CMakeLists.txt +++ b/plugins/aja/CMakeLists.txt @@ -58,19 +58,7 @@ target_compile_options( aja-support PUBLIC $<$:-Wno-deprecated-declarations> $<$:-Wno-unused-variable>) -target_link_libraries( - aja - PRIVATE OBS::aja-support - $<$:netapi32.lib> - $<$:setupapi.lib> - $<$:shlwapi.lib> - $<$:winmm.lib> - $<$:ws2_32.lib> - "$<$:$>" - "$<$:$>" - "$<$:$>") - -target_link_options(aja PRIVATE $<$:/IGNORE:4099>) +target_link_libraries(aja PRIVATE OBS::aja-support) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in win-aja.rc) diff --git a/plugins/aja/aja-widget-io.cpp b/plugins/aja/aja-widget-io.cpp index 18e571dfe98c51..c7d01c0acd4658 100644 --- a/plugins/aja/aja-widget-io.cpp +++ b/plugins/aja/aja-widget-io.cpp @@ -1,6 +1,7 @@ #include "aja-widget-io.hpp" #include "aja-common.hpp" +#include #include #include @@ -35,21 +36,21 @@ static const WidgetInputSocket kWidgetInputSockets[] = { //NTV2InputCrosspointID | NTV2WidgetID | Name | DatastreamIndex { NTV2_INPUT_CROSSPOINT_INVALID, NTV2_WIDGET_INVALID, "", -1}, { NTV2_XptFrameBuffer1Input, NTV2_WgtFrameBuffer1, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer1BInput, NTV2_WgtFrameBuffer1, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer1DS2Input, NTV2_WgtFrameBuffer1, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer2Input, NTV2_WgtFrameBuffer2, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer2BInput, NTV2_WgtFrameBuffer2, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer2DS2Input, NTV2_WgtFrameBuffer2, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer3Input, NTV2_WgtFrameBuffer3, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer3BInput, NTV2_WgtFrameBuffer3, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer3DS2Input, NTV2_WgtFrameBuffer3, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer4Input, NTV2_WgtFrameBuffer4, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer4BInput, NTV2_WgtFrameBuffer4, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer4DS2Input, NTV2_WgtFrameBuffer4, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer5Input, NTV2_WgtFrameBuffer5, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer5BInput, NTV2_WgtFrameBuffer5, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer5DS2Input, NTV2_WgtFrameBuffer5, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer6Input, NTV2_WgtFrameBuffer6, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer6BInput, NTV2_WgtFrameBuffer6, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer6DS2Input, NTV2_WgtFrameBuffer6, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer7Input, NTV2_WgtFrameBuffer7, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer7BInput, NTV2_WgtFrameBuffer7, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer7DS2Input, NTV2_WgtFrameBuffer7, kFramebufferNickname, 1}, { NTV2_XptFrameBuffer8Input, NTV2_WgtFrameBuffer8, kFramebufferNickname, 0}, - { NTV2_XptFrameBuffer8BInput, NTV2_WgtFrameBuffer8, kFramebufferNickname, 1}, + { NTV2_XptFrameBuffer8DS2Input, NTV2_WgtFrameBuffer8, kFramebufferNickname, 1}, { NTV2_XptCSC1VidInput, NTV2_WgtCSC1, kCSCNickname, 0}, { NTV2_XptCSC1KeyInput, NTV2_WgtCSC1, kCSCNickname, 1}, { NTV2_XptCSC2VidInput, NTV2_WgtCSC2, kCSCNickname, 0}, From d7bf65a80b40bec6446dcb4a2f03629fb74cc3f9 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 28 Apr 2024 06:20:05 +0200 Subject: [PATCH 0054/1073] CI: Rename and update windows-signing action --- .../Ensure-Location.ps1 | 0 .../Invoke-External.ps1 | 0 .../{bouf => windows-signing}/Logger.ps1 | 0 .../{bouf => windows-signing}/action.yaml | 69 +++++++++---------- .../cng-release-signing-key.pem | 0 .../{bouf => windows-signing}/config.toml | 7 +- .../{bouf => windows-signing}/prod.crt | 0 .../{bouf => windows-signing}/test.crt | 0 .github/workflows/sign-windows.yaml | 22 ++---- 9 files changed, 42 insertions(+), 56 deletions(-) rename .github/actions/{bouf => windows-signing}/Ensure-Location.ps1 (100%) rename .github/actions/{bouf => windows-signing}/Invoke-External.ps1 (100%) rename .github/actions/{bouf => windows-signing}/Logger.ps1 (100%) rename .github/actions/{bouf => windows-signing}/action.yaml (67%) rename .github/actions/{bouf => windows-signing}/cng-release-signing-key.pem (100%) rename .github/actions/{bouf => windows-signing}/config.toml (87%) rename .github/actions/{bouf => windows-signing}/prod.crt (100%) rename .github/actions/{bouf => windows-signing}/test.crt (100%) diff --git a/.github/actions/bouf/Ensure-Location.ps1 b/.github/actions/windows-signing/Ensure-Location.ps1 similarity index 100% rename from .github/actions/bouf/Ensure-Location.ps1 rename to .github/actions/windows-signing/Ensure-Location.ps1 diff --git a/.github/actions/bouf/Invoke-External.ps1 b/.github/actions/windows-signing/Invoke-External.ps1 similarity index 100% rename from .github/actions/bouf/Invoke-External.ps1 rename to .github/actions/windows-signing/Invoke-External.ps1 diff --git a/.github/actions/bouf/Logger.ps1 b/.github/actions/windows-signing/Logger.ps1 similarity index 100% rename from .github/actions/bouf/Logger.ps1 rename to .github/actions/windows-signing/Logger.ps1 diff --git a/.github/actions/bouf/action.yaml b/.github/actions/windows-signing/action.yaml similarity index 67% rename from .github/actions/bouf/action.yaml rename to .github/actions/windows-signing/action.yaml index 2183152c3e40e1..e7b175c991cc58 100644 --- a/.github/actions/bouf/action.yaml +++ b/.github/actions/windows-signing/action.yaml @@ -1,5 +1,5 @@ -name: Run bouf -description: Generates signed OBS install files and updater files +name: Run bouf Packaging +description: Generates signed OBS install files inputs: gcpWorkloadIdentityProvider: description: GCP Identity Provider Pool ID @@ -7,9 +7,6 @@ inputs: gcpServiceAccountName: description: Google service account name required: true - gcpManifestSigningKeyName: - description: Name of the manifest signing key in GCP KMS - required: false version: description: Version string (e.g., 30.0.0-rc1) required: true @@ -30,9 +27,9 @@ runs: - name: Setup bouf shell: pwsh env: - BOUF_TAG: 'v0.6.2' - BOUF_HASH: '40ca34457a8ac60b9710a41b4cde2a0fc36d8740ab21b01d702069be2e1c5fb9' - BOUF_NSIS_HASH: '88958a9e4e0f3cb6f78e8359fdfa3343d050d5c2158e3ee77cb2cc4a8785ac61' + BOUF_TAG: 'v0.6.3' + BOUF_HASH: '7f1d266467620aa553a705391ee06128e8ee14af66129a0e64a282997fb6fd83' + BOUF_NSIS_HASH: 'a234126de89f122b6a552df3416de3eabcb4195217626c7f4eaec71b20fe36eb' GH_TOKEN: ${{ github.token }} run: | # Download bouf release @@ -53,10 +50,10 @@ runs: Expand-Archive -Path $windows_zip -DestinationPath bin Expand-Archive -Path $nsis_zip -DestinationPath nsis - - name: Download Google CNG Provider + - name: Setup Google CNG Provider shell: pwsh env: - CNG_TAG: 'cng-v1.0' + CNG_TAG: 'cng-v1.1' GH_TOKEN: ${{ github.token }} run: | # Download Google CNG provider release from github @@ -73,18 +70,10 @@ runs: # Finally, install the CNG provider Invoke-External msiexec /i $msiPath /qn /norestart - - name: Install pandoc and rclone + - name: Install rclone shell: pwsh run: | choco install rclone --version=1.64.2 -y --no-progress - choco install pandoc --version=3.1.9 -y --no-progress - - - name: Prepare Release Notes - shell: pwsh - run: | - # Release notes are just the tag body on Windows - Set-Location repo - git tag -l --format='%(contents:body)' ${{ inputs.version }} > "${{ github.workspace }}/notes.rst" - name: 'Authenticate to Google Cloud' uses: 'google-github-actions/auth@5a50e581162a13f4baa8916d01180d2acbc04363' @@ -92,15 +81,21 @@ runs: workload_identity_provider: ${{ inputs.gcpWorkloadIdentityProvider }} service_account: ${{ inputs.gcpServiceAccountName }} - - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@98ddc00a17442e89a24bbf282954a3b65ce6d200' + - name: Download Previous Build + shell: pwsh + env: + RCLONE_GCS_ENV_AUTH: 'true' + run: | + . ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1 + Ensure-Location "${{ github.workspace }}/old_builds" + rclone copy --transfers 100 ":gcs:obs-latest/${{ inputs.channel }}" . - - name: Download Old Builds + - name: Download Presigned Game Capture Files (REMOVE AFTER 30.2!!) shell: pwsh env: RCLONE_GCS_ENV_AUTH: 'true' run: | - rclone copy --transfers 100 :gcs:obs-builds "${{ github.workspace }}/old_builds" + rclone copy :gcs:obs-game-capture "${{ github.workspace }}/build/data/obs-plugins/win-capture" - name: Run bouf shell: pwsh @@ -110,24 +105,26 @@ runs: "--config", "${env:GITHUB_ACTION_PATH}/config.toml", "--version", "${{ inputs.version }}" "--branch", "${{ inputs.channel }}" - "--notes-file", "${{ github.workspace }}/notes.rst" "-i", "${{ github.workspace }}/build" "-p", "${{ github.workspace }}/old_builds" "-o", "${{ github.workspace }}/output" + "--packaging-only" ) Invoke-External "${{ github.workspace }}\bouf\bin\bouf.exe" @boufArgs - - name: Sign Updater Manifest + - name: Sync Latest Build shell: pwsh - if: inputs.gcpManifestSigningKeyName != '' + env: + RCLONE_INCLUDE: '**/${{ inputs.version }}/**' + RCLONE_GCS_ENV_AUTH: 'true' + RCLONE_GCS_BUCKET_POLICY_ONLY: 'true' run: | - $gcloudArgs = @( - "--input-file", "${{ github.workspace }}/output/manifest.json" - "--signature-file", "${{ github.workspace }}/output/manifest.json.sig" - "--digest-algorithm", "sha512" - "--location", "global" - "--keyring", "production" - "--key", "${{ inputs.gcpManifestSigningKeyName }}" - "--version", "1" - ) - gcloud kms asymmetric-sign @gcloudArgs + rclone sync --delete-excluded --transfers 100 "${{ github.workspace }}/old_builds" ":gcs:obs-latest/${{ inputs.channel }}" + + - name: Upload Build to Archive + shell: pwsh + env: + RCLONE_GCS_ENV_AUTH: 'true' + RCLONE_GCS_BUCKET_POLICY_ONLY: 'true' + run: | + rclone copy --transfers 100 "${{ github.workspace }}/old_builds" ":gcs:obs-builds" diff --git a/.github/actions/bouf/cng-release-signing-key.pem b/.github/actions/windows-signing/cng-release-signing-key.pem similarity index 100% rename from .github/actions/bouf/cng-release-signing-key.pem rename to .github/actions/windows-signing/cng-release-signing-key.pem diff --git a/.github/actions/bouf/config.toml b/.github/actions/windows-signing/config.toml similarity index 87% rename from .github/actions/bouf/config.toml rename to .github/actions/windows-signing/config.toml index 80da49a9951460..06b89ae6548dbc 100644 --- a/.github/actions/bouf/config.toml +++ b/.github/actions/windows-signing/config.toml @@ -19,11 +19,13 @@ never_copy = [ ] [prepare.codesign] -sign_cert_file = "repo/.github/actions/bouf/prod.crt" +sign_cert_file = "repo/.github/actions/windows-signing/prod.crt" sign_kms_key_id = "projects/ci-signing/locations/global/keyRings/production/cryptoKeys/release-sign-hsm/cryptoKeyVersions/1" sign_digest = "sha384" sign_ts_serv = "http://timestamp.digicert.com" sign_exts = ['exe', 'dll', 'pyd'] +sign_append = true +sign_ts_algo = "sha256" [prepare.strip_pdbs] # PDBs to not strip @@ -47,8 +49,7 @@ exclude_from_parallel = [ nsis_script = "bouf/nsis/mp-installer.nsi" [package.zip] -skip = true -name = "OBS-Studio-{version}.zip" +name = "OBS-Studio-{version}-x64.zip" pdb_name = "OBS-Studio-{version}-pdbs.zip" [package.updater] diff --git a/.github/actions/bouf/prod.crt b/.github/actions/windows-signing/prod.crt similarity index 100% rename from .github/actions/bouf/prod.crt rename to .github/actions/windows-signing/prod.crt diff --git a/.github/actions/bouf/test.crt b/.github/actions/windows-signing/test.crt similarity index 100% rename from .github/actions/bouf/test.crt rename to .github/actions/windows-signing/test.crt diff --git a/.github/workflows/sign-windows.yaml b/.github/workflows/sign-windows.yaml index 5ce98091b86f56..5003819c20466e 100644 --- a/.github/workflows/sign-windows.yaml +++ b/.github/workflows/sign-windows.yaml @@ -45,7 +45,7 @@ jobs: path: ${{ github.workspace }}/build - name: Run bouf 🥩 - uses: ./repo/.github/actions/bouf + uses: ./repo/.github/actions/windows-signing with: gcpWorkloadIdentityProvider: ${{ secrets.GCP_IDENTITY_POOL }} gcpServiceAccountName: ${{ secrets.GCP_SERVICE_ACCOUNT_NAME }} @@ -56,15 +56,15 @@ jobs: uses: actions/upload-artifact@v4 with: name: obs-studio-windows-x64-${{ github.ref_name }}-signed - compression-level: 6 - path: ${{ github.workspace }}/output/install + compression-level: 0 + path: ${{ github.workspace }}/output/*-x64.zip - name: Upload PDBs uses: actions/upload-artifact@v4 with: name: obs-studio-windows-x64-${{ github.ref_name }}-pdbs - compression-level: 9 - path: ${{ github.workspace }}/output/pdbs + compression-level: 0 + path: ${{ github.workspace }}/output/*-pdbs.zip - name: Upload Installer uses: actions/upload-artifact@v4 @@ -72,15 +72,3 @@ jobs: name: obs-studio-windows-x64-${{ github.ref_name }}-installer compression-level: 0 path: ${{ github.workspace }}/output/*.exe - - - name: Upload Updater Files - uses: actions/upload-artifact@v4 - with: - name: obs-studio-windows-x64-${{ github.ref_name }}-patches - compression-level: 0 - path: | - ${{ github.workspace }}/output/updater - ${{ github.workspace }}/output/*.json - ${{ github.workspace }}/output/*.sig - ${{ github.workspace }}/output/*.txt - ${{ github.workspace }}/output/*.rst From fe42236b1b3b394bd5c9b1ed403b4ea142f320e4 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 28 Apr 2024 06:20:47 +0200 Subject: [PATCH 0055/1073] CI: Update Windows signing workflow commit --- .github/workflows/push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 77402ece7efc11..9a6935699ac91e 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -207,7 +207,7 @@ jobs: sign-windows-build: name: Windows Signing ✍️ - uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@ffd5879ec95f09e4e551b839f5fe6738058231c1 + uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@2f261c4029973bc84774a08197a0d5d8a6b1b046 if: github.repository_owner == 'obsproject' && github.ref_type == 'tag' needs: build-project permissions: From f7f06dea8d26953f5520de54aa0423831915b3b8 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 28 Apr 2024 06:22:16 +0200 Subject: [PATCH 0056/1073] CI: Include Windows artifacts in draft release --- .github/workflows/push.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 9a6935699ac91e..808addbf38fc92 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -219,7 +219,7 @@ jobs: name: Create Release 🛫 if: github.ref_type == 'tag' runs-on: ubuntu-22.04 - needs: build-project + needs: [build-project, sign-windows-build] defaults: run: shell: bash @@ -265,6 +265,9 @@ jobs: ubuntu_x86_64_artifact_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}" ubuntu_x86_64_debug_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}-dbgsym" ubuntu_sources_name="obs-studio-ubuntu-22.04-sources-${commit_hash}" + windows_artifact_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-signed" + windows_installer_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-installer" + windows_debug_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-pdbs" echo '::group::Renaming Artifacts' mv -v "${macos_arm64_artifact_name}/"obs-studio-*-macos-apple.dmg \ @@ -281,6 +284,12 @@ jobs: "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-x86_64-dbsym.ddeb mv -v "${ubuntu_sources_name}/"obs-studio-*-sources.tar.gz \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz + mv -v "${windows_installer_name}/"OBS-Studio-*.exe \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-Installer.exe + mv -v "${windows_artifact_name}/"OBS-Studio-*.zip \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows.zip + mv -v "${windows_debug_name}/"OBS-Studio-*-pdbs.zip \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-PDBs.zip echo '::endgroup::' - name: Generate Checksums 🪪 @@ -291,7 +300,7 @@ jobs: shopt -s extglob echo "### Checksums" > ${{ github.workspace }}/CHECKSUMS.txt - for file in ${{ github.workspace }}/@(*.deb|*.ddeb|*.dmg|*.tar.xz|*.tar.gz); do + for file in ${{ github.workspace }}/@(*.deb|*.ddeb|*.dmg|*.tar.xz|*.tar.gz|*.exe|*.zip); do echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt done @@ -311,3 +320,6 @@ jobs: ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-*.deb ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-*.ddeb ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz + ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows.zip + ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-PDBs.zip + ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-Installer.exe From 7968f567b1a2ffb8850b56041eaf9965e35b56b1 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 28 Apr 2024 07:22:32 +0200 Subject: [PATCH 0057/1073] CI: Add Windows Patches Action --- .../windows-patches/Ensure-Location.ps1 | 29 ++++ .../windows-patches/Invoke-External.ps1 | 40 +++++ .github/actions/windows-patches/Logger.ps1 | 149 ++++++++++++++++++ .github/actions/windows-patches/action.yaml | 111 +++++++++++++ .github/actions/windows-patches/config.toml | 25 +++ .github/workflows/dispatch.yaml | 19 +++ .github/workflows/publish.yaml | 17 ++ 7 files changed, 390 insertions(+) create mode 100644 .github/actions/windows-patches/Ensure-Location.ps1 create mode 100644 .github/actions/windows-patches/Invoke-External.ps1 create mode 100644 .github/actions/windows-patches/Logger.ps1 create mode 100644 .github/actions/windows-patches/action.yaml create mode 100644 .github/actions/windows-patches/config.toml diff --git a/.github/actions/windows-patches/Ensure-Location.ps1 b/.github/actions/windows-patches/Ensure-Location.ps1 new file mode 100644 index 00000000000000..36fb2f7ae3cf36 --- /dev/null +++ b/.github/actions/windows-patches/Ensure-Location.ps1 @@ -0,0 +1,29 @@ +function Ensure-Location { + <# + .SYNOPSIS + Ensures current location to be set to specified directory. + .DESCRIPTION + If specified directory exists, switch to it. Otherwise create it, + then switch. + .EXAMPLE + Ensure-Location "My-Directory" + Ensure-Location -Path "Path-To-My-Directory" + #> + + param( + [Parameter(Mandatory)] + [string] $Path + ) + + if ( ! ( Test-Path $Path ) ) { + $_Params = @{ + ItemType = "Directory" + Path = ${Path} + ErrorAction = "SilentlyContinue" + } + + New-Item @_Params | Set-Location + } else { + Set-Location -Path ${Path} + } +} diff --git a/.github/actions/windows-patches/Invoke-External.ps1 b/.github/actions/windows-patches/Invoke-External.ps1 new file mode 100644 index 00000000000000..d1bfb758515d2e --- /dev/null +++ b/.github/actions/windows-patches/Invoke-External.ps1 @@ -0,0 +1,40 @@ +function Invoke-External { + <# + .SYNOPSIS + Invokes a non-PowerShell command. + .DESCRIPTION + Runs a non-PowerShell command, and captures its return code. + Throws an exception if the command returns non-zero. + .EXAMPLE + Invoke-External 7z x $MyArchive + #> + + if ( $args.Count -eq 0 ) { + throw 'Invoke-External called without arguments.' + } + + if ( ! ( Test-Path function:Log-Information ) ) { + . $PSScriptRoot/Logger.ps1 + } + + $Command = $args[0] + $CommandArgs = @() + + if ( $args.Count -gt 1) { + $CommandArgs = $args[1..($args.Count - 1)] + } + + $_EAP = $ErrorActionPreference + $ErrorActionPreference = "Continue" + + Log-Debug "Invoke-External: ${Command} ${CommandArgs}" + + & $command $commandArgs + $Result = $LASTEXITCODE + + $ErrorActionPreference = $_EAP + + if ( $Result -ne 0 ) { + throw "${Command} ${CommandArgs} exited with non-zero code ${Result}." + } +} diff --git a/.github/actions/windows-patches/Logger.ps1 b/.github/actions/windows-patches/Logger.ps1 new file mode 100644 index 00000000000000..61a81e515237d0 --- /dev/null +++ b/.github/actions/windows-patches/Logger.ps1 @@ -0,0 +1,149 @@ +function Log-Debug { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Debug $m + } + } +} + +function Log-Verbose { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Verbose $m + } + } +} + +function Log-Warning { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Warning $m + } + } +} + +function Log-Error { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Error $m + } + } +} + +function Log-Information { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + if ( ! ( $script:Quiet ) ) { + $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) + $Icon = ' =>' + + foreach($m in $Message) { + Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) " + Write-Host "${m}" + } + } + } +} + +function Log-Group { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + [string[]] $Message + ) + + Process { + if ( $Env:CI -ne $null ) { + if ( $script:LogGroup ) { + Write-Output '::endgroup::' + $script:LogGroup = $false + } + + if ( $Message.count -ge 1 ) { + Write-Output "::group::$($Message -join ' ')" + $script:LogGroup = $true + } + } else { + if ( $Message.count -ge 1 ) { + Log-Information $Message + } + } + } +} + +function Log-Status { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + if ( ! ( $script:Quiet ) ) { + $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' }) + $Icon = ' >' + + foreach($m in $Message) { + Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) " + Write-Host "${m}" + } + } + } +} + +function Log-Output { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + if ( ! ( $script:Quiet ) ) { + $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) + $Icon = '' + + foreach($m in $Message) { + Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}" + } + } + } +} + +$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5 diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml new file mode 100644 index 00000000000000..e9103867eaa1c0 --- /dev/null +++ b/.github/actions/windows-patches/action.yaml @@ -0,0 +1,111 @@ +name: Run bouf Patch Generation +description: Generates OBS updater manifest and patches +inputs: + gcsAccessKeyId: + description: GCS S3 Access Key ID + required: true + gcsAccessKeySecret: + description: GCS S3 Access Key Secret + required: true + workflowSecret: + description: GitHub API token to use for API calls + required: true + tagName: + description: GitHub Release tag + required: true + channel: + description: Update channel + required: false + default: 'stable' + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + path: "repo" + fetch-depth: 0 + + - name: Download Release Artifact + shell: pwsh + env: + GH_TOKEN: ${{ inputs.workflowSecret }} + run: | + # Download OBS release + . ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1 + Invoke-External gh release download "${{ inputs.tagName }}" -p "*-Windows.zip" + Expand-Archive -Path "*-Windows.zip" -DestinationPath "${{ github.workspace }}/build" + + - name: Setup bouf + shell: pwsh + env: + BOUF_TAG: 'v0.6.3' + BOUF_HASH: '7f1d266467620aa553a705391ee06128e8ee14af66129a0e64a282997fb6fd83' + BOUF_NSIS_HASH: 'a234126de89f122b6a552df3416de3eabcb4195217626c7f4eaec71b20fe36eb' + GH_TOKEN: ${{ github.token }} + run: | + # Download bouf release + . ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1 + . ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1 + Ensure-Location bouf + $windows_zip = "bouf-windows-${env:BOUF_TAG}.zip" + $nsis_zip = "bouf-nsis-${env:BOUF_TAG}.zip" + Invoke-External gh release download "${env:BOUF_TAG}" -R "obsproject/bouf" -p $windows_zip -p $nsis_zip + + if ((Get-FileHash $windows_zip -Algorithm SHA256).Hash -ne "${env:BOUF_HASH}") { + throw "bouf hash does not match." + } + if ((Get-FileHash $nsis_zip -Algorithm SHA256).Hash -ne "${env:BOUF_NSIS_HASH}") { + throw "NSIS package hash does not match." + } + + Expand-Archive -Path $windows_zip -DestinationPath bin + Expand-Archive -Path $nsis_zip -DestinationPath nsis + + - name: Install rclone and pandoc + shell: pwsh + run: | + choco install rclone pandoc -y --no-progress + + - name: Download Previous Builds + shell: pwsh + env: + RCLONE_TRANSFERS: '100' + RCLONE_FAST_LIST: 'true' + RCLONE_EXCLUDE: '{pdbs/**,**/${{ inputs.tagName }}/**}' + RCLONE_S3_PROVIDER: 'GCS' + RCLONE_S3_ACCESS_KEY_ID: '${{ inputs.gcsAccessKeyId }}' + RCLONE_S3_SECRET_ACCESS_KEY: '${{ inputs.gcsAccessKeySecret }}' + RCLONE_S3_ENDPOINT: 'https://storage.googleapis.com' + run: | + rclone -q copy ":s3:obs-builds" "${{ github.workspace }}/old_builds" + + - name: Prepare Release Notes + shell: pwsh + run: | + # Release notes are just the tag body on Windows + Set-Location repo + git tag -l --format='%(contents:body)' ${{ inputs.version }} > "${{ github.workspace }}/notes.rst" + + - name: Run bouf + shell: pwsh + run: | + . ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1 + $boufArgs = @( + "--config", "${env:GITHUB_ACTION_PATH}/config.toml", + "--version", "${{ inputs.tagName }}" + "--branch", "${{ inputs.channel }}" + "--notes-file", "${{ github.workspace }}/notes.rst" + "-i", "${{ github.workspace }}/build" + "-p", "${{ github.workspace }}/old_builds" + "-o", "${{ github.workspace }}/output" + "--updater-data-only" + ) + Invoke-External "${{ github.workspace }}\bouf\bin\bouf.exe" @boufArgs + + - name: Upload Outputs + uses: actions/upload-artifact@v4 + with: + name: windows-updater-files + compression-level: 0 + path: ${{ github.workspace }}/output diff --git a/.github/actions/windows-patches/config.toml b/.github/actions/windows-patches/config.toml new file mode 100644 index 00000000000000..93546e0f288fee --- /dev/null +++ b/.github/actions/windows-patches/config.toml @@ -0,0 +1,25 @@ +[general] +log_level = "trace" + +[env] +# On CI these should be in %PATH% +sevenzip_path = "7z" +makensis_path = "makensis" +pandoc_path = "pandoc" +pdbcopy_path = "C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe" + +[prepare.codesign] +skip_sign = true + +[generate] +patch_type = "zstd" +compress_files = true + +[package] + +[package.installer] +skip = true + +[package.updater] +vc_redist_path = "bouf/nsis/VC_redist.x64.exe" +skip_sign = true diff --git a/.github/workflows/dispatch.yaml b/.github/workflows/dispatch.yaml index 496c707f9b18d2..cf6bc78d08ebf1 100644 --- a/.github/workflows/dispatch.yaml +++ b/.github/workflows/dispatch.yaml @@ -12,6 +12,7 @@ on: - services - translations - documentation + - patches ref: description: GitHub reference to use for job type: string @@ -28,6 +29,10 @@ on: description: Custom macOS Intel build for Steam Upload type: string required: false + channel: + description: Channel to use when generating Windows update files + type: string + required: false permissions: contents: write jobs: @@ -130,3 +135,17 @@ jobs: apiToken: ${{ secrets.CF_API_TOKEN }} accountId: ${{ secrets.CF_ACCOUNT_ID }} command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ steps.setup.outputs.commitHash }}' + + windows-patches: + name: Create Windows Patches 🩹 + if: github.repository_owner == 'obsproject' && inputs.job == 'patches' + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/windows-patches + with: + tagName: ${{ inputs.ref }} + workflowSecret: ${{ github.token }} + channel: ${{ inputs.channel }} + gcsAccessKeyId: ${{ secrets.GCS_ACCESS_KEY_ID }} + gcsAccessKeySecret: ${{ secrets.GCS_ACCESS_KEY_SECRET }} diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 16c371b0f5015a..0b023e3f94d096 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -32,10 +32,12 @@ jobs: +([0-9]).+([0-9]).+([0-9]) ) echo 'validTag=true' >> $GITHUB_OUTPUT echo 'flatpakMatrix=["beta", "stable"]' >> $GITHUB_OUTPUT + echo 'updateChannel=stable' >> $GITHUB_OUTPUT ;; +([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) ) echo 'validTag=true' >> $GITHUB_OUTPUT echo 'flatpakMatrix=["beta"]' >> $GITHUB_OUTPUT + echo 'updateChannel=beta' >> $GITHUB_OUTPUT ;; *) echo 'validTag=false' >> $GITHUB_OUTPUT ;; esac @@ -171,3 +173,18 @@ jobs: workflowSecret: ${{ github.token }} tagName: ${{ github.ref_name }} preview: false + + windows-patches: + name: Create Windows Patches 🩹 + needs: check-tag + if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag) + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/windows-patches + with: + tagName: ${{ github.ref_name }} + workflowSecret: ${{ github.token }} + channel: ${{ needs.check-tag.outputs.updateChannel }} + gcsAccessKeyId: ${{ secrets.GCS_ACCESS_KEY_ID }} + gcsAccessKeySecret: ${{ secrets.GCS_ACCESS_KEY_SECRET }} From ad63efd4b16316168a37d5ab58b3f31bbf9f8693 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 2 May 2024 16:49:34 -0400 Subject: [PATCH 0058/1073] CI: Fix Windows Signing action commit hash --- .github/workflows/push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 808addbf38fc92..c61b25da43b3f6 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -207,7 +207,7 @@ jobs: sign-windows-build: name: Windows Signing ✍️ - uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@2f261c4029973bc84774a08197a0d5d8a6b1b046 + uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@d7bf65a80b40bec6446dcb4a2f03629fb74cc3f9 if: github.repository_owner == 'obsproject' && github.ref_type == 'tag' needs: build-project permissions: From 01b61fafbf5f20e69d176e45635de3197b9e2eee Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Fri, 19 Apr 2024 20:18:26 -0700 Subject: [PATCH 0059/1073] obs-ffmpeg: Release encode texture early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During high graphics thread pressure it can take a significant time to acquire the graphics lock. This change releases the OpenGL texture after rendering to avoid the 2nd lock after sending the frame to FFmpeg. This improves 99%-tile/100%-tile and median encode in a near encoder overload scenario, and modestly raises the ceiling before encoder overload in my test scene. Master: min=0 ms, median=4.29 ms, max=33.072 ms, 99th percentile=8.877 ms min=0 ms, median=4.438 ms, max=77.157 ms, 99th percentile=9.853 ms min=0 ms, median=4.527 ms, max=57.292 ms, 99th percentile=9.282 ms This commit: min=0.97 ms, median=3.009 ms, max=13.215 ms, 99th percentile=5.899 ms min=1.181 ms, median=2.91 ms, max=9.854 ms, 99th percentile=5.56 ms min=0.461 ms, median=3.013 ms, max=10.693 ms, 99th percentile=5.871 ms --- plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c | 28 ++++++++++++--------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c index 96464abd98c2e1..2f580f8118b39d 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c @@ -410,8 +410,6 @@ static inline enum gs_color_format drm_to_gs_color_format(int format) static void vaapi_destroy_surface(struct vaapi_surface *out) { - obs_enter_graphics(); - for (uint32_t i = 0; i < out->num_textures; ++i) { if (out->textures[i]) { gs_texture_destroy(out->textures[i]); @@ -419,8 +417,6 @@ static void vaapi_destroy_surface(struct vaapi_surface *out) } } - obs_leave_graphics(); - av_frame_free(&out->frame); } @@ -463,8 +459,6 @@ static bool vaapi_create_surface(struct vaapi_encoder *enc, goto fail; } - obs_enter_graphics(); - for (uint32_t i = 0; i < desc.num_layers; ++i) { unsigned int width = desc.width; unsigned int height = desc.height; @@ -489,8 +483,6 @@ static bool vaapi_create_surface(struct vaapi_encoder *enc, out->num_textures++; } - obs_leave_graphics(); - for (uint32_t i = 0; i < desc.num_objects; ++i) close(desc.objects[i].fd); @@ -591,11 +583,11 @@ static inline bool vaapi_test_texencode(struct vaapi_encoder *enc) !obs_encoder_gpu_scaling_enabled(enc->encoder)) return false; - if (!vaapi_create_surface(enc, &surface)) - return false; - + obs_enter_graphics(); + bool success = vaapi_create_surface(enc, &surface); vaapi_destroy_surface(&surface); - return true; + obs_leave_graphics(); + return success; } static void *vaapi_create_tex_internal(obs_data_t *settings, @@ -840,19 +832,23 @@ static bool vaapi_encode_tex(void *data, struct encoder_texture *texture, *received_packet = false; + obs_enter_graphics(); + + // Destroyed piecemeal to avoid taking the graphics lock again. if (!vaapi_create_surface(enc, &surface)) { warn("vaapi_encode_tex: failed to create texture hw frame"); + obs_leave_graphics(); return false; } - obs_enter_graphics(); - for (uint32_t i = 0; i < surface.num_textures; ++i) { if (!texture->tex[i]) { warn("vaapi_encode_tex: unexpected number of textures"); + obs_leave_graphics(); goto fail; } gs_copy_texture(surface.textures[i], texture->tex[i]); + gs_texture_destroy(surface.textures[i]); } gs_flush(); @@ -871,11 +867,11 @@ static bool vaapi_encode_tex(void *data, struct encoder_texture *texture, if (!vaapi_encode_internal(enc, surface.frame, packet, received_packet)) goto fail; - vaapi_destroy_surface(&surface); + av_frame_free(&surface.frame); return true; fail: - vaapi_destroy_surface(&surface); + av_frame_free(&surface.frame); return false; } From 7f82426dc4dea790c2c4a31141241502cba6631a Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Mon, 8 Jan 2024 13:57:52 +0100 Subject: [PATCH 0060/1073] obs-outputs: Always define `CODEC_HEVC` --- plugins/obs-outputs/flv-mux.c | 4 +++- plugins/obs-outputs/flv-mux.h | 2 -- plugins/obs-outputs/rtmp-stream.c | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c index ff3eb905c76e1d..541176f698aded 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -65,13 +65,15 @@ static void s_w4cc(struct serializer *s, enum video_id_t id) s_w8(s, '0'); s_w8(s, '1'); break; -#ifdef ENABLE_HEVC case CODEC_HEVC: +#ifdef ENABLE_HEVC s_w8(s, 'h'); s_w8(s, 'v'); s_w8(s, 'c'); s_w8(s, '1'); break; +#else + assert(0); #endif case CODEC_H264: assert(0); diff --git a/plugins/obs-outputs/flv-mux.h b/plugins/obs-outputs/flv-mux.h index df4fe629ef5252..f1cd884f6f097d 100644 --- a/plugins/obs-outputs/flv-mux.h +++ b/plugins/obs-outputs/flv-mux.h @@ -24,9 +24,7 @@ enum video_id_t { CODEC_H264 = 1, // legacy CODEC_AV1, // Y2023 spec -#ifdef ENABLE_HEVC CODEC_HEVC, -#endif }; static enum video_id_t to_video_type(const char *codec) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index ac50535db4be8b..f1c09082231b53 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -810,10 +810,12 @@ static bool send_video_header(struct rtmp_stream *stream) case CODEC_H264: packet.size = obs_parse_avc_header(&packet.data, header, size); return send_packet(stream, &packet, true, 0) >= 0; -#ifdef ENABLE_HEVC case CODEC_HEVC: +#ifdef ENABLE_HEVC packet.size = obs_parse_hevc_header(&packet.data, header, size); return send_packet_ex(stream, &packet, true, 0) >= 0; +#else + return false; #endif case CODEC_AV1: packet.size = obs_parse_av1_header(&packet.data, header, size); @@ -1695,10 +1697,12 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) case CODEC_H264: obs_parse_avc_packet(&new_packet, packet); break; -#ifdef ENABLE_HEVC case CODEC_HEVC: +#ifdef ENABLE_HEVC obs_parse_hevc_packet(&new_packet, packet); break; +#else + return; #endif case CODEC_AV1: obs_parse_av1_packet(&new_packet, packet); From c5ef7beda83cc23563a2785995c07f7c83e04fd9 Mon Sep 17 00:00:00 2001 From: John Bradley Date: Thu, 21 Sep 2023 20:22:49 +0200 Subject: [PATCH 0061/1073] obs-outputs: Add initial eRTMP multitrack implementation --- plugins/obs-outputs/flv-mux.c | 115 ++++++++++++++++------ plugins/obs-outputs/flv-mux.h | 10 +- plugins/obs-outputs/rtmp-stream.c | 153 ++++++++++++++++++++---------- plugins/obs-outputs/rtmp-stream.h | 2 +- 4 files changed, 193 insertions(+), 87 deletions(-) diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c index 541176f698aded..e544da85fd5caf 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -32,6 +32,7 @@ #define AUDIODATA_AAC 10.0 #define VIDEO_FRAMETYPE_OFFSET 4 + enum video_frametype_t { FT_KEY = 1 << VIDEO_FRAMETYPE_OFFSET, FT_INTER = 2 << VIDEO_FRAMETYPE_OFFSET, @@ -43,10 +44,16 @@ enum packet_type_t { PACKETTYPE_SEQ_START = 0, PACKETTYPE_FRAMES = 1, PACKETTYPE_SEQ_END = 2, -#ifdef ENABLE_HEVC PACKETTYPE_FRAMESX = 3, -#endif - PACKETTYPE_METADATA = 4 + PACKETTYPE_METADATA = 4, + PACKETTYPE_MPEG2TS_SEQ_START = 5, + PACKETTYPE_MULTITRACK = 6 +}; + +enum multitrack_type_t { + MULTITRACKTYPE_ONE_TRACK = 0x00, + MULTITRACKTYPE_MANY_TRACKS = 0x10, + MULTITRACKTYPE_MANY_TRACKS_MANY_CODECS = 0x20, }; enum datatype_t { @@ -76,7 +83,11 @@ static void s_w4cc(struct serializer *s, enum video_id_t id) assert(0); #endif case CODEC_H264: - assert(0); + s_w8(s, 'a'); + s_w8(s, 'v'); + s_w8(s, 'c'); + s_w8(s, '1'); + break; } } @@ -363,7 +374,8 @@ void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset, // Y2023 spec void flv_packet_ex(struct encoder_packet *packet, enum video_id_t codec_id, - int32_t dts_offset, uint8_t **output, size_t *size, int type) + int32_t dts_offset, uint8_t **output, size_t *size, int type, + size_t idx) { struct array_output_data data; struct serializer s; @@ -373,30 +385,45 @@ void flv_packet_ex(struct encoder_packet *packet, enum video_id_t codec_id, int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset; + bool is_multitrack = idx > 0; + // packet head - int header_metadata_size = 5; -#ifdef ENABLE_HEVC + int header_metadata_size = 5; // w8+w4cc // 3 extra bytes for composition time offset - if (codec_id == CODEC_HEVC && type == PACKETTYPE_FRAMES) { - header_metadata_size = 8; + if ((codec_id == CODEC_H264 || codec_id == CODEC_HEVC) && + type == PACKETTYPE_FRAMES) { + header_metadata_size += 3; // w24 } -#endif + if (is_multitrack) + header_metadata_size += 2; // w8+w8 + s_w8(&s, RTMP_PACKET_TYPE_VIDEO); s_wb24(&s, (uint32_t)packet->size + header_metadata_size); s_wtimestamp(&s, time_ms); s_wb24(&s, 0); // always 0 - // packet ext header - s_w8(&s, - FRAME_HEADER_EX | type | (packet->keyframe ? FT_KEY : FT_INTER)); - s_w4cc(&s, codec_id); + uint8_t frame_type = packet->keyframe ? FT_KEY : FT_INTER; -#ifdef ENABLE_HEVC - // hevc composition time offset - if (codec_id == CODEC_HEVC && type == PACKETTYPE_FRAMES) { + /* + * We only explicitly emit trackIds iff idx > 0. + * The default trackId is 0. + */ + if (is_multitrack) { + s_w8(&s, FRAME_HEADER_EX | PACKETTYPE_MULTITRACK | frame_type); + s_w8(&s, MULTITRACKTYPE_ONE_TRACK | type); + s_w4cc(&s, codec_id); + // trackId + s_w8(&s, (uint8_t)idx); + } else { + s_w8(&s, FRAME_HEADER_EX | type | frame_type); + s_w4cc(&s, codec_id); + } + + // H.264/HEVC composition time offset + if ((codec_id == CODEC_H264 || codec_id == CODEC_HEVC) && + type == PACKETTYPE_FRAMES) { s_wb24(&s, get_ms_time(packet, packet->pts - packet->dts)); } -#endif // packet data s_write(&s, packet->data, packet->size); @@ -409,34 +436,37 @@ void flv_packet_ex(struct encoder_packet *packet, enum video_id_t codec_id, } void flv_packet_start(struct encoder_packet *packet, enum video_id_t codec, - uint8_t **output, size_t *size) + uint8_t **output, size_t *size, size_t idx) { - flv_packet_ex(packet, codec, 0, output, size, PACKETTYPE_SEQ_START); + flv_packet_ex(packet, codec, 0, output, size, PACKETTYPE_SEQ_START, + idx); } void flv_packet_frames(struct encoder_packet *packet, enum video_id_t codec, - int32_t dts_offset, uint8_t **output, size_t *size) + int32_t dts_offset, uint8_t **output, size_t *size, + size_t idx) { int packet_type = PACKETTYPE_FRAMES; -#ifdef ENABLE_HEVC // PACKETTYPE_FRAMESX is an optimization to avoid sending composition // time offsets of 0. See Enhanced RTMP spec. - if (codec == CODEC_HEVC && packet->dts == packet->pts) + if ((codec == CODEC_H264 || codec == CODEC_HEVC) && + packet->dts == packet->pts) packet_type = PACKETTYPE_FRAMESX; -#endif - flv_packet_ex(packet, codec, dts_offset, output, size, packet_type); + flv_packet_ex(packet, codec, dts_offset, output, size, packet_type, + idx); } void flv_packet_end(struct encoder_packet *packet, enum video_id_t codec, - uint8_t **output, size_t *size) + uint8_t **output, size_t *size, size_t idx) { - flv_packet_ex(packet, codec, 0, output, size, PACKETTYPE_SEQ_END); + flv_packet_ex(packet, codec, 0, output, size, PACKETTYPE_SEQ_END, idx); } void flv_packet_metadata(enum video_id_t codec_id, uint8_t **output, size_t *size, int bits_per_raw_sample, uint8_t color_primaries, int color_trc, - int color_space, int min_luminance, int max_luminance) + int color_space, int min_luminance, int max_luminance, + size_t idx) { // metadata array struct array_output_data data; @@ -500,16 +530,39 @@ void flv_packet_metadata(enum video_id_t codec_id, uint8_t **output, s_w8(&s, DATA_TYPE_OBJECT_END); } + bool is_multitrack = idx > 0; + // packet head + // w8+w4cc + int header_metadata_size = 5; + if (is_multitrack) { + // w8+w8 + header_metadata_size += 2; + } + s_w8(&s, RTMP_PACKET_TYPE_VIDEO); - s_wb24(&s, (uint32_t)metadata.bytes.num + 5); // 5 = (w8+w4cc) + s_wb24(&s, (uint32_t)metadata.bytes.num + header_metadata_size); s_wtimestamp(&s, 0); s_wb24(&s, 0); // always 0 // packet ext header // these are the 5 extra bytes mentioned above - s_w8(&s, FRAME_HEADER_EX | PACKETTYPE_METADATA); - s_w4cc(&s, codec_id); + s_w8(&s, FRAME_HEADER_EX | (is_multitrack ? PACKETTYPE_MULTITRACK + : PACKETTYPE_METADATA)); + + /* + * We only add explicitly emit trackIds iff idx > 0. + * The default trackId is 0. + */ + if (is_multitrack) { + s_w8(&s, MULTITRACKTYPE_ONE_TRACK | PACKETTYPE_METADATA); + s_w4cc(&s, codec_id); + // trackId + s_w8(&s, (uint8_t)idx); + } else { + s_w4cc(&s, codec_id); + } + // packet data s_write(&s, metadata.bytes.array, metadata.bytes.num); array_output_serializer_free(&metadata); // must be freed diff --git a/plugins/obs-outputs/flv-mux.h b/plugins/obs-outputs/flv-mux.h index f1cd884f6f097d..032cc8876f1877 100644 --- a/plugins/obs-outputs/flv-mux.h +++ b/plugins/obs-outputs/flv-mux.h @@ -22,7 +22,7 @@ #define MILLISECOND_DEN 1000 enum video_id_t { - CODEC_H264 = 1, // legacy + CODEC_H264 = 1, // legacy & Y2023 spec CODEC_AV1, // Y2023 spec CODEC_HEVC, }; @@ -60,14 +60,14 @@ extern void flv_additional_packet_mux(struct encoder_packet *packet, // Y2023 spec extern void flv_packet_start(struct encoder_packet *packet, enum video_id_t codec, uint8_t **output, - size_t *size); + size_t *size, size_t idx); extern void flv_packet_frames(struct encoder_packet *packet, enum video_id_t codec, int32_t dts_offset, - uint8_t **output, size_t *size); + uint8_t **output, size_t *size, size_t idx); extern void flv_packet_end(struct encoder_packet *packet, enum video_id_t codec, - uint8_t **output, size_t *size); + uint8_t **output, size_t *size, size_t idx); extern void flv_packet_metadata(enum video_id_t codec, uint8_t **output, size_t *size, int bits_per_raw_sample, uint8_t color_primaries, int color_trc, int color_space, int min_luminance, - int max_luminance); + int max_luminance, size_t idx); diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index f1c09082231b53..d3075b64bd580e 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -444,7 +444,7 @@ static int send_packet(struct rtmp_stream *stream, static int send_packet_ex(struct rtmp_stream *stream, struct encoder_packet *packet, bool is_header, - bool is_footer) + bool is_footer, size_t idx) { uint8_t *data; size_t size = 0; @@ -454,12 +454,14 @@ static int send_packet_ex(struct rtmp_stream *stream, return -1; if (is_header) { - flv_packet_start(packet, stream->video_codec, &data, &size); + flv_packet_start(packet, stream->video_codec[idx], &data, &size, + idx); } else if (is_footer) { - flv_packet_end(packet, stream->video_codec, &data, &size); + flv_packet_end(packet, stream->video_codec[idx], &data, &size, + idx); } else { - flv_packet_frames(packet, stream->video_codec, - stream->start_dts_offset, &data, &size); + flv_packet_frames(packet, stream->video_codec[idx], + stream->start_dts_offset, &data, &size, idx); } #ifdef TEST_FRAMEDROPS @@ -669,8 +671,11 @@ static void *send_thread(void *data) int sent; if (packet.type == OBS_ENCODER_VIDEO && - stream->video_codec != CODEC_H264) { - sent = send_packet_ex(stream, &packet, false, false); + (stream->video_codec[packet.track_idx] != CODEC_H264 || + (stream->video_codec[packet.track_idx] == CODEC_H264 && + packet.track_idx != 0))) { + sent = send_packet_ex(stream, &packet, false, false, + packet.track_idx); } else { sent = send_packet(stream, &packet, false, packet.track_idx); @@ -792,10 +797,10 @@ static bool send_audio_header(struct rtmp_stream *stream, size_t idx, return send_packet(stream, &packet, true, idx) >= 0; } -static bool send_video_header(struct rtmp_stream *stream) +static bool send_video_header(struct rtmp_stream *stream, size_t idx) { obs_output_t *context = stream->output; - obs_encoder_t *vencoder = obs_output_get_video_encoder(context); + obs_encoder_t *vencoder = obs_output_get_video_encoder2(context, idx); uint8_t *header; size_t size; @@ -803,35 +808,61 @@ static bool send_video_header(struct rtmp_stream *stream) .timebase_den = 1, .keyframe = true}; + if (!vencoder) + return false; + if (!obs_encoder_get_extra_data(vencoder, &header, &size)) return false; - switch (stream->video_codec) { + switch (stream->video_codec[idx]) { case CODEC_H264: packet.size = obs_parse_avc_header(&packet.data, header, size); - return send_packet(stream, &packet, true, 0) >= 0; + // Always send H.264 on track 0 as old style for compatibility. + if (idx == 0) { + return send_packet(stream, &packet, true, idx) >= 0; + } else { + return send_packet_ex(stream, &packet, true, false, + idx) >= 0; + } case CODEC_HEVC: #ifdef ENABLE_HEVC packet.size = obs_parse_hevc_header(&packet.data, header, size); - return send_packet_ex(stream, &packet, true, 0) >= 0; + return send_packet_ex(stream, &packet, true, false, idx) >= 0; #else return false; #endif case CODEC_AV1: packet.size = obs_parse_av1_header(&packet.data, header, size); - return send_packet_ex(stream, &packet, true, 0) >= 0; + return send_packet_ex(stream, &packet, true, false, idx) >= 0; } return false; } -static bool send_video_metadata(struct rtmp_stream *stream) +// only returns false if there's an error, not if no metadata needs to be sent +static bool send_video_metadata(struct rtmp_stream *stream, size_t idx) { + // send metadata only if HDR + obs_encoder_t *encoder = + obs_output_get_video_encoder2(stream->output, idx); + if (!encoder) + return false; + + video_t *video = obs_encoder_video(encoder); + if (!video) + return false; + + const struct video_output_info *info = video_output_get_info(video); + enum video_colorspace colorspace = info->colorspace; + if (!(colorspace == VIDEO_CS_2100_PQ || + colorspace == VIDEO_CS_2100_HLG)) + return true; + if (handle_socket_read(stream)) - return -1; + return false; // Y2023 spec - if (stream->video_codec != CODEC_H264) { + if (stream->video_codec[idx] != CODEC_H264) { uint8_t *data; size_t size; @@ -892,9 +923,9 @@ static bool send_video_metadata(struct rtmp_stream *stream) max_luminance = (int)obs_get_video_hdr_nominal_peak_level(); - flv_packet_metadata(stream->video_codec, &data, &size, + flv_packet_metadata(stream->video_codec[idx], &data, &size, bits_per_raw_sample, pri, trc, spc, 0, - max_luminance); + max_luminance, idx); int ret = RTMP_Write(&stream->rtmp, (char *)data, (int)size, 0); bfree(data); @@ -906,14 +937,14 @@ static bool send_video_metadata(struct rtmp_stream *stream) return true; } -static bool send_video_footer(struct rtmp_stream *stream) +static bool send_video_footer(struct rtmp_stream *stream, size_t idx) { struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, .timebase_den = 1, .keyframe = false}; packet.size = 0; - return send_packet_ex(stream, &packet, 0, true) >= 0; + return send_packet_ex(stream, &packet, false, true, idx) >= 0; } static inline bool send_headers(struct rtmp_stream *stream) @@ -925,16 +956,16 @@ static inline bool send_headers(struct rtmp_stream *stream) if (!send_audio_header(stream, i++, &next)) return false; - // send metadata only if HDR - video_t *video = obs_get_video(); - const struct video_output_info *info = video_output_get_info(video); - enum video_colorspace colorspace = info->colorspace; - if (colorspace == VIDEO_CS_2100_PQ || colorspace == VIDEO_CS_2100_HLG) - if (!send_video_metadata(stream)) // Y2023 spec - return false; + for (size_t j = 0; j < MAX_OUTPUT_VIDEO_ENCODERS; j++) { + obs_output_t *enc = + obs_output_get_video_encoder2(stream->output, j); + if (!enc) + continue; - if (!send_video_header(stream)) - return false; + if (!send_video_metadata(stream, j) || + !send_video_header(stream, j)) + return false; + } while (next) { if (!send_audio_header(stream, i++, &next)) @@ -946,11 +977,20 @@ static inline bool send_headers(struct rtmp_stream *stream) static inline bool send_footers(struct rtmp_stream *stream) { - if (stream->video_codec == CODEC_H264) - return false; + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *encoder = + obs_output_get_video_encoder2(stream->output, i); + if (!encoder) + continue; - // Y2023 spec - return send_video_footer(stream); + if (i == 0 && stream->video_codec[i] == CODEC_H264) + continue; + + if (!send_video_footer(stream, i)) + return false; + } + + return true; } static inline bool reset_semaphore(struct rtmp_stream *stream) @@ -997,8 +1037,12 @@ static int init_send(struct rtmp_stream *stream) int total_bitrate = 0; - obs_encoder_t *vencoder = obs_output_get_video_encoder(context); - if (vencoder) { + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *vencoder = + obs_output_get_video_encoder2(context, i); + if (!vencoder) + continue; + obs_data_t *params = obs_encoder_get_settings(vencoder); if (params) { int bitrate = @@ -1284,9 +1328,15 @@ static bool init_connect(struct rtmp_stream *stream) obs_encoder_t *aenc = obs_output_get_audio_encoder(stream->output, 0); obs_data_t *vsettings = obs_encoder_get_settings(venc); obs_data_t *asettings = obs_encoder_get_settings(aenc); + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *enc = + obs_output_get_video_encoder2(stream->output, i); - const char *codec = obs_encoder_get_codec(venc); - stream->video_codec = to_video_type(codec); + if (enc) { + const char *codec = obs_encoder_get_codec(enc); + stream->video_codec[i] = to_video_type(codec); + } + } deque_free(&stream->dbr_frames); stream->audio_bitrate = (long)obs_data_get_int(asettings, "bitrate"); @@ -1371,17 +1421,20 @@ static void *connect_thread(void *data) } // HDR streaming disabled for AV1 - if (stream->video_codec != CODEC_H264 && - stream->video_codec != CODEC_HEVC) { - video_t *video = obs_get_video(); - const struct video_output_info *info = - video_output_get_info(video); - - if (info->colorspace == VIDEO_CS_2100_HLG || - info->colorspace == VIDEO_CS_2100_PQ) { - obs_output_signal_stop(stream->output, - OBS_OUTPUT_HDR_DISABLED); - return NULL; + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + if (stream->video_codec[i] && + stream->video_codec[i] != CODEC_H264 && + stream->video_codec[i] != CODEC_HEVC) { + video_t *video = obs_get_video(); + const struct video_output_info *info = + video_output_get_info(video); + + if (info->colorspace == VIDEO_CS_2100_HLG || + info->colorspace == VIDEO_CS_2100_PQ) { + obs_output_signal_stop(stream->output, + OBS_OUTPUT_HDR_DISABLED); + return NULL; + } } } @@ -1693,7 +1746,7 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) stream->got_first_video = true; } - switch (stream->video_codec) { + switch (stream->video_codec[packet->track_idx]) { case CODEC_H264: obs_parse_avc_packet(&new_packet, packet); break; @@ -1821,7 +1874,7 @@ static int rtmp_stream_connect_time(void *data) struct obs_output_info rtmp_output_info = { .id = "rtmp_output", .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE | - OBS_OUTPUT_MULTI_TRACK, + OBS_OUTPUT_MULTI_TRACK_AV, #ifdef NO_CRYPTO .protocols = "RTMP", #else diff --git a/plugins/obs-outputs/rtmp-stream.h b/plugins/obs-outputs/rtmp-stream.h index 29912e5014cc08..6444faeab50224 100644 --- a/plugins/obs-outputs/rtmp-stream.h +++ b/plugins/obs-outputs/rtmp-stream.h @@ -114,7 +114,7 @@ struct rtmp_stream { long dbr_inc_bitrate; bool dbr_enabled; - enum video_id_t video_codec; + enum video_id_t video_codec[MAX_OUTPUT_VIDEO_ENCODERS]; RTMP rtmp; From fab8f0f80e3fc261ba337aba906be872abfdbeea Mon Sep 17 00:00:00 2001 From: John Bradley Date: Thu, 28 Sep 2023 20:28:35 -0500 Subject: [PATCH 0062/1073] obs-outputs: Add eRTMP/eFLV support for FLV --- plugins/obs-outputs/flv-output.c | 356 ++++++++++++++++++++++++++++-- plugins/obs-outputs/rtmp-stream.c | 2 +- 2 files changed, 340 insertions(+), 18 deletions(-) diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 99a6a8aa9a1ba3..8a801bdb0a8e02 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -18,6 +18,11 @@ #include #include #include +#ifdef ENABLE_HEVC +#include "rtmp-hevc.h" +#include +#endif +#include "rtmp-av1.h" #include #include #include @@ -41,12 +46,101 @@ struct flv_output { bool sent_headers; int64_t last_packet_ts; + enum video_id_t video_codec[MAX_OUTPUT_VIDEO_ENCODERS]; + pthread_mutex_t mutex; bool got_first_video; int32_t start_dts_offset; }; +/* Adapted from FFmpeg's libavutil/pixfmt.h + * + * Renamed to make it apparent that these are not imported as this module does + * not use or link against FFmpeg. + */ + +/* clang-format off */ + +/** + * Chromaticity Coordinates of the Source Primaries + * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.1 and ITU-T H.273. + */ +enum OBSColorPrimaries { + OBSCOL_PRI_RESERVED0 = 0, + OBSCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B + OBSCOL_PRI_UNSPECIFIED = 2, + OBSCOL_PRI_RESERVED = 3, + OBSCOL_PRI_BT470M = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + OBSCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM + OBSCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + OBSCOL_PRI_SMPTE240M = 7, ///< identical to above, also called "SMPTE C" even though it uses D65 + OBSCOL_PRI_FILM = 8, ///< colour filters using Illuminant C + OBSCOL_PRI_BT2020 = 9, ///< ITU-R BT2020 + OBSCOL_PRI_SMPTE428 = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ) + OBSCOL_PRI_SMPTEST428_1 = OBSCOL_PRI_SMPTE428, + OBSCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3 + OBSCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3 + OBSCOL_PRI_EBU3213 = 22, ///< EBU Tech. 3213-E (nothing there) / one of JEDEC P22 group phosphors + OBSCOL_PRI_JEDEC_P22 = OBSCOL_PRI_EBU3213, + OBSCOL_PRI_NB ///< Not part of ABI +}; + +/** + * Color Transfer Characteristic + * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.2. + */ +enum OBSColorTransferCharacteristic { + OBSCOL_TRC_RESERVED0 = 0, + OBSCOL_TRC_BT709 = 1, ///< also ITU-R BT1361 + OBSCOL_TRC_UNSPECIFIED = 2, + OBSCOL_TRC_RESERVED = 3, + OBSCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM + OBSCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG + OBSCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC + OBSCOL_TRC_SMPTE240M = 7, + OBSCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics" + OBSCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)" + OBSCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" + OBSCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4 + OBSCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut + OBSCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC) + OBSCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10-bit system + OBSCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12-bit system + OBSCOL_TRC_SMPTE2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems + OBSCOL_TRC_SMPTEST2084 = OBSCOL_TRC_SMPTE2084, + OBSCOL_TRC_SMPTE428 = 17, ///< SMPTE ST 428-1 + OBSCOL_TRC_SMPTEST428_1 = OBSCOL_TRC_SMPTE428, + OBSCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma" + OBSCOL_TRC_NB ///< Not part of ABI +}; + +/** + * YUV Colorspace Type + * These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.3. + */ +enum OBSColorSpace { + OBSCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1 + OBSCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / derived in SMPTE RP 177 Annex B + OBSCOL_SPC_UNSPECIFIED = 2, + OBSCOL_SPC_RESERVED = 3, ///< reserved for future use by ITU-T and ISO/IEC just like 15-255 are + OBSCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + OBSCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 + OBSCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above + OBSCOL_SPC_SMPTE240M = 7, ///< derived from 170M primaries and D65 white point, 170M is derived from BT470 System M's primaries + OBSCOL_SPC_YCGCO = 8, ///< used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16 + OBSCOL_SPC_YCOCG = OBSCOL_SPC_YCGCO, + OBSCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system + OBSCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system + OBSCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x + OBSCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system + OBSCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system + OBSCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp + OBSCOL_SPC_NB ///< Not part of ABI +}; + +/* clang-format on */ + static inline bool stopping(struct flv_output *stream) { return os_atomic_load_bool(&stream->stopping); @@ -101,6 +195,37 @@ static int write_packet(struct flv_output *stream, return ret; } +static int write_packet_ex(struct flv_output *stream, + struct encoder_packet *packet, bool is_header, + bool is_footer, size_t idx) +{ + uint8_t *data; + size_t size = 0; + int ret = 0; + + if (is_header) { + flv_packet_start(packet, stream->video_codec[idx], &data, &size, + idx); + } else if (is_footer) { + flv_packet_end(packet, stream->video_codec[idx], &data, &size, + idx); + } else { + flv_packet_frames(packet, stream->video_codec[idx], + stream->start_dts_offset, &data, &size, idx); + } + + fwrite(data, 1, size, stream->file); + bfree(data); + + // manually created packets + if (is_header || is_footer) + bfree(packet->data); + else + obs_encoder_packet_release(packet); + + return ret; +} + static void write_meta_data(struct flv_output *stream) { uint8_t *meta_data; @@ -111,23 +236,27 @@ static void write_meta_data(struct flv_output *stream) bfree(meta_data); } -static void write_audio_header(struct flv_output *stream) +static bool write_audio_header(struct flv_output *stream, size_t idx) { obs_output_t *context = stream->output; - obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 0); + obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, idx); struct encoder_packet packet = {.type = OBS_ENCODER_AUDIO, .timebase_den = 1}; - if (!obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size)) - return; - write_packet(stream, &packet, true); + if (!aencoder) + return false; + + if (obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size)) + write_packet(stream, &packet, true); + + return true; } -static void write_video_header(struct flv_output *stream) +static bool write_video_header(struct flv_output *stream, size_t idx) { obs_output_t *context = stream->output; - obs_encoder_t *vencoder = obs_output_get_video_encoder(context); + obs_encoder_t *vencoder = obs_output_get_video_encoder2(context, idx); uint8_t *header; size_t size; @@ -135,18 +264,184 @@ static void write_video_header(struct flv_output *stream) .timebase_den = 1, .keyframe = true}; + if (!vencoder) + return false; + if (!obs_encoder_get_extra_data(vencoder, &header, &size)) - return; - packet.size = obs_parse_avc_header(&packet.data, header, size); - write_packet(stream, &packet, true); - bfree(packet.data); + return false; + + switch (stream->video_codec[idx]) { + case CODEC_H264: + packet.size = obs_parse_avc_header(&packet.data, header, size); + // Always send H.264 on track 0 as old style for compatibility. + if (idx == 0) { + write_packet(stream, &packet, true); + } else { + write_packet_ex(stream, &packet, true, false, idx); + } + return true; + case CODEC_HEVC: +#ifdef ENABLE_HEVC + packet.size = obs_parse_hevc_header(&packet.data, header, size); + break; +#else + return false; +#endif + case CODEC_AV1: + packet.size = obs_parse_av1_header(&packet.data, header, size); + break; + } + write_packet_ex(stream, &packet, true, false, idx); + + return true; +} + +// only returns false if there's an error, not if no metadata needs to be sent +static bool write_video_metadata(struct flv_output *stream, size_t idx) +{ + // send metadata only if HDR + obs_encoder_t *encoder = + obs_output_get_video_encoder2(stream->output, idx); + if (!encoder) + return false; + + video_t *video = obs_encoder_video(encoder); + if (!video) + return false; + + const struct video_output_info *info = video_output_get_info(video); + enum video_colorspace colorspace = info->colorspace; + if (!(colorspace == VIDEO_CS_2100_PQ || + colorspace == VIDEO_CS_2100_HLG)) + return true; + + // Y2023 spec + if (stream->video_codec[idx] == CODEC_H264) + return true; + + uint8_t *data; + size_t size; + + enum video_format format = info->format; + + int bits_per_raw_sample; + switch (format) { + case VIDEO_FORMAT_I010: + case VIDEO_FORMAT_P010: + case VIDEO_FORMAT_I210: + bits_per_raw_sample = 10; + break; + case VIDEO_FORMAT_I412: + case VIDEO_FORMAT_YA2L: + bits_per_raw_sample = 12; + break; + default: + bits_per_raw_sample = 8; + } + + int pri = 0; + int trc = 0; + int spc = 0; + switch (colorspace) { + case VIDEO_CS_601: + pri = OBSCOL_PRI_SMPTE170M; + trc = OBSCOL_PRI_SMPTE170M; + spc = OBSCOL_PRI_SMPTE170M; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + pri = OBSCOL_PRI_BT709; + trc = OBSCOL_PRI_BT709; + spc = OBSCOL_PRI_BT709; + break; + case VIDEO_CS_SRGB: + pri = OBSCOL_PRI_BT709; + trc = OBSCOL_TRC_IEC61966_2_1; + spc = OBSCOL_PRI_BT709; + break; + case VIDEO_CS_2100_PQ: + pri = OBSCOL_PRI_BT2020; + trc = OBSCOL_TRC_SMPTE2084; + spc = OBSCOL_SPC_BT2020_NCL; + break; + case VIDEO_CS_2100_HLG: + pri = OBSCOL_PRI_BT2020; + trc = OBSCOL_TRC_ARIB_STD_B67; + spc = OBSCOL_SPC_BT2020_NCL; + } + + int max_luminance = 0; + if (trc == OBSCOL_TRC_ARIB_STD_B67) + max_luminance = 1000; + else if (trc == OBSCOL_TRC_SMPTE2084) + max_luminance = (int)obs_get_video_hdr_nominal_peak_level(); + + flv_packet_metadata(stream->video_codec[idx], &data, &size, + bits_per_raw_sample, pri, trc, spc, 0, + max_luminance, idx); + + fwrite(data, 1, size, stream->file); + bfree(data); + + return true; } static void write_headers(struct flv_output *stream) { + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *enc = + obs_output_get_video_encoder2(stream->output, i); + if (!enc) + break; + + const char *codec = obs_encoder_get_codec(enc); + stream->video_codec[i] = to_video_type(codec); + } + write_meta_data(stream); - write_video_header(stream); - write_audio_header(stream); + write_audio_header(stream, 0); + + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *enc = + obs_output_get_video_encoder2(stream->output, i); + if (!enc) + continue; + + if (!write_video_header(stream, i) || + !write_video_metadata(stream, i)) + return; + } + + for (size_t i = 1; write_audio_header(stream, i); i++) + ; +} + +static bool write_video_footer(struct flv_output *stream, size_t idx) +{ + struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, + .timebase_den = 1, + .keyframe = false}; + packet.size = 0; + + return write_packet_ex(stream, &packet, false, true, idx) >= 0; +} + +static inline bool write_footers(struct flv_output *stream) +{ + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *encoder = + obs_output_get_video_encoder2(stream->output, i); + if (!encoder) + continue; + + if (i == 0 && stream->video_codec[i] == CODEC_H264) + continue; + + if (!write_video_footer(stream, i)) + return false; + } + + return true; } static bool flv_output_start(void *data) @@ -196,6 +491,7 @@ static void flv_output_actual_stop(struct flv_output *stream, int code) os_atomic_set_bool(&stream->active, false); if (stream->file) { + write_footers(stream); write_file_info(stream->file, stream->last_packet_ts, os_ftelli64(stream->file)); @@ -244,8 +540,30 @@ static void flv_output_data(void *data, struct encoder_packet *packet) stream->got_first_video = true; } - obs_parse_avc_packet(&parsed_packet, packet); - write_packet(stream, &parsed_packet, false); + switch (stream->video_codec[packet->track_idx]) { + case CODEC_H264: + obs_parse_avc_packet(&parsed_packet, packet); + break; + case CODEC_HEVC: +#ifdef ENABLE_HEVC + obs_parse_hevc_packet(&parsed_packet, packet); + break; +#else + goto unlock; +#endif + case CODEC_AV1: + obs_parse_av1_packet(&parsed_packet, packet); + break; + } + + if (stream->video_codec[packet->track_idx] != CODEC_H264 || + (stream->video_codec[packet->track_idx] == CODEC_H264 && + packet->track_idx != 0)) { + write_packet_ex(stream, &parsed_packet, false, false, + packet->track_idx); + } else { + write_packet(stream, &parsed_packet, false); + } obs_encoder_packet_release(&parsed_packet); } else { write_packet(stream, packet, false); @@ -269,8 +587,12 @@ static obs_properties_t *flv_output_properties(void *unused) struct obs_output_info flv_output_info = { .id = "flv_output", - .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED, - .encoded_video_codecs = "h264", + .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_MULTI_TRACK_AV, +#ifdef ENABLE_HEVC + .encoded_video_codecs = "h264;hevc;av1", +#else + .encoded_video_codecs = "h264;av1", +#endif .encoded_audio_codecs = "aac", .get_name = flv_output_getname, .create = flv_output_create, diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index d3075b64bd580e..40e3cbbb4d8253 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -957,7 +957,7 @@ static inline bool send_headers(struct rtmp_stream *stream) return false; for (size_t j = 0; j < MAX_OUTPUT_VIDEO_ENCODERS; j++) { - obs_output_t *enc = + obs_encoder_t *enc = obs_output_get_video_encoder2(stream->output, j); if (!enc) continue; From 64caf0401dab65b4311a814ee85b17df6f90d4fc Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Tue, 28 Nov 2023 15:36:14 +0100 Subject: [PATCH 0063/1073] obs-outputs: Add `video_id_t` value for 0 This will be initialized to 0 in various cases, so let's make that a valid enum value (even if it's not valid in rtmp?) --- plugins/obs-outputs/flv-mux.c | 4 ++++ plugins/obs-outputs/flv-mux.h | 1 + plugins/obs-outputs/flv-output.c | 11 +++++++++++ plugins/obs-outputs/rtmp-stream.c | 11 +++++++++++ 4 files changed, 27 insertions(+) diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c index e544da85fd5caf..7a2f140848cd5b 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -66,6 +66,10 @@ enum datatype_t { static void s_w4cc(struct serializer *s, enum video_id_t id) { switch (id) { + case CODEC_NONE: + assert(0 && "Tried to serialize CODEC_NONE"); + break; + case CODEC_AV1: s_w8(s, 'a'); s_w8(s, 'v'); diff --git a/plugins/obs-outputs/flv-mux.h b/plugins/obs-outputs/flv-mux.h index 032cc8876f1877..abebda6b1a0089 100644 --- a/plugins/obs-outputs/flv-mux.h +++ b/plugins/obs-outputs/flv-mux.h @@ -22,6 +22,7 @@ #define MILLISECOND_DEN 1000 enum video_id_t { + CODEC_NONE = 0, // not valid in rtmp CODEC_H264 = 1, // legacy & Y2023 spec CODEC_AV1, // Y2023 spec CODEC_HEVC, diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 8a801bdb0a8e02..97a1b3d13bfd72 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -271,6 +271,12 @@ static bool write_video_header(struct flv_output *stream, size_t idx) return false; switch (stream->video_codec[idx]) { + case CODEC_NONE: + do_log(LOG_ERROR, + "Codec not initialized for track %zu while sending header", + idx); + return false; + case CODEC_H264: packet.size = obs_parse_avc_header(&packet.data, header, size); // Always send H.264 on track 0 as old style for compatibility. @@ -541,6 +547,11 @@ static void flv_output_data(void *data, struct encoder_packet *packet) } switch (stream->video_codec[packet->track_idx]) { + case CODEC_NONE: + do_log(LOG_ERROR, "Codec not initialized for track %zu", + packet->track_idx); + goto unlock; + case CODEC_H264: obs_parse_avc_packet(&parsed_packet, packet); break; diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 40e3cbbb4d8253..8903406ffda9b5 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -815,6 +815,12 @@ static bool send_video_header(struct rtmp_stream *stream, size_t idx) return false; switch (stream->video_codec[idx]) { + case CODEC_NONE: + do_log(LOG_ERROR, + "Codec not initialized for track %zu while sending header", + idx); + return false; + case CODEC_H264: packet.size = obs_parse_avc_header(&packet.data, header, size); // Always send H.264 on track 0 as old style for compatibility. @@ -1747,6 +1753,11 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) } switch (stream->video_codec[packet->track_idx]) { + case CODEC_NONE: + do_log(LOG_ERROR, "Codec not initialized for track %zu", + packet->track_idx); + return; + case CODEC_H264: obs_parse_avc_packet(&new_packet, packet); break; From d584aed501157726e049cfb9197f97fe47e7ce1e Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 27 Jan 2024 03:56:16 +0000 Subject: [PATCH 0064/1073] libobs: Add `obs_encoder_parent_video()` method Allows parent video object of an encoder with an FPS divisor to be fetched. --- docs/sphinx/reference-encoders.rst | 6 +++++- libobs/obs-encoder.c | 15 +++++++++++++++ libobs/obs.h | 7 +++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/reference-encoders.rst b/docs/sphinx/reference-encoders.rst index 3f4e44f030988f..a87df0a0c26737 100644 --- a/docs/sphinx/reference-encoders.rst +++ b/docs/sphinx/reference-encoders.rst @@ -526,10 +526,14 @@ General Encoder Functions --------------------- .. function:: video_t *obs_encoder_video(const obs_encoder_t *encoder) + video_t *obs_encoder_parent_video(const obs_encoder_t *encoder) audio_t *obs_encoder_audio(const obs_encoder_t *encoder) :return: The video/audio handler associated with this encoder, or - *NULL* if none or not a matching encoder type + *NULL* if none or not a matching encoder type. + *parent_video* returns the "original" video handler + associated with this encoder, regardless of whether an FPS + divisor is set. --------------------- diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 3a0bea971cb914..4ad69def463639 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -1232,6 +1232,21 @@ video_t *obs_encoder_video(const obs_encoder_t *encoder) return encoder->fps_override ? encoder->fps_override : encoder->media; } +video_t *obs_encoder_parent_video(const obs_encoder_t *encoder) +{ + if (!obs_encoder_valid(encoder, "obs_encoder_parent_video")) + return NULL; + if (encoder->info.type != OBS_ENCODER_VIDEO) { + blog(LOG_WARNING, + "obs_encoder_parent_video: " + "encoder '%s' is not a video encoder", + obs_encoder_get_name(encoder)); + return NULL; + } + + return encoder->media; +} + audio_t *obs_encoder_audio(const obs_encoder_t *encoder) { if (!obs_encoder_valid(encoder, "obs_encoder_audio")) diff --git a/libobs/obs.h b/libobs/obs.h index 615f6166d84670..5e65084f6e9b34 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2558,6 +2558,13 @@ EXPORT void obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio); */ EXPORT video_t *obs_encoder_video(const obs_encoder_t *encoder); +/** + * Returns the parent video output context used with this encoder, or NULL if not + * a video context. Used when an FPS divisor is set, where the original video + * context would not otherwise be gettable. + */ +EXPORT video_t *obs_encoder_parent_video(const obs_encoder_t *encoder); + /** * Returns the audio output context used with this encoder, or NULL if not * a audio context From 9fa23c8cca33adf90e4b5a38380514d426286d16 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 28 Apr 2024 15:58:34 +0200 Subject: [PATCH 0065/1073] obs-scripting: Remove Python version upper limit on Linux Also removes it for FreeBSD and OpenBSD --- deps/obs-scripting/cmake/python.cmake | 5 +++++ deps/obs-scripting/obspython/CMakeLists.txt | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/deps/obs-scripting/cmake/python.cmake b/deps/obs-scripting/cmake/python.cmake index 0cf5170f84f4a2..7eb98260e69b12 100644 --- a/deps/obs-scripting/cmake/python.cmake +++ b/deps/obs-scripting/cmake/python.cmake @@ -7,6 +7,11 @@ if(ENABLE_SCRIPTING_PYTHON) if(OS_WINDOWS) find_package(Python 3.8...<3.11 REQUIRED Interpreter Development) + elseif( + OS_LINUX + OR OS_FREEBSD + OR OS_OPENBSD) + find_package(Python 3.8 REQUIRED Interpreter Development) else() find_package(Python 3.8...<3.12 REQUIRED Interpreter Development) endif() diff --git a/deps/obs-scripting/obspython/CMakeLists.txt b/deps/obs-scripting/obspython/CMakeLists.txt index 9d17384cc386c9..e90b1499546f8b 100644 --- a/deps/obs-scripting/obspython/CMakeLists.txt +++ b/deps/obs-scripting/obspython/CMakeLists.txt @@ -14,7 +14,13 @@ if(POLICY CMP0094) cmake_policy(SET CMP0094 NEW) endif() -find_package(Python 3.8...<3.12 REQUIRED Interpreter Development) +if(OS_LINUX + OR OS_FREEBSD + OR OS_OPENBSD) + find_package(Python 3.8 REQUIRED Interpreter Development) +else() + find_package(Python 3.8...<3.12 REQUIRED Interpreter Development) +endif() find_package(SWIG 4 REQUIRED) include(UseSWIG) From fa482b6b49c73832644a1ba95485f700a3851fbe Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 3 May 2024 21:05:13 -0700 Subject: [PATCH 0066/1073] libobs: Fix I40A plane height calculation for fourth plane The commit 2fc13540 introduced a typo bug causing the defined height of the fourth plane in I40A to be 0, instead of the original frame height. This changes a 0 to a 3, in order to populate that value correctly. --- libobs/media-io/video-frame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index 5662812feaa91a..3c074d4c605255 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -170,7 +170,7 @@ void video_frame_get_plane_heights(uint32_t heights[MAX_AV_PLANES], heights[0] = height; heights[1] = height / 2; heights[2] = height / 2; - heights[0] = height; + heights[3] = height; break; case VIDEO_FORMAT_I42A: /* four planes: all full height */ From 627308bd53c0ce08c28991de90c813f846425ab2 Mon Sep 17 00:00:00 2001 From: John Bradley Date: Mon, 29 Apr 2024 12:12:46 -0500 Subject: [PATCH 0067/1073] obs-webrtc: Add null terminator to codec array This fixes an issue where, when the MAX_CODECS length was equal to the amount of supported codecs (3), it would leave the list without a null terminator and crash when iterating over the elements. --- plugins/obs-webrtc/whip-service.cpp | 4 ++-- plugins/obs-webrtc/whip-service.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/obs-webrtc/whip-service.cpp b/plugins/obs-webrtc/whip-service.cpp index add9525f63aae0..d8b2dab98dd8c2 100644 --- a/plugins/obs-webrtc/whip-service.cpp +++ b/plugins/obs-webrtc/whip-service.cpp @@ -1,7 +1,7 @@ #include "whip-service.h" -const char *audio_codecs[MAX_CODECS] = {"opus"}; -const char *video_codecs[MAX_CODECS] = {"h264", "hevc", "av1"}; +const char *audio_codecs[] = {"opus", nullptr}; +const char *video_codecs[] = {"h264", "hevc", "av1", nullptr}; WHIPService::WHIPService(obs_data_t *settings, obs_service_t *) : server(), diff --git a/plugins/obs-webrtc/whip-service.h b/plugins/obs-webrtc/whip-service.h index 28e6a7c6cdbdb5..e5ecd63e134b3d 100644 --- a/plugins/obs-webrtc/whip-service.h +++ b/plugins/obs-webrtc/whip-service.h @@ -2,8 +2,6 @@ #include #include -#define MAX_CODECS 3 - struct WHIPService { std::string server; std::string bearer_token; From ec31c7e5bfe1a8fea7f6baff64a474336d945c4a Mon Sep 17 00:00:00 2001 From: Aleks Todorov Date: Thu, 29 Feb 2024 20:46:07 +0000 Subject: [PATCH 0068/1073] UI: Set recording paused before anything else When this call was first introduced in eab10d48b2, it was at top of this block, albeit after the calls to `pause`. Over time it has slowly shifted lower and lower in the block. In reality, it should be the first thing in the block to ensure that further calls have accurate information about the pause state to update themselves. --- UI/window-basic-main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index e3e03023aa65b8..16e074046d95af 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -10719,6 +10719,8 @@ void OBSBasic::PauseRecording() obs_output_t *output = outputHandler->fileOutput; if (obs_output_pause(output, true)) { + os_atomic_set_bool(&recording_paused, true); + pause->setAccessibleName(QTStr("Basic.Main.UnpauseRecording")); pause->setToolTip(QTStr("Basic.Main.UnpauseRecording")); pause->blockSignals(true); @@ -10741,8 +10743,6 @@ void OBSBasic::PauseRecording() trayIconFile)); } - os_atomic_set_bool(&recording_paused, true); - auto replay = replayBufferButton ? replayBufferButton->second() : nullptr; if (replay) @@ -10765,6 +10765,8 @@ void OBSBasic::UnpauseRecording() obs_output_t *output = outputHandler->fileOutput; if (obs_output_pause(output, false)) { + os_atomic_set_bool(&recording_paused, false); + pause->setAccessibleName(QTStr("Basic.Main.PauseRecording")); pause->setToolTip(QTStr("Basic.Main.PauseRecording")); pause->blockSignals(true); @@ -10787,8 +10789,6 @@ void OBSBasic::UnpauseRecording() trayIconFile)); } - os_atomic_set_bool(&recording_paused, false); - auto replay = replayBufferButton ? replayBufferButton->second() : nullptr; if (replay) From bad7b78fe427666e8521e82efe04e30de124c6ea Mon Sep 17 00:00:00 2001 From: Aleks Todorov Date: Thu, 29 Feb 2024 21:03:14 +0000 Subject: [PATCH 0069/1073] UI: Fix text stacking in paused indicator Currently, the paused indicator is never undone, instead relying on the update timer to eventually erase it away when the recording duration is updated. If the user spams pause fast enough, the indicator will stack several times before it is erased - especially if the unpaused branch in the update timer never has a chance to run. Instead of mutating the recordTime text on pause and requiring an undo, extract the UI update to a separate function which computes the full text based on the current state. Call this function when pause is toggled, thereby forcing an accurate UI update that either does or does not incude the paused text. An added benefit is that the paused indicator now disappears immediately. --- UI/window-basic-status-bar.cpp | 35 +++++++++++++++++++++------------- UI/window-basic-status-bar.hpp | 1 + 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/UI/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp index f6e61ca789d21a..02ff7f0539165d 100644 --- a/UI/window-basic-status-bar.cpp +++ b/UI/window-basic-status-bar.cpp @@ -304,15 +304,6 @@ void OBSBasicStatusBar::UpdateRecordTime() if (!paused) { totalRecordSeconds++; - int seconds = totalRecordSeconds % 60; - int totalMinutes = totalRecordSeconds / 60; - int minutes = totalMinutes % 60; - int hours = totalMinutes / 60; - - QString text = QString::asprintf("%02d:%02d:%02d", hours, - minutes, seconds); - - statusWidget->ui->recordTime->setText(text); if (recordOutput && !statusWidget->ui->recordTime->isEnabled()) statusWidget->ui->recordTime->setDisabled(false); } else { @@ -322,6 +313,24 @@ void OBSBasicStatusBar::UpdateRecordTime() streamPauseIconToggle = !streamPauseIconToggle; } + + UpdateRecordTimeLabel(); +} + +void OBSBasicStatusBar::UpdateRecordTimeLabel() +{ + int seconds = totalRecordSeconds % 60; + int totalMinutes = totalRecordSeconds / 60; + int minutes = totalMinutes % 60; + int hours = totalMinutes / 60; + + QString text = + QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds); + if (os_atomic_load_bool(&recording_paused)) { + text += QStringLiteral(" (PAUSED)"); + } + + statusWidget->ui->recordTime->setText(text); } void OBSBasicStatusBar::UpdateDroppedFrames() @@ -547,14 +556,12 @@ void OBSBasicStatusBar::RecordingStopped() void OBSBasicStatusBar::RecordingPaused() { - QString text = statusWidget->ui->recordTime->text() + - QStringLiteral(" (PAUSED)"); - statusWidget->ui->recordTime->setText(text); - if (recordOutput) { statusWidget->ui->recordIcon->setPixmap(recordingPausePixmap); streamPauseIconToggle = true; } + + UpdateRecordTimeLabel(); } void OBSBasicStatusBar::RecordingUnpaused() @@ -562,6 +569,8 @@ void OBSBasicStatusBar::RecordingUnpaused() if (recordOutput) { statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap); } + + UpdateRecordTimeLabel(); } static QPixmap GetPixmap(const QString &filename) diff --git a/UI/window-basic-status-bar.hpp b/UI/window-basic-status-bar.hpp index a78199b4b4aea7..cd79f0db335409 100644 --- a/UI/window-basic-status-bar.hpp +++ b/UI/window-basic-status-bar.hpp @@ -84,6 +84,7 @@ class OBSBasicStatusBar : public QStatusBar { void UpdateBandwidth(); void UpdateStreamTime(); void UpdateRecordTime(); + void UpdateRecordTimeLabel(); void UpdateDroppedFrames(); static void OBSOutputReconnect(void *data, calldata_t *params); From 832ac4bfa8e94695950854d032fe141b3a37050f Mon Sep 17 00:00:00 2001 From: tytan652 Date: Mon, 22 Apr 2024 23:09:03 +0200 Subject: [PATCH 0070/1073] linux-capture: Rename Screen Capture as Display Capture --- plugins/linux-capture/data/locale/en-US.ini | 4 ++-- plugins/linux-capture/xshm-input.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/linux-capture/data/locale/en-US.ini b/plugins/linux-capture/data/locale/en-US.ini index 749c79d61ac506..e34490bd10a188 100644 --- a/plugins/linux-capture/data/locale/en-US.ini +++ b/plugins/linux-capture/data/locale/en-US.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Screen Capture (XSHM)" -Screen="Screen" +X11SharedMemoryDisplayInput="Display Capture (XSHM)" +Display="Display" CaptureCursor="Capture Cursor" AdvancedSettings="Advanced Settings" XServer="X Server" diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 265d739b9907d5..252625a067fb25 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -180,7 +180,7 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data) static const char *xshm_getname(void *unused) { UNUSED_PARAMETER(unused); - return obs_module_text("X11SharedMemoryScreenInput"); + return obs_module_text("X11SharedMemoryDisplayInput"); } /** @@ -376,8 +376,8 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, } dstr_printf(&screen_info, - "Screen %s (%" PRIuFAST32 "x%" PRIuFAST32 - " @ %" PRIuFAST32 ",%" PRIuFAST32 ")", + "%s (%" PRIuFAST32 "x%" PRIuFAST32 " @ %" PRIuFAST32 + ",%" PRIuFAST32 ")", name, w, h, x, y); if (name != name_tmp) @@ -390,7 +390,7 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, /* handle missing screen */ if (old_screen + 1 > count) { - dstr_printf(&screen_info, "Screen %" PRIuFAST32 " (not found)", + dstr_printf(&screen_info, "Display %" PRIuFAST32 " (not found)", old_screen); size_t index = obs_property_list_add_int( screens, screen_info.array, old_screen); @@ -415,7 +415,7 @@ static obs_properties_t *xshm_properties(void *vptr) obs_properties_t *props = obs_properties_create(); obs_property_t *prop; - obs_properties_add_list(props, "screen", obs_module_text("Screen"), + obs_properties_add_list(props, "screen", obs_module_text("Display"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_properties_add_bool(props, "show_cursor", obs_module_text("CaptureCursor")); From c54f4371d620a80f2b04cf112db86376ab15a897 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Mon, 22 Apr 2024 23:14:22 -0400 Subject: [PATCH 0071/1073] UI: Adjust preview scrolling clamp values --- UI/window-basic-preview.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 588c7421283fe1..8594074789ba49 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -2678,6 +2678,9 @@ void OBSBasicPreview::ClampScrollingOffsets() vec3_mulf(&offset, &offset, 0.5f); vec3_maxf(&offset, &offset, 0.0f); + vec3_divf(&target, &target, 2.0f); + vec3_add(&offset, &offset, &target); + scrollingOffset.x = std::clamp(scrollingOffset.x, -offset.x, offset.x); scrollingOffset.y = std::clamp(scrollingOffset.y, -offset.y, offset.y); } From 8721bf61074adfe3bd33251da9eae9638386a619 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 26 Apr 2024 00:14:14 -0400 Subject: [PATCH 0072/1073] UI: Update media source time labels while seeking --- UI/media-controls.cpp | 36 ++++++++++++++++++++++++++---------- UI/media-controls.hpp | 1 + 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/UI/media-controls.cpp b/UI/media-controls.cpp index 836099045f8d4b..8f24e98caa7c4e 100644 --- a/UI/media-controls.cpp +++ b/UI/media-controls.cpp @@ -160,6 +160,7 @@ void MediaControls::MediaSliderReleased() obs_source_media_set_time(source, GetSliderTime(seek)); } + UpdateLabels(seek); seek = lastSeek = -1; } @@ -179,6 +180,7 @@ void MediaControls::MediaSliderMoved(int val) { if (seekTimer.isActive()) { seek = val; + UpdateLabels(seek); } } @@ -358,16 +360,7 @@ void MediaControls::SetSliderPosition() sliderPosition = 0.0f; ui->slider->setValue((int)sliderPosition); - - ui->timerLabel->setText(FormatSeconds((int)(time / 1000.0f))); - - if (!countDownTimer) - ui->durationLabel->setText( - FormatSeconds((int)(duration / 1000.0f))); - else - ui->durationLabel->setText( - QString("-") + - FormatSeconds((int)((duration - time) / 1000.0f))); + UpdateLabels((int)sliderPosition); } QString MediaControls::FormatSeconds(int totalSeconds) @@ -535,3 +528,26 @@ void MediaControls::UpdateSlideCounter() ui->durationLabel->setText("-"); } } + +void MediaControls::UpdateLabels(int val) +{ + OBSSource source = OBSGetStrongRef(weakSource); + if (!source) { + return; + } + + float duration = (float)obs_source_media_get_duration(source); + float percent = (float)val / (float)ui->slider->maximum(); + + float time = percent * duration; + + ui->timerLabel->setText(FormatSeconds((int)(time / 1000.0f))); + + if (!countDownTimer) + ui->durationLabel->setText( + FormatSeconds((int)(duration / 1000.0f))); + else + ui->durationLabel->setText( + QString("-") + + FormatSeconds((int)((duration - time) / 1000.0f))); +} diff --git a/UI/media-controls.hpp b/UI/media-controls.hpp index 82bfd6004f067c..c58254ffe6d7b0 100644 --- a/UI/media-controls.hpp +++ b/UI/media-controls.hpp @@ -64,6 +64,7 @@ private slots: void MoveSliderBackwards(int seconds = 5); void UpdateSlideCounter(); + void UpdateLabels(int val); public slots: void PlayMedia(); From a3876a41d63cfac13d83949ec0a245ecb0f41878 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Mon, 29 Apr 2024 17:20:20 -0400 Subject: [PATCH 0073/1073] UI: Adjust styling of QTabBar tabs --- UI/data/themes/Yami.obt | 30 +++++++++++++----------------- UI/data/themes/Yami_Acri.ovt | 14 +++++--------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index bebca101ea5b6b..a4d1a85a6c7c42 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -165,14 +165,14 @@ --button_border_hover: var(--grey1); --button_border_focus: var(--grey1); - --tab_bg: var(--input_bg); - --tab_bg_hover: var(--grey3); - --tab_bg_down: var(--grey7); - --tab_bg_disabled: var(--grey6); - - --tab_border: var(--grey1); - --tab_border_hover: var(--grey1); - --tab_border_focus: var(--grey1); + --tab_bg: var(--button_bg_disabled); + --tab_bg_hover: var(--button_bg_hover); + --tab_bg_down: var(--primary); + --tab_bg_disabled: var(--button_bg_disabled); + + --tab_border: var(--border_color); + --tab_border_hover: var(--button_border_hover); + --tab_border_focus: var(--button_border_focus); --tab_border_selected: var(--primary); --scrollbar: var(--grey4); @@ -718,7 +718,7 @@ QToolBarExtension { /* Tab Widget */ QTabWidget::pane { /* The tab widget frame */ - border-top: 4px solid var(--bg_base); + border-top: 4px solid var(--tab_bg); } QTabWidget::tab-bar { @@ -771,17 +771,13 @@ QTabBar::tab:selected { } QTabBar::tab:top { -} - -QTabBar::tab:top:selected { - border-bottom: 2px solid var(--tab_border_selected); + border-bottom: 0px solid transparent; + margin-bottom: 0px; } QTabBar::tab:bottom { -} - -QTabBar::tab:bottom:selected { - border-top: 2px solid var(--tab_border_selected); + border-top: 0px solid transparent; + margin-top: 0px; } QTabBar QToolButton { diff --git a/UI/data/themes/Yami_Acri.ovt b/UI/data/themes/Yami_Acri.ovt index e375704801a824..d727a804fc7f55 100644 --- a/UI/data/themes/Yami_Acri.ovt +++ b/UI/data/themes/Yami_Acri.ovt @@ -48,15 +48,11 @@ --button_border_hover: var(--button_bg_hover); --button_border_focus: var(--button_bg_hover); - --tab_bg: var(--input_bg); - --tab_bg_hover: var(--grey3); - --tab_bg_down: var(--grey7); - --tab_bg_disabled: var(--grey6); - - --tab_border: var(--grey3); - --tab_border_hover: var(--grey1); - --tab_border_focus: var(--grey1); - --tab_border_selected: var(--primary_light); + --tab_bg_down: #162458; + + --tab_border_hover: var(--primary_light); + --tab_border_focus: var(--input_border_focus); + --tab_border_selected: var(--primary); --scrollbar: var(--grey5); --scrollbar_hover: var(--primary_light); From fad6f43608cbfb22e132b83f53ad04e45522a144 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Thu, 2 May 2024 22:14:16 +0200 Subject: [PATCH 0074/1073] UI: Set default recording format to Fragmented MOV on macOS In 22205d582c6b2c010e2f48d52067eecb77c8857f, the change to default to Fragmented MP4/MOV on all platforms was reverted due to compatibility issues with the default video player on Windows. On macOS however, the default player (QuickTime Player) works fine with fragmented formats, and instead is unable to play MKV files (which have always been the default format for OBS). This change makes videos with the default settings playable on macOS with the default player. --- UI/window-basic-main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 16e074046d95af..cf464c64603e99 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1453,10 +1453,11 @@ static const double scaled_vals[] = {1.0, 1.25, (1.0 / 0.75), 1.5, 2.5, 2.75, 3.0, 0.0}; extern void CheckExistingCookieId(); -#if OBS_RELEASE_CANDIDATE == 0 && OBS_BETA == 0 -#define DEFAULT_CONTAINER "mkv" -#elif defined(__APPLE__) + +#ifdef __APPLE__ #define DEFAULT_CONTAINER "fragmented_mov" +#elif OBS_RELEASE_CANDIDATE == 0 && OBS_BETA == 0 +#define DEFAULT_CONTAINER "mkv" #else #define DEFAULT_CONTAINER "fragmented_mp4" #endif From 0fc4eb8c9667d386d5af98df29f5df5ec05f2475 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 1 May 2024 10:31:24 +0200 Subject: [PATCH 0075/1073] UI: Restore MuteCheckBox indeterminate state icon in Yami --- UI/data/themes/Yami.obt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index a4d1a85a6c7c42..6b482523f498f8 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -1513,6 +1513,10 @@ MuteCheckBox::indicator:checked { image: url(theme:Dark/mute.svg); } +MuteCheckBox::indicator:indeterminate { + image: url(theme:Dark/unassigned.svg); +} + MuteCheckBox::indicator:unchecked { image: url(theme:Dark/settings/audio.svg); } From 4029ff2ac146586e288a199d60f8105a79f6d0b7 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Jan 2024 14:24:59 +0100 Subject: [PATCH 0076/1073] rtmp-services: Extract common json->settings logic --- plugins/rtmp-services/rtmp-common.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index b8d7dc3a38e07b..c2242bd1203df2 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -489,23 +489,24 @@ static void fill_servers(obs_property_t *servers_prop, json_t *service, } } -static void fill_more_info_link(json_t *service, obs_data_t *settings) +static void copy_string_from_json_if_available(json_t *service, + obs_data_t *settings, + const char *name) { - const char *more_info_link; + const char *string = get_string_val(service, name); + if (string) + obs_data_set_string(settings, name, string); +} - more_info_link = get_string_val(service, "more_info_link"); - if (more_info_link) - obs_data_set_string(settings, "more_info_link", more_info_link); +static void fill_more_info_link(json_t *service, obs_data_t *settings) +{ + copy_string_from_json_if_available(service, settings, "more_info_link"); } static void fill_stream_key_link(json_t *service, obs_data_t *settings) { - const char *stream_key_link; - - stream_key_link = get_string_val(service, "stream_key_link"); - if (stream_key_link) - obs_data_set_string(settings, "stream_key_link", - stream_key_link); + copy_string_from_json_if_available(service, settings, + "stream_key_link"); } static void update_protocol(json_t *service, obs_data_t *settings) From 1959f6852e510791e8095d026d0a5798a95c40d0 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Jan 2024 18:43:27 +0100 Subject: [PATCH 0077/1073] rtmp-services: Always copy service info to settings --- plugins/rtmp-services/rtmp-common.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index c2242bd1203df2..196cc93244017c 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -133,6 +133,8 @@ static const char *get_protocol(json_t *service, obs_data_t *settings) return "RTMP"; } +static void copy_info_to_settings(json_t *service, obs_data_t *settings); + static void rtmp_common_update(void *data, obs_data_t *settings) { struct rtmp_common *service = data; @@ -176,6 +178,8 @@ static void rtmp_common_update(void *data, obs_data_t *settings) } if (serv) { + copy_info_to_settings(serv, settings); + json_t *rec = json_object_get(serv, "recommended"); if (json_is_object(rec)) { update_recommendations(service, rec); @@ -532,6 +536,13 @@ static void update_protocol(json_t *service, obs_data_t *settings) obs_data_set_string(settings, "protocol", "RTMP"); } +static void copy_info_to_settings(json_t *service, obs_data_t *settings) +{ + fill_more_info_link(service, settings); + fill_stream_key_link(service, settings); + update_protocol(service, settings); +} + static inline json_t *find_service(json_t *root, const char *name, const char **p_new_name) { @@ -598,9 +609,8 @@ static bool service_selected(obs_properties_t *props, obs_property_t *p, } fill_servers(obs_properties_get(props, "server"), service, name); - fill_more_info_link(service, settings); - fill_stream_key_link(service, settings); - update_protocol(service, settings); + copy_info_to_settings(service, settings); + return true; } From 103ef76e5e11319864d221ea68c280c570279d7d Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Jan 2024 15:42:57 +0100 Subject: [PATCH 0078/1073] rtmp-services: Allow loading Multitrack Video config from JSON Add support for loading Multitrack Video configuration data from services.json. --- .../rtmp-services/data/schema/service-schema-v5.json | 8 ++++++++ plugins/rtmp-services/rtmp-common.c | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/plugins/rtmp-services/data/schema/service-schema-v5.json b/plugins/rtmp-services/data/schema/service-schema-v5.json index e5bdee3008271f..cf1f97f0e5701d 100644 --- a/plugins/rtmp-services/data/schema/service-schema-v5.json +++ b/plugins/rtmp-services/data/schema/service-schema-v5.json @@ -198,6 +198,14 @@ "$ref": "#/definitions/saneUrl", "description": "Link that provides additional info about the service, presented in the UI as a button next to the services dropdown." }, + "multitrack_video_configuration_url": { + "$ref": "#/definitions/saneUrl", + "description": "Accessed for multitrack video auto configuration" + }, + "multitrack_video_name": { + "type": "string", + "description": "Marketing name for eRTMP multitrack video (e.g., Enhanced Broadcasting for Twitch)" + }, "alt_names": { "type": "array", "description": "Previous names of the service used for migrating existing users to the updated entry.", diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 196cc93244017c..5bef45ae0a1ee1 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -538,8 +538,19 @@ static void update_protocol(json_t *service, obs_data_t *settings) static void copy_info_to_settings(json_t *service, obs_data_t *settings) { + const char *name = obs_data_get_string(settings, "service"); + fill_more_info_link(service, settings); fill_stream_key_link(service, settings); + copy_string_from_json_if_available( + service, settings, "multitrack_video_configuration_url"); + copy_string_from_json_if_available(service, settings, + "multitrack_video_name"); + if (!obs_data_has_user_value(settings, "multitrack_video_name")) { + obs_data_set_string(settings, "multitrack_video_name", + "Multitrack Video"); + } + update_protocol(service, settings); } From d0b35261af5773a9703bd5450fc252ac16fad6ab Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 10 Apr 2024 13:34:06 +0200 Subject: [PATCH 0079/1073] rtmp-services: Add Multitrack Video disclaimer --- plugins/rtmp-services/data/locale/en-US.ini | 2 ++ .../data/schema/service-schema-v5.json | 4 ++++ plugins/rtmp-services/rtmp-common.c | 22 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/plugins/rtmp-services/data/locale/en-US.ini b/plugins/rtmp-services/data/locale/en-US.ini index da2debb660d699..f5f2147522f618 100644 --- a/plugins/rtmp-services/data/locale/en-US.ini +++ b/plugins/rtmp-services/data/locale/en-US.ini @@ -8,3 +8,5 @@ UseAuth="Use authentication" Username="Username" Password="Password" ShowAll="Show all services" +MultitrackVideo.Disclaimer="%1 automatically optimizes your settings to encode and send multiple video qualities to %2. Selecting this option will send %2 information about your computer and software setup." +MultitrackVideo.LearnMoreLink=" Learn More" diff --git a/plugins/rtmp-services/data/schema/service-schema-v5.json b/plugins/rtmp-services/data/schema/service-schema-v5.json index cf1f97f0e5701d..16e07848303b57 100644 --- a/plugins/rtmp-services/data/schema/service-schema-v5.json +++ b/plugins/rtmp-services/data/schema/service-schema-v5.json @@ -206,6 +206,10 @@ "type": "string", "description": "Marketing name for eRTMP multitrack video (e.g., Enhanced Broadcasting for Twitch)" }, + "multitrack_video_learn_more_link": { + "$ref": "#/definitions/saneUrl", + "description": "Link to additional information and privacy policy (for e.g., data sent to `multitrack_video_configuration_url`)" + }, "alt_names": { "type": "array", "description": "Previous names of the service used for migrating existing users to the updated entry.", diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 5bef45ae0a1ee1..59899a8019b351 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -551,6 +551,28 @@ static void copy_info_to_settings(json_t *service, obs_data_t *settings) "Multitrack Video"); } + const char *learn_more_link_url = + get_string_val(service, "multitrack_video_learn_more_link"); + struct dstr learn_more_link = {0}; + if (learn_more_link_url) { + dstr_init_copy( + &learn_more_link, + obs_module_text("MultitrackVideo.LearnMoreLink")); + dstr_replace(&learn_more_link, "%1", learn_more_link_url); + } + + struct dstr str; + dstr_init_copy(&str, obs_module_text("MultitrackVideo.Disclaimer")); + dstr_replace(&str, "%1", + obs_data_get_string(settings, "multitrack_video_name")); + dstr_replace(&str, "%2", name); + if (learn_more_link.array) { + dstr_cat(&str, learn_more_link.array); + } + obs_data_set_string(settings, "multitrack_video_disclaimer", str.array); + dstr_free(&learn_more_link); + dstr_free(&str); + update_protocol(service, settings); } From e92accf136b9ffd1ea812c72c9aca2ffac5d7047 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Jan 2024 18:43:48 +0100 Subject: [PATCH 0080/1073] rtmp-services: Populate Twitch specific multitrack video settings --- plugins/rtmp-services/data/services.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 41dbede6fabc5a..90145baee9cd01 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -6,6 +6,9 @@ "name": "Twitch", "common": true, "stream_key_link": "https://dashboard.twitch.tv/settings/stream", + "multitrack_video_configuration_url": "https://ingest.twitch.tv/api/v3/GetClientConfiguration", + "multitrack_video_name": "Enhanced Broadcasting", + "multitrack_video_learn_more_link": "https://help.twitch.tv/s/article/multiple-encodes", "servers": [ { "name": "Asia: Hong Kong", From 5b51d202b5880190eca3bcb5b59da8707a8fe69d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 7 May 2024 22:38:07 -0700 Subject: [PATCH 0081/1073] obs-webrtc: Remove duplicate initialize calls `obs_output_initialize_encoders()` and can_begin_data_capture are already being called in the `Start()` function, so these duplicate calls serve no function. I'm assuming they were introduced accidentally during a previous refactor. --- plugins/obs-webrtc/whip-output.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index b236111e55a6b3..56e087beec0867 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -188,18 +188,12 @@ void WHIPOutput::ConfigureVideoTrack(std::string media_stream_id, } /** - * @brief Initialize encoders and store connect info provided by the service. + * @brief Store connect info provided by the service. * * @return bool */ bool WHIPOutput::Init() { - if (!obs_output_can_begin_data_capture(output, 0)) - return false; - - if (!obs_output_initialize_encoders(output, 0)) - return false; - obs_service_t *service = obs_output_get_service(output); if (!service) { obs_output_signal_stop(output, OBS_OUTPUT_ERROR); From 5f1d60b1a0852233533c4f652e98953a5d38846b Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 4 May 2024 21:56:10 +0200 Subject: [PATCH 0082/1073] build-aux: Disable DeckLink in the Flatpak Since the Flatpak became part of the repo, nothing has changed on BlackMagic side. DeckLink libraries are still not redistributable and without stable download link so even extra-data is not usable. This makes enabling DeckLink feature in the Flatpak impossible without involving customization on the end-user side which is not how Flatpak is designed for. --- build-aux/com.obsproject.Studio.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index 74468291a951a4..6a66aed439ad71 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -87,7 +87,8 @@ "-DENABLE_VLC=OFF", "-DENABLE_AJA=ON", "-DENABLE_LIBFDK=ON", - "-DENABLE_QSV11=ON" + "-DENABLE_QSV11=ON", + "-DENABLE_DECKLINK=OFF" ], "secret-opts": [ "-DRESTREAM_CLIENTID=$RESTREAM_CLIENTID", From 130be55973d2a52e4ea01e62ff7122237acb4434 Mon Sep 17 00:00:00 2001 From: MrMahgu Date: Fri, 19 Apr 2024 16:01:06 -0400 Subject: [PATCH 0083/1073] libobs-winrt: Remove interop code now provided by Windows SDK Remove the custom declarations of the external functions CreateDirect3D11DeviceFromDXGIDevice and CreateDirect3D11SurfaceFromDXGISurface, as well as the IDirect3DDxgiInterfaceAccess interface. --- libobs-winrt/CMakeLists.txt | 1 + libobs-winrt/cmake/legacy.cmake | 1 + libobs-winrt/winrt-capture.cpp | 17 ++--------------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/libobs-winrt/CMakeLists.txt b/libobs-winrt/CMakeLists.txt index 2940840c38cbe9..fb6b957ca9983a 100644 --- a/libobs-winrt/CMakeLists.txt +++ b/libobs-winrt/CMakeLists.txt @@ -22,6 +22,7 @@ target_precompile_headers( + ) diff --git a/libobs-winrt/cmake/legacy.cmake b/libobs-winrt/cmake/legacy.cmake index 86f439ec7c483d..63aa345f672f4c 100644 --- a/libobs-winrt/cmake/legacy.cmake +++ b/libobs-winrt/cmake/legacy.cmake @@ -14,6 +14,7 @@ target_precompile_headers( + ) diff --git a/libobs-winrt/winrt-capture.cpp b/libobs-winrt/winrt-capture.cpp index 3be44612578a7c..a7bef794bd6cf2 100644 --- a/libobs-winrt/winrt-capture.cpp +++ b/libobs-winrt/winrt-capture.cpp @@ -1,19 +1,5 @@ #include "winrt-capture.h" -extern "C" { -HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice( - ::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice); - -HRESULT __stdcall CreateDirect3D11SurfaceFromDXGISurface( - ::IDXGISurface *dgxiSurface, ::IInspectable **graphicsSurface); -} - -struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) - IDirect3DDxgiInterfaceAccess : ::IUnknown { - virtual HRESULT __stdcall GetInterface(GUID const &id, - void **object) = 0; -}; - extern "C" EXPORT BOOL winrt_capture_supported() try { /* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */ @@ -50,7 +36,8 @@ template static winrt::com_ptr GetDXGIInterfaceFromObject( winrt::Windows::Foundation::IInspectable const &object) { - auto access = object.as(); + auto access = object.as(); winrt::com_ptr result; winrt::check_hresult( access->GetInterface(winrt::guid_of(), result.put_void())); From 06e2b31001f488e633defea0b293e83655ab230a Mon Sep 17 00:00:00 2001 From: pkv Date: Sun, 5 May 2024 19:09:33 +0200 Subject: [PATCH 0084/1073] obs-ffmpeg: Fix 7.1 ALAC encoding Commit [1] added ALAC & PCM support. But 7.1 ALAC encoding fails. This fixes the issue by assigning the correct 7.1 layout supported by FFmpeg ALAC encoder (7.1(wide)). [1] https://github.com/obsproject/obs-studio/commit/3ae98511d09f1ebaf5c9cc005a285efd1a26aeff Signed-off-by: pkv --- plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c index d65be4073262ab..49ee82dae85421 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c @@ -271,12 +271,21 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder, #else av_channel_layout_default(&enc->context->ch_layout, (int)audio_output_get_channels(audio)); + /* The avutil default channel layout for 5 channels is 5.0, which OBS + * does not support. Manually set 5 channels to 4.1. */ if (aoi->speakers == SPEAKERS_4POINT1) enc->context->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1; + /* AAC, ALAC, & FLAC default to 3.0 for 3 channels instead of 2.1. + * Tell the encoder to deal with 2.1 as if it were 3.0. */ if (aoi->speakers == SPEAKERS_2POINT1) enc->context->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_SURROUND; + // ALAC supports 7.1 wide instead of regular 7.1. + if (aoi->speakers == SPEAKERS_7POINT1 && + astrcmpi(enc->type, "alac") == 0) + enc->context->ch_layout = + (AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK; #endif enc->context->sample_rate = audio_output_get_sample_rate(audio); From 5b2e2a9c6847d18162f5ec606ffb0598e128d201 Mon Sep 17 00:00:00 2001 From: pkv Date: Mon, 6 May 2024 21:41:47 +0200 Subject: [PATCH 0085/1073] libobs/media-io: Fix media-remux channel layout for 5 channels FFmpeg has 5.0 as default layout for 5 channels. But obs-studio uses 4.1. This is a fix when remuxing. Signed-off-by: pkv --- libobs/media-io/media-remux.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libobs/media-io/media-remux.c b/libobs/media-io/media-remux.c index c063f6fea5b788..1ab843e75715b1 100644 --- a/libobs/media-io/media-remux.c +++ b/libobs/media-io/media-remux.c @@ -158,10 +158,23 @@ static inline bool init_output(media_remux_job_t job, const char *out_filename) out_stream->codecpar->channel_layout = av_get_default_channel_layout( in_stream->codecpar->channels); + /* The avutil default channel layout for 5 channels is + * 5.0, which OBS does not support. Manually set 5 + * channels to 4.1. */ + if (in_stream->codecpar->channels == 5) + out_stream->codecpar->channel_layout = + av_get_channel_layout("4.1"); #else av_channel_layout_default( &out_stream->codecpar->ch_layout, in_stream->codecpar->ch_layout.nb_channels); + /* The avutil default channel layout for 5 channels is + * 5.0, which OBS does not support. Manually set 5 + * channels to 4.1. */ + if (in_stream->codecpar->ch_layout.nb_channels == 5) + out_stream->codecpar->ch_layout = + (AVChannelLayout) + AV_CHANNEL_LAYOUT_4POINT1; #endif } } From dfef65c30fac557db6ba016112aad006fadac0db Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 18 Apr 2024 13:41:34 +0200 Subject: [PATCH 0086/1073] linux-pipewire: Unify ScreenCast sources as Screen Capture Allow to select both main type of ScreenCast (monitor, window) with only one source type rather than adding a source type per ScreenCast type. This change is made as a new Screen Capture source type which obsoletes the use of the previous Screen Capture (monitor-only) and Window Capture. --- plugins/linux-pipewire/data/locale/en-US.ini | 1 + plugins/linux-pipewire/screencast-portal.c | 87 +++++++++++++++----- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/plugins/linux-pipewire/data/locale/en-US.ini b/plugins/linux-pipewire/data/locale/en-US.ini index d73ce8f6148b14..f7482bc5401c25 100644 --- a/plugins/linux-pipewire/data/locale/en-US.ini +++ b/plugins/linux-pipewire/data/locale/en-US.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Screen Capture (PipeWire)" PipeWireSelectMonitor="Select Monitor" PipeWireSelectWindow="Select Window" PipeWireWindowCapture="Window Capture (PipeWire)" +PipeWireSelectScreenCast="Open Selector" Resolution="Resolution" ShowCursor="Show Cursor" VideoFormat="Video Format" diff --git a/plugins/linux-pipewire/screencast-portal.c b/plugins/linux-pipewire/screencast-portal.c index 0a1ff211858093..a5cdec7265761f 100644 --- a/plugins/linux-pipewire/screencast-portal.c +++ b/plugins/linux-pipewire/screencast-portal.c @@ -35,8 +35,15 @@ enum portal_cursor_mode { PORTAL_CURSOR_MODE_METADATA = 1 << 2, }; +enum obs_portal_capture_type { + OBS_PORTAL_CAPTURE_TYPE_MONITOR = PORTAL_CAPTURE_TYPE_MONITOR, + OBS_PORTAL_CAPTURE_TYPE_WINDOW = PORTAL_CAPTURE_TYPE_WINDOW, + OBS_PORTAL_CAPTURE_TYPE_UNIFIED = PORTAL_CAPTURE_TYPE_MONITOR | + PORTAL_CAPTURE_TYPE_WINDOW, +}; + struct screencast_portal_capture { - enum portal_capture_type capture_type; + enum obs_portal_capture_type capture_type; GCancellable *cancellable; @@ -139,18 +146,19 @@ static uint32_t get_screencast_version(void) /* ------------------------------------------------- */ -static const char *capture_type_to_string(enum portal_capture_type capture_type) +static const char * +capture_type_to_string(enum obs_portal_capture_type capture_type) { switch (capture_type) { - case PORTAL_CAPTURE_TYPE_MONITOR: - return "desktop"; - case PORTAL_CAPTURE_TYPE_WINDOW: + case OBS_PORTAL_CAPTURE_TYPE_MONITOR: + return "monitor"; + case OBS_PORTAL_CAPTURE_TYPE_WINDOW: return "window"; - case PORTAL_CAPTURE_TYPE_VIRTUAL: + case OBS_PORTAL_CAPTURE_TYPE_UNIFIED: + return "monitor and window"; default: return "unknown"; } - return "unknown"; } /* ------------------------------------------------- */ @@ -285,8 +293,7 @@ static void on_start_response_received_cb(GVariant *parameters, void *user_data) obs_source_save(capture->source); } - blog(LOG_INFO, "[pipewire] %s selected, setting up screencast", - capture_type_to_string(capture->capture_type)); + blog(LOG_INFO, "[pipewire] source selected, setting up screencast"); open_pipewire_remote(capture); } @@ -582,7 +589,7 @@ static void *screencast_portal_desktop_capture_create(obs_data_t *settings, struct screencast_portal_capture *capture; capture = bzalloc(sizeof(struct screencast_portal_capture)); - capture->capture_type = PORTAL_CAPTURE_TYPE_MONITOR; + capture->capture_type = OBS_PORTAL_CAPTURE_TYPE_MONITOR; capture->cursor_visible = obs_data_get_bool(settings, "ShowCursor"); capture->restore_token = bstrdup(obs_data_get_string(settings, "RestoreToken")); @@ -598,7 +605,24 @@ static void *screencast_portal_window_capture_create(obs_data_t *settings, struct screencast_portal_capture *capture; capture = bzalloc(sizeof(struct screencast_portal_capture)); - capture->capture_type = PORTAL_CAPTURE_TYPE_WINDOW; + capture->capture_type = OBS_PORTAL_CAPTURE_TYPE_WINDOW; + capture->cursor_visible = obs_data_get_bool(settings, "ShowCursor"); + capture->restore_token = + bstrdup(obs_data_get_string(settings, "RestoreToken")); + capture->source = source; + + init_screencast_capture(capture); + + return capture; +} + +static void *screencast_portal_capture_create(obs_data_t *settings, + obs_source_t *source) +{ + struct screencast_portal_capture *capture; + + capture = bzalloc(sizeof(struct screencast_portal_capture)); + capture->capture_type = OBS_PORTAL_CAPTURE_TYPE_UNIFIED; capture->cursor_visible = obs_data_get_bool(settings, "ShowCursor"); capture->restore_token = bstrdup(obs_data_get_string(settings, "RestoreToken")); @@ -657,13 +681,15 @@ static obs_properties_t *screencast_portal_capture_get_properties(void *data) obs_properties_t *properties; switch (capture->capture_type) { - case PORTAL_CAPTURE_TYPE_MONITOR: + case OBS_PORTAL_CAPTURE_TYPE_MONITOR: reload_string_id = "PipeWireSelectMonitor"; break; - case PORTAL_CAPTURE_TYPE_WINDOW: + case OBS_PORTAL_CAPTURE_TYPE_WINDOW: reload_string_id = "PipeWireSelectWindow"; break; - case PORTAL_CAPTURE_TYPE_VIRTUAL: + case OBS_PORTAL_CAPTURE_TYPE_UNIFIED: + reload_string_id = "PipeWireSelectScreenCast"; + break; default: return NULL; } @@ -744,21 +770,21 @@ void screencast_portal_load(void) (available_capture_types & PORTAL_CAPTURE_TYPE_WINDOW) != 0; if (available_capture_types == 0) { - blog(LOG_INFO, "[pipewire] No captures available"); + blog(LOG_INFO, "[pipewire] No capture sources available"); return; } - blog(LOG_INFO, "[pipewire] Available captures:"); + blog(LOG_INFO, "[pipewire] Available capture sources:"); if (desktop_capture_available) - blog(LOG_INFO, "[pipewire] - Desktop capture"); + blog(LOG_INFO, "[pipewire] - Monitor source"); if (window_capture_available) - blog(LOG_INFO, "[pipewire] - Window capture"); + blog(LOG_INFO, "[pipewire] - Window source"); // Desktop capture const struct obs_source_info screencast_portal_desktop_capture_info = { .id = "pipewire-desktop-capture-source", .type = OBS_SOURCE_TYPE_INPUT, - .output_flags = OBS_SOURCE_VIDEO, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE, .get_name = screencast_portal_desktop_capture_get_name, .create = screencast_portal_desktop_capture_create, .destroy = screencast_portal_capture_destroy, @@ -780,7 +806,7 @@ void screencast_portal_load(void) const struct obs_source_info screencast_portal_window_capture_info = { .id = "pipewire-window-capture-source", .type = OBS_SOURCE_TYPE_INPUT, - .output_flags = OBS_SOURCE_VIDEO, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_OBSOLETE, .get_name = screencast_portal_window_capture_get_name, .create = screencast_portal_window_capture_create, .destroy = screencast_portal_capture_destroy, @@ -797,6 +823,27 @@ void screencast_portal_load(void) }; if (window_capture_available) obs_register_source(&screencast_portal_window_capture_info); + + // Screen capture (monitor and window) + const struct obs_source_info screencast_portal_capture_info = { + .id = "pipewire-screen-capture-source", + .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_VIDEO, + .get_name = screencast_portal_desktop_capture_get_name, + .create = screencast_portal_capture_create, + .destroy = screencast_portal_capture_destroy, + .save = screencast_portal_capture_save, + .get_defaults = screencast_portal_capture_get_defaults, + .get_properties = screencast_portal_capture_get_properties, + .update = screencast_portal_capture_update, + .show = screencast_portal_capture_show, + .hide = screencast_portal_capture_hide, + .get_width = screencast_portal_capture_get_width, + .get_height = screencast_portal_capture_get_height, + .video_render = screencast_portal_capture_video_render, + .icon_type = OBS_ICON_TYPE_DESKTOP_CAPTURE, + }; + obs_register_source(&screencast_portal_capture_info); } void screencast_portal_unload(void) From 2ea39b11e7dbf3cc551015cb3ddcfd8137cbd8c0 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 8 May 2024 18:07:45 -0400 Subject: [PATCH 0087/1073] CI: Update deps to 2024-05-08 release Notable changes: * deps.ffmpeg: Update libpng to 1.6.43 * deps.ffmpeg: Update opus to 1.5.2 * deps.ffmpeg: Update libvpx to v1.14.0 * deps.ffmpeg: Update SVT-AV1 to 2.0.0 * deps.ffmpeg: Update aom to 3.9.0 * deps.ffmpeg: Update libdatachannel to v0.21.0 * deps.ffmpeg: Update AMF to 1.4.33 * deps.ffmpeg: Update FFmpeg to 7.0 * deps.macos: Update LuaJIT to 2.1 5790d25397 * deps.macos: Update libpng to 1.6.43 * deps.macos: Update Asio to 1.30.2 * deps.macos: Replace ntv2 with libajantv2 * deps.macos: Update Sparkle to 2.6.2 * deps.windows: Update curl to 8.7.1 * deps.windows: Update LuaJIT to 2.1 5790d25397 * deps.windows: Update Asio to 1.30.2 * deps.windows: Update Zstandard to 1.5.6 * deps.windows: Update VPL to v2.10.2 * deps.windows: Replace ntv2 with libajantv2 * deps.windows: Add WIL headers * deps.qt: Update Qt6 to 6.6.3 for Windows * deps.qt: Update Qt6 to 6.6.3 for macOS --- buildspec.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/buildspec.json b/buildspec.json index 2832be1bd6cfe7..6cde01eaa4247f 100644 --- a/buildspec.json +++ b/buildspec.json @@ -1,25 +1,25 @@ { "dependencies": { "prebuilt": { - "version": "2024-03-19", + "version": "2024-05-08", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "label": "Pre-Built obs-deps", "hashes": { - "macos-universal": "2e9bfb55a5e0e4c1086fa1fda4cf268debfead473089df2aaea80e1c7a3ca7ff", - "windows-x64": "6e86068371526a967e805f6f9903f9407adb683c21820db5f07da8f30d11e998", - "windows-x86": "1002a7a19c55a6eb3cd0fb9c4c6d6bb1d649176439b6705e4bb5bd12948456a0" + "macos-universal": "da3167a3efecfa67dd72e4f2b7964c3456d74c639d4c62aa21ec300ebd5007a8", + "windows-x64": "773c87a0d173125ef2768aaca6f6de64a7d6053213cc9f7d3970a301152042d8", + "windows-x86": "8f4bc6b37acbabe5434e7ebce3bbc8a213d6c265cd7b4e708cd17ca4aa334130" } }, "qt6": { - "version": "2024-03-19", + "version": "2024-05-08", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "label": "Pre-Built Qt6", "hashes": { - "macos-universal": "694f1e639c017e3b1f456f735330dc5afae287cbea85757101af1368de3142c8", - "windows-x64": "72d1df34a0ef7413a681d5fcc88cae81da60adc03dcd23ef17862ab170bcc0dd" + "macos-universal": "248fb342e7ddf574af0960aaedeffb832deda1485dc81583302646e979593a6e", + "windows-x64": "8f459af5115ce081ae24b108712327e113893f250e14a902b1bd188b43873ed1" }, "debugSymbols": { - "windows-x64": "fbddd1f659c360f2291911ac5709b67b6f8182e6bca519d24712e4f6fd3cc865" + "windows-x64": "e4bc882f23195becbe53f1594fe75bdb9ff4f1d01e319f6caf8e0a38000cb42b" } }, "cef": { @@ -44,10 +44,10 @@ }, "tools": { "sparkle": { - "version": "2.5.2", + "version": "2.6.2", "baseUrl": "https://github.com/sparkle-project/Sparkle/releases/download", "label": "Sparkle 2", - "hash": "572dd67ae398a466f19f343a449e1890bac1ef74885b4739f68f979a8a89884b" + "hash": "2300a7dc2545a4968e54621b7f351d388ddf1a5cb49e79f6c99e9a09d826f5e8" }, "ccache-win": { "version": "4.8.1", From 92822311a8c9fcf9bdd422b2ce533e4ea049ba26 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 9 May 2024 17:27:52 +0200 Subject: [PATCH 0088/1073] UI: Improve and update app CMake 3 metainfo file CMake 2 path have a newer version of this file. See the following commits for metainfo change details: - 853ae5ea6ec3a3b29bf48cf4e26c7e889b28f3cf - 904fe87f83e60ddff2c46e3cde2ab63f54b53dcd - 0cc6068ed4e673fbade63e954233b00f7862be05 --- ... => com.obsproject.Studio.metainfo.xml.in} | 40 ++++++++++++++----- UI/cmake/os-linux.cmake | 16 +++++++- 2 files changed, 45 insertions(+), 11 deletions(-) rename UI/cmake/linux/{com.obsproject.Studio.appdata.xml.in => com.obsproject.Studio.metainfo.xml.in} (73%) diff --git a/UI/cmake/linux/com.obsproject.Studio.appdata.xml.in b/UI/cmake/linux/com.obsproject.Studio.metainfo.xml.in similarity index 73% rename from UI/cmake/linux/com.obsproject.Studio.appdata.xml.in rename to UI/cmake/linux/com.obsproject.Studio.metainfo.xml.in index b11b1afa0e1a4b..a59d7946d846ce 100644 --- a/UI/cmake/linux/com.obsproject.Studio.appdata.xml.in +++ b/UI/cmake/linux/com.obsproject.Studio.metainfo.xml.in @@ -3,10 +3,12 @@ com.obsproject.Studio com.obsproject.Studio.desktop CC0-1.0 - GPL-2.0 + GPL-2.0-or-later OBS Studio - OBS Project - Live streaming and video recording software + + OBS Project + + Live stream and record videos

Free and open source software for video capturing, recording, and live streaming.

Features:

@@ -30,17 +32,37 @@ https://github.com/obsproject/obs-studio/issues https://obsproject.com/contribute https://crowdin.com/project/obs-studio + + + always + pointing + keyboard + + + + offline-only + touch + + - https://obsproject.com/assets/images/OBSDemoApp2610.png + https://obsproject.com/assets/images/OBSDemoApp301Flathub.png + The OBS Studio window showing a card game, the project website, a video, and a purple rectangle + + + #284cb8 + + - - ModernToolkit - HiDpiIcon - + - + + https://github.com/obsproject/obs-studio/releases/tag/@OBS_VERSION@ + + + https://github.com/obsproject/obs-studio/blob/@GIT_HASH@/build-aux/ + diff --git a/UI/cmake/os-linux.cmake b/UI/cmake/os-linux.cmake index 91d9175dc14dac..37a02268fb8e00 100644 --- a/UI/cmake/os-linux.cmake +++ b/UI/cmake/os-linux.cmake @@ -22,9 +22,21 @@ if(NOT DEFINED APPDATA_RELEASE_DATE) endif() endif() -configure_file(cmake/linux/com.obsproject.Studio.appdata.xml.in com.obsproject.Studio.appdata.xml) +if(NOT DEFINED GIT_HASH) + if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + execute_process( + COMMAND git rev-parse HEAD + OUTPUT_VARIABLE GIT_HASH + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + set(GIT_HASH "master") + endif() +endif() + +configure_file(cmake/linux/com.obsproject.Studio.metainfo.xml.in com.obsproject.Studio.metainfo.xml) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.appdata.xml" +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo") install(FILES cmake/linux/com.obsproject.Studio.desktop DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications") From 03ab26ecacdff1ae5195ed339b7126efe47b1227 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 3 May 2024 10:08:59 +0200 Subject: [PATCH 0089/1073] build-aux: Update Flatpak modules * Update SVT-AV1 to 2.0.0 * Update aom to 3.9.0 * Freedesktop SDK already provides it, so the module is disabled * Update FFmpeg 7.0 * Update LuaJIT 5790d25397 * Update libdatachannel to 0.21.0 * Update Asio to 1.30.2 --- build-aux/com.obsproject.Studio.json | 1 - build-aux/modules/{20-aom.json => 20-aom.json.disabled} | 5 ++--- build-aux/modules/20-svt-av1.json | 4 ++-- build-aux/modules/30-ffmpeg.json | 8 ++++---- build-aux/modules/40-luajit.json | 2 +- build-aux/modules/50-libdatachannel.json | 4 ++-- build-aux/modules/90-asio.json | 4 ++-- 7 files changed, 13 insertions(+), 15 deletions(-) rename build-aux/modules/{20-aom.json => 20-aom.json.disabled} (79%) diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index 6a66aed439ad71..19f86a1be3d579 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -45,7 +45,6 @@ ], "modules": [ "modules/10-mbedtls.json", - "modules/20-aom.json", "modules/20-librist.json", "modules/20-nv-codec.json", "modules/20-srt.json", diff --git a/build-aux/modules/20-aom.json b/build-aux/modules/20-aom.json.disabled similarity index 79% rename from build-aux/modules/20-aom.json rename to build-aux/modules/20-aom.json.disabled index 1413dd74834165..fb1f9f9103dfbf 100644 --- a/build-aux/modules/20-aom.json +++ b/build-aux/modules/20-aom.json.disabled @@ -3,7 +3,6 @@ "buildsystem": "cmake-ninja", "builddir": true, "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release", "-DBUILD_SHARED_LIBS=ON", "-DENABLE_DOCS=OFF", "-DENABLE_EXAMPLES=OFF", @@ -19,8 +18,8 @@ { "type": "git", "url": "https://aomedia.googlesource.com/aom.git", - "tag": "v3.8.0", - "commit": "b681eac83963950afc7be55df56c22fa5210aaa2" + "tag": "v3.9.0", + "commit": "6cab58c3925e0f4138e15a4ed510161ea83b6db1" } ] } diff --git a/build-aux/modules/20-svt-av1.json b/build-aux/modules/20-svt-av1.json index 1ff7024d1652f6..993e1649e5be91 100644 --- a/build-aux/modules/20-svt-av1.json +++ b/build-aux/modules/20-svt-av1.json @@ -18,8 +18,8 @@ { "type": "git", "url": "https://gitlab.com/AOMediaCodec/SVT-AV1.git", - "tag": "v1.8.0", - "commit": "59645eea34e2815b627b8293aa3af254eddd0d69" + "tag": "v2.0.0", + "commit": "2aeeb4f1a1d495b84bf5c21dbb60ae10e991fada" } ] } diff --git a/build-aux/modules/30-ffmpeg.json b/build-aux/modules/30-ffmpeg.json index 057ddccc10e0f8..de8f2cdc7daf97 100644 --- a/build-aux/modules/30-ffmpeg.json +++ b/build-aux/modules/30-ffmpeg.json @@ -25,16 +25,16 @@ { "type": "git", "url": "https://github.com/FFmpeg/FFmpeg.git", - "tag": "n6.1.1", - "commit": "e38092ef9395d7049f871ef4d5411eb410e283e0", + "tag": "n7.0", + "commit": "083443d67cb159ce469e5d902346b8d0c2cd1c93", "disable-shallow-clone": true }, { "type": "git", "dest": "obs-deps", "url": "https://github.com/obsproject/obs-deps.git", - "tag": "2023-06-22", - "commit": "d86fb6f86455d328f67e563d87f494bcf1ff6dca" + "tag": "2024-05-08", + "commit": "85173b23c575ea09cdf33bada01b1d38dd1251ea" }, { "type": "shell", diff --git a/build-aux/modules/40-luajit.json b/build-aux/modules/40-luajit.json index 2080fc5b868db7..378b5c4bae401b 100644 --- a/build-aux/modules/40-luajit.json +++ b/build-aux/modules/40-luajit.json @@ -11,7 +11,7 @@ { "type": "git", "url": "https://luajit.org/git/luajit-2.0.git", - "commit": "c525bcb9024510cad9e170e12b6209aedb330f83", + "commit": "5790d253972c9d78a0c2aece527eda5b134bbbf7", "disable-shallow-clone": true }, { diff --git a/build-aux/modules/50-libdatachannel.json b/build-aux/modules/50-libdatachannel.json index d5896258dc8334..89d89b8aa04c29 100644 --- a/build-aux/modules/50-libdatachannel.json +++ b/build-aux/modules/50-libdatachannel.json @@ -14,8 +14,8 @@ "type": "git", "url": "https://github.com/paullouisageneau/libdatachannel.git", "disable-submodules": true, - "tag": "v0.20.1", - "commit": "7841d9f34cf9bd735958ae203a2536c14240c8a5" + "tag": "v0.21.0", + "commit": "9d5c46b8f506943727104d766e5dad0693c5a223" } ] } diff --git a/build-aux/modules/90-asio.json b/build-aux/modules/90-asio.json index 1cbaa5ceccf915..3ab57ebd94a92f 100644 --- a/build-aux/modules/90-asio.json +++ b/build-aux/modules/90-asio.json @@ -10,8 +10,8 @@ { "type": "git", "url": "https://github.com/chriskohlhoff/asio.git", - "tag": "asio-1-29-0", - "commit": "814f67e730e154547aea3f4d99f709cbdf1ea4a0" + "tag": "asio-1-30-2", + "commit": "12e0ce9e0500bf0f247dbd1ae894272656456079" } ] } From aa3f98060328b5a9fe10caeb1f448b3ad51fa15e Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 3 May 2024 10:31:06 +0200 Subject: [PATCH 0090/1073] build-aux: Update Flatpak VPL modules * Update libvpl to 2.11.0 * Adds a build option to no longer require ONEVPL_SEARCH_PATH to be set * Update onevpl-intel-gpu to vpl-gpu-rt 24.2.2 --- build-aux/com.obsproject.Studio.json | 5 ++--- build-aux/modules/50-libvpl.json | 13 +++++-------- ...{50-onevpl-intel-gpu.json => 50-vpl-gpu-rt.json} | 8 ++++---- 3 files changed, 11 insertions(+), 15 deletions(-) rename build-aux/modules/{50-onevpl-intel-gpu.json => 50-vpl-gpu-rt.json} (71%) diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index 19f86a1be3d579..fb32fd29269aa4 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -18,8 +18,7 @@ "--talk-name=org.freedesktop.Notifications", "--talk-name=org.a11y.Bus", "--system-talk-name=org.freedesktop.Avahi", - "--env=VST_PATH=/app/extensions/Plugins/vst", - "--env=ONEVPL_SEARCH_PATH=/app/lib" + "--env=VST_PATH=/app/extensions/Plugins/vst" ], "add-extensions": { "com.obsproject.Studio.Plugin": { @@ -60,10 +59,10 @@ "modules/50-libqrcodegencpp.json", "modules/50-libvpl.json", "modules/50-ntv2.json", - "modules/50-onevpl-intel-gpu.json", "modules/50-rnnoise.json", "modules/50-swig.json", "modules/50-v4l-utils.json", + "modules/50-vpl-gpu-rt.json", "modules/90-asio.json", "modules/90-nlohmann-json.json", "modules/90-uthash.json", diff --git a/build-aux/modules/50-libvpl.json b/build-aux/modules/50-libvpl.json index 61a7d235e3030f..7a054e92d01c6d 100644 --- a/build-aux/modules/50-libvpl.json +++ b/build-aux/modules/50-libvpl.json @@ -4,13 +4,10 @@ "builddir": true, "config-opts": [ "-DCMAKE_BUILD_TYPE=Release", - "-DBUILD_DEV=ON", - "-DBUILD_PREVIEW=OFF", - "-DBUILD_TOOLS=OFF", - "-DBUILD_TOOLS_ONEVPL_EXPERIMENTAL=OFF", - "-DINSTALL_EXAMPLE_CODE=OFF", + "-DENABLE_LIBDIR_IN_RUNTIME_SEARCH=ON", + "-DINSTALL_DEV=ON", "-DBUILD_EXAMPLES=OFF", - "-DBUILD_DISPATCHER_ONEVPL_EXPERIMENTAL=OFF", + "-DBUILD_EXPERIMENTAL=OFF", "-DBUILD_SHARED_LIBS=ON" ], "cleanup": [ @@ -21,8 +18,8 @@ { "type": "git", "url": "https://github.com/intel/libvpl.git", - "commit": "79ef61b11790c70941cfa4d167b5d20d3a4e9744", - "tag": "v2.10.1" + "commit": "11a9bbda5b22ac1c544da59b4007bb57f737b487", + "tag": "v2.11.0" } ] } diff --git a/build-aux/modules/50-onevpl-intel-gpu.json b/build-aux/modules/50-vpl-gpu-rt.json similarity index 71% rename from build-aux/modules/50-onevpl-intel-gpu.json rename to build-aux/modules/50-vpl-gpu-rt.json index a2d8be083d9c24..86b88794d28790 100644 --- a/build-aux/modules/50-onevpl-intel-gpu.json +++ b/build-aux/modules/50-vpl-gpu-rt.json @@ -1,5 +1,5 @@ { - "name": "onevpl-intel-gpu", + "name": "vpl-gpu-rt", "buildsystem": "cmake-ninja", "builddir": true, "config-opts": [ @@ -14,9 +14,9 @@ "sources": [ { "type": "git", - "url": "https://github.com/oneapi-src/oneVPL-intel-gpu.git", - "commit": "852fa9f705ef44c004d014548601f3804a6de705", - "tag": "intel-onevpl-23.4.3" + "url": "https://github.com/intel/vpl-gpu-rt.git", + "commit": "d74cb6391eaad4a2db9cbec6a8a335b6f1a555c4", + "tag": "intel-onevpl-24.2.2" }, { "type": "shell", From c974a40b51ebeb9c6319c463c41c8c97476e8f98 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 5 May 2024 01:17:12 +0200 Subject: [PATCH 0091/1073] build-aux: Update Flatpak ntv2 module to libajantv2 --- build-aux/modules/50-ntv2.json | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/build-aux/modules/50-ntv2.json b/build-aux/modules/50-ntv2.json index b0dc8fd19f7674..7470bfa5d6a8ad 100644 --- a/build-aux/modules/50-ntv2.json +++ b/build-aux/modules/50-ntv2.json @@ -4,9 +4,17 @@ "builddir": true, "config-opts": [ "-DCMAKE_BUILD_TYPE=Release", - "-DAJA_BUILD_OPENSOURCE=ON", - "-DAJA_BUILD_APPS=OFF", - "-DAJA_INSTALL_HEADERS=ON" + "-DAJANTV2_BUILD_OPENSOURCE=ON", + "-DAJANTV2_BUILD_SHARED=OFF", + "-DAJANTV2_DISABLE_DEMOS=ON", + "-DAJANTV2_DISABLE_DRIVER=ON", + "-DAJANTV2_DISABLE_TESTS=ON", + "-DAJANTV2_DISABLE_TOOLS=ON", + "-DAJANTV2_DISABLE_PLUGINS=ON", + "-DAJA_INSTALL_HEADERS=ON", + "-DAJA_INSTALL_SOURCES=OFF", + "-DAJA_INSTALL_MISC=OFF", + "-DAJA_INSTALL_CMAKE=OFF" ], "cleanup": [ "/include" @@ -14,9 +22,9 @@ "sources": [ { "type": "git", - "url": "https://github.com/aja-video/ntv2.git", - "tag": "v16.2-bugfix5", - "commit": "0acbac70a0b5e6509cca78cfbf69974c73c10db9" + "url": "https://github.com/aja-video/libajantv2.git", + "tag": "ntv2_17_0_1", + "commit": "b6acce6b135c3d9ae7a2bce966180b159ced619f" } ] } From 79f680cab68cba5d207a99ce7818406804b227b7 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Mon, 6 May 2024 23:26:16 +0200 Subject: [PATCH 0092/1073] UI: Use weak_source_expired instead of getting the source --- UI/window-basic-main.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index cf464c64603e99..2519f8e2cfcb19 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3833,14 +3833,8 @@ void OBSBasic::VolControlContextMenu() copyFiltersAction.setEnabled(obs_source_filter_count(vol->GetSource()) > 0); - - OBSSourceAutoRelease source = - obs_weak_source_get_source(copyFiltersSource); - if (source) { - pasteFiltersAction.setEnabled(true); - } else { - pasteFiltersAction.setEnabled(false); - } + pasteFiltersAction.setEnabled( + !obs_weak_source_expired(copyFiltersSource)); QMenu popup; vol->SetContextMenu(&popup); From 279e9424c0e590b48a0f56357fffc5ba65ea68bf Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 9 May 2024 12:45:27 +0200 Subject: [PATCH 0093/1073] libobs: Fix obs_parse_avc_header missing high profile parameters --- libobs/obs-avc.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/libobs/obs-avc.c b/libobs/obs-avc.c index f95367a6c0c141..db3a311f4dfab3 100644 --- a/libobs/obs-avc.c +++ b/libobs/obs-avc.c @@ -20,6 +20,7 @@ #include "obs.h" #include "obs-nal.h" #include "util/array-serializer.h" +#include "util/bitstream.h" bool obs_avc_keyframe(const uint8_t *data, size_t size) { @@ -170,6 +171,60 @@ static void get_sps_pps(const uint8_t *data, size_t size, const uint8_t **sps, } } +static inline uint8_t get_ue_golomb(struct bitstream_reader *gb) +{ + int i = 0; + while (i < 32 && !bitstream_reader_read_bits(gb, 1)) + i++; + + return bitstream_reader_read_bits(gb, i) + (1 << i) - 1; +} + +static void get_sps_high_params(const uint8_t *sps, size_t size, + uint8_t *chroma_format_idc, + uint8_t *bit_depth_luma, + uint8_t *bit_depth_chroma) +{ + struct bitstream_reader gb; + + /* Extract RBSP */ + uint8_t *rbsp = bzalloc(size); + + size_t i = 0; + size_t rbsp_size = 0; + + while (i + 2 < size) { + if (sps[i] == 0 && sps[i + 1] == 0 && sps[i + 2] == 3) { + rbsp[rbsp_size++] = sps[i++]; + rbsp[rbsp_size++] = sps[i++]; + // skip emulation_prevention_three_byte + i++; + } else { + rbsp[rbsp_size++] = sps[i++]; + } + } + + while (i < size) + rbsp[rbsp_size++] = sps[i++]; + + /* Read relevant information from SPS */ + bitstream_reader_init(&gb, rbsp, rbsp_size); + + // skip a whole bunch of stuff we don't care about + bitstream_reader_read_bits(&gb, 24); // profile, constraint flags, level + get_ue_golomb(&gb); // id + + *chroma_format_idc = get_ue_golomb(&gb); + // skip separate_colour_plane_flag + if (*chroma_format_idc == 3) + bitstream_reader_read_bits(&gb, 1); + + *bit_depth_luma = get_ue_golomb(&gb); + *bit_depth_chroma = get_ue_golomb(&gb); + + bfree(rbsp); +} + size_t obs_parse_avc_header(uint8_t **header, const uint8_t *data, size_t size) { struct array_output_data output; @@ -202,6 +257,26 @@ size_t obs_parse_avc_header(uint8_t **header, const uint8_t *data, size_t size) s_wb16(&s, (uint16_t)pps_size); s_write(&s, pps, pps_size); + uint8_t profile_idc = sps[1]; + + /* Additional data required for high, high10, high422, high444 profiles. + * See ISO/IEC 14496-15 Section 5.3.3.1.2. */ + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244) { + uint8_t chroma_format_idc, bit_depth_luma, bit_depth_chroma; + get_sps_high_params(sps + 1, sps_size - 1, &chroma_format_idc, + &bit_depth_luma, &bit_depth_chroma); + + // reserved + chroma_format + s_w8(&s, 0xfc | chroma_format_idc); + // reserved + bit_depth_luma_minus8 + s_w8(&s, 0xf8 | bit_depth_luma); + // reserved + bit_depth_chroma_minus8 + s_w8(&s, 0xf8 | bit_depth_chroma); + // numOfSequenceParameterSetExt + s_w8(&s, 0); + } + *header = output.bytes.array; return output.bytes.num; } From af685be40d9af51e0b2f594c94212507327b245d Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 10 May 2024 15:37:11 -0400 Subject: [PATCH 0094/1073] UI: Add OS specific theme variables via prefix --- UI/obs-app-theming.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 984a7136a2cfd1..22184ba9b05813 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -317,6 +317,19 @@ static vector ParseThemeVariables(const char *themeData) OBSThemeVariable var; var.name = key; +#ifdef _WIN32 + const QString osPrefix = "os_win_"; +#elif __APPLE__ + const QString osPrefix = "os_mac_"; +#else + const QString osPrefix = "os_lin_"; +#endif + + if (key.startsWith(osPrefix) && + key.length() > osPrefix.length()) { + var.name = key.sliced(osPrefix.length()); + } + ret = cf_next_token_should_be(cfp, ":", ";", nullptr); if (ret != PARSE_SUCCESS) continue; From dff302364c1d1d7b7ba4ea7c5f315cc29af732ac Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 20 Apr 2024 18:55:15 -0700 Subject: [PATCH 0095/1073] obs-webrtc: Fix comment capitalization --- plugins/obs-webrtc/whip-output.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index 56e087beec0867..9e4e6a696f91ad 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -577,12 +577,12 @@ void WHIPOutput::Send(void *data, uintptr_t size, uint64_t duration, // Set new timestamp rtp_config->timestamp = rtp_config->timestamp + elapsed_timestamp; - // get elapsed time in clock rate from last RTCP sender report + // Get elapsed time in clock rate from last RTCP sender report auto report_elapsed_timestamp = rtp_config->timestamp - rtcp_sr_reporter->lastReportedTimestamp(); - // check if last report was at least 1 second ago + // Check if last report was at least 1 second ago if (rtp_config->timestampToSeconds(report_elapsed_timestamp) > 1) rtcp_sr_reporter->setNeedsToReport(); From 5802077f788fe5cdd07670e6969454bd06ecfd4d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 20 Apr 2024 18:47:13 -0700 Subject: [PATCH 0096/1073] obs-webrtc: Correctly guard HEVC in output using ifdefs If an output does not support HEVC, it should not advertise it. For services, however, a service primarily defines the codecs which are supported for the protocol spec itself/destination platform, so HEVC can be advertised still. --- plugins/obs-webrtc/whip-output.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index 9e4e6a696f91ad..a27a1964c7c16b 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -162,7 +162,7 @@ void WHIPOutput::ConfigureVideoTrack(std::string media_stream_id, packetizer = std::make_shared( rtc::H264RtpPacketizer::Separator::StartSequence, rtp_config, MAX_VIDEO_FRAGMENT_SIZE); -#if ENABLE_HEVC +#ifdef ENABLE_HEVC } else if (strcmp("hevc", codec) == 0) { video_description.addH265Codec(video_payload_type); packetizer = std::make_shared( @@ -632,7 +632,11 @@ void register_whip_output() info.get_connect_time_ms = [](void *priv_data) -> int { return static_cast(priv_data)->GetConnectTime(); }; +#ifdef ENABLE_HEVC + info.encoded_video_codecs = "h264;hevc;av1"; +#else info.encoded_video_codecs = "h264;av1"; +#endif info.encoded_audio_codecs = "opus"; info.protocols = "WHIP"; From 09be4f9aed29e86a01da3f8586148ffcdb78bfda Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 4 May 2024 19:55:13 -0700 Subject: [PATCH 0097/1073] obs-webrtc: Only advertise relevant codecs for a or v-only outputs A video-only output should not advertise any audio codecs. --- plugins/obs-webrtc/whip-output.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index a27a1964c7c16b..eb271af0f22bab 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -598,6 +598,13 @@ void register_whip_output() { const uint32_t base_flags = OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE; + const char *audio_codecs = "opus"; +#ifdef ENABLE_HEVC + const char *video_codecs = "h264;hevc;av1"; +#else + const char *video_codecs = "h264;av1"; +#endif + struct obs_output_info info = {}; info.id = "whip_output"; info.flags = OBS_OUTPUT_AV | base_flags; @@ -632,21 +639,20 @@ void register_whip_output() info.get_connect_time_ms = [](void *priv_data) -> int { return static_cast(priv_data)->GetConnectTime(); }; -#ifdef ENABLE_HEVC - info.encoded_video_codecs = "h264;hevc;av1"; -#else - info.encoded_video_codecs = "h264;av1"; -#endif - info.encoded_audio_codecs = "opus"; + info.encoded_video_codecs = video_codecs; + info.encoded_audio_codecs = audio_codecs; info.protocols = "WHIP"; obs_register_output(&info); info.id = "whip_output_video"; info.flags = OBS_OUTPUT_VIDEO | base_flags; + info.encoded_audio_codecs = nullptr; obs_register_output(&info); info.id = "whip_output_audio"; info.flags = OBS_OUTPUT_AUDIO | base_flags; + info.encoded_video_codecs = nullptr; + info.encoded_audio_codecs = audio_codecs; obs_register_output(&info); } From 023d9bd851d0b4742adee6db68368d20f112d0cd Mon Sep 17 00:00:00 2001 From: cg2121 Date: Wed, 14 Feb 2024 01:42:01 -0600 Subject: [PATCH 0098/1073] UI: Use OBSSourceLabel for item widget labels This changes the labels for the source tree/visibility item widgets to use OBSSourceLabel, as it handles the renaming of sources. --- UI/source-tree.cpp | 19 ++----------------- UI/source-tree.hpp | 4 ++-- UI/visibility-item-widget.cpp | 24 +++--------------------- UI/visibility-item-widget.hpp | 7 ++----- 4 files changed, 9 insertions(+), 45 deletions(-) diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 799b5f064a0780..14fb89deaea6c0 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -3,6 +3,7 @@ #include "source-tree.hpp" #include "qt-wrappers.hpp" #include "platform.hpp" +#include "source-label.hpp" #include #include @@ -96,7 +97,7 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) lock->setAccessibleDescription( QTStr("Basic.Main.Sources.LockDescription").arg(name)); - label = new QLabel(QT_UTF8(name)); + label = new OBSSourceLabel(source); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); label->setAttribute(Qt::WA_TranslucentBackground); @@ -288,15 +289,6 @@ void SourceTreeItem::ReconnectSignals() /* --------------------------------------------------------- */ - auto renamed = [](void *data, calldata_t *cd) { - SourceTreeItem *this_ = - reinterpret_cast(data); - const char *name = calldata_string(cd, "new_name"); - - QMetaObject::invokeMethod(this_, "Renamed", - Q_ARG(QString, QT_UTF8(name))); - }; - auto removeSource = [](void *data, calldata_t *) { SourceTreeItem *this_ = reinterpret_cast(data); @@ -307,7 +299,6 @@ void SourceTreeItem::ReconnectSignals() obs_source_t *source = obs_sceneitem_get_source(sceneitem); signal = obs_source_get_signal_handler(source); - sigs.emplace_back(signal, "rename", renamed, this); sigs.emplace_back(signal, "remove", removeSource, this); } @@ -470,7 +461,6 @@ void SourceTreeItem::ExitEditModeInternal(bool save) redo, uuid, uuid); obs_source_set_name(source, newName.c_str()); - label->setText(QT_UTF8(newName.c_str())); } bool SourceTreeItem::eventFilter(QObject *object, QEvent *event) @@ -509,11 +499,6 @@ void SourceTreeItem::LockedChanged(bool locked) OBSBasic::Get()->UpdateEditMenu(); } -void SourceTreeItem::Renamed(const QString &name) -{ - label->setText(name); -} - void SourceTreeItem::Update(bool force) { OBSScene scene = GetCurrentScene(); diff --git a/UI/source-tree.hpp b/UI/source-tree.hpp index 7ac920540133ca..cdf2e205b9471c 100644 --- a/UI/source-tree.hpp +++ b/UI/source-tree.hpp @@ -13,6 +13,7 @@ #include class QLabel; +class OBSSourceLabel; class QCheckBox; class QLineEdit; class SourceTree; @@ -57,7 +58,7 @@ class SourceTreeItem : public QFrame { QCheckBox *vis = nullptr; QCheckBox *lock = nullptr; QHBoxLayout *boxLayout = nullptr; - QLabel *label = nullptr; + OBSSourceLabel *label = nullptr; QLineEdit *editor = nullptr; @@ -79,7 +80,6 @@ private slots: void VisibilityChanged(bool visible); void LockedChanged(bool locked); - void Renamed(const QString &name); void ExpandClicked(bool checked); diff --git a/UI/visibility-item-widget.cpp b/UI/visibility-item-widget.cpp index 2924d0b898a298..23571e9f6c3589 100644 --- a/UI/visibility-item-widget.cpp +++ b/UI/visibility-item-widget.cpp @@ -1,6 +1,7 @@ #include "visibility-item-widget.hpp" #include "qt-wrappers.hpp" #include "obs-app.hpp" +#include "source-label.hpp" #include #include #include @@ -12,11 +13,8 @@ VisibilityItemWidget::VisibilityItemWidget(obs_source_t *source_) : source(source_), enabledSignal(obs_source_get_signal_handler(source), "enable", - OBSSourceEnabled, this), - renamedSignal(obs_source_get_signal_handler(source), "rename", - OBSSourceRenamed, this) + OBSSourceEnabled, this) { - const char *name = obs_source_get_name(source); bool enabled = obs_source_enabled(source); vis = new QCheckBox(); @@ -24,7 +22,7 @@ VisibilityItemWidget::VisibilityItemWidget(obs_source_t *source_) vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); vis->setChecked(enabled); - label = new QLabel(QT_UTF8(name)); + label = new OBSSourceLabel(source); label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); QHBoxLayout *itemLayout = new QHBoxLayout(); @@ -50,28 +48,12 @@ void VisibilityItemWidget::OBSSourceEnabled(void *param, calldata_t *data) Q_ARG(bool, enabled)); } -void VisibilityItemWidget::OBSSourceRenamed(void *param, calldata_t *data) -{ - VisibilityItemWidget *window = - reinterpret_cast(param); - const char *name = calldata_string(data, "new_name"); - - QMetaObject::invokeMethod(window, "SourceRenamed", - Q_ARG(QString, QT_UTF8(name))); -} - void VisibilityItemWidget::SourceEnabled(bool enabled) { if (vis->isChecked() != enabled) vis->setChecked(enabled); } -void VisibilityItemWidget::SourceRenamed(QString name) -{ - if (label && name != label->text()) - label->setText(name); -} - void VisibilityItemWidget::SetColor(const QColor &color, bool active_, bool selected_) { diff --git a/UI/visibility-item-widget.hpp b/UI/visibility-item-widget.hpp index 48b06718e74bbf..8fbcb45f39b116 100644 --- a/UI/visibility-item-widget.hpp +++ b/UI/visibility-item-widget.hpp @@ -9,28 +9,25 @@ class QLineEdit; class QListWidget; class QListWidgetItem; class QCheckBox; +class OBSSourceLabel; class VisibilityItemWidget : public QWidget { Q_OBJECT private: OBSSource source; - QLabel *label = nullptr; + OBSSourceLabel *label = nullptr; QCheckBox *vis = nullptr; - QString oldName; OBSSignal enabledSignal; - OBSSignal renamedSignal; bool active = false; bool selected = false; static void OBSSourceEnabled(void *param, calldata_t *data); - static void OBSSourceRenamed(void *param, calldata_t *data); private slots: void SourceEnabled(bool enabled); - void SourceRenamed(QString name); public: VisibilityItemWidget(obs_source_t *source); From cd918a7f4c9caf0e3c220be080305b59cfd52750 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 13 Feb 2024 03:27:33 -0600 Subject: [PATCH 0099/1073] UI: Move projector rename signal This moves the renaming of projectors from OBSBasic to OBSProjector. --- UI/window-basic-main.cpp | 5 ----- UI/window-projector.cpp | 19 +++++++++++++++++-- UI/window-projector.hpp | 5 +++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 2519f8e2cfcb19..46d40389b71071 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3344,11 +3344,6 @@ void OBSBasic::RenameSources(OBSSource source, QString newName, volumes[i]->SetName(newName); } - for (size_t i = 0; i < projectors.size(); i++) { - if (projectors[i]->GetSource() == source) - projectors[i]->RenameProjector(prevName, newName); - } - if (vcamConfig.type == VCamOutputType::SourceOutput && prevName == QString::fromStdString(vcamConfig.source)) vcamConfig.source = newName.toStdString(); diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 24793d553890c2..e61f231825254d 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -21,8 +21,10 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, { OBSSource source = GetSource(); if (source) { - destroyedSignal.Connect(obs_source_get_signal_handler(source), - "destroy", OBSSourceDestroyed, this); + sigs.emplace_back(obs_source_get_signal_handler(source), + "rename", OBSSourceRenamed, this); + sigs.emplace_back(obs_source_get_signal_handler(source), + "destroy", OBSSourceDestroyed, this); } isAlwaysOnTop = config_get_bool(GetGlobalConfig(), "BasicWindow", @@ -106,6 +108,8 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, OBSProjector::~OBSProjector() { + sigs.clear(); + bool isMultiview = type == ProjectorType::Multiview; obs_display_remove_draw_callback( GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, @@ -215,6 +219,17 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) endRegion(); } +void OBSProjector::OBSSourceRenamed(void *data, calldata_t *params) +{ + OBSProjector *window = reinterpret_cast(data); + QString oldName = calldata_string(params, "prev_name"); + QString newName = calldata_string(params, "new_name"); + + QMetaObject::invokeMethod(window, "RenameProjector", + Q_ARG(QString, oldName), + Q_ARG(QString, newName)); +} + void OBSProjector::OBSSourceDestroyed(void *data, calldata_t *) { OBSProjector *window = reinterpret_cast(data); diff --git a/UI/window-projector.hpp b/UI/window-projector.hpp index 2a4bdd6d150e84..8be23d4d4fa019 100644 --- a/UI/window-projector.hpp +++ b/UI/window-projector.hpp @@ -19,10 +19,11 @@ class OBSProjector : public OBSQTDisplay { private: OBSWeakSourceAutoRelease weakSource; - OBSSignal destroyedSignal; + std::vector sigs; static void OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy); static void OBSRender(void *data, uint32_t cx, uint32_t cy); + static void OBSSourceRenamed(void *data, calldata_t *params); static void OBSSourceDestroyed(void *data, calldata_t *params); void mousePressEvent(QMouseEvent *event) override; @@ -53,6 +54,7 @@ private slots: void OpenWindowedProjector(); void AlwaysOnTopToggled(bool alwaysOnTop); void ScreenRemoved(QScreen *screen_); + void RenameProjector(QString oldName, QString newName); public: OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, @@ -63,7 +65,6 @@ private slots: ProjectorType GetProjectorType(); int GetMonitor(); static void UpdateMultiviewProjectors(); - void RenameProjector(QString oldName, QString newName); void SetHideCursor(); bool IsAlwaysOnTop() const; From 272a5edbaaef45dcc1bcf4d80359d69d6de1c5e0 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 13 Feb 2024 03:35:14 -0600 Subject: [PATCH 0100/1073] UI: Use vector for volume control signals This cleans up the volume control code by using a vector for signals. --- UI/volume-control.cpp | 24 ++++++++---------------- UI/volume-control.hpp | 1 + 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index f8ebff1dc35c6c..e4b6125e4f6da4 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -379,14 +379,13 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) obs_fader_add_callback(obs_fader, OBSVolumeChanged, this); obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this); - signal_handler_connect(obs_source_get_signal_handler(source), "mute", - OBSVolumeMuted, this); - signal_handler_connect(obs_source_get_signal_handler(source), - "audio_mixers", OBSMixersOrMonitoringChanged, - this); - signal_handler_connect(obs_source_get_signal_handler(source), - "audio_monitoring", OBSMixersOrMonitoringChanged, - this); + sigs.emplace_back(obs_source_get_signal_handler(source), "mute", + OBSVolumeMuted, this); + sigs.emplace_back(obs_source_get_signal_handler(source), "audio_mixers", + OBSMixersOrMonitoringChanged, this); + sigs.emplace_back(obs_source_get_signal_handler(source), + "audio_monitoring", OBSMixersOrMonitoringChanged, + this); QWidget::connect(slider, &VolumeSlider::valueChanged, this, &VolControl::SliderChanged); @@ -422,14 +421,7 @@ VolControl::~VolControl() obs_fader_remove_callback(obs_fader, OBSVolumeChanged, this); obs_volmeter_remove_callback(obs_volmeter, OBSVolumeLevel, this); - signal_handler_disconnect(obs_source_get_signal_handler(source), "mute", - OBSVolumeMuted, this); - signal_handler_disconnect(obs_source_get_signal_handler(source), - "audio_mixers", OBSMixersOrMonitoringChanged, - this); - signal_handler_disconnect(obs_source_get_signal_handler(source), - "audio_monitoring", - OBSMixersOrMonitoringChanged, this); + sigs.clear(); if (contextMenu) contextMenu->close(); diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 613add27f704ff..ecc7b23f9f238c 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -278,6 +278,7 @@ class VolControl : public QWidget { private: OBSSource source; + std::vector sigs; QLabel *nameLabel; QLabel *volLabel; VolumeMeter *volMeter; From b82a49e88166b55a799a08e4ca7e3f39ec827acb Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 13 Feb 2024 03:41:48 -0600 Subject: [PATCH 0101/1073] UI: Use OBSSourceLabel for VolControl The OBSSourceLabel handles renaming of a source, so we don't have to use the rename signals for volume controls. --- UI/volume-control.cpp | 13 ++----------- UI/volume-control.hpp | 6 ++---- UI/window-basic-main.cpp | 5 ----- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index e4b6125e4f6da4..a900a3fa72e100 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -5,6 +5,7 @@ #include "mute-checkbox.hpp" #include "slider-ignorewheel.hpp" #include "slider-absoluteset-style.hpp" +#include "source-label.hpp" #include #include #include @@ -219,16 +220,6 @@ void VolControl::updateText() slider->setAccessibleName(accText); } -QString VolControl::GetName() const -{ - return nameLabel->text(); -} - -void VolControl::SetName(const QString &newName) -{ - nameLabel->setText(newName); -} - void VolControl::EmitConfigClicked() { emit ConfigClicked(); @@ -253,7 +244,7 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) vertical(vertical), contextMenu(nullptr) { - nameLabel = new QLabel(); + nameLabel = new OBSSourceLabel(source); volLabel = new QLabel(); mute = new MuteCheckBox(); diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index ecc7b23f9f238c..5f9a6762fea92d 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -272,6 +272,7 @@ class VolumeMeterTimer : public QTimer { class QLabel; class QSlider; class MuteCheckBox; +class OBSSourceLabel; class VolControl : public QWidget { Q_OBJECT @@ -279,7 +280,7 @@ class VolControl : public QWidget { private: OBSSource source; std::vector sigs; - QLabel *nameLabel; + OBSSourceLabel *nameLabel; QLabel *volLabel; VolumeMeter *volMeter; QSlider *slider; @@ -321,9 +322,6 @@ private slots: inline obs_source_t *GetSource() const { return source; } - QString GetName() const; - void SetName(const QString &newName); - void SetMeterDecayRate(qreal q); void setPeakMeterType(enum obs_peak_meter_type peakMeterType); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 46d40389b71071..591a91e48b5e0d 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3339,11 +3339,6 @@ void OBSBasic::RenameSources(OBSSource source, QString newName, { RenameListValues(ui->scenes, newName, prevName); - for (size_t i = 0; i < volumes.size(); i++) { - if (volumes[i]->GetName().compare(prevName) == 0) - volumes[i]->SetName(newName); - } - if (vcamConfig.type == VCamOutputType::SourceOutput && prevName == QString::fromStdString(vcamConfig.source)) vcamConfig.source = newName.toStdString(); From 03313a6af4a381b8c2586ad26da288b17d13ae40 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Fri, 10 May 2024 20:21:21 +0200 Subject: [PATCH 0102/1073] UI: Fix visibility and lock checkbox styling on system theme In 7931f2acb8688326375775dcef9c289f64951ac4 the classes have been removed in favor of properties, but this styling change appears to have been reverted by accident, likely by the theming overhaul. --- UI/data/themes/System.obt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UI/data/themes/System.obt b/UI/data/themes/System.obt index 6e34569ebeee49..eaa41bf78007bf 100644 --- a/UI/data/themes/System.obt +++ b/UI/data/themes/System.obt @@ -213,31 +213,31 @@ OBSBasicSettings QListWidget::item { /* Locked CheckBox */ -LockedCheckBox { +QCheckBox[lockCheckBox=true] { outline: none; background: transparent; } -LockedCheckBox::indicator:checked { +QCheckBox[lockCheckBox=true]::indicator:checked { image: url(:res/images/locked.svg); } -LockedCheckBox::indicator:unchecked { +QCheckBox[lockCheckBox=true]::indicator:unchecked { image: url(:res/images/unlocked.svg); } /* Visibility CheckBox */ -VisibilityCheckBox { +QCheckBox[visibilityCheckBox=true] { outline: none; background: transparent; } -VisibilityCheckBox::indicator:checked { +QCheckBox[visibilityCheckBox=true]::indicator:checked { image: url(:res/images/visible.svg); } -VisibilityCheckBox::indicator:unchecked { +QCheckBox[visibilityCheckBox=true]::indicator:unchecked { image: url(:res/images/invisible.svg); } From 67e4853b7d681f441f725c1da6866f268499a570 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 4 May 2024 23:36:39 +0200 Subject: [PATCH 0103/1073] UI: Give private spacing helper sources names --- UI/window-basic-preview.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 8594074789ba49..54daacdabd7881 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "window-basic-preview.hpp" #include "window-basic-main.hpp" #include "obs-app.hpp" @@ -2331,7 +2332,7 @@ OBSBasicPreview *OBSBasicPreview::Get() return OBSBasic::Get()->ui->preview; } -static obs_source_t *CreateLabel(float pixelRatio) +static obs_source_t *CreateLabel(float pixelRatio, int i) { OBSDataAutoRelease settings = obs_data_create(); OBSDataAutoRelease font = obs_data_create(); @@ -2357,7 +2358,9 @@ static obs_source_t *CreateLabel(float pixelRatio) const char *text_source_id = "text_ft2_source"; #endif - return obs_source_create_private(text_source_id, NULL, settings); + DStr name; + dstr_printf(name, "Preview spacing label %d", i); + return obs_source_create_private(text_source_id, name, settings); } static void SetLabelText(int sourceIndex, int px) @@ -2639,7 +2642,7 @@ void OBSBasicPreview::DrawSpacingHelpers() float pixelRatio = main->GetDevicePixelRatio(); for (int i = 0; i < 4; i++) { if (!spacerLabel[i]) - spacerLabel[i] = CreateLabel(pixelRatio); + spacerLabel[i] = CreateLabel(pixelRatio, i); } vec3_set(&start, top.x, 0.0f, 1.0f); From 41650479febd0bc76fae4278ac2ae6374891543a Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 5 May 2024 20:42:47 +0200 Subject: [PATCH 0104/1073] UI: Center preview spacing lines Currently, the spacing lines begin in the middle of the box and then are their width wide towards one direction. This means that the larger they are, the more off-center their middle is. This commit changes them to start at half their width left/top of the center and end half the width to the right/bottom of the center, putting the middle in the center. --- UI/window-basic-preview.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 54daacdabd7881..042dd458a0b6b2 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -1758,13 +1758,16 @@ static void DrawLine(float x1, float y1, float x2, float y2, float thickness, gs_render_start(true); - gs_vertex2f(x1, y1); - gs_vertex2f(x1 + (xSide * (thickness / scale.x)), - y1 + (ySide * (thickness / scale.y))); - gs_vertex2f(x2 + (xSide * (thickness / scale.x)), - y2 + (ySide * (thickness / scale.y))); - gs_vertex2f(x2, y2); - gs_vertex2f(x1, y1); + gs_vertex2f(x1 - (xSide * (thickness / scale.x) / 2), + y1 + (ySide * (thickness / scale.y) / 2)); + gs_vertex2f(x1 + (xSide * (thickness / scale.x) / 2), + y1 - (ySide * (thickness / scale.y) / 2)); + gs_vertex2f(x2 + (xSide * (thickness / scale.x) / 2), + y2 + (ySide * (thickness / scale.y) / 2)); + gs_vertex2f(x2 - (xSide * (thickness / scale.x) / 2), + y2 - (ySide * (thickness / scale.y) / 2)); + gs_vertex2f(x1 - (xSide * (thickness / scale.x) / 2), + y1 + (ySide * (thickness / scale.y) / 2)); gs_vertbuffer_t *line = gs_render_save(); From c9f7a3c325f52c7fdd1431ec63ad5384158cac20 Mon Sep 17 00:00:00 2001 From: moocowsheep <103583807+moocowsheep@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:14:42 -0400 Subject: [PATCH 0105/1073] deps/media-playback: Prioritize CUDA decoder --- deps/media-playback/media-playback/decode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/media-playback/media-playback/decode.c b/deps/media-playback/media-playback/decode.c index 600824796b9773..e159a489038b40 100644 --- a/deps/media-playback/media-playback/decode.c +++ b/deps/media-playback/media-playback/decode.c @@ -21,8 +21,8 @@ #include enum AVHWDeviceType hw_priority[] = { - AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, - AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_VAAPI, + AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, + AV_HWDEVICE_TYPE_DXVA2, AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_VDPAU, AV_HWDEVICE_TYPE_QSV, AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_HWDEVICE_TYPE_NONE, }; From ce4c99be4e52950a4bd49eb68d99ccdda2e87f96 Mon Sep 17 00:00:00 2001 From: moocowsheep <103583807+moocowsheep@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:16:04 -0400 Subject: [PATCH 0106/1073] plugins/win-dshow: Add CUDA decoder --- plugins/win-dshow/ffmpeg-decode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/win-dshow/ffmpeg-decode.c b/plugins/win-dshow/ffmpeg-decode.c index e5ba5ea4fa9ebc..20ad565903650f 100644 --- a/plugins/win-dshow/ffmpeg-decode.c +++ b/plugins/win-dshow/ffmpeg-decode.c @@ -23,10 +23,8 @@ #endif enum AVHWDeviceType hw_priority[] = { - AV_HWDEVICE_TYPE_D3D11VA, - AV_HWDEVICE_TYPE_DXVA2, - AV_HWDEVICE_TYPE_QSV, - AV_HWDEVICE_TYPE_NONE, + AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, + AV_HWDEVICE_TYPE_QSV, AV_HWDEVICE_TYPE_NONE, }; static bool has_hw_type(const AVCodec *c, enum AVHWDeviceType type) From 7843a822e01f3cb30995fa90280f8a5fb205e228 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 10 May 2024 16:23:22 -0400 Subject: [PATCH 0107/1073] UI: Adjust font size on macOS --- UI/data/themes/Yami.obt | 3 +++ UI/data/themes/Yami_Classic.ovt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 6b482523f498f8..e6056dcd631cae 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -100,6 +100,9 @@ --border_highlight: "transparent"; /* TODO: Better Accessibility focus state */ /* TODO: Move Accessibilty Colors to Theme config system */ + /* OS Fixes */ + --os_mac_font_base_value: 12; + --font_base: calc(1pt * var(--font_base_value)); --font_small: calc(0.9pt * var(--font_base_value)); --font_large: calc(1.1pt * var(--font_base_value)); diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index ffc6a863b84214..608fd6b8c25c7d 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -28,6 +28,9 @@ --spacing_base_value: 2; --padding_base_value: 0.25; + /* OS Fixes */ + --os_mac_font_base_value: 11; + --icon_base: calc(6px + var(--font_base_value)); --padding_wide: calc(18px + calc(0.25 * var(--padding_base_value))); From b3ee2f89b9070f0f25c1343dead63f2af4a7f7b6 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 14 May 2024 20:37:31 -0400 Subject: [PATCH 0108/1073] CI: Add Ubuntu 24.04 Add a job matrix for the Ubuntu Build so that we can test builds on both Ubuntu 22.04 and 24.04. --- .github/workflows/build-project.yaml | 17 ++++++++++------- .github/workflows/push.yaml | 22 ++++++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-project.yaml b/.github/workflows/build-project.yaml index 63bd85ad6517fa..2ccb337e0d91f6 100644 --- a/.github/workflows/build-project.yaml +++ b/.github/workflows/build-project.yaml @@ -170,7 +170,10 @@ jobs: ubuntu-build: name: Ubuntu 🐧 - runs-on: ubuntu-22.04 + strategy: + matrix: + os: [ubuntu-22.04, ubuntu-24.04] + runs-on: ${{ matrix.os }} needs: check-event defaults: run: @@ -185,9 +188,9 @@ jobs: id: ccache-cache with: path: ${{ github.workspace }}/.ccache - key: ${{ runner.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }} + key: ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }} restore-keys: | - ${{ runner.os }}-ccache-x86_64- + ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64- - name: Build OBS Studio 🧱 uses: ./.github/actions/build-obs @@ -214,27 +217,27 @@ jobs: - name: Upload Source Tarball 🗜️ uses: actions/upload-artifact@v4 with: - name: obs-studio-ubuntu-22.04-sources-${{ needs.check-event.outputs.commitHash }} + name: obs-studio-${{ matrix.os }}-sources-${{ needs.check-event.outputs.commitHash }} path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-sources.* - name: Upload Artifacts 📡 uses: actions/upload-artifact@v4 with: - name: obs-studio-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }} + name: obs-studio-${{ matrix.os }}-x86_64-${{ needs.check-event.outputs.commitHash }} path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-x86_64-ubuntu-gnu.* - name: Upload Debug Symbol Artifacts 🪲 uses: actions/upload-artifact@v4 if: ${{ fromJSON(needs.check-event.outputs.package) }} with: - name: obs-studio-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym + name: obs-studio-${{ matrix.os }}-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb - uses: actions/cache/save@v4 if: github.event_name != 'pull_request' && steps.ccache-cache.outputs.cache-hit != 'true' with: path: ${{ github.workspace }}/.ccache - key: ${{ runner.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }} + key: ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }} flatpak-build: name: Flatpak 📦 diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index c61b25da43b3f6..2433cde085358a 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -262,9 +262,11 @@ jobs: macos_arm64_dsym_artifact_name="obs-studio-macos-arm64-${commit_hash}-dSYMs" macos_intel_artifact_name="obs-studio-macos-x86_64-${commit_hash}" macos_intel_dsym_artifact_name="obs-studio-macos-x86_64-${commit_hash}-dSYMs" - ubuntu_x86_64_artifact_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}" - ubuntu_x86_64_debug_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}-dbgsym" - ubuntu_sources_name="obs-studio-ubuntu-22.04-sources-${commit_hash}" + ubuntu_2204_x86_64_artifact_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}" + ubuntu_2204_x86_64_debug_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}-dbgsym" + ubuntu_2404_x86_64_artifact_name="obs-studio-ubuntu-24.04-x86_64-${commit_hash}" + ubuntu_2404_x86_64_debug_name="obs-studio-ubuntu-24.04-x86_64-${commit_hash}-dbgsym" + ubuntu_2404_sources_name="obs-studio-ubuntu-24.04-sources-${commit_hash}" windows_artifact_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-signed" windows_installer_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-installer" windows_debug_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-pdbs" @@ -278,11 +280,15 @@ jobs: "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel.dmg mv -v "${macos_intel_dsym_artifact_name}/"obs-studio-*-macos-intel-dSYMs.tar.xz \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel-dSYMs.tar.xz - mv -v "${ubuntu_x86_64_artifact_name}/"obs-studio-*-x86_64-linux-gnu.deb \ - "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-x86_64.deb - mv -v "${ubuntu_x86_64_debug_name}/"obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb \ - "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-x86_64-dbsym.ddeb - mv -v "${ubuntu_sources_name}/"obs-studio-*-sources.tar.gz \ + mv -v "${ubuntu_2204_x86_64_artifact_name}/"obs-studio-*-x86_64-linux-gnu.deb \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-22.04-x86_64.deb + mv -v "${ubuntu_2204_x86_64_debug_name}/"obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-22.04-x86_64-dbsym.ddeb + mv -v "${ubuntu_2404_x86_64_artifact_name}/"obs-studio-*-x86_64-linux-gnu.deb \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64.deb + mv -v "${ubuntu_2404_x86_64_debug_name}/"obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb \ + "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64-dbsym.ddeb + mv -v "${ubuntu_2404_sources_name}/"obs-studio-*-sources.tar.gz \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz mv -v "${windows_installer_name}/"OBS-Studio-*.exe \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-Installer.exe From c3c6c6c93409b72931fa566dbc7adbfadc0cee72 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 15 May 2024 14:55:58 -0400 Subject: [PATCH 0109/1073] CI: Add additional log group to setup_ubuntu script Add clear separation between the CEF setup and the apt/apt-get calls. This makes it easier to see how much time is being spent on each task. --- .github/scripts/utils.zsh/setup_ubuntu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/scripts/utils.zsh/setup_ubuntu b/.github/scripts/utils.zsh/setup_ubuntu index b8df083461218c..12ce107b412866 100644 --- a/.github/scripts/utils.zsh/setup_ubuntu +++ b/.github/scripts/utils.zsh/setup_ubuntu @@ -62,6 +62,8 @@ mkdir -p ${_target} && pushd ${_target} XZ_OPT=-T0 tar --strip-components 1 -xJf ../${_filename} && log_status "${deps_label} extracted." popd +log_group 'Installing obs-studio build dependencies from apt...' + local suffix if [[ ${CPUTYPE} != ${target##*-} ]] { local -A arch_mappings=( From 7824e164b18ede8cf958c0ad4d964a19ab65390e Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 29 Mar 2024 15:42:18 +0100 Subject: [PATCH 0110/1073] mac-capture: Replace pragmas with availability markers Also changes clang-format SeparateDefinitionBlocks to Leave on ObjC files, which avoid having an empty new line after API_AVAIABLE and the declaration. --- .clang-format | 2 +- plugins/mac-capture/mac-sck-audio-capture.m | 15 ++++++++------ plugins/mac-capture/mac-sck-common.h | 22 ++++++++------------ plugins/mac-capture/mac-sck-common.m | 2 +- plugins/mac-capture/mac-sck-video-capture.m | 23 ++++++++++++--------- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/.clang-format b/.clang-format index 38b431e173f125..3abb4e62e8afe8 100644 --- a/.clang-format +++ b/.clang-format @@ -181,7 +181,7 @@ ReferenceAlignment: Right RemoveSemicolon: false RequiresClausePosition: WithPreceding RequiresExpressionIndentation: OuterScope -SeparateDefinitionBlocks: Always +SeparateDefinitionBlocks: Leave ShortNamespaceLines: 1 SortIncludes: false #SortUsingDeclarations: LexicographicNumeric diff --git a/plugins/mac-capture/mac-sck-audio-capture.m b/plugins/mac-capture/mac-sck-audio-capture.m index 32b39cd650f0ba..595e46b7261b22 100644 --- a/plugins/mac-capture/mac-sck-audio-capture.m +++ b/plugins/mac-capture/mac-sck-audio-capture.m @@ -5,7 +5,7 @@ return obs_module_text("SCK.Audio.Name"); } -static void destroy_audio_screen_stream(struct screen_capture *sc) +API_AVAILABLE(macos(13.0)) static void destroy_audio_screen_stream(struct screen_capture *sc) { if (sc->disp && !sc->capture_failed) { [sc->disp stopCaptureWithCompletionHandler:^(NSError *_Nullable error) { @@ -32,7 +32,7 @@ static void destroy_audio_screen_stream(struct screen_capture *sc) os_event_destroy(sc->stream_start_completed); } -static void sck_audio_capture_destroy(void *data) +API_AVAILABLE(macos(13.0)) static void sck_audio_capture_destroy(void *data) { struct screen_capture *sc = data; @@ -57,7 +57,7 @@ static void sck_audio_capture_destroy(void *data) bfree(sc); } -static bool init_audio_screen_stream(struct screen_capture *sc) +API_AVAILABLE(macos(13.0)) static bool init_audio_screen_stream(struct screen_capture *sc) { SCContentFilter *content_filter; if (sc->capture_failed) { @@ -178,7 +178,7 @@ static void sck_audio_capture_defaults(obs_data_t *settings) obs_data_set_default_int(settings, "type", ScreenCaptureAudioDesktopStream); } -static void *sck_audio_capture_create(obs_data_t *settings, obs_source_t *source) +API_AVAILABLE(macos(13.0)) static void *sck_audio_capture_create(obs_data_t *settings, obs_source_t *source) { struct screen_capture *sc = bzalloc(sizeof(struct screen_capture)); @@ -209,6 +209,7 @@ static void sck_audio_capture_defaults(obs_data_t *settings) #pragma mark - obs_properties +API_AVAILABLE(macos(13.0)) static bool audio_capture_method_changed(void *data, obs_properties_t *props, obs_property_t *list __unused, obs_data_t *settings) { @@ -233,6 +234,7 @@ static bool audio_capture_method_changed(void *data, obs_properties_t *props, ob return true; } +API_AVAILABLE(macos(13.0)) static bool reactivate_capture(obs_properties_t *props __unused, obs_property_t *property, void *data) { struct screen_capture *sc = data; @@ -248,7 +250,7 @@ static bool reactivate_capture(obs_properties_t *props __unused, obs_property_t return true; } -static obs_properties_t *sck_audio_capture_properties(void *data) +API_AVAILABLE(macos(13.0)) static obs_properties_t *sck_audio_capture_properties(void *data) { struct screen_capture *sc = data; @@ -284,7 +286,7 @@ static bool reactivate_capture(obs_properties_t *props __unused, obs_property_t return props; } -static void sck_audio_capture_update(void *data, obs_data_t *settings) +API_AVAILABLE(macos(13.0)) static void sck_audio_capture_update(void *data, obs_data_t *settings) { struct screen_capture *sc = data; @@ -300,6 +302,7 @@ static void sck_audio_capture_update(void *data, obs_data_t *settings) #pragma mark - obs_source_info +API_AVAILABLE(macos(13.0)) struct obs_source_info sck_audio_capture_info = { .id = "sck_audio_capture", .type = OBS_SOURCE_TYPE_INPUT, diff --git a/plugins/mac-capture/mac-sck-common.h b/plugins/mac-capture/mac-sck-common.h index 32ca8d246a8a5b..6bbd0a6e822694 100644 --- a/plugins/mac-capture/mac-sck-common.h +++ b/plugins/mac-capture/mac-sck-common.h @@ -1,9 +1,6 @@ #include #include -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - #include #include #include @@ -28,15 +25,16 @@ typedef enum { ScreenCaptureAudioApplicationStream = 1, } ScreenCaptureAudioStreamType; -typedef SCDisplay *SCDisplayRef; +API_AVAILABLE(macos(12.5)) typedef SCDisplay *SCDisplayRef; +API_AVAILABLE(macos(12.5)) @interface ScreenCaptureDelegate : NSObject @property struct screen_capture *sc; @end -struct screen_capture { +struct API_AVAILABLE(macos(12.5)) screen_capture { obs_source_t *source; gs_effect_t *effect; @@ -71,18 +69,16 @@ struct screen_capture { bool is_screen_capture_available(void); -void screen_capture_build_content_list(struct screen_capture *sc, bool display_capture); +API_AVAILABLE(macos(12.5)) void screen_capture_build_content_list(struct screen_capture *sc, bool display_capture); -bool build_display_list(struct screen_capture *sc, obs_properties_t *props); +API_AVAILABLE(macos(12.5)) bool build_display_list(struct screen_capture *sc, obs_properties_t *props); -bool build_window_list(struct screen_capture *sc, obs_properties_t *props); +API_AVAILABLE(macos(12.5)) bool build_window_list(struct screen_capture *sc, obs_properties_t *props); -bool build_application_list(struct screen_capture *sc, obs_properties_t *props); +API_AVAILABLE(macos(12.5)) bool build_application_list(struct screen_capture *sc, obs_properties_t *props); static const char *screen_capture_getname(void *unused __unused); -void screen_stream_video_update(struct screen_capture *sc, CMSampleBufferRef sample_buffer); - -void screen_stream_audio_update(struct screen_capture *sc, CMSampleBufferRef sample_buffer); +API_AVAILABLE(macos(12.5)) void screen_stream_video_update(struct screen_capture *sc, CMSampleBufferRef sample_buffer); -#pragma clang diagnostic pop +API_AVAILABLE(macos(12.5)) void screen_stream_audio_update(struct screen_capture *sc, CMSampleBufferRef sample_buffer); diff --git a/plugins/mac-capture/mac-sck-common.m b/plugins/mac-capture/mac-sck-common.m index f9f6de7d08a945..67e488170ff8f1 100644 --- a/plugins/mac-capture/mac-sck-common.m +++ b/plugins/mac-capture/mac-sck-common.m @@ -198,7 +198,7 @@ bool build_application_list(struct screen_capture *sc, obs_properties_t *props) #pragma mark - audio/video -void screen_stream_video_update(struct screen_capture *sc, CMSampleBufferRef sample_buffer) +API_AVAILABLE(macos(12.5)) void screen_stream_video_update(struct screen_capture *sc, CMSampleBufferRef sample_buffer) { bool frame_detail_errored = false; float scale_factor = 1.0f; diff --git a/plugins/mac-capture/mac-sck-video-capture.m b/plugins/mac-capture/mac-sck-video-capture.m index 0e2c700b35d617..2b34851e959f8d 100644 --- a/plugins/mac-capture/mac-sck-video-capture.m +++ b/plugins/mac-capture/mac-sck-video-capture.m @@ -1,7 +1,7 @@ #include "mac-sck-common.h" #include "window-utils.h" -static void destroy_screen_stream(struct screen_capture *sc) +API_AVAILABLE(macos(12.5)) static void destroy_screen_stream(struct screen_capture *sc) { if (sc->disp && !sc->capture_failed) { [sc->disp stopCaptureWithCompletionHandler:^(NSError *_Nullable error) { @@ -45,7 +45,7 @@ static void destroy_screen_stream(struct screen_capture *sc) os_event_destroy(sc->stream_start_completed); } -static void sck_video_capture_destroy(void *data) +API_AVAILABLE(macos(12.5)) static void sck_video_capture_destroy(void *data) { struct screen_capture *sc = data; @@ -74,7 +74,7 @@ static void sck_video_capture_destroy(void *data) bfree(sc); } -static bool init_screen_stream(struct screen_capture *sc) +API_AVAILABLE(macos(12.5)) static bool init_screen_stream(struct screen_capture *sc) { SCContentFilter *content_filter; if (sc->capture_failed) { @@ -270,7 +270,7 @@ static bool init_screen_stream(struct screen_capture *sc) return did_stream_start; } -static void *sck_video_capture_create(obs_data_t *settings, obs_source_t *source) +API_AVAILABLE(macos(12.5)) static void *sck_video_capture_create(obs_data_t *settings, obs_source_t *source) { struct screen_capture *sc = bzalloc(sizeof(struct screen_capture)); @@ -309,7 +309,7 @@ static bool init_screen_stream(struct screen_capture *sc) return NULL; } -static void sck_video_capture_tick(void *data, float seconds __unused) +API_AVAILABLE(macos(12.5)) static void sck_video_capture_tick(void *data, float seconds __unused) { struct screen_capture *sc = data; @@ -341,7 +341,7 @@ static void sck_video_capture_tick(void *data, float seconds __unused) } } -static void sck_video_capture_render(void *data, gs_effect_t *effect __unused) +API_AVAILABLE(macos(12.5)) static void sck_video_capture_render(void *data, gs_effect_t *effect __unused) { struct screen_capture *sc = data; @@ -368,14 +368,14 @@ static void sck_video_capture_render(void *data, gs_effect_t *effect __unused) return obs_module_text("SCK.Name.Beta"); } -static uint32_t sck_video_capture_getwidth(void *data) +API_AVAILABLE(macos(12.5)) static uint32_t sck_video_capture_getwidth(void *data) { struct screen_capture *sc = data; return (uint32_t) sc->frame.size.width; } -static uint32_t sck_video_capture_getheight(void *data) +API_AVAILABLE(macos(12.5)) static uint32_t sck_video_capture_getheight(void *data) { struct screen_capture *sc = data; @@ -410,7 +410,7 @@ static void sck_video_capture_defaults(obs_data_t *settings) obs_data_set_default_bool(settings, "show_hidden_windows", false); } -static void sck_video_capture_update(void *data, obs_data_t *settings) +API_AVAILABLE(macos(12.5)) static void sck_video_capture_update(void *data, obs_data_t *settings) { struct screen_capture *sc = data; @@ -472,6 +472,7 @@ static void sck_video_capture_update(void *data, obs_data_t *settings) #pragma mark - obs_properties +API_AVAILABLE(macos(12.5)) static bool content_settings_changed(void *data, obs_properties_t *props, obs_property_t *list __unused, obs_data_t *settings) { @@ -543,6 +544,7 @@ static bool content_settings_changed(void *data, obs_properties_t *props, obs_pr return true; } +API_AVAILABLE(macos(12.5)) static bool reactivate_capture(obs_properties_t *props __unused, obs_property_t *property, void *data) { struct screen_capture *sc = data; @@ -560,7 +562,7 @@ static bool reactivate_capture(obs_properties_t *props __unused, obs_property_t return true; } -static obs_properties_t *sck_video_capture_properties(void *data) +API_AVAILABLE(macos(12.5)) static obs_properties_t *sck_video_capture_properties(void *data) { struct screen_capture *sc = data; @@ -693,6 +695,7 @@ enum gs_color_space sck_video_capture_get_color_space(void *data, size_t count, #pragma mark - obs_source_info +API_AVAILABLE(macos(12.5)) struct obs_source_info sck_video_capture_info = { .id = "screen_capture", .type = OBS_SOURCE_TYPE_INPUT, From f60d6bcfe2c6e58490b12e2c01b55316ccb3c3ea Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sat, 11 May 2024 21:32:45 -0500 Subject: [PATCH 0111/1073] rnnoise: Fix compilation not working on latest gcc The latest gcc spits out an error about calloc parameters that are in the wrong order. --- plugins/obs-filters/rnnoise/src/denoise.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/obs-filters/rnnoise/src/denoise.c b/plugins/obs-filters/rnnoise/src/denoise.c index 7639f745630e8e..c1907f1442e7d0 100644 --- a/plugins/obs-filters/rnnoise/src/denoise.c +++ b/plugins/obs-filters/rnnoise/src/denoise.c @@ -265,9 +265,9 @@ int rnnoise_init(DenoiseState *st, RNNModel *model) { st->rnn.model = model; else st->rnn.model = &rnnoise_model_orig; - st->rnn.vad_gru_state = calloc(sizeof(float), st->rnn.model->vad_gru_size); - st->rnn.noise_gru_state = calloc(sizeof(float), st->rnn.model->noise_gru_size); - st->rnn.denoise_gru_state = calloc(sizeof(float), st->rnn.model->denoise_gru_size); + st->rnn.vad_gru_state = calloc(st->rnn.model->vad_gru_size, sizeof(float)); + st->rnn.noise_gru_state = calloc(st->rnn.model->noise_gru_size, sizeof(float)); + st->rnn.denoise_gru_state = calloc(st->rnn.model->denoise_gru_size, sizeof(float)); return 0; } From a18305dd3fec62b97573478223131f8ef975e1db Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Apr 2024 15:04:40 +0200 Subject: [PATCH 0112/1073] UI: Keep weak stream output ref in status bar The status bar doesn't currently own its references to outputs, which can be a problem if streaming outputs are released soon after stream stop. The Multitrack Video output does exactly that, so the status bar will sometimes try to access an invalid pointer for updating its stats. Keeping a weak reference around and upgrading it to collect stats, similar to how the stats window behaves. --- UI/window-basic-status-bar.cpp | 37 +++++++++++++++++++++++++--------- UI/window-basic-status-bar.hpp | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/UI/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp index 02ff7f0539165d..3526149d9f1a18 100644 --- a/UI/window-basic-status-bar.cpp +++ b/UI/window-basic-status-bar.cpp @@ -195,7 +195,11 @@ void OBSBasicStatusBar::UpdateBandwidth() if (++seconds < bitrateUpdateSeconds) return; - uint64_t bytesSent = obs_output_get_total_bytes(streamOutput); + OBSOutput output = OBSGetStrongRef(streamOutput); + if (!output) + return; + + uint64_t bytesSent = obs_output_get_total_bytes(output); uint64_t bytesSentTime = os_gettime_ns(); if (bytesSent < lastBytesSent) @@ -338,8 +342,12 @@ void OBSBasicStatusBar::UpdateDroppedFrames() if (!streamOutput) return; - int totalDropped = obs_output_get_frames_dropped(streamOutput); - int totalFrames = obs_output_get_total_frames(streamOutput); + OBSOutput output = OBSGetStrongRef(streamOutput); + if (!output) + return; + + int totalDropped = obs_output_get_frames_dropped(output); + int totalFrames = obs_output_get_total_frames(output); double percent = (double)totalDropped / (double)totalFrames * 100.0; if (!totalFrames) @@ -356,7 +364,7 @@ void OBSBasicStatusBar::UpdateDroppedFrames() /* ----------------------------------- * * calculate congestion color */ - float congestion = obs_output_get_congestion(streamOutput); + float congestion = obs_output_get_congestion(output); float avgCongestion = (congestion + lastCongestion) * 0.5f; if (avgCongestion < congestion) avgCongestion = congestion; @@ -425,7 +433,11 @@ void OBSBasicStatusBar::Reconnect(int seconds) reconnectTimeout = seconds; if (streamOutput) { - delaySecTotal = obs_output_get_active_delay(streamOutput); + OBSOutput output = OBSGetStrongRef(streamOutput); + if (!output) + return; + + delaySecTotal = obs_output_get_active_delay(output); UpdateDelayMsg(); retries++; @@ -453,7 +465,11 @@ void OBSBasicStatusBar::ReconnectSuccess() ReconnectClear(); if (streamOutput) { - delaySecTotal = obs_output_get_active_delay(streamOutput); + OBSOutput output = OBSGetStrongRef(streamOutput); + if (!output) + return; + + delaySecTotal = obs_output_get_active_delay(output); UpdateDelayMsg(); disconnected = false; firstCongestionUpdate = true; @@ -501,7 +517,8 @@ void OBSBasicStatusBar::StreamDelayStarting(int sec) if (!main || !main->outputHandler) return; - streamOutput = main->outputHandler->streamOutput; + OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); + streamOutput = obs_output_get_weak_output(output); delaySecTotal = delaySecStarting = sec; UpdateDelayMsg(); @@ -516,11 +533,11 @@ void OBSBasicStatusBar::StreamDelayStopping(int sec) void OBSBasicStatusBar::StreamStarted(obs_output_t *output) { - streamOutput = output; + streamOutput = obs_output_get_weak_output(output); - streamSigs.emplace_back(obs_output_get_signal_handler(streamOutput), + streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect", OBSOutputReconnect, this); - streamSigs.emplace_back(obs_output_get_signal_handler(streamOutput), + streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect_success", OBSOutputReconnectSuccess, this); diff --git a/UI/window-basic-status-bar.hpp b/UI/window-basic-status-bar.hpp index cd79f0db335409..d0c645ad88a317 100644 --- a/UI/window-basic-status-bar.hpp +++ b/UI/window-basic-status-bar.hpp @@ -27,7 +27,7 @@ class OBSBasicStatusBar : public QStatusBar { private: StatusBarWidget *statusWidget = nullptr; - obs_output_t *streamOutput = nullptr; + OBSWeakOutputAutoRelease streamOutput; std::vector streamSigs; obs_output_t *recordOutput = nullptr; bool active = false; From 842d249cb99c9e9d924aaa44e89945e5e346868d Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 17 May 2024 15:16:38 -0400 Subject: [PATCH 0113/1073] rtmp-services: Bump package version e92accf136b9ffd1ea812c72c9aca2ffac5d7047 updated services.json, but didn't bump the package version. --- plugins/rtmp-services/data/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 1b6aa095425095..95877f3f37a0b2 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 251, + "version": 252, "files": [ { "name": "services.json", - "version": 251 + "version": 252 } ] } From dc4cba742746b4dc9410912ff6de513af6afc281 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 26 Apr 2024 21:48:53 +0200 Subject: [PATCH 0114/1073] libobs/util: Add seeking support to array serializer --- .../reference-libobs-util-serializers.rst | 10 ++++ libobs/util/array-serializer.c | 52 ++++++++++++++++++- libobs/util/array-serializer.h | 2 + 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/reference-libobs-util-serializers.rst b/docs/sphinx/reference-libobs-util-serializers.rst index 63bdd077c0e06a..5c59e6927f6fdd 100644 --- a/docs/sphinx/reference-libobs-util-serializers.rst +++ b/docs/sphinx/reference-libobs-util-serializers.rst @@ -95,6 +95,9 @@ Array Output Serializer Provides an output serializer used with dynamic arrays. +.. versionchanged:: 30.2 + Array output serializer now supports seeking. + .. code:: cpp #include @@ -119,6 +122,13 @@ Array Output Serializer Functions --------------------- +.. function:: void array_output_serializer_reset(struct array_output_data *data) + + Resets serializer without freeing data. + + .. versionadded:: 30.2 + +--------------------- File Input/Output Serializers ============================= diff --git a/libobs/util/array-serializer.c b/libobs/util/array-serializer.c index 6e8a36842a353d..cc8094057fb308 100644 --- a/libobs/util/array-serializer.c +++ b/libobs/util/array-serializer.c @@ -20,7 +20,23 @@ static size_t array_output_write(void *param, const void *data, size_t size) { struct array_output_data *output = param; - da_push_back_array(output->bytes, (uint8_t *)data, size); + + if (output->cur_pos < output->bytes.num) { + size_t new_size = output->cur_pos + size; + + if (new_size > output->bytes.num) { + darray_ensure_capacity(sizeof(uint8_t), + &output->bytes.da, new_size); + output->bytes.num = new_size; + } + + memcpy(output->bytes.array + output->cur_pos, data, size); + output->cur_pos += size; + } else { + da_push_back_array(output->bytes, (uint8_t *)data, size); + output->cur_pos += size; + } + return size; } @@ -30,6 +46,33 @@ static int64_t array_output_get_pos(void *param) return (int64_t)data->bytes.num; } +static int64_t array_output_seek(void *param, int64_t offset, + enum serialize_seek_type seek_type) +{ + struct array_output_data *output = param; + + size_t new_pos = 0; + + switch (seek_type) { + case SERIALIZE_SEEK_START: + new_pos = offset; + break; + case SERIALIZE_SEEK_CURRENT: + new_pos = output->cur_pos + offset; + break; + case SERIALIZE_SEEK_END: + new_pos = output->bytes.num - offset; + break; + } + + if (new_pos > output->bytes.num) + return -1; + + output->cur_pos = new_pos; + + return (int64_t)new_pos; +} + void array_output_serializer_init(struct serializer *s, struct array_output_data *data) { @@ -38,9 +81,16 @@ void array_output_serializer_init(struct serializer *s, s->data = data; s->write = array_output_write; s->get_pos = array_output_get_pos; + s->seek = array_output_seek; } void array_output_serializer_free(struct array_output_data *data) { da_free(data->bytes); } + +void array_output_serializer_reset(struct array_output_data *data) +{ + da_clear(data->bytes); + data->cur_pos = 0; +} diff --git a/libobs/util/array-serializer.h b/libobs/util/array-serializer.h index 28907898a2a902..1848debe142be3 100644 --- a/libobs/util/array-serializer.h +++ b/libobs/util/array-serializer.h @@ -25,11 +25,13 @@ extern "C" { struct array_output_data { DARRAY(uint8_t) bytes; + size_t cur_pos; }; EXPORT void array_output_serializer_init(struct serializer *s, struct array_output_data *data); EXPORT void array_output_serializer_free(struct array_output_data *data); +EXPORT void array_output_serializer_reset(struct array_output_data *data); #ifdef __cplusplus } From 89c7a9608b86e5868dfe929bc422ac9b12ad0c77 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 27 Apr 2024 06:22:22 +0200 Subject: [PATCH 0115/1073] libobs/util: Add buffered file serializer Adapted from 898256d41620878367bc40a2dd66d236e892c99f Co-authored-by: Richard Stanway --- .../reference-libobs-util-serializers.rst | 41 ++ libobs/CMakeLists.txt | 2 + libobs/util/buffered-file-serializer.c | 414 ++++++++++++++++++ libobs/util/buffered-file-serializer.h | 34 ++ 4 files changed, 491 insertions(+) create mode 100644 libobs/util/buffered-file-serializer.c create mode 100644 libobs/util/buffered-file-serializer.h diff --git a/docs/sphinx/reference-libobs-util-serializers.rst b/docs/sphinx/reference-libobs-util-serializers.rst index 5c59e6927f6fdd..3276cd5d3d86d0 100644 --- a/docs/sphinx/reference-libobs-util-serializers.rst +++ b/docs/sphinx/reference-libobs-util-serializers.rst @@ -181,3 +181,44 @@ File Output Serializer Functions .. function:: void file_output_serializer_free(struct serializer *s) Frees the file output serializer and saves the file. + +--------------------- + +Buffered File Output Serializer +=============================== + +Provides a buffered file serializer that writes data asynchronously to prevent stalls due to slow disk I/O. + +Writes will only block when the buffer is full. + +The buffer and chunk size are configurable with the defaults being 256 MiB and 1 MiB respectively. + +.. versionadded:: 30.2 + +.. code:: cpp + + #include + + +Buffered File Output Serializer Functions +----------------------------------------- + +.. function:: bool buffered_file_serializer_init_defaults(struct serializer *s, const char *path) + + Initializes a buffered file output serializer with default buffer and chunk sizes. + + :return: *true* if file created successfully, *false* otherwise + +--------------------- + +.. function:: bool buffered_file_serializer_init(struct serializer *s, const char *path, size_t max_bufsize, size_t chunk_size) + + Initialize buffered writer with specified buffer and chunk sizes. Setting either to `0` will use the default value. + + :return: *true* if file created successfully, *false* otherwise + +--------------------- + +.. function:: void buffered_file_serializer_free(struct serializer *s) + + Frees the file output serializer and saves the file. Will block until I/O thread completes outstanding writes. diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index affb0c966bcc07..3d759a13d0a4c7 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -99,6 +99,8 @@ target_sources( util/bitstream.h util/bmem.c util/bmem.h + util/buffered-file-serializer.c + util/buffered-file-serializer.h util/c99defs.h util/cf-lexer.c util/cf-lexer.h diff --git a/libobs/util/buffered-file-serializer.c b/libobs/util/buffered-file-serializer.c new file mode 100644 index 00000000000000..6b65652eeba8aa --- /dev/null +++ b/libobs/util/buffered-file-serializer.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2024 Dennis Sädtler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "buffered-file-serializer.h" + +#include + +#include "platform.h" +#include "threading.h" +#include "deque.h" +#include "dstr.h" + +static const size_t DEFAULT_BUF_SIZE = 256ULL * 1048576ULL; // 256 MiB +static const size_t DEFAULT_CHUNK_SIZE = 1048576; // 1 MiB + +/* ========================================================================== */ +/* Buffered writer based on ffmpeg-mux implementation */ + +struct io_header { + uint64_t seek_offset; + uint64_t data_length; +}; + +struct io_buffer { + bool active; + bool shutdown_requested; + bool output_error; + os_event_t *buffer_space_available_event; + os_event_t *new_data_available_event; + pthread_t io_thread; + pthread_mutex_t data_mutex; + FILE *output_file; + struct deque data; + uint64_t next_pos; + + size_t buffer_size; + size_t chunk_size; +}; + +struct file_output_data { + struct dstr filename; + struct io_buffer io; +}; + +static void *io_thread(void *opaque) +{ + struct file_output_data *out = opaque; + os_set_thread_name("buffered writer i/o thread"); + + // Chunk collects the writes into a larger batch + size_t chunk_used = 0; + size_t chunk_size = out->io.chunk_size; + + unsigned char *chunk = bmalloc(chunk_size); + if (!chunk) { + os_atomic_set_bool(&out->io.output_error, true); + fprintf(stderr, "Error allocating memory for output\n"); + goto error; + } + + bool shutting_down; + bool want_seek = false; + bool force_flush_chunk = false; + + // current_seek_position is a virtual position updated as we read from + // the buffer, if it becomes discontinuous due to a seek request we + // flush the chunk. next_seek_position is the actual offset we should + // seek to when we write the chunk. + uint64_t current_seek_position = 0; + uint64_t next_seek_position; + + for (;;) { + // Wait for data to be written to the buffer + os_event_wait(out->io.new_data_available_event); + + // Loop to write in chunk_size chunks + for (;;) { + pthread_mutex_lock(&out->io.data_mutex); + + shutting_down = os_atomic_load_bool( + &out->io.shutdown_requested); + + // Fetch as many writes as possible from the deque + // and fill up our local chunk. This may involve + // seeking, so take care of that as well. + for (;;) { + size_t available = out->io.data.size; + + // Buffer is empty (now) or was already empty (we got + // woken up to exit) + if (!available) + break; + + // Get seek offset and data size + struct io_header header; + deque_peek_front(&out->io.data, &header, + sizeof(header)); + + // Do we need to seek? + if (header.seek_offset != + current_seek_position) { + + // If there's already part of a chunk pending, + // flush it at the current offset. Similarly, + // if we already plan to seek, then seek. + if (chunk_used || want_seek) { + force_flush_chunk = true; + break; + } + + // Mark that we need to seek and where to + want_seek = true; + next_seek_position = header.seek_offset; + + // Update our virtual position + current_seek_position = + header.seek_offset; + } + + // Make sure there's enough room for the data, if + // not then force a flush + if (header.data_length + chunk_used > + chunk_size) { + force_flush_chunk = true; + break; + } + + // Remove header that we already read + deque_pop_front(&out->io.data, NULL, + sizeof(header)); + + // Copy from the buffer to our local chunk + deque_pop_front(&out->io.data, + chunk + chunk_used, + header.data_length); + + // Update offsets + chunk_used += header.data_length; + current_seek_position += header.data_length; + } + + // Signal that there is more room in the buffer + os_event_signal(out->io.buffer_space_available_event); + + // Try to avoid lots of small writes unless this was the final + // data left in the buffer. The buffer might be entirely empty + // if we were woken up to exit. + if (!force_flush_chunk && + (!chunk_used || + (chunk_used < 65536 && !shutting_down))) { + os_event_reset( + out->io.new_data_available_event); + pthread_mutex_unlock(&out->io.data_mutex); + break; + } + + pthread_mutex_unlock(&out->io.data_mutex); + + // Seek if we need to + if (want_seek) { + os_fseeki64(out->io.output_file, + next_seek_position, SEEK_SET); + + // Update the next virtual position, making sure to take + // into account the size of the chunk we're about to write. + current_seek_position = + next_seek_position + chunk_used; + + want_seek = false; + + // If we did a seek but do not have any data left to write + // return to the start of the loop. + if (!chunk_used) { + force_flush_chunk = false; + continue; + } + } + + // Write the current chunk to the output file + size_t bytes_written = fwrite(chunk, 1, chunk_used, + out->io.output_file); + if (bytes_written != chunk_used) { + blog(LOG_ERROR, + "Error writing to '%s': %s (%zu != %zu)\n", + out->filename.array, strerror(errno), + bytes_written, chunk_used); + os_atomic_set_bool(&out->io.output_error, true); + + goto error; + } + + chunk_used = 0; + force_flush_chunk = false; + } + + // If this was the last chunk, time to exit + if (shutting_down) + break; + } + +error: + if (chunk) + bfree(chunk); + + fclose(out->io.output_file); + return NULL; +} + +/* ========================================================================== */ +/* Serializer Implementation */ + +static int64_t file_output_seek(void *opaque, int64_t offset, + enum serialize_seek_type seek_type) +{ + struct file_output_data *out = opaque; + + // If the output thread failed, signal that back up the stack + if (os_atomic_load_bool(&out->io.output_error)) + return -1; + + // Update where the next write should go + pthread_mutex_lock(&out->io.data_mutex); + + switch (seek_type) { + case SERIALIZE_SEEK_START: + out->io.next_pos = offset; + break; + case SERIALIZE_SEEK_CURRENT: + out->io.next_pos += offset; + break; + case SERIALIZE_SEEK_END: + out->io.next_pos -= offset; + break; + } + + pthread_mutex_unlock(&out->io.data_mutex); + + return (int64_t)out->io.next_pos; +} + +#ifndef _WIN32 +static inline size_t max(size_t a, size_t b) +{ + return a > b ? a : b; +} + +static inline size_t min(size_t a, size_t b) +{ + return a < b ? a : b; +} +#endif + +static size_t file_output_write(void *opaque, const void *buf, size_t buf_size) +{ + struct file_output_data *out = opaque; + + if (!buf_size) + return 0; + + // Split writes into at chunks that are at most chunk_size bytes + uintptr_t ptr = (uintptr_t)buf; + size_t remaining = buf_size; + + while (remaining) { + if (os_atomic_load_bool(&out->io.output_error)) + return 0; + + pthread_mutex_lock(&out->io.data_mutex); + + size_t next_chunk_size = min(remaining, out->io.chunk_size); + + // Avoid unbounded growth of the deque, cap to buffer_size + size_t cap = max(out->io.data.capacity, out->io.buffer_size); + size_t free_space = cap - out->io.data.size; + + if (free_space < next_chunk_size + sizeof(struct io_header)) { + blog(LOG_DEBUG, "Waiting for I/O thread..."); + // No space, wait for the I/O thread to make space + os_event_reset(out->io.buffer_space_available_event); + pthread_mutex_unlock(&out->io.data_mutex); + os_event_wait(out->io.buffer_space_available_event); + continue; + } + + // Calculate how many chunks we can fit into the buffer + size_t num_chunks = free_space / (next_chunk_size + + sizeof(struct io_header)); + + while (remaining && num_chunks--) { + struct io_header header = { + .data_length = next_chunk_size, + .seek_offset = out->io.next_pos, + }; + + // Copy the data into the buffer + deque_push_back(&out->io.data, &header, sizeof(header)); + deque_push_back(&out->io.data, (const void *)ptr, + next_chunk_size); + + // Advance the next write position + out->io.next_pos += next_chunk_size; + + // Update remainder and advance data pointer + remaining -= next_chunk_size; + ptr += next_chunk_size; + next_chunk_size = min(remaining, out->io.chunk_size); + } + + // Tell the I/O thread that there's new data to be written + os_event_signal(out->io.new_data_available_event); + + pthread_mutex_unlock(&out->io.data_mutex); + } + + return buf_size - remaining; +} + +static int64_t file_output_get_pos(void *opaque) +{ + struct file_output_data *out = opaque; + // If thread failed return -1 + if (os_atomic_load_bool(&out->io.output_error)) + return -1; + + return (int64_t)out->io.next_pos; +} + +bool buffered_file_serializer_init_defaults(struct serializer *s, + const char *path) +{ + return buffered_file_serializer_init(s, path, 0, 0); +} + +bool buffered_file_serializer_init(struct serializer *s, const char *path, + size_t max_bufsize, size_t chunk_size) +{ + struct file_output_data *out; + + out = bzalloc(sizeof(*out)); + + dstr_init_copy(&out->filename, path); + + out->io.output_file = os_fopen(path, "wb"); + if (!out->io.output_file) + return false; + + out->io.buffer_size = max_bufsize ? max_bufsize : DEFAULT_BUF_SIZE; + out->io.chunk_size = chunk_size ? chunk_size : DEFAULT_CHUNK_SIZE; + + // Start at 1MB, this can grow up to max_bufsize depending + // on how fast data is going in and out. + deque_reserve(&out->io.data, 1048576); + + pthread_mutex_init(&out->io.data_mutex, NULL); + + os_event_init(&out->io.buffer_space_available_event, + OS_EVENT_TYPE_AUTO); + os_event_init(&out->io.new_data_available_event, OS_EVENT_TYPE_AUTO); + + pthread_create(&out->io.io_thread, NULL, io_thread, out); + + out->io.active = true; + + s->data = out; + s->read = NULL; + s->write = file_output_write; + s->seek = file_output_seek; + s->get_pos = file_output_get_pos; + return true; +} + +void buffered_file_serializer_free(struct serializer *s) +{ + struct file_output_data *out = s->data; + + if (!out) + return; + + if (out->io.active) { + os_atomic_set_bool(&out->io.shutdown_requested, true); + + // Wakes up the I/O thread and waits for it to finish + pthread_mutex_lock(&out->io.data_mutex); + os_event_signal(out->io.new_data_available_event); + pthread_mutex_unlock(&out->io.data_mutex); + pthread_join(out->io.io_thread, NULL); + + os_event_destroy(out->io.new_data_available_event); + os_event_destroy(out->io.buffer_space_available_event); + + pthread_mutex_destroy(&out->io.data_mutex); + + blog(LOG_DEBUG, "Final buffer capacity: %zu KiB", + out->io.data.capacity / 1024); + + deque_free(&out->io.data); + } + + dstr_free(&out->filename); + bfree(out); +} diff --git a/libobs/util/buffered-file-serializer.h b/libobs/util/buffered-file-serializer.h new file mode 100644 index 00000000000000..8a7be1a53d50d6 --- /dev/null +++ b/libobs/util/buffered-file-serializer.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Dennis Sädtler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include "serializer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +EXPORT bool buffered_file_serializer_init_defaults(struct serializer *s, + const char *path); +EXPORT bool buffered_file_serializer_init(struct serializer *s, + const char *path, size_t max_bufsize, + size_t chunk_size); +EXPORT void buffered_file_serializer_free(struct serializer *s); + +#ifdef __cplusplus +} +#endif From 7cd72781c816de0bcd4d10bab6666dc4fb097aba Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 27 Apr 2024 21:36:26 +0200 Subject: [PATCH 0116/1073] obs-outputs: Adjust HEVCDecoderConfigurationRecord for hvc1 tag We mux HEVC with the hvc1 tag, which requires the parameter sets' array_completeness to be set to 1. --- plugins/obs-outputs/rtmp-hevc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-outputs/rtmp-hevc.c b/plugins/obs-outputs/rtmp-hevc.c index 78315e1bc4c3ce..5047bf254f7443 100644 --- a/plugins/obs-outputs/rtmp-hevc.c +++ b/plugins/obs-outputs/rtmp-hevc.c @@ -852,7 +852,7 @@ size_t obs_parse_hevc_header(uint8_t **header, const uint8_t *data, size_t size) OBS_HEVC_NAL_SEI_SUFFIX}; if (type == array_idx_to_type[i]) { - int ps_array_completeness = 0; + int ps_array_completeness = 1; int ret = hvcc_add_nal_unit( buf, len, ps_array_completeness, &hvcc, i); From adf744e6f018636c7faee3589e5c1c7adbc47733 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 18 May 2024 21:21:49 +0200 Subject: [PATCH 0117/1073] libobs: Ensure audio offsets are positive --- libobs/obs-output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 8528fcb011a62f..14413c204399ba 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -2063,7 +2063,7 @@ static bool initialize_interleaved_packets(struct obs_output *output) } } for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { - if (output->audio_encoders[i]) { + if (output->audio_encoders[i] && audio[i]->dts > 0) { output->audio_offsets[i] = audio[i]->dts; } } From c815d6ad616da6961972307c3e27799a0c480a44 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 18 May 2024 19:23:26 +0200 Subject: [PATCH 0118/1073] coreaudio-encoder: Fix pts/dts not including encoder delay --- plugins/coreaudio-encoder/encoder.cpp | 11 +++++++++-- plugins/coreaudio-encoder/windows-imports.h | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/coreaudio-encoder/encoder.cpp b/plugins/coreaudio-encoder/encoder.cpp index 0521418122e05b..ded74642060eea 100644 --- a/plugins/coreaudio-encoder/encoder.cpp +++ b/plugins/coreaudio-encoder/encoder.cpp @@ -112,6 +112,7 @@ struct ca_encoder { uint64_t total_samples = 0; uint64_t samples_per_second = 0; + uint32_t priming_samples = 0; vector extra_data; @@ -595,6 +596,11 @@ static void *aac_create(obs_data_t *settings, obs_encoder_t *encoder) ca->converter, kAudioConverterCurrentOutputStreamDescription, &size, &out)); + AudioConverterPrimeInfo primeInfo; + size = sizeof(primeInfo); + STATUS_CHECK(AudioConverterGetProperty( + ca->converter, kAudioConverterPrimeInfo, &size, &primeInfo)); + /* * Fix channel map differences between CoreAudio AAC, FFmpeg, Wav * New channel mappings below assume 2.1, 4.0, 4.1, 5.1, 7.1 resp. @@ -649,6 +655,7 @@ static void *aac_create(obs_data_t *settings, obs_encoder_t *encoder) ca->in_bytes_required = ca->in_packets * ca->in_frame_size; ca->out_frames_per_packet = out.mFramesPerPacket; + ca->priming_samples = primeInfo.leadingFrames; ca->output_buffer_size = out.mBytesPerPacket; @@ -770,8 +777,8 @@ static bool aac_encode(void *data, struct encoder_frame *frame, if (!(*received_packet = packets > 0)) return true; - packet->pts = ca->total_samples; - packet->dts = ca->total_samples; + packet->pts = ca->total_samples - ca->priming_samples; + packet->dts = ca->total_samples - ca->priming_samples; packet->timebase_num = 1; packet->timebase_den = (uint32_t)ca->samples_per_second; packet->type = OBS_ENCODER_AUDIO; diff --git a/plugins/coreaudio-encoder/windows-imports.h b/plugins/coreaudio-encoder/windows-imports.h index a889a9d824eecf..8a400062a340c8 100644 --- a/plugins/coreaudio-encoder/windows-imports.h +++ b/plugins/coreaudio-encoder/windows-imports.h @@ -79,6 +79,12 @@ struct AudioChannelLayout { }; typedef struct AudioChannelLayout AudioChannelLayout; +struct AudioConverterPrimeInfo { + UInt32 leadingFrames; + UInt32 trailingFrames; +}; +typedef struct AudioConverterPrimeInfo AudioConverterPrimeInfo; + typedef OSStatus (*AudioConverterComplexInputDataProc)( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, From 8a38e3375b63692b01e5088d37f531f36a5d39f9 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 14 Apr 2024 13:49:43 +0200 Subject: [PATCH 0119/1073] libobs: Mix audio of each source in a scene only once --- libobs/obs-scene.c | 75 +++++++++++++++++++++++++++++++++++++++------- libobs/obs-scene.h | 11 +++++++ 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 9b51956aeadd7b..82cbd96592c1d4 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -239,6 +239,7 @@ static void scene_destroy(void *data) pthread_mutex_destroy(&scene->video_mutex); pthread_mutex_destroy(&scene->audio_mutex); + da_free(scene->mix_sources); bfree(scene); } @@ -1527,9 +1528,11 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, item = scene->first_item; while (item) { uint64_t source_ts; - size_t pos, count; + size_t pos; bool apply_buf; struct obs_source *source; + struct scene_source_mix *source_mix = NULL; + if (item->visible && transition_active(item->show_transition)) source = item->show_transition; else if (!item->visible && @@ -1560,15 +1563,64 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, continue; } - count = AUDIO_OUTPUT_FRAMES - pos; - if (!apply_buf && !item->visible && !transition_active(item->hide_transition)) { item = item->next; continue; } - obs_source_get_audio_mix(source, &child_audio); + for (size_t i = 0; i < scene->mix_sources.num; i++) { + struct scene_source_mix *mix = + &scene->mix_sources.array[i]; + if (mix->source == item->source) { + source_mix = mix; + break; + } + } + + if (!source_mix) { + source_mix = da_push_back_new(scene->mix_sources); + source_mix->source = item->source; + source_mix->transition = source != item->source ? source + : NULL; + source_mix->apply_buf = apply_buf; + source_mix->pos = pos; + source_mix->count = AUDIO_OUTPUT_FRAMES - pos; + if (apply_buf) { + memcpy(source_mix->buf, buf, + sizeof(float) * source_mix->count); + } + } else { + /* Only transition audio if there are no + * non-transitioning scene items. */ + if (source_mix->transition && source == item->source) + source_mix->transition = NULL; + /* Only apply buf to mix if all scene items for this + * source require it. */ + source_mix->apply_buf = source_mix->apply_buf && + apply_buf; + /* Update buf so that only highest value across all + * items is used. */ + if (source_mix->apply_buf && + memcmp(source_mix->buf, buf, + source_mix->count * sizeof(float)) != 0) { + for (size_t i = 0; i < source_mix->count; i++) { + if (buf[i] > source_mix->buf[i]) + source_mix->buf[i] = buf[i]; + } + } + } + + item = item->next; + } + + for (size_t i = 0; i < scene->mix_sources.num; i++) { + struct scene_source_mix *source_mix = + &scene->mix_sources.array[i]; + obs_source_get_audio_mix(source_mix->transition + ? source_mix->transition + : source_mix->source, + &child_audio); for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) { if ((mixers & (1 << mix)) == 0) @@ -1578,17 +1630,20 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, float *out = audio_output->output[mix].data[ch]; float *in = child_audio.output[mix].data[ch]; - if (apply_buf) - mix_audio_with_buf(out, in, buf, pos, - count); + if (source_mix->apply_buf) + mix_audio_with_buf(out, in, + source_mix->buf, + source_mix->pos, + source_mix->count); else - mix_audio(out, in, pos, count); + mix_audio(out, in, source_mix->pos, + source_mix->count); } } - - item = item->next; } + da_clear(scene->mix_sources); + *ts_out = timestamp; audio_unlock(scene); diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 1efdfc08a53ca4..d78e96ad778ec0 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -93,6 +93,15 @@ struct obs_scene_item { struct obs_scene_item *next; }; +struct scene_source_mix { + obs_source_t *source; + obs_source_t *transition; + size_t pos; + size_t count; + bool apply_buf; + float buf[AUDIO_OUTPUT_FRAMES]; +}; + struct obs_scene { struct obs_source *source; @@ -106,4 +115,6 @@ struct obs_scene { pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; struct obs_scene_item *first_item; + + DARRAY(struct scene_source_mix) mix_sources; }; From 72924ac1f3c4c55de038fe1f03a0e197a155d5e7 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 9 May 2024 12:05:02 +0200 Subject: [PATCH 0120/1073] libobs: Deduplicate audio for nested scenes/groups if not transitioning --- libobs/obs-scene.c | 81 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 82cbd96592c1d4..4b44ebbf033010 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -1476,16 +1476,28 @@ static inline void mix_audio(float *p_out, float *p_in, size_t pos, *(out++) += *(in++); } -static bool scene_audio_render(void *data, uint64_t *ts_out, - struct obs_source_audio_mix *audio_output, - uint32_t mixers, size_t channels, - size_t sample_rate) +static inline struct scene_source_mix *get_source_mix(struct obs_scene *scene, + struct obs_source *source) +{ + for (size_t i = 0; i < scene->mix_sources.num; i++) { + struct scene_source_mix *mix = &scene->mix_sources.array[i]; + if (mix->source == source) + return mix; + } + + return NULL; +} + +static bool scene_audio_render_internal( + struct obs_scene *scene, struct obs_scene *parent, uint64_t *ts_out, + struct obs_source_audio_mix *audio_output, uint32_t mixers, + size_t channels, size_t sample_rate, float *parent_buf) { uint64_t timestamp = 0; float buf[AUDIO_OUTPUT_FRAMES]; struct obs_source_audio_mix child_audio; - struct obs_scene *scene = data; struct obs_scene_item *item; + struct obs_scene *mix_scene = parent ? parent : scene; audio_lock(scene); @@ -1499,6 +1511,7 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, source = item->hide_transition; else source = item->source; + if (!obs_source_audio_pending(source) && (item->visible || transition_active(item->hide_transition))) { @@ -1531,7 +1544,7 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, size_t pos; bool apply_buf; struct obs_source *source; - struct scene_source_mix *source_mix = NULL; + struct scene_source_mix *source_mix; if (item->visible && transition_active(item->show_transition)) source = item->show_transition; @@ -1569,23 +1582,48 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, continue; } - for (size_t i = 0; i < scene->mix_sources.num; i++) { - struct scene_source_mix *mix = - &scene->mix_sources.array[i]; - if (mix->source == item->source) { - source_mix = mix; - break; + size_t count = AUDIO_OUTPUT_FRAMES - pos; + + /* Update buf so that parent mute state applies to all current + * scene items as well */ + if (parent_buf && + (!apply_buf || + memcmp(buf, parent_buf, sizeof(float) * count) != 0)) { + for (size_t i = 0; i < count; i++) { + if (!apply_buf) { + buf[i] = parent_buf[i]; + } else { + buf[i] = buf[i] < parent_buf[i] + ? buf[i] + : parent_buf[i]; + } } + + apply_buf = true; } + /* If "source" is a group/scene and has no transition, + * add their items to the current list */ + if (source == item->source && (obs_source_is_group(source) || + obs_source_is_scene(source))) { + scene_audio_render_internal(source->context.data, + mix_scene, NULL, NULL, 0, 0, + sample_rate, + apply_buf ? buf : NULL); + item = item->next; + continue; + } + + source_mix = get_source_mix(mix_scene, item->source); + if (!source_mix) { - source_mix = da_push_back_new(scene->mix_sources); + source_mix = da_push_back_new(mix_scene->mix_sources); source_mix->source = item->source; source_mix->transition = source != item->source ? source : NULL; source_mix->apply_buf = apply_buf; source_mix->pos = pos; - source_mix->count = AUDIO_OUTPUT_FRAMES - pos; + source_mix->count = count; if (apply_buf) { memcpy(source_mix->buf, buf, sizeof(float) * source_mix->count); @@ -1614,6 +1652,11 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, item = item->next; } + if (!audio_output) { + audio_unlock(scene); + return true; + } + for (size_t i = 0; i < scene->mix_sources.num; i++) { struct scene_source_mix *source_mix = &scene->mix_sources.array[i]; @@ -1650,6 +1693,16 @@ static bool scene_audio_render(void *data, uint64_t *ts_out, return true; } +static bool scene_audio_render(void *data, uint64_t *ts_out, + struct obs_source_audio_mix *audio_output, + uint32_t mixers, size_t channels, + size_t sample_rate) +{ + struct obs_scene *scene = data; + return scene_audio_render_internal(scene, NULL, ts_out, audio_output, + mixers, channels, sample_rate, NULL); +} + enum gs_color_space scene_video_get_color_space(void *data, size_t count, const enum gs_color_space *preferred_spaces) From 27fa9b1eedbed06753b8d1e090aa760987ff3e46 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 17 Apr 2024 13:20:24 +0200 Subject: [PATCH 0121/1073] UI: Give unnamed settings labels relevant names --- UI/forms/OBSBasicSettings.ui | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index da5591e1e6dc09..cf0e4fbd5114f8 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1746,7 +1746,7 @@ 9 - + 170 @@ -1778,7 +1778,7 @@ - + Basic.Settings.Output.AudioBitrate @@ -1889,7 +1889,7 @@ - + Basic.Settings.Output.CustomEncoderSettings @@ -2537,7 +2537,7 @@ 8 - + 170 @@ -4245,7 +4245,7 @@ - + 170 @@ -4297,7 +4297,7 @@ 9 - + 170 @@ -4437,7 +4437,7 @@ 9 - + 170 @@ -4577,7 +4577,7 @@ 9 - + 170 @@ -4717,7 +4717,7 @@ 9 - + 170 @@ -4857,7 +4857,7 @@ 9 - + 170 @@ -8251,7 +8251,7 @@ simpleOutAdvanced toggled(bool) - label_23 + simpleOutCustomLabel setVisible(bool) From 132f3f3d690d30c740602c89734dc77d874b1c7a Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Tue, 21 May 2024 23:40:53 -0400 Subject: [PATCH 0122/1073] UI: Adjust Classic theme mixer button styling --- UI/data/themes/Yami_Classic.ovt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index 608fd6b8c25c7d..828bec6efa26b3 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -201,16 +201,38 @@ QPushButton[toolButton="true"] { icon-size: var(--icon_base), var(--icon_base); } +#stackedMixerArea QPushButton:!hover { + background-color: var(--bg_base); + border: none; +} + +#stackedMixerArea QPushButton:hover { + background-color: var(--bg_base); + border: none; +} + MuteCheckBox::indicator, MuteCheckBox::indicator:unchecked { + background-color: var(--bg_base); + border: none; width: var(--icon_base_mixer); height: var(--icon_base_mixer); icon-size: var(--icon_base_mixer), var(--icon_base_mixer); } +MuteCheckBox::indicator:checked { + background-color: var(--bg_base); +} + +MuteCheckBox::indicator:checked:hover, +MuteCheckBox::indicator:unchecked:hover { + background-color: var(--bg_base); +} + MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { icon-size: var(--icon_base_mixer), var(--icon_base_mixer); + border: none; } #contextContainer { From e79fea301d842171b14e29c8951ae2868b9efc38 Mon Sep 17 00:00:00 2001 From: Jeremy Woertink Date: Tue, 21 May 2024 15:09:15 -0700 Subject: [PATCH 0123/1073] rtmp-services: Update Joystick.TV servers and recommended settings --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 95877f3f37a0b2..c92a215456c487 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 252, + "version": 253, "files": [ { "name": "services.json", - "version": 252 + "version": 253 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 90145baee9cd01..cc56182271f08f 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2523,21 +2523,26 @@ }, { "name": "Joystick.TV", - "more_info_link": "https://support.joystick.tv/support/creator-support/setting-up-your-stream/", + "more_info_link": "https://support.joystick.tv/live_streaming/", "stream_key_link": "https://joystick.tv/stream-settings", "servers": [ { - "name": "RTMP", + "name": "North America", "url": "rtmp://live.joystick.tv/live/" + }, + { + "name": "Europe", + "url": "rtmp://eu.live.joystick.tv/live/" } ], "recommended": { "keyint": 2, "max video bitrate": 7500, "max audio bitrate": 192, + "max fps": 60, "profile": "main", "bframes": 0, - "x264opts": "tune=zerolatency" + "x264opts": "tune=zerolatency scenecut=0" }, "supported video codecs": [ "h264" From aba600cec09015f09035c5eef5f40ae756f04755 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sun, 28 Apr 2024 12:57:41 -0400 Subject: [PATCH 0124/1073] UI: Move VolumeSlider widget to volume control --- UI/obs-app.cpp | 2 +- UI/slider-ignorewheel.cpp | 74 --------------------------------------- UI/slider-ignorewheel.hpp | 33 ----------------- UI/volume-control.cpp | 74 +++++++++++++++++++++++++++++++++++++++ UI/volume-control.hpp | 38 ++++++++++++++++++-- 5 files changed, 111 insertions(+), 110 deletions(-) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 781838e5e17179..7e9bac2b57e71f 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -42,7 +42,7 @@ #include "obs-app.hpp" #include "obs-proxy-style.hpp" #include "log-viewer.hpp" -#include "slider-ignorewheel.hpp" +#include "volume-control.hpp" #include "window-basic-main.hpp" #ifdef __APPLE__ #include "window-permissions.hpp" diff --git a/UI/slider-ignorewheel.cpp b/UI/slider-ignorewheel.cpp index 8ce293f3c1e24a..69f595405657b0 100644 --- a/UI/slider-ignorewheel.cpp +++ b/UI/slider-ignorewheel.cpp @@ -21,80 +21,6 @@ void SliderIgnoreScroll::wheelEvent(QWheelEvent *event) QSlider::wheelEvent(event); } -VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) - : SliderIgnoreScroll(parent) -{ - fad = fader; -} - -VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, - QWidget *parent) - : SliderIgnoreScroll(orientation, parent) -{ - fad = fader; -} - -VolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w) - : QAccessibleWidget(w) -{ -} - -VolumeSlider *VolumeAccessibleInterface::slider() const -{ - return qobject_cast(object()); -} - -QString VolumeAccessibleInterface::text(QAccessible::Text t) const -{ - if (slider()->isVisible()) { - switch (t) { - case QAccessible::Text::Value: - return currentValue().toString(); - default: - break; - } - } - return QAccessibleWidget::text(t); -} - -QVariant VolumeAccessibleInterface::currentValue() const -{ - QString text; - float db = obs_fader_get_db(slider()->fad); - - if (db < -96.0f) - text = "-inf dB"; - else - text = QString::number(db, 'f', 1).append(" dB"); - - return text; -} - -void VolumeAccessibleInterface::setCurrentValue(const QVariant &value) -{ - slider()->setValue(value.toInt()); -} - -QVariant VolumeAccessibleInterface::maximumValue() const -{ - return slider()->maximum(); -} - -QVariant VolumeAccessibleInterface::minimumValue() const -{ - return slider()->minimum(); -} - -QVariant VolumeAccessibleInterface::minimumStepSize() const -{ - return slider()->singleStep(); -} - -QAccessible::Role VolumeAccessibleInterface::role() const -{ - return QAccessible::Role::Slider; -} - void SliderIgnoreClick::mousePressEvent(QMouseEvent *event) { QStyleOptionSlider styleOption; diff --git a/UI/slider-ignorewheel.hpp b/UI/slider-ignorewheel.hpp index 2c996ac0f29c5a..24e4056f21230b 100644 --- a/UI/slider-ignorewheel.hpp +++ b/UI/slider-ignorewheel.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include class SliderIgnoreScroll : public QSlider { @@ -19,38 +18,6 @@ class SliderIgnoreScroll : public QSlider { virtual void wheelEvent(QWheelEvent *event) override; }; -class VolumeSlider : public SliderIgnoreScroll { - Q_OBJECT - -public: - obs_fader_t *fad; - - VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr); - VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, - QWidget *parent = nullptr); -}; - -class VolumeAccessibleInterface : public QAccessibleWidget { - -public: - VolumeAccessibleInterface(QWidget *w); - - QVariant currentValue() const; - void setCurrentValue(const QVariant &value); - - QVariant maximumValue() const; - QVariant minimumValue() const; - - QVariant minimumStepSize() const; - -private: - VolumeSlider *slider() const; - -protected: - virtual QAccessible::Role role() const override; - virtual QString text(QAccessible::Text t) const override; -}; - class SliderIgnoreClick : public SliderIgnoreScroll { public: inline SliderIgnoreClick(Qt::Orientation orientation, diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index a900a3fa72e100..755be2723ecae6 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -1518,3 +1518,77 @@ void VolumeMeterTimer::timerEvent(QTimerEvent *) } } } + +VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) + : SliderIgnoreScroll(parent) +{ + fad = fader; +} + +VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, + QWidget *parent) + : SliderIgnoreScroll(orientation, parent) +{ + fad = fader; +} + +VolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w) + : QAccessibleWidget(w) +{ +} + +VolumeSlider *VolumeAccessibleInterface::slider() const +{ + return qobject_cast(object()); +} + +QString VolumeAccessibleInterface::text(QAccessible::Text t) const +{ + if (slider()->isVisible()) { + switch (t) { + case QAccessible::Text::Value: + return currentValue().toString(); + default: + break; + } + } + return QAccessibleWidget::text(t); +} + +QVariant VolumeAccessibleInterface::currentValue() const +{ + QString text; + float db = obs_fader_get_db(slider()->fad); + + if (db < -96.0f) + text = "-inf dB"; + else + text = QString::number(db, 'f', 1).append(" dB"); + + return text; +} + +void VolumeAccessibleInterface::setCurrentValue(const QVariant &value) +{ + slider()->setValue(value.toInt()); +} + +QVariant VolumeAccessibleInterface::maximumValue() const +{ + return slider()->maximum(); +} + +QVariant VolumeAccessibleInterface::minimumValue() const +{ + return slider()->minimum(); +} + +QVariant VolumeAccessibleInterface::minimumStepSize() const +{ + return slider()->singleStep(); +} + +QAccessible::Role VolumeAccessibleInterface::role() const +{ + return QAccessible::Role::Slider; +} diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 5f9a6762fea92d..99271609b1cafc 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include "slider-ignorewheel.hpp" class QPushButton; class VolumeMeterTimer; @@ -270,7 +272,7 @@ class VolumeMeterTimer : public QTimer { }; class QLabel; -class QSlider; +class VolumeSlider; class MuteCheckBox; class OBSSourceLabel; @@ -283,7 +285,7 @@ class VolControl : public QWidget { OBSSourceLabel *nameLabel; QLabel *volLabel; VolumeMeter *volMeter; - QSlider *slider; + VolumeSlider *slider; MuteCheckBox *mute; QPushButton *config = nullptr; float levelTotal; @@ -330,3 +332,35 @@ private slots: void refreshColors(); }; + +class VolumeSlider : public SliderIgnoreScroll { + Q_OBJECT + +public: + obs_fader_t *fad; + + VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr); + VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, + QWidget *parent = nullptr); +}; + +class VolumeAccessibleInterface : public QAccessibleWidget { + +public: + VolumeAccessibleInterface(QWidget *w); + + QVariant currentValue() const; + void setCurrentValue(const QVariant &value); + + QVariant maximumValue() const; + QVariant minimumValue() const; + + QVariant minimumStepSize() const; + +private: + VolumeSlider *slider() const; + +protected: + virtual QAccessible::Role role() const override; + virtual QString text(QAccessible::Text t) const override; +}; From c54393335d5ef7f3422dc8d1cda923a4003fccec Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sun, 28 Apr 2024 13:13:02 -0400 Subject: [PATCH 0125/1073] UI: Rename MediaSlider to AbsoluteSlider --- UI/{media-slider.cpp => absolute-slider.cpp} | 10 ++++---- UI/{media-slider.hpp => absolute-slider.hpp} | 6 ++--- UI/cmake/legacy.cmake | 6 ++--- UI/cmake/ui-elements.cmake | 4 ++-- UI/forms/source-toolbar/media-controls.ui | 6 ++--- UI/media-controls.cpp | 24 ++++++++++---------- UI/media-controls.hpp | 8 +++---- 7 files changed, 32 insertions(+), 32 deletions(-) rename UI/{media-slider.cpp => absolute-slider.cpp} (77%) rename UI/{media-slider.hpp => absolute-slider.hpp} (64%) diff --git a/UI/media-slider.cpp b/UI/absolute-slider.cpp similarity index 77% rename from UI/media-slider.cpp rename to UI/absolute-slider.cpp index 86c31092674617..4582219cf72bad 100644 --- a/UI/media-slider.cpp +++ b/UI/absolute-slider.cpp @@ -1,8 +1,8 @@ #include "slider-absoluteset-style.hpp" -#include "media-slider.hpp" +#include "absolute-slider.hpp" #include -MediaSlider::MediaSlider(QWidget *parent) : SliderIgnoreScroll(parent) +AbsoluteSlider::AbsoluteSlider(QWidget *parent) : SliderIgnoreScroll(parent) { installEventFilter(this); setMouseTracking(true); @@ -20,7 +20,7 @@ MediaSlider::MediaSlider(QWidget *parent) : SliderIgnoreScroll(parent) this->setStyle(style); } -void MediaSlider::mouseMoveEvent(QMouseEvent *event) +void AbsoluteSlider::mouseMoveEvent(QMouseEvent *event) { int val = minimum() + ((maximum() - minimum()) * event->pos().x()) / width(); @@ -30,12 +30,12 @@ void MediaSlider::mouseMoveEvent(QMouseEvent *event) else if (val < minimum()) val = minimum(); - emit mediaSliderHovered(val); + emit absoluteSliderHovered(val); event->accept(); QSlider::mouseMoveEvent(event); } -bool MediaSlider::eventFilter(QObject *obj, QEvent *event) +bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); diff --git a/UI/media-slider.hpp b/UI/absolute-slider.hpp similarity index 64% rename from UI/media-slider.hpp rename to UI/absolute-slider.hpp index e29ea9f344a213..b2055b85ef0690 100644 --- a/UI/media-slider.hpp +++ b/UI/absolute-slider.hpp @@ -3,14 +3,14 @@ #include #include "slider-ignorewheel.hpp" -class MediaSlider : public SliderIgnoreScroll { +class AbsoluteSlider : public SliderIgnoreScroll { Q_OBJECT public: - MediaSlider(QWidget *parent = nullptr); + AbsoluteSlider(QWidget *parent = nullptr); signals: - void mediaSliderHovered(int value); + void absoluteSliderHovered(int value); protected: virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index c59013173a7b7f..1d5480c3550e50 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -160,7 +160,9 @@ target_sources( target_sources( obs - PRIVATE adv-audio-control.cpp + PRIVATE absolute-slider.cpp + absolute-slider.hpp + adv-audio-control.cpp adv-audio-control.hpp audio-encoders.cpp audio-encoders.hpp @@ -184,8 +186,6 @@ target_sources( log-viewer.hpp media-controls.cpp media-controls.hpp - media-slider.cpp - media-slider.hpp menu-button.cpp menu-button.hpp mute-checkbox.hpp diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index 3c2dc437ad0d3d..cc1a4d4503c6e3 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -31,6 +31,8 @@ target_link_libraries(obs-studio PRIVATE OBS::ui-support) target_sources( obs-studio PRIVATE # cmake-format: sortable + absolute-slider.cpp + absolute-slider.hpp adv-audio-control.cpp adv-audio-control.hpp audio-encoders.cpp @@ -50,8 +52,6 @@ target_sources( log-viewer.hpp media-controls.cpp media-controls.hpp - media-slider.cpp - media-slider.hpp menu-button.cpp menu-button.hpp mute-checkbox.hpp diff --git a/UI/forms/source-toolbar/media-controls.ui b/UI/forms/source-toolbar/media-controls.ui index ceac1550b3f917..af38bf252f09dd 100644 --- a/UI/forms/source-toolbar/media-controls.ui +++ b/UI/forms/source-toolbar/media-controls.ui @@ -203,7 +203,7 @@ - + 0 @@ -300,9 +300,9 @@
clickable-label.hpp
- MediaSlider + AbsoluteSlider QSlider -
media-slider.hpp
+
absolute-slider.hpp
diff --git a/UI/media-controls.cpp b/UI/media-controls.cpp index 8f24e98caa7c4e..dadc11d0df2b9b 100644 --- a/UI/media-controls.cpp +++ b/UI/media-controls.cpp @@ -58,14 +58,14 @@ MediaControls::MediaControls(QWidget *parent) &MediaControls::SetSliderPosition); connect(&seekTimer, &QTimer::timeout, this, &MediaControls::SeekTimerCallback); - connect(ui->slider, &MediaSlider::sliderPressed, this, - &MediaControls::MediaSliderClicked); - connect(ui->slider, &MediaSlider::mediaSliderHovered, this, - &MediaControls::MediaSliderHovered); - connect(ui->slider, &MediaSlider::sliderReleased, this, - &MediaControls::MediaSliderReleased); - connect(ui->slider, &MediaSlider::sliderMoved, this, - &MediaControls::MediaSliderMoved); + connect(ui->slider, &AbsoluteSlider::sliderPressed, this, + &MediaControls::AbsoluteSliderClicked); + connect(ui->slider, &AbsoluteSlider::absoluteSliderHovered, this, + &MediaControls::AbsoluteSliderHovered); + connect(ui->slider, &AbsoluteSlider::sliderReleased, this, + &MediaControls::AbsoluteSliderReleased); + connect(ui->slider, &AbsoluteSlider::sliderMoved, this, + &MediaControls::AbsoluteSliderMoved); countDownTimer = config_get_bool(App()->GlobalConfig(), "BasicWindow", "MediaControlsCountdownTimer"); @@ -126,7 +126,7 @@ int64_t MediaControls::GetSliderTime(int val) return seekTo; } -void MediaControls::MediaSliderClicked() +void MediaControls::AbsoluteSliderClicked() { OBSSource source = OBSGetStrongRef(weakSource); if (!source) { @@ -147,7 +147,7 @@ void MediaControls::MediaSliderClicked() seekTimer.start(100); } -void MediaControls::MediaSliderReleased() +void MediaControls::AbsoluteSliderReleased() { OBSSource source = OBSGetStrongRef(weakSource); if (!source) { @@ -170,13 +170,13 @@ void MediaControls::MediaSliderReleased() } } -void MediaControls::MediaSliderHovered(int val) +void MediaControls::AbsoluteSliderHovered(int val) { float seconds = ((float)GetSliderTime(val) / 1000.0f); QToolTip::showText(QCursor::pos(), FormatSeconds((int)seconds), this); } -void MediaControls::MediaSliderMoved(int val) +void MediaControls::AbsoluteSliderMoved(int val) { if (seekTimer.isActive()) { seek = val; diff --git a/UI/media-controls.hpp b/UI/media-controls.hpp index c58254ffe6d7b0..652ddadc4ff270 100644 --- a/UI/media-controls.hpp +++ b/UI/media-controls.hpp @@ -45,10 +45,10 @@ private slots: void on_previousButton_clicked(); void on_durationLabel_clicked(); - void MediaSliderClicked(); - void MediaSliderReleased(); - void MediaSliderHovered(int val); - void MediaSliderMoved(int val); + void AbsoluteSliderClicked(); + void AbsoluteSliderReleased(); + void AbsoluteSliderHovered(int val); + void AbsoluteSliderMoved(int val); void SetSliderPosition(); void SetPlayingState(); void SetPausedState(); From 8dcfae9a390763645d354700f36caef6b8f482c7 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sun, 28 Apr 2024 13:29:02 -0400 Subject: [PATCH 0126/1073] UI: Remove unnecessary ProxyStyle usage --- UI/absolute-slider.cpp | 81 +++++++++++++++++++++++++++------ UI/absolute-slider.hpp | 8 ++++ UI/cmake/legacy.cmake | 2 - UI/cmake/ui-elements.cmake | 2 - UI/slider-absoluteset-style.cpp | 20 -------- UI/slider-absoluteset-style.hpp | 12 ----- UI/volume-control.cpp | 20 ++------ UI/volume-control.hpp | 4 +- UI/window-basic-main.cpp | 16 ------- 9 files changed, 80 insertions(+), 85 deletions(-) delete mode 100644 UI/slider-absoluteset-style.cpp delete mode 100644 UI/slider-absoluteset-style.hpp diff --git a/UI/absolute-slider.cpp b/UI/absolute-slider.cpp index 4582219cf72bad..449e2c4906fff8 100644 --- a/UI/absolute-slider.cpp +++ b/UI/absolute-slider.cpp @@ -1,29 +1,42 @@ -#include "slider-absoluteset-style.hpp" #include "absolute-slider.hpp" -#include AbsoluteSlider::AbsoluteSlider(QWidget *parent) : SliderIgnoreScroll(parent) { installEventFilter(this); setMouseTracking(true); +} - QString styleName = style()->objectName(); - QStyle *style; - style = QStyleFactory::create(styleName); - if (!style) { - style = new SliderAbsoluteSetStyle(); - } else { - style = new SliderAbsoluteSetStyle(style); +AbsoluteSlider::AbsoluteSlider(Qt::Orientation orientation, QWidget *parent) + : SliderIgnoreScroll(orientation, parent) +{ + installEventFilter(this); + setMouseTracking(true); +} + +void AbsoluteSlider::mousePressEvent(QMouseEvent *event) +{ + dragging = (event->buttons() & Qt::LeftButton || + event->buttons() & Qt::MiddleButton); + + if (dragging) { + setSliderDown(true); + setValue(posToRangeValue(event)); + emit AbsoluteSlider::sliderMoved(posToRangeValue(event)); } - style->setParent(this); - this->setStyle(style); + event->accept(); +} + +void AbsoluteSlider::mouseReleaseEvent(QMouseEvent *event) +{ + dragging = false; + setSliderDown(false); + event->accept(); } void AbsoluteSlider::mouseMoveEvent(QMouseEvent *event) { - int val = minimum() + - ((maximum() - minimum()) * event->pos().x()) / width(); + int val = posToRangeValue(event); if (val > maximum()) val = maximum(); @@ -31,8 +44,14 @@ void AbsoluteSlider::mouseMoveEvent(QMouseEvent *event) val = minimum(); emit absoluteSliderHovered(val); - event->accept(); + + if (dragging) { + setValue(posToRangeValue(event)); + emit AbsoluteSlider::sliderMoved(posToRangeValue(event)); + } + QSlider::mouseMoveEvent(event); + event->accept(); } bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event) @@ -51,3 +70,37 @@ bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event) return QSlider::eventFilter(obj, event); } + +int AbsoluteSlider::posToRangeValue(QMouseEvent *event) +{ + QStyleOptionSlider opt; + initStyleOption(&opt); + + int pos; + int sliderMin; + int sliderMax; + int handleLength; + + const QRect groove = style()->subControlRect( + QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this); + const QRect handle = style()->subControlRect( + QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + + if (orientation() == Qt::Horizontal) { + pos = event->pos().x(); + handleLength = handle.width(); + sliderMin = groove.left() + (handleLength / 2); + sliderMax = groove.right() - (handleLength / 2) + 1; + } else { + pos = event->pos().y(); + handleLength = handle.height(); + sliderMin = groove.top() + (handleLength / 2); + sliderMax = groove.bottom() - (handleLength / 2) + 1; + } + + int sliderValue = style()->sliderValueFromPosition( + minimum(), maximum(), pos - sliderMin, sliderMax - sliderMin, + opt.upsideDown); + + return sliderValue; +} diff --git a/UI/absolute-slider.hpp b/UI/absolute-slider.hpp index b2055b85ef0690..a5469932411003 100644 --- a/UI/absolute-slider.hpp +++ b/UI/absolute-slider.hpp @@ -8,11 +8,19 @@ class AbsoluteSlider : public SliderIgnoreScroll { public: AbsoluteSlider(QWidget *parent = nullptr); + AbsoluteSlider(Qt::Orientation orientation, QWidget *parent = nullptr); signals: void absoluteSliderHovered(int value); protected: virtual void mouseMoveEvent(QMouseEvent *event) override; + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual bool eventFilter(QObject *obj, QEvent *event) override; + + int posToRangeValue(QMouseEvent *event); + +private: + bool dragging = false; }; diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 1d5480c3550e50..586b1149f36b3c 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -201,8 +201,6 @@ target_sources( scene-tree.cpp scene-tree.hpp screenshot-obj.hpp - slider-absoluteset-style.cpp - slider-absoluteset-style.hpp slider-ignorewheel.cpp slider-ignorewheel.hpp source-label.cpp diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index cc1a4d4503c6e3..4b4ccb66e1f27f 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -62,8 +62,6 @@ target_sources( scene-tree.cpp scene-tree.hpp screenshot-obj.hpp - slider-absoluteset-style.cpp - slider-absoluteset-style.hpp source-label.cpp source-label.hpp source-tree.cpp diff --git a/UI/slider-absoluteset-style.cpp b/UI/slider-absoluteset-style.cpp deleted file mode 100644 index 79fd14e1db76a5..00000000000000 --- a/UI/slider-absoluteset-style.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "slider-absoluteset-style.hpp" - -SliderAbsoluteSetStyle::SliderAbsoluteSetStyle(const QString &baseStyle) - : QProxyStyle(baseStyle) -{ -} -SliderAbsoluteSetStyle::SliderAbsoluteSetStyle(QStyle *baseStyle) - : QProxyStyle(baseStyle) -{ -} - -int SliderAbsoluteSetStyle::styleHint(QStyle::StyleHint hint, - const QStyleOption *option = 0, - const QWidget *widget = 0, - QStyleHintReturn *returnData = 0) const -{ - if (hint == QStyle::SH_Slider_AbsoluteSetButtons) - return (Qt::LeftButton | Qt::MiddleButton); - return QProxyStyle::styleHint(hint, option, widget, returnData); -} diff --git a/UI/slider-absoluteset-style.hpp b/UI/slider-absoluteset-style.hpp deleted file mode 100644 index 63b4e680cc1d67..00000000000000 --- a/UI/slider-absoluteset-style.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -class SliderAbsoluteSetStyle : public QProxyStyle { -public: - SliderAbsoluteSetStyle(const QString &baseStyle); - SliderAbsoluteSetStyle(QStyle *baseStyle = Q_NULLPTR); - int styleHint(QStyle::StyleHint hint, const QStyleOption *option, - const QWidget *widget, - QStyleHintReturn *returnData) const; -}; diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 755be2723ecae6..a8d29f3ca18951 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -3,15 +3,13 @@ #include "qt-wrappers.hpp" #include "obs-app.hpp" #include "mute-checkbox.hpp" -#include "slider-ignorewheel.hpp" -#include "slider-absoluteset-style.hpp" +#include "absolute-slider.hpp" #include "source-label.hpp" #include #include #include #include #include -#include using namespace std; @@ -386,18 +384,6 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) obs_fader_attach_source(obs_fader, source); obs_volmeter_attach_source(obs_volmeter, source); - QString styleName = slider->style()->objectName(); - QStyle *style; - style = QStyleFactory::create(styleName); - if (!style) { - style = new SliderAbsoluteSetStyle(); - } else { - style = new SliderAbsoluteSetStyle(style); - } - - style->setParent(slider); - slider->setStyle(style); - /* Call volume changed once to init the slider position and label */ VolumeChanged(); } @@ -1520,14 +1506,14 @@ void VolumeMeterTimer::timerEvent(QTimerEvent *) } VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) - : SliderIgnoreScroll(parent) + : AbsoluteSlider(parent) { fad = fader; } VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent) - : SliderIgnoreScroll(orientation, parent) + : AbsoluteSlider(orientation, parent) { fad = fader; } diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 99271609b1cafc..5a3efd5de2bf7d 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -8,7 +8,7 @@ #include #include #include -#include "slider-ignorewheel.hpp" +#include "absolute-slider.hpp" class QPushButton; class VolumeMeterTimer; @@ -333,7 +333,7 @@ private slots: void refreshColors(); }; -class VolumeSlider : public SliderIgnoreScroll { +class VolumeSlider : public AbsoluteSlider { Q_OBJECT public: diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 591a91e48b5e0d..9ac1d2606fd139 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -332,9 +332,6 @@ OBSBasic::OBSBasic(QWidget *parent) ui->setupUi(this); ui->previewDisabledWidget->setVisible(false); - QStyle *contextBarStyle = new OBSContextBarProxyStyle(); - contextBarStyle->setParent(ui->contextContainer); - ui->contextContainer->setStyle(contextBarStyle); ui->broadcastButton->setVisible(false); startingDockLayout = saveState(); @@ -11142,19 +11139,6 @@ float OBSBasic::GetDevicePixelRatio() void OBSBasic::ThemeChanged() { - /* Since volume/media sliders are using QProxyStyle, they are not - * updated when themes are changed, so re-initialize them. */ - vector sources; - for (size_t i = 0; i != volumes.size(); i++) - sources.emplace_back(volumes[i]->GetSource()); - - ClearVolumeControls(); - - for (const auto &source : sources) - ActivateAudioSource(source); - - UpdateContextBar(true); - if (api) api->on_event(OBS_FRONTEND_EVENT_THEME_CHANGED); } From c419465137068540ead88828c206ab4626013944 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 17 May 2024 22:14:16 +0200 Subject: [PATCH 0127/1073] librtmp: Fix incorrect usage of type 3 chunks Per [1] type 3 chunks/RTMP_PACKET_SIZE_MINIMUM always use the previously sent (delta) timestamp as their _delta_ timestamp, so we need to inspect whatever was previously sent, rather than just looking at the previous packet's absolute timestamp. I.e., type 3 chunks are only permissible in this case if the previously encoded (delta) timestamp equals the current delta timestamp. [1] https://rtmp.veriskope.com/docs/spec/#53124-type-3 --- plugins/obs-outputs/librtmp/rtmp.c | 14 ++++++++++++-- plugins/obs-outputs/librtmp/rtmp.h | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/obs-outputs/librtmp/rtmp.c b/plugins/obs-outputs/librtmp/rtmp.c index 014a594d7fd949..dd3acaf5946412 100644 --- a/plugins/obs-outputs/librtmp/rtmp.c +++ b/plugins/obs-outputs/librtmp/rtmp.c @@ -4123,8 +4123,17 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue) && packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM) packet->m_headerType = RTMP_PACKET_SIZE_SMALL; - if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp - && packet->m_headerType == RTMP_PACKET_SIZE_SMALL) + /* Per https://rtmp.veriskope.com/docs/spec/#53124-type-3 type 3 chunks/RTMP_PACKET_SIZE_MINIMUM + * always use the previously sent (delta) timestamp as their _delta_ timestamp, so we need to inspect + * whatever was previously sent, rather than just looking at the previous packet's absolute timestamp. + * + * The type 3 chunks/RTMP_PACKET_SIZE_MINIMUM packets produced here specify the beginning of a new + * message as opposed to message continuation type 3 chunks that are handled in the loop further down + * in this function. + */ + uint32_t delta = packet->m_nTimeStamp - prevPacket->m_nTimeStamp; + if (delta == prevPacket->m_nLastWireTimeStamp + && packet->m_headerType == RTMP_PACKET_SIZE_SMALL) packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM; last = prevPacket->m_nTimeStamp; } @@ -4140,6 +4149,7 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue) hSize = nSize; cSize = 0; t = packet->m_nTimeStamp - last; + packet->m_nLastWireTimeStamp = t; if (packet->m_body) { diff --git a/plugins/obs-outputs/librtmp/rtmp.h b/plugins/obs-outputs/librtmp/rtmp.h index d1cddd412c3c25..c1b8d906175111 100644 --- a/plugins/obs-outputs/librtmp/rtmp.h +++ b/plugins/obs-outputs/librtmp/rtmp.h @@ -256,6 +256,7 @@ extern "C" uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */ int m_nChannel; uint32_t m_nTimeStamp; /* timestamp */ + uint32_t m_nLastWireTimeStamp; /* timestamp that was encoded when sending */ int32_t m_nInfoField2; /* last 4 bytes in a long header */ uint32_t m_nBodySize; uint32_t m_nBytesRead; From 82193970f408ae719023e3edc736c45cbd268bc1 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Tue, 12 Mar 2024 14:26:59 +0100 Subject: [PATCH 0128/1073] obs-outputs: Add eFLV multitrack audio support --- plugins/obs-outputs/flv-mux.c | 97 ++++++++++++++++++++++++++++++++ plugins/obs-outputs/flv-mux.h | 18 ++++++ plugins/obs-outputs/flv-output.c | 50 +++++++++++++++- 3 files changed, 162 insertions(+), 3 deletions(-) diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c index 7a2f140848cd5b..9e18d5ef7c1c2d 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -31,6 +31,7 @@ #define AUDIODATA_AAC 10.0 +#define AUDIO_FRAMETYPE_OFFSET 4 #define VIDEO_FRAMETYPE_OFFSET 4 enum video_frametype_t { @@ -39,6 +40,14 @@ enum video_frametype_t { }; // Y2023 spec +const uint8_t AUDIO_HEADER_EX = 9 << AUDIO_FRAMETYPE_OFFSET; +enum audio_packet_type_t { + AUDIO_PACKETTYPE_SEQ_START = 0, + AUDIO_PACKETTYPE_FRAMES = 1, + AUDIO_PACKETTYPE_MULTICHANNEL_CONFIG = 4, + AUDIO_PACKETTYPE_MULTITRACK = 5, +}; + const uint8_t FRAME_HEADER_EX = 8 << VIDEO_FRAMETYPE_OFFSET; enum packet_type_t { PACKETTYPE_SEQ_START = 0, @@ -63,6 +72,22 @@ enum datatype_t { DATA_TYPE_OBJECT_END = 9, }; +static void s_wa4cc(struct serializer *s, enum audio_id_t id) +{ + switch (id) { + case AUDIO_CODEC_NONE: + assert(0 && "Tried to serialize AUDIO_CODEC_NONE"); + break; + + case AUDIO_CODEC_AAC: + s_w8(s, 'm'); + s_w8(s, 'p'); + s_w8(s, '4'); + s_w8(s, 'a'); + break; + } +} + static void s_w4cc(struct serializer *s, enum video_id_t id) { switch (id) { @@ -376,6 +401,62 @@ void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset, *size = data.bytes.num; } +void flv_packet_audio_ex(struct encoder_packet *packet, + enum audio_id_t codec_id, int32_t dts_offset, + uint8_t **output, size_t *size, int type, size_t idx) +{ + struct array_output_data data; + struct serializer s; + + array_output_serializer_init(&s, &data); + + assert(packet->type == OBS_ENCODER_AUDIO); + + int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset; + + bool is_multitrack = idx > 0; + + if (!packet->data || !packet->size) + return; + + int header_metadata_size = 5; // w8+wa4cc + if (is_multitrack) + header_metadata_size += 2; // w8 + w8 + + s_w8(&s, RTMP_PACKET_TYPE_AUDIO); + +#ifdef DEBUG_TIMESTAMPS + blog(LOG_DEBUG, "Audio: %lu", time_ms); + + if (last_time > time_ms) + blog(LOG_DEBUG, "Non-monotonic"); + + last_time = time_ms; +#endif + + s_wb24(&s, (uint32_t)packet->size + header_metadata_size); + s_wb24(&s, (uint32_t)time_ms); + s_w8(&s, (time_ms >> 24) & 0x7F); + s_wb24(&s, 0); + + s_w8(&s, AUDIO_HEADER_EX | + (is_multitrack ? AUDIO_PACKETTYPE_MULTITRACK : type)); + if (is_multitrack) { + s_w8(&s, MULTITRACKTYPE_ONE_TRACK | type); + s_wa4cc(&s, codec_id); + s_w8(&s, (uint8_t)idx); + } else { + s_wa4cc(&s, codec_id); + } + + s_write(&s, packet->data, packet->size); + + write_previous_tag_size(&s); + + *output = data.bytes.array; + *size = data.bytes.num; +} + // Y2023 spec void flv_packet_ex(struct encoder_packet *packet, enum video_id_t codec_id, int32_t dts_offset, uint8_t **output, size_t *size, int type, @@ -466,6 +547,22 @@ void flv_packet_end(struct encoder_packet *packet, enum video_id_t codec, flv_packet_ex(packet, codec, 0, output, size, PACKETTYPE_SEQ_END, idx); } +void flv_packet_audio_start(struct encoder_packet *packet, + enum audio_id_t codec, uint8_t **output, + size_t *size, size_t idx) +{ + flv_packet_audio_ex(packet, codec, 0, output, size, + AUDIO_PACKETTYPE_SEQ_START, idx); +} + +void flv_packet_audio_frames(struct encoder_packet *packet, + enum audio_id_t codec, int32_t dts_offset, + uint8_t **output, size_t *size, size_t idx) +{ + flv_packet_audio_ex(packet, codec, dts_offset, output, size, + AUDIO_PACKETTYPE_FRAMES, idx); +} + void flv_packet_metadata(enum video_id_t codec_id, uint8_t **output, size_t *size, int bits_per_raw_sample, uint8_t color_primaries, int color_trc, diff --git a/plugins/obs-outputs/flv-mux.h b/plugins/obs-outputs/flv-mux.h index abebda6b1a0089..94c830ebfa15ed 100644 --- a/plugins/obs-outputs/flv-mux.h +++ b/plugins/obs-outputs/flv-mux.h @@ -21,6 +21,11 @@ #define MILLISECOND_DEN 1000 +enum audio_id_t { + AUDIO_CODEC_NONE = 0, + AUDIO_CODEC_AAC = 1, +}; + enum video_id_t { CODEC_NONE = 0, // not valid in rtmp CODEC_H264 = 1, // legacy & Y2023 spec @@ -28,6 +33,13 @@ enum video_id_t { CODEC_HEVC, }; +static enum audio_id_t to_audio_type(const char *codec) +{ + if (strcmp(codec, "aac") == 0) + return AUDIO_CODEC_AAC; + return AUDIO_CODEC_NONE; +} + static enum video_id_t to_video_type(const char *codec) { if (strcmp(codec, "h264") == 0) @@ -72,3 +84,9 @@ extern void flv_packet_metadata(enum video_id_t codec, uint8_t **output, uint8_t color_primaries, int color_trc, int color_space, int min_luminance, int max_luminance, size_t idx); +extern void flv_packet_audio_start(struct encoder_packet *packet, + enum audio_id_t codec, uint8_t **output, + size_t *size, size_t idx); +extern void flv_packet_audio_frames(struct encoder_packet *packet, + enum audio_id_t codec, int32_t dts_offset, + uint8_t **output, size_t *size, size_t idx); diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 97a1b3d13bfd72..9d2159964a57de 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -46,6 +46,7 @@ struct flv_output { bool sent_headers; int64_t last_packet_ts; + enum audio_id_t audio_codec[MAX_OUTPUT_AUDIO_ENCODERS]; enum video_id_t video_codec[MAX_OUTPUT_VIDEO_ENCODERS]; pthread_mutex_t mutex; @@ -226,6 +227,29 @@ static int write_packet_ex(struct flv_output *stream, return ret; } +static int write_audio_packet_ex(struct flv_output *stream, + struct encoder_packet *packet, bool is_header, + size_t idx) +{ + uint8_t *data; + size_t size = 0; + int ret = 0; + + if (is_header) { + flv_packet_audio_start(packet, stream->audio_codec[idx], &data, + &size, idx); + } else { + flv_packet_audio_frames(packet, stream->audio_codec[idx], + stream->start_dts_offset, &data, &size, + idx); + } + + fwrite(data, 1, size, stream->file); + bfree(data); + + return ret; +} + static void write_meta_data(struct flv_output *stream) { uint8_t *meta_data; @@ -247,8 +271,13 @@ static bool write_audio_header(struct flv_output *stream, size_t idx) if (!aencoder) return false; - if (obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size)) - write_packet(stream, &packet, true); + if (obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size)) { + if (idx == 0) { + write_packet(stream, &packet, true); + } else { + write_audio_packet_ex(stream, &packet, true, idx); + } + } return true; } @@ -394,6 +423,16 @@ static bool write_video_metadata(struct flv_output *stream, size_t idx) static void write_headers(struct flv_output *stream) { + for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { + obs_encoder_t *enc = + obs_output_get_audio_encoder(stream->output, i); + if (!enc) + break; + + const char *codec = obs_encoder_get_codec(enc); + stream->audio_codec[i] = to_audio_type(codec); + } + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { obs_encoder_t *enc = obs_output_get_video_encoder2(stream->output, i); @@ -577,7 +616,12 @@ static void flv_output_data(void *data, struct encoder_packet *packet) } obs_encoder_packet_release(&parsed_packet); } else { - write_packet(stream, packet, false); + if (packet->track_idx != 0) { + write_audio_packet_ex(stream, packet, false, + packet->track_idx); + } else { + write_packet(stream, packet, false); + } } unlock: From 98ab3663d66398e173a64458c739a16230a9218e Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Mon, 25 Mar 2024 17:47:37 +0100 Subject: [PATCH 0129/1073] obs-outputs: Add eRTMP multitrack audio support --- plugins/obs-outputs/rtmp-stream.c | 58 ++++++++++++++++++++++++++++--- plugins/obs-outputs/rtmp-stream.h | 1 + 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 8903406ffda9b5..42fc671997264e 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -480,6 +480,37 @@ static int send_packet_ex(struct rtmp_stream *stream, return ret; } +static int send_audio_packet_ex(struct rtmp_stream *stream, + struct encoder_packet *packet, bool is_header, + size_t idx) +{ + uint8_t *data; + size_t size = 0; + int ret = 0; + + if (handle_socket_read(stream)) + return -1; + + if (is_header) { + flv_packet_audio_start(packet, stream->audio_codec[idx], &data, + &size, idx); + } else { + flv_packet_audio_frames(packet, stream->audio_codec[idx], + stream->start_dts_offset, &data, &size, + idx); + } + + ret = RTMP_Write(&stream->rtmp, (char *)data, (int)size, 0); + bfree(data); + + if (is_header) + bfree(packet->data); + else + obs_encoder_packet_release(packet); + + return ret; +} + static inline bool send_headers(struct rtmp_stream *stream); static inline bool send_footers(struct rtmp_stream *stream); @@ -676,6 +707,10 @@ static void *send_thread(void *data) packet.track_idx != 0))) { sent = send_packet_ex(stream, &packet, false, false, packet.track_idx); + } else if (packet.type == OBS_ENCODER_AUDIO && + packet.track_idx != 0) { + sent = send_audio_packet_ex(stream, &packet, false, + packet.track_idx); } else { sent = send_packet(stream, &packet, false, packet.track_idx); @@ -791,10 +826,16 @@ static bool send_audio_header(struct rtmp_stream *stream, size_t idx, return true; } - if (!obs_encoder_get_extra_data(aencoder, &header, &packet.size)) - return false; - packet.data = bmemdup(header, packet.size); - return send_packet(stream, &packet, true, idx) >= 0; + if (obs_encoder_get_extra_data(aencoder, &header, &packet.size)) { + packet.data = bmemdup(header, packet.size); + if (idx == 0) { + return send_packet(stream, &packet, true, idx) >= 0; + } else { + return send_audio_packet_ex(stream, &packet, true, + idx) >= 0; + } + } + return false; } static bool send_video_header(struct rtmp_stream *stream, size_t idx) @@ -1334,6 +1375,15 @@ static bool init_connect(struct rtmp_stream *stream) obs_encoder_t *aenc = obs_output_get_audio_encoder(stream->output, 0); obs_data_t *vsettings = obs_encoder_get_settings(venc); obs_data_t *asettings = obs_encoder_get_settings(aenc); + for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { + obs_encoder_t *enc = + obs_output_get_audio_encoder(stream->output, i); + if (enc) { + const char *codec = obs_encoder_get_codec(enc); + stream->audio_codec[i] = to_audio_type(codec); + } + } + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { obs_encoder_t *enc = obs_output_get_video_encoder2(stream->output, i); diff --git a/plugins/obs-outputs/rtmp-stream.h b/plugins/obs-outputs/rtmp-stream.h index 6444faeab50224..580607111a7d1d 100644 --- a/plugins/obs-outputs/rtmp-stream.h +++ b/plugins/obs-outputs/rtmp-stream.h @@ -114,6 +114,7 @@ struct rtmp_stream { long dbr_inc_bitrate; bool dbr_enabled; + enum audio_id_t audio_codec[MAX_OUTPUT_AUDIO_ENCODERS]; enum video_id_t video_codec[MAX_OUTPUT_VIDEO_ENCODERS]; RTMP rtmp; From 36d3290879f9c167543f4263c522d44dcbd954f5 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Mon, 25 Mar 2024 18:13:09 +0100 Subject: [PATCH 0130/1073] obs-outputs: Remove special handling for audio encoders 1 and 2 --- plugins/obs-outputs/rtmp-stream.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 42fc671997264e..3fdcedf2b056a4 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -1158,18 +1158,6 @@ static int init_send(struct rtmp_stream *stream) return OBS_OUTPUT_DISCONNECTED; } - obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 1); - if (aencoder && !send_additional_meta_data(stream)) { - warn("Disconnected while attempting to send additional " - "metadata"); - return OBS_OUTPUT_DISCONNECTED; - } - - if (obs_output_get_audio_encoder(context, 2) != NULL) { - warn("Additional audio streams not supported"); - return OBS_OUTPUT_DISCONNECTED; - } - obs_output_begin_data_capture(stream->output, 0); return OBS_OUTPUT_SUCCESS; From 52660beae2af3b5cbb792823248d52d628dcfdcb Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Mon, 25 Mar 2024 18:13:17 +0100 Subject: [PATCH 0131/1073] obs-outputs: Remove FLV byte array style multitrack audio support --- plugins/obs-outputs/flv-mux.c | 248 ------------------------------ plugins/obs-outputs/flv-mux.h | 6 - plugins/obs-outputs/rtmp-stream.c | 35 +---- 3 files changed, 6 insertions(+), 283 deletions(-) diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c index 9e18d5ef7c1c2d..2b323e6843dd15 100644 --- a/plugins/obs-outputs/flv-mux.c +++ b/plugins/obs-outputs/flv-mux.c @@ -674,251 +674,3 @@ void flv_packet_metadata(enum video_id_t codec_id, uint8_t **output, *output = data.bytes.array; *size = data.bytes.num; } - -/* ------------------------------------------------------------------------- */ -/* stuff for additional media streams */ - -#define s_amf_conststring(s, str) \ - do { \ - const size_t len = sizeof(str) - 1; \ - s_wb16(s, (uint16_t)len); \ - serialize(s, str, len); \ - } while (false) - -#define s_amf_double(s, d) \ - do { \ - double d_val = d; \ - uint64_t u_val = *(uint64_t *)&d_val; \ - s_wb64(s, u_val); \ - } while (false) - -static void flv_build_additional_meta_data(uint8_t **data, size_t *size) -{ - struct array_output_data out; - struct serializer s; - - array_output_serializer_init(&s, &out); - - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "@setDataFrame"); - - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "onExpectAdditionalMedia"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "processingIntents"); - - s_w8(&s, AMF_STRICT_ARRAY); - s_wb32(&s, 1); - { - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "ArchiveProgramNarrationAudio"); - } - - /* ---- */ - - s_amf_conststring(&s, "additionalMedia"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "stream0"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "type"); - - s_w8(&s, AMF_NUMBER); - s_amf_double(&s, RTMP_PACKET_TYPE_AUDIO); - - /* ---- */ - - s_amf_conststring(&s, "mediaLabels"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "contentType"); - - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "PNAR"); - } - s_wb24(&s, AMF_OBJECT_END); - } - s_wb24(&s, AMF_OBJECT_END); - } - s_wb24(&s, AMF_OBJECT_END); - - /* ---- */ - - s_amf_conststring(&s, "defaultMedia"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "audio"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "mediaLabels"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "contentType"); - - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "PRM"); - } - s_wb24(&s, AMF_OBJECT_END); - } - s_wb24(&s, AMF_OBJECT_END); - } - s_wb24(&s, AMF_OBJECT_END); - } - s_wb24(&s, AMF_OBJECT_END); - - *data = out.bytes.array; - *size = out.bytes.num; -} - -void flv_additional_meta_data(obs_output_t *context, uint8_t **data, - size_t *size) -{ - UNUSED_PARAMETER(context); - struct array_output_data out; - struct serializer s; - uint8_t *meta_data = NULL; - size_t meta_data_size; - - flv_build_additional_meta_data(&meta_data, &meta_data_size); - - array_output_serializer_init(&s, &out); - - s_w8(&s, RTMP_PACKET_TYPE_INFO); //18 - - s_wb24(&s, (uint32_t)meta_data_size); - s_wb32(&s, 0); - s_wb24(&s, 0); - - s_write(&s, meta_data, meta_data_size); - bfree(meta_data); - - write_previous_tag_size(&s); - - *data = out.bytes.array; - *size = out.bytes.num; -} - -static inline void s_u29(struct serializer *s, uint32_t val) -{ - if (val <= 0x7F) { - s_w8(s, val); - } else if (val <= 0x3FFF) { - s_w8(s, 0x80 | (val >> 7)); - s_w8(s, val & 0x7F); - } else if (val <= 0x1FFFFF) { - s_w8(s, 0x80 | (val >> 14)); - s_w8(s, 0x80 | ((val >> 7) & 0x7F)); - s_w8(s, val & 0x7F); - } else { - s_w8(s, 0x80 | (val >> 22)); - s_w8(s, 0x80 | ((val >> 15) & 0x7F)); - s_w8(s, 0x80 | ((val >> 8) & 0x7F)); - s_w8(s, val & 0xFF); - } -} - -static inline void s_u29b_value(struct serializer *s, uint32_t val) -{ - s_u29(s, 1 | ((val & 0xFFFFFFF) << 1)); -} - -static void flv_build_additional_audio(uint8_t **data, size_t *size, - struct encoder_packet *packet, - bool is_header, size_t index) -{ - UNUSED_PARAMETER(index); - struct array_output_data out; - struct serializer s; - - array_output_serializer_init(&s, &out); - - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "additionalMedia"); - - s_w8(&s, AMF_OBJECT); - { - s_amf_conststring(&s, "id"); - - s_w8(&s, AMF_STRING); - s_amf_conststring(&s, "stream0"); - - /* ----- */ - - s_amf_conststring(&s, "media"); - - s_w8(&s, AMF_AVMPLUS); - s_w8(&s, AMF3_BYTE_ARRAY); - s_u29b_value(&s, (uint32_t)packet->size + 2); - s_w8(&s, 0xaf); - s_w8(&s, is_header ? 0 : 1); - s_write(&s, packet->data, packet->size); - } - s_wb24(&s, AMF_OBJECT_END); - - *data = out.bytes.array; - *size = out.bytes.num; -} - -static void flv_additional_audio(struct serializer *s, int32_t dts_offset, - struct encoder_packet *packet, bool is_header, - size_t index) -{ - int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset; - uint8_t *data; - size_t size; - - if (!packet->data || !packet->size) - return; - - flv_build_additional_audio(&data, &size, packet, is_header, index); - - s_w8(s, RTMP_PACKET_TYPE_INFO); //18 - -#ifdef DEBUG_TIMESTAMPS - blog(LOG_DEBUG, "Audio2: %lu", time_ms); - - if (last_time > time_ms) - blog(LOG_DEBUG, "Non-monotonic"); - - last_time = time_ms; -#endif - - s_wb24(s, (uint32_t)size); - s_wb24(s, (uint32_t)time_ms); - s_w8(s, (time_ms >> 24) & 0x7F); - s_wb24(s, 0); - - serialize(s, data, size); - bfree(data); - - write_previous_tag_size(s); -} - -void flv_additional_packet_mux(struct encoder_packet *packet, - int32_t dts_offset, uint8_t **data, size_t *size, - bool is_header, size_t index) -{ - struct array_output_data out; - struct serializer s; - - array_output_serializer_init(&s, &out); - - if (packet->type == OBS_ENCODER_VIDEO) { - //currently unsupported - bcrash("who said you could output an additional video packet?"); - } else { - flv_additional_audio(&s, dts_offset, packet, is_header, index); - } - - *data = out.bytes.array; - *size = out.bytes.num; -} diff --git a/plugins/obs-outputs/flv-mux.h b/plugins/obs-outputs/flv-mux.h index 94c830ebfa15ed..e5cab0ddf8227c 100644 --- a/plugins/obs-outputs/flv-mux.h +++ b/plugins/obs-outputs/flv-mux.h @@ -62,14 +62,8 @@ extern void write_file_info(FILE *file, int64_t duration_ms, int64_t size); extern void flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size, bool write_header); -extern void flv_additional_meta_data(obs_output_t *context, uint8_t **output, - size_t *size); extern void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset, uint8_t **output, size_t *size, bool is_header); -extern void flv_additional_packet_mux(struct encoder_packet *packet, - int32_t dts_offset, uint8_t **output, - size_t *size, bool is_header, - size_t index); // Y2023 spec extern void flv_packet_start(struct encoder_packet *packet, enum video_id_t codec, uint8_t **output, diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 3fdcedf2b056a4..aa4f84de63ceec 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -406,25 +406,17 @@ static int handle_socket_read(struct rtmp_stream *stream) } static int send_packet(struct rtmp_stream *stream, - struct encoder_packet *packet, bool is_header, - size_t idx) + struct encoder_packet *packet, bool is_header) { uint8_t *data; size_t size; int ret = 0; - assert(idx < RTMP_MAX_STREAMS); if (handle_socket_read(stream)) return -1; - if (idx > 0) { - flv_additional_packet_mux( - packet, is_header ? 0 : stream->start_dts_offset, &data, - &size, is_header, idx); - } else { - flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset, - &data, &size, is_header); - } + flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset, &data, + &size, is_header); #ifdef TEST_FRAMEDROPS droptest_cap_data_rate(stream, size); @@ -712,8 +704,7 @@ static void *send_thread(void *data) sent = send_audio_packet_ex(stream, &packet, false, packet.track_idx); } else { - sent = send_packet(stream, &packet, false, - packet.track_idx); + sent = send_packet(stream, &packet, false); } if (sent < 0) { @@ -783,20 +774,6 @@ static void *send_thread(void *data) return NULL; } -static bool send_additional_meta_data(struct rtmp_stream *stream) -{ - uint8_t *meta_data; - size_t meta_data_size; - bool success = true; - - flv_additional_meta_data(stream->output, &meta_data, &meta_data_size); - success = RTMP_Write(&stream->rtmp, (char *)meta_data, - (int)meta_data_size, 0) >= 0; - bfree(meta_data); - - return success; -} - static bool send_meta_data(struct rtmp_stream *stream) { uint8_t *meta_data; @@ -829,7 +806,7 @@ static bool send_audio_header(struct rtmp_stream *stream, size_t idx, if (obs_encoder_get_extra_data(aencoder, &header, &packet.size)) { packet.data = bmemdup(header, packet.size); if (idx == 0) { - return send_packet(stream, &packet, true, idx) >= 0; + return send_packet(stream, &packet, true) >= 0; } else { return send_audio_packet_ex(stream, &packet, true, idx) >= 0; @@ -866,7 +843,7 @@ static bool send_video_header(struct rtmp_stream *stream, size_t idx) packet.size = obs_parse_avc_header(&packet.data, header, size); // Always send H.264 on track 0 as old style for compatibility. if (idx == 0) { - return send_packet(stream, &packet, true, idx) >= 0; + return send_packet(stream, &packet, true) >= 0; } else { return send_packet_ex(stream, &packet, true, false, idx) >= 0; From cffdc15aacd63e1bc6d734c941e28761297cc827 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Sat, 18 May 2024 20:56:41 -0400 Subject: [PATCH 0132/1073] UI: Fix Grid Mode not persisting from View Menu When clicking Grid Mode or List Mode from the context menu within the Scenes list, whether or not Grid Mode is enabled persists between OBS sessions. When clicking Grid or List from the View Menu, the setting does not persist between OBS sessions. This seems to be a regression from fc8c42852173ed17729f343f8423901cc97ee351. Previously, SceneTree's SetGridMode would set this config value. If the intent is to not have SetGridMode directly set the config value, then any function that calls SetGridMode other than the OBSBasic constructor must instead set the config value. --- UI/window-basic-main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 9ac1d2606fd139..eebc0c03bbc328 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -5588,11 +5588,14 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) void OBSBasic::on_actionSceneListMode_triggered() { ui->scenes->SetGridMode(false); + config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", + false); } void OBSBasic::on_actionSceneGridMode_triggered() { ui->scenes->SetGridMode(true); + config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", true); } void OBSBasic::GridActionClicked() @@ -9673,6 +9676,9 @@ void OBSBasic::on_resetUI_triggered() ui->toggleStatusBar->setChecked(true); ui->scenes->SetGridMode(false); ui->actionSceneListMode->setChecked(true); + + config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", + false); } void OBSBasic::on_multiviewProjectorWindowed_triggered() From 5eeae2d8b2b513667b0901a03e182c4d52c93a28 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sun, 19 May 2024 04:55:28 -0500 Subject: [PATCH 0133/1073] UI: Change how the status bar gets weak stream output The changes obs_output_get_weak_output to OBSGetWeakRef to be consistent with the other UI code. --- UI/window-basic-status-bar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp index 3526149d9f1a18..bb4255480e1814 100644 --- a/UI/window-basic-status-bar.cpp +++ b/UI/window-basic-status-bar.cpp @@ -518,7 +518,7 @@ void OBSBasicStatusBar::StreamDelayStarting(int sec) return; OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); - streamOutput = obs_output_get_weak_output(output); + streamOutput = OBSGetWeakRef(output); delaySecTotal = delaySecStarting = sec; UpdateDelayMsg(); @@ -533,7 +533,7 @@ void OBSBasicStatusBar::StreamDelayStopping(int sec) void OBSBasicStatusBar::StreamStarted(obs_output_t *output) { - streamOutput = obs_output_get_weak_output(output); + streamOutput = OBSGetWeakRef(output); streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect", OBSOutputReconnect, this); From 8500515d27e96a2c5a9797b8e983346b4825f31f Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Thu, 23 May 2024 14:14:40 -0400 Subject: [PATCH 0134/1073] UI: Adjust appearance of multiview labels --- UI/multiview.cpp | 5 +++-- UI/multiview.hpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/UI/multiview.cpp b/UI/multiview.cpp index dccdc5aa6b21fe..737bbb1c578eff 100644 --- a/UI/multiview.cpp +++ b/UI/multiview.cpp @@ -34,9 +34,9 @@ static OBSSource CreateLabel(const char *name, size_t h) OBSDataAutoRelease font = obs_data_create(); std::string text; - text += " "; + text += " "; text += name; - text += " "; + text += " "; #if defined(_WIN32) obs_data_set_string(font, "face", "Arial"); @@ -51,6 +51,7 @@ static OBSSource CreateLabel(const char *name, size_t h) obs_data_set_obj(settings, "font", font); obs_data_set_string(settings, "text", text.c_str()); obs_data_set_bool(settings, "outline", false); + obs_data_set_int(settings, "opacity", 50); #ifdef _WIN32 const char *text_source_id = "text_gdiplus"; diff --git a/UI/multiview.hpp b/UI/multiview.hpp index 42828fd4e2c1bf..d8dbc05f41a3c1 100644 --- a/UI/multiview.hpp +++ b/UI/multiview.hpp @@ -47,8 +47,8 @@ class Multiview { siScaleY, fw, fh, ratio; // argb colors - static const uint32_t outerColor = 0xFFD0D0D0; - static const uint32_t labelColor = 0xD91F1F1F; + static const uint32_t outerColor = 0xFF999999; + static const uint32_t labelColor = 0x33000000; static const uint32_t backgroundColor = 0xFF000000; static const uint32_t previewColor = 0xFF00D000; static const uint32_t programColor = 0xFFD00000; From 40885b6b1927880b8f159a640975f85266f6eb88 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Thu, 23 May 2024 15:51:11 -0400 Subject: [PATCH 0135/1073] UI: Calculate label position using height and border --- UI/multiview.cpp | 54 +++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/UI/multiview.cpp b/UI/multiview.cpp index 737bbb1c578eff..082a3e23a190f8 100644 --- a/UI/multiview.cpp +++ b/UI/multiview.cpp @@ -34,9 +34,9 @@ static OBSSource CreateLabel(const char *name, size_t h) OBSDataAutoRelease font = obs_data_create(); std::string text; - text += " "; + text += " "; text += name; - text += " "; + text += " "; #if defined(_WIN32) obs_data_set_string(font, "face", "Arial"); @@ -320,7 +320,7 @@ void Multiview::Render(uint32_t cx, uint32_t cy) sourceX = thickness + pvwprgCX / 2; sourceY = thickness; labelX = offset + pvwprgCX / 2; - labelY = pvwprgCY * 0.85f; + labelY = pvwprgCY; if (program) { sourceX += pvwprgCX; labelX += pvwprgCX; @@ -330,27 +330,27 @@ void Multiview::Render(uint32_t cx, uint32_t cy) sourceX = thickness; sourceY = pvwprgCY + thickness; labelX = offset; - labelY = pvwprgCY * 1.85f; + labelY = pvwprgCY * 2; if (program) { sourceY = thickness; - labelY = pvwprgCY * 0.85f; + labelY = pvwprgCY; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: sourceX = pvwprgCX + thickness; sourceY = pvwprgCY + thickness; labelX = pvwprgCX + offset; - labelY = pvwprgCY * 1.85f; + labelY = pvwprgCY * 2; if (program) { sourceY = thickness; - labelY = pvwprgCY * 0.85f; + labelY = pvwprgCY; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: sourceX = thickness; sourceY = pvwprgCY + thickness; labelX = offset; - labelY = pvwprgCY * 1.85f; + labelY = pvwprgCY * 2; if (program) { sourceX += pvwprgCX; labelX += pvwprgCX; @@ -367,7 +367,7 @@ void Multiview::Render(uint32_t cx, uint32_t cy) sourceX = thickness; sourceY = thickness; labelX = offset; - labelY = pvwprgCY * 0.85f; + labelY = pvwprgCY; if (program) { sourceX += pvwprgCX; labelX += pvwprgCX; @@ -444,12 +444,16 @@ void Multiview::Render(uint32_t cx, uint32_t cy) offset = labelOffset(multiviewLayout, label, scenesCX); gs_matrix_push(); - gs_matrix_translate3f(sourceX + offset, - (scenesCY * 0.85f) + sourceY, 0.0f); + gs_matrix_translate3f( + sourceX + offset, + sourceY + scenesCY - + (obs_source_get_height(label) * ppiScaleY) - + (thickness * 3), + 0.0f); gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f); drawBox(obs_source_get_width(label), - obs_source_get_height(label) + int(sourceY * 0.015f), - labelColor); + obs_source_get_height(label) + thicknessx2, labelColor); + gs_matrix_translate3f(0, thickness, 0.0f); obs_source_video_render(label); gs_matrix_pop(); } @@ -499,12 +503,18 @@ void Multiview::Render(uint32_t cx, uint32_t cy) // Draw the Label if (drawLabel) { gs_matrix_push(); - gs_matrix_translate3f(labelX, labelY, 0.0f); + gs_matrix_translate3f( + labelX, + labelY - + (obs_source_get_height(previewLabel) * + ppiScaleY) - + (thickness * 3), + 0.0f); gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f); drawBox(obs_source_get_width(previewLabel), - obs_source_get_height(previewLabel) + - int(pvwprgCX * 0.015f), + obs_source_get_height(previewLabel) + thicknessx2, labelColor); + gs_matrix_translate3f(0, thickness, 0.0f); obs_source_video_render(previewLabel); gs_matrix_pop(); } @@ -532,12 +542,18 @@ void Multiview::Render(uint32_t cx, uint32_t cy) // Draw the Label if (drawLabel) { gs_matrix_push(); - gs_matrix_translate3f(labelX, labelY, 0.0f); + gs_matrix_translate3f( + labelX, + labelY - + (obs_source_get_height(programLabel) * + ppiScaleY) - + (thickness * 3), + 0.0f); gs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f); drawBox(obs_source_get_width(programLabel), - obs_source_get_height(programLabel) + - int(pvwprgCX * 0.015f), + obs_source_get_height(programLabel) + thicknessx2, labelColor); + gs_matrix_translate3f(0, thickness, 0.0f); obs_source_video_render(programLabel); gs_matrix_pop(); } From 76bde59e2222e399de28f6dcea0e7a3982f5f374 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Thu, 23 May 2024 18:16:32 -0400 Subject: [PATCH 0136/1073] UI: Adjust multiview border size --- UI/multiview.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/multiview.hpp b/UI/multiview.hpp index d8dbc05f41a3c1..2fa1d23094dbd9 100644 --- a/UI/multiview.hpp +++ b/UI/multiview.hpp @@ -40,7 +40,7 @@ class Multiview { std::vector multiviewLabels; // Multiview position helpers - float thickness = 4; + float thickness = 6; float offset, thicknessx2 = thickness * 2, pvwprgCX, pvwprgCY, sourceX, sourceY, labelX, labelY, scenesCX, scenesCY, ppiCX, ppiCY, siX, siY, siCX, siCY, ppiScaleX, ppiScaleY, siScaleX, From 34d577d74873168025c63736bf5783fe24ef6e85 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 24 May 2024 10:35:18 +0200 Subject: [PATCH 0137/1073] UI: Enforce completely Fusion Qt style on Linux As of 8dcfae9a390763645d354700f36caef6b8f482c7, indicating the base style to the proxy is completely functional. It also bypasses QT_STYLE_OVERRIDE and -style, but since the system theme is no longer available on Linux this is a non-issue. --- UI/obs-app-theming.cpp | 4 ++++ UI/obs-app.cpp | 6 ------ UI/obs-proxy-style.hpp | 4 ++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 22184ba9b05813..6571c9529fee65 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -930,7 +930,11 @@ static map themeMigrations = { bool OBSApp::InitTheme() { defaultPalette = palette(); +#if !defined(_WIN32) && !defined(__APPLE__) + setStyle(new OBSProxyStyle("Fusion")); +#else setStyle(new OBSProxyStyle()); +#endif /* Set search paths for custom 'theme:' URI prefix */ string searchDir; diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 7e9bac2b57e71f..88ee9c66acdc86 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1987,12 +1987,6 @@ static int run_program(fstream &logFile, int argc, char *argv[]) #endif #if !defined(_WIN32) && !defined(__APPLE__) - /* NOTE: The Breeze Qt style plugin adds frame arround QDockWidget with - * QPainter which can not be modifed. To avoid this the base style is - * enforce to the Qt default style on Linux: Fusion. */ - - setenv("QT_STYLE_OVERRIDE", "Fusion", false); - /* NOTE: Users blindly set this, but this theme is incompatble with Qt6 and * crashes loading saved geometry. Just turn off this theme and let users complain OBS * looks ugly instead of crashing. */ diff --git a/UI/obs-proxy-style.hpp b/UI/obs-proxy-style.hpp index 437ce1e27332ff..325b2abd4e6eab 100644 --- a/UI/obs-proxy-style.hpp +++ b/UI/obs-proxy-style.hpp @@ -4,6 +4,10 @@ class OBSProxyStyle : public QProxyStyle { public: + OBSProxyStyle() : QProxyStyle() {} + + OBSProxyStyle(const QString &key) : QProxyStyle(key) {} + int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const override; From 8184fa10a30b57f4e634305802a34d88fc9f8c53 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Fri, 24 May 2024 19:07:19 +0200 Subject: [PATCH 0138/1073] UI: Inline OBSBasic::ThemeChanged() The majority of this method got removed in a prior commit [1]. Let's inline the rest. [1] 8dcfae9a390763645d354700f36caef6b8f482c7 --- UI/window-basic-main.cpp | 11 ++++------- UI/window-basic-main.hpp | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index eebc0c03bbc328..65f896a52e080d 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -536,7 +536,10 @@ OBSBasic::OBSBasic(QWidget *parent) connect(ui->broadcastButton, &QPushButton::clicked, this, &OBSBasic::BroadcastButtonClicked); - connect(App(), &OBSApp::StyleChanged, this, &OBSBasic::ThemeChanged); + connect(App(), &OBSApp::StyleChanged, this, [this]() { + if (api) + api->on_event(OBS_FRONTEND_EVENT_THEME_CHANGED); + }); QActionGroup *actionGroup = new QActionGroup(this); actionGroup->addAction(ui->actionSceneListMode); @@ -11142,9 +11145,3 @@ float OBSBasic::GetDevicePixelRatio() { return dpi; } - -void OBSBasic::ThemeChanged() -{ - if (api) - api->on_event(OBS_FRONTEND_EVENT_THEME_CHANGED); -} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 7a15f13f990b31..28d18d81b6647c 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -848,7 +848,6 @@ private slots: void TBarReleased(); void LockVolumeControl(bool lock); - void ThemeChanged(); void UpdateVirtualCamConfig(const VCamConfig &config); void RestartVirtualCam(const VCamConfig &config); From 4cf18a9abf93e64819a733332fcbce2f12f9cd3b Mon Sep 17 00:00:00 2001 From: gxalpha Date: Fri, 24 May 2024 23:04:49 +0200 Subject: [PATCH 0139/1073] UI: Add undo/redo to Paste Filters on audio mixer and scenes Copy-Pasting filters on a scene or on a source via the audio mixer context menu would not add an undo/redo action. This commit factors the undo/redo logic out into a generic paste filters function that can be used for pasting filters everywhere. --- UI/window-basic-main.cpp | 47 ++++++++++++++++++++-------------------- UI/window-basic-main.hpp | 6 ++--- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 65f896a52e080d..061ed71d5f3992 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -10107,6 +10107,26 @@ void OBSBasic::on_actionPasteDup_triggered() redo_data); } +void OBSBasic::SourcePasteFilters(OBSSource source, OBSSource dstSource) +{ + if (source == dstSource) + return; + + OBSDataArrayAutoRelease undo_array = + obs_source_backup_filters(dstSource); + obs_source_copy_filters(dstSource, source); + OBSDataArrayAutoRelease redo_array = + obs_source_backup_filters(dstSource); + + const char *srcName = obs_source_get_name(source); + const char *dstName = obs_source_get_name(dstSource); + QString text = + QTStr("Undo.Filters.Paste.Multiple").arg(srcName, dstName); + + CreateFilterPasteUndoRedoAction(text, dstSource, undo_array, + redo_array); +} + void OBSBasic::AudioMixerCopyFilters() { QAction *action = reinterpret_cast(sender()); @@ -10126,10 +10146,7 @@ void OBSBasic::AudioMixerPasteFilters() OBSSourceAutoRelease source = obs_weak_source_get_source(copyFiltersSource); - if (source == dstSource) - return; - - obs_source_copy_filters(dstSource, source); + SourcePasteFilters(source.Get(), dstSource); } void OBSBasic::SceneCopyFilters() @@ -10145,10 +10162,7 @@ void OBSBasic::ScenePasteFilters() OBSSource dstSource = GetCurrentSceneSource(); - if (source == dstSource) - return; - - obs_source_copy_filters(dstSource, source); + SourcePasteFilters(source.Get(), dstSource); } void OBSBasic::on_actionCopyFilters_triggered() @@ -10206,22 +10220,7 @@ void OBSBasic::on_actionPasteFilters_triggered() OBSSceneItem sceneItem = GetCurrentSceneItem(); OBSSource dstSource = obs_sceneitem_get_source(sceneItem); - if (source == dstSource) - return; - - OBSDataArrayAutoRelease undo_array = - obs_source_backup_filters(dstSource); - obs_source_copy_filters(dstSource, source); - OBSDataArrayAutoRelease redo_array = - obs_source_backup_filters(dstSource); - - const char *srcName = obs_source_get_name(source); - const char *dstName = obs_source_get_name(dstSource); - QString text = - QTStr("Undo.Filters.Paste.Multiple").arg(srcName, dstName); - - CreateFilterPasteUndoRedoAction(text, dstSource, undo_array, - redo_array); + SourcePasteFilters(source.Get(), dstSource); } static void ConfirmColor(SourceTree *sources, const QColor &color, diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 28d18d81b6647c..d941c490e60f26 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -803,6 +803,9 @@ private slots: void on_actionCopyFilters_triggered(); void on_actionPasteFilters_triggered(); + void AudioMixerCopyFilters(); + void AudioMixerPasteFilters(); + void SourcePasteFilters(OBSSource source, OBSSource dstSource); void ColorChange(); @@ -810,9 +813,6 @@ private slots: void on_actionShowAbout_triggered(); - void AudioMixerCopyFilters(); - void AudioMixerPasteFilters(); - void EnablePreview(); void DisablePreview(); From a1e0626c14c832c4fff8939691e2b5021e1d362b Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 25 May 2024 13:40:36 +0200 Subject: [PATCH 0140/1073] libobs: Add global source filter add/remove signals Adds global signals for when a filter is added to or removed from a source. These will be helpful for listening for changes to a sources filters without having to attach and detach a signal handler, which would be annoying in the context of for example context menus which change sources rapidly. --- docs/sphinx/reference-core.rst | 8 ++++++++ libobs/obs-source.c | 2 ++ libobs/obs.c | 2 ++ 3 files changed, 12 insertions(+) diff --git a/docs/sphinx/reference-core.rst b/docs/sphinx/reference-core.rst index 3e55dc94e9a655..ba986cfc02a6a3 100644 --- a/docs/sphinx/reference-core.rst +++ b/docs/sphinx/reference-core.rst @@ -702,6 +702,14 @@ Core OBS Signals Called when a source's audio becomes inactive. +**source_filter_add** (ptr source, ptr filter) + + Called when a filter is added to a source. + +**source_filter_remove** (ptr source, ptr filter) + + Called when a filter is removed from a source. + **source_transition_start** (ptr source) Called when a transition has started its transition. diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 01abb87d042cdc..28182d8130e20f 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -3218,6 +3218,7 @@ void obs_source_filter_add(obs_source_t *source, obs_source_t *filter) calldata_set_ptr(&cd, "source", source); calldata_set_ptr(&cd, "filter", filter); + signal_handler_signal(obs->signals, "source_filter_add", &cd); signal_handler_signal(source->context.signals, "filter_add", &cd); blog(LOG_DEBUG, "- filter '%s' (%s) added to source '%s'", @@ -3256,6 +3257,7 @@ static bool obs_source_filter_remove_refless(obs_source_t *source, calldata_set_ptr(&cd, "source", source); calldata_set_ptr(&cd, "filter", filter); + signal_handler_signal(obs->signals, "source_filter_remove", &cd); signal_handler_signal(source->context.signals, "filter_remove", &cd); blog(LOG_DEBUG, "- filter '%s' (%s) removed from source '%s'", diff --git a/libobs/obs.c b/libobs/obs.c index f3713396ef87b8..ea930cc6757b65 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1117,6 +1117,8 @@ static const char *obs_signals[] = { "void source_hide(ptr source)", "void source_audio_activate(ptr source)", "void source_audio_deactivate(ptr source)", + "void source_filter_add(ptr source, ptr filter)", + "void source_filter_remove(ptr source, ptr filter)", "void source_rename(ptr source, string new_name, string prev_name)", "void source_volume(ptr source, in out float volume)", "void source_volume_level(ptr source, float level, float magnitude, " From 175b0cbc83a5e9cda9ae9fc8c939df5c9cd8a3ff Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 25 May 2024 13:43:59 +0200 Subject: [PATCH 0141/1073] UI: Update edit menu on source filter changes Currently, when adding a filter to a source and then right-clicking it or using the menu bar edit menu, it's not possible to copy the source's filters. This is because the edit menu does not update on filter changes. Listening to the new global filters add/remove signal and updating the edit menu will enable the copy option if a filter get added or disable the option if the last filter gets removed. --- UI/window-basic-main.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 061ed71d5f3992..515797990db8bd 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1866,6 +1866,18 @@ void OBSBasic::InitOBSCallbacks() OBSBasic::SourceAudioDeactivated, this); signalHandlers.emplace_back(obs_get_signal_handler(), "source_rename", OBSBasic::SourceRenamed, this); + signalHandlers.emplace_back( + obs_get_signal_handler(), "source_filter_add", + [](void *data, calldata_t *) { + static_cast(data)->UpdateEditMenu(); + }, + this); + signalHandlers.emplace_back( + obs_get_signal_handler(), "source_filter_remove", + [](void *data, calldata_t *) { + static_cast(data)->UpdateEditMenu(); + }, + this); } void OBSBasic::InitPrimitives() From 79632f96dda9783e0e2a5d20b2c14b2fb84c062d Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 25 May 2024 13:59:27 +0200 Subject: [PATCH 0142/1073] libobs: Fix buffer overrun in video_frame_init --- libobs/media-io/video-frame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index 3c074d4c605255..6da6d2a9e00a66 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -196,7 +196,7 @@ void video_frame_init(struct video_frame *frame, enum video_format format, size_t size = 0; uint32_t linesizes[MAX_AV_PLANES]; uint32_t heights[MAX_AV_PLANES]; - size_t offsets[MAX_AV_PLANES - 1]; + size_t offsets[MAX_AV_PLANES]; int alignment = base_get_alignment(); if (!frame) From a92852f96beecfa40d0eee3b3356d08e7fd0c46a Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 25 May 2024 14:01:34 +0200 Subject: [PATCH 0143/1073] mac-avcapture: Improve av_capture_sync_info formatting --- plugins/mac-avcapture/plugin-main.m | 33 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/plugins/mac-avcapture/plugin-main.m b/plugins/mac-avcapture/plugin-main.m index a15821d447650e..8ba7db39ddd5ed 100644 --- a/plugins/mac-avcapture/plugin-main.m +++ b/plugins/mac-avcapture/plugin-main.m @@ -284,23 +284,22 @@ bool obs_module_load(void) obs_register_source(&av_capture_info); - struct obs_source_info av_capture_sync_info = {.id = "macos-avcapture-fast", - .type = OBS_SOURCE_TYPE_INPUT, - .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | - OBS_SOURCE_AUDIO | OBS_SOURCE_SRGB | - OBS_SOURCE_DO_NOT_DUPLICATE, - .create = av_fast_capture_create, - .get_name = av_fast_capture_get_name, - .get_defaults = av_fast_capture_set_defaults, - .get_properties = av_capture_properties, - .update = av_capture_update, - .destroy = av_capture_destroy, - .video_tick = av_fast_capture_tick, - .video_render = av_fast_capture_render, - .get_width = av_fast_capture_get_width, - .get_height = av_fast_capture_get_height, - .icon_type = OBS_ICON_TYPE_CAMERA - + struct obs_source_info av_capture_sync_info = { + .id = "macos-avcapture-fast", + .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_AUDIO | OBS_SOURCE_SRGB | + OBS_SOURCE_DO_NOT_DUPLICATE, + .create = av_fast_capture_create, + .get_name = av_fast_capture_get_name, + .get_defaults = av_fast_capture_set_defaults, + .get_properties = av_capture_properties, + .update = av_capture_update, + .destroy = av_capture_destroy, + .video_tick = av_fast_capture_tick, + .video_render = av_fast_capture_render, + .get_width = av_fast_capture_get_width, + .get_height = av_fast_capture_get_height, + .icon_type = OBS_ICON_TYPE_CAMERA, }; obs_register_source(&av_capture_sync_info); From 94d158c425a0a95586e0549cabb18ed87ba0c6a0 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 19 May 2024 05:42:40 +0200 Subject: [PATCH 0144/1073] libobs: Add functions to serialize JSON with default values --- libobs/obs-data.c | 56 ++++++++++++++++++++++++++++------------------- libobs/obs-data.h | 2 ++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/libobs/obs-data.c b/libobs/obs-data.c index 7c9be118a4229c..be2d07293cb0d7 100644 --- a/libobs/obs-data.c +++ b/libobs/obs-data.c @@ -554,18 +554,18 @@ static inline void set_json_bool(json_t *json, const char *name, json_object_set_new(json, name, val ? json_true() : json_false()); } -static json_t *obs_data_to_json(obs_data_t *data); +static json_t *obs_data_to_json(obs_data_t *data, bool with_defaults); static inline void set_json_obj(json_t *json, const char *name, - obs_data_item_t *item) + obs_data_item_t *item, bool with_defaults) { obs_data_t *obj = obs_data_item_get_obj(item); - json_object_set_new(json, name, obs_data_to_json(obj)); + json_object_set_new(json, name, obs_data_to_json(obj, with_defaults)); obs_data_release(obj); } static inline void set_json_array(json_t *json, const char *name, - obs_data_item_t *item) + obs_data_item_t *item, bool with_defaults) { json_t *jarray = json_array(); obs_data_array_t *array = obs_data_item_get_array(item); @@ -573,7 +573,7 @@ static inline void set_json_array(json_t *json, const char *name, for (size_t idx = 0; idx < count; idx++) { obs_data_t *sub_item = obs_data_array_item(array, idx); - json_t *jitem = obs_data_to_json(sub_item); + json_t *jitem = obs_data_to_json(sub_item, with_defaults); json_array_append_new(jarray, jitem); obs_data_release(sub_item); } @@ -582,7 +582,7 @@ static inline void set_json_array(json_t *json, const char *name, obs_data_array_release(array); } -static json_t *obs_data_to_json(obs_data_t *data) +static json_t *obs_data_to_json(obs_data_t *data, bool with_defaults) { json_t *json = json_object(); @@ -593,7 +593,7 @@ static json_t *obs_data_to_json(obs_data_t *data) enum obs_data_type type = obs_data_item_gettype(item); const char *name = get_item_name(item); - if (!obs_data_item_has_user_value(item)) + if (!with_defaults && !obs_data_item_has_user_value(item)) continue; if (type == OBS_DATA_STRING) @@ -603,9 +603,9 @@ static json_t *obs_data_to_json(obs_data_t *data) else if (type == OBS_DATA_BOOLEAN) set_json_bool(json, name, item); else if (type == OBS_DATA_OBJECT) - set_json_obj(json, name, item); + set_json_obj(json, name, item, with_defaults); else if (type == OBS_DATA_ARRAY) - set_json_array(json, name, item); + set_json_array(json, name, item, with_defaults); } return json; @@ -716,36 +716,48 @@ void obs_data_release(obs_data_t *data) obs_data_destroy(data); } -const char *obs_data_get_json(obs_data_t *data) +static const char *obs_data_get_json_internal(obs_data_t *data, bool pretty, + bool with_defaults) { if (!data) return NULL; + size_t flags = JSON_PRESERVE_ORDER; + + if (pretty) + flags |= JSON_INDENT(4); + else + flags |= JSON_COMPACT; + /* NOTE: don't use libobs bfree for json text */ free(data->json); data->json = NULL; - json_t *root = obs_data_to_json(data); - data->json = json_dumps(root, JSON_PRESERVE_ORDER | JSON_COMPACT); + json_t *root = obs_data_to_json(data, with_defaults); + data->json = json_dumps(root, flags); json_decref(root); return data->json; } -const char *obs_data_get_json_pretty(obs_data_t *data) +const char *obs_data_get_json(obs_data_t *data) { - if (!data) - return NULL; + return obs_data_get_json_internal(data, false, false); +} - /* NOTE: don't use libobs bfree for json text */ - free(data->json); - data->json = NULL; +const char *obs_data_get_json_with_defaults(obs_data_t *data) +{ + return obs_data_get_json_internal(data, false, true); +} - json_t *root = obs_data_to_json(data); - data->json = json_dumps(root, JSON_PRESERVE_ORDER | JSON_INDENT(4)); - json_decref(root); +const char *obs_data_get_json_pretty(obs_data_t *data) +{ + return obs_data_get_json_internal(data, true, false); +} - return data->json; +const char *obs_data_get_json_pretty_with_defaults(obs_data_t *data) +{ + return obs_data_get_json_internal(data, true, true); } const char *obs_data_get_last_json(obs_data_t *data) diff --git a/libobs/obs-data.h b/libobs/obs-data.h index a41856107e626b..540cde67d20f25 100644 --- a/libobs/obs-data.h +++ b/libobs/obs-data.h @@ -70,7 +70,9 @@ EXPORT void obs_data_addref(obs_data_t *data); EXPORT void obs_data_release(obs_data_t *data); EXPORT const char *obs_data_get_json(obs_data_t *data); +EXPORT const char *obs_data_get_json_with_defaults(obs_data_t *data); EXPORT const char *obs_data_get_json_pretty(obs_data_t *data); +EXPORT const char *obs_data_get_json_pretty_with_defaults(obs_data_t *data); EXPORT const char *obs_data_get_last_json(obs_data_t *data); EXPORT bool obs_data_save_json(obs_data_t *data, const char *file); EXPORT bool obs_data_save_json_safe(obs_data_t *data, const char *file, From e54af0d559a9cd66fe7c369b5b4206826f33b7e0 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 26 May 2024 02:00:01 +0200 Subject: [PATCH 0145/1073] docs: Document newer JSON serialization functions --- docs/sphinx/reference-settings.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/sphinx/reference-settings.rst b/docs/sphinx/reference-settings.rst index 5d72ef087edd49..e15be3a2702de5 100644 --- a/docs/sphinx/reference-settings.rst +++ b/docs/sphinx/reference-settings.rst @@ -78,6 +78,30 @@ General Functions --------------------- +.. function:: const char *obs_data_get_json_with_defaults(obs_data_t *data) + + Same as :c:func:`obs_data_get_json()` but default values are also serialized. + + :return: Json string for this object + +--------------------- + +.. function:: const char *obs_data_get_json_pretty(obs_data_t *data) + + Same as :c:func:`obs_data_get_json()` but the JSON data is pretty-printed. + + :return: Json string for this object + +--------------------- + +.. function:: const char *obs_data_get_json_pretty_with_defaults(obs_data_t *data) + + Same as :c:func:`obs_data_get_json_pretty()` but default values are also serialized. + + :return: Json string for this object + +--------------------- + .. function:: const char *obs_data_get_last_json(obs_data_t *data) Returns the last json string generated for this data object. Does not From df2a75fe4b7512ed7ce11f098f999a03c49e00dd Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 26 Apr 2024 10:49:14 +0200 Subject: [PATCH 0146/1073] obs-outputs: Add native MP4 muxer --- plugins/obs-outputs/CMakeLists.txt | 5 + plugins/obs-outputs/data/locale/en-US.ini | 5 + plugins/obs-outputs/mp4-mux-internal.h | 345 +++ plugins/obs-outputs/mp4-mux.c | 2843 +++++++++++++++++++++ plugins/obs-outputs/mp4-mux.h | 43 + plugins/obs-outputs/mp4-output.c | 613 +++++ plugins/obs-outputs/obs-outputs.c | 2 + 7 files changed, 3856 insertions(+) create mode 100644 plugins/obs-outputs/mp4-mux-internal.h create mode 100644 plugins/obs-outputs/mp4-mux.c create mode 100644 plugins/obs-outputs/mp4-mux.h create mode 100644 plugins/obs-outputs/mp4-output.c diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index a7286117212377..576911a0c399c7 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -36,6 +36,10 @@ target_sources( librtmp/rtmp.c librtmp/rtmp.h librtmp/rtmp_sys.h + mp4-mux-internal.h + mp4-mux.c + mp4-mux.h + mp4-output.c net-if.c net-if.h null-output.c @@ -60,6 +64,7 @@ target_link_libraries( obs-outputs PRIVATE OBS::libobs OBS::happy-eyeballs + OBS::opts-parser MbedTLS::MbedTLS ZLIB::ZLIB $<$:OBS::w32-pthreads> diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index 4c759295956d5c..aabfd794526d0a 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -7,6 +7,11 @@ FLVOutput="FLV File Output" FLVOutput.FilePath="File Path" Default="Default" +MP4Output="MP4 File Output" +MP4Output.FilePath="File Path" +MP4Output.StartChapter="Start" +MP4Output.UnnamedChapter="Unnamed" + IPFamily="IP Address Family" IPFamily.Both="IPv4 and IPv6 (Default)" IPFamily.V4Only="IPv4 Only" diff --git a/plugins/obs-outputs/mp4-mux-internal.h b/plugins/obs-outputs/mp4-mux-internal.h new file mode 100644 index 00000000000000..40856df8a2dcd7 --- /dev/null +++ b/plugins/obs-outputs/mp4-mux-internal.h @@ -0,0 +1,345 @@ +/****************************************************************************** + Copyright (C) 2024 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include "mp4-mux.h" + +#include +#include +#include + +/* Flavour for target compatibility */ +enum mp4_flavour { + MP4, /* ISO/IEC 14496-12 */ + MOV, /* Apple QuickTime */ + CMAF, /* ISO/IEC 23000-19 */ +}; + +enum mp4_track_type { + TRACK_UNKNOWN, + TRACK_VIDEO, + TRACK_AUDIO, + TRACK_CHAPTERS, +}; + +enum mp4_codec { + CODEC_UNKNOWN, + + /* Video Codecs */ + CODEC_H264, + CODEC_HEVC, + CODEC_AV1, + + /* Audio Codecs */ + CODEC_AAC, + CODEC_OPUS, + CODEC_FLAC, + CODEC_ALAC, + CODEC_PCM_I16, + CODEC_PCM_I24, + CODEC_PCM_F32, + + /* Text/Chapter trakcs */ + CODEC_TEXT, +}; + +struct chunk { + uint64_t offset; + uint32_t size; + uint32_t samples; +}; + +struct sample_delta { + uint32_t count; + uint32_t delta; +}; + +struct sample_offset { + uint32_t count; + int32_t offset; +}; + +struct fragment_sample { + uint32_t size; + int32_t offset; + uint32_t duration; +}; + +struct mp4_track { + enum mp4_track_type type; + enum mp4_codec codec; + + /* Track ID in container */ + uint8_t track_id; + /* Number of samples for this track */ + uint64_t samples; + /* Duration for this track */ + uint64_t duration; + + /* Encoder associated with this track */ + obs_encoder_t *encoder; + + /* Time Base (1/FPS for video, 1/sample rate for audio) */ + uint32_t timebase_num; + uint32_t timebase_den; + /* Output timescale calculated from time base (Video only) */ + uint32_t timescale; + + /* First PTS this track has seen (in track timescale) */ + int64_t first_pts; + /* Highest PTS this track has seen (in usec) */ + int64_t last_pts_usec; + + /* deque of encoder_packet belonging to this track */ + struct deque packets; + + /* Sample sizes (fixed for PCM) */ + uint32_t sample_size; + DARRAY(uint32_t) sample_sizes; + /* Data chunks in file containing samples for this track */ + DARRAY(struct chunk) chunks; + /* Time delta between samples */ + DARRAY(struct sample_delta) deltas; + + /* Sample CT-DT offset, i.e. DTS-PTS offset (Video only) */ + bool needs_ctts; + int32_t dts_offset; + DARRAY(struct sample_offset) offsets; + /* Sync samples, i.e. keyframes (Video only) */ + DARRAY(uint32_t) sync_samples; + + /* Temporary array with information about the samples to be included + * in the next fragment. */ + DARRAY(struct fragment_sample) fragment_samples; +}; + +struct mp4_mux { + obs_output_t *output; + struct serializer *serializer; + + /* Target format compatibility */ + enum mp4_flavour mode; + + /* Flags */ + enum mp4_mux_flags flags; + + uint32_t fragments_written; + /* PTS where next fragmentation should take place */ + int64_t next_frag_pts; + + /* Creation time (seconds since Jan 1 1904) */ + uint64_t creation_time; + + /* Offset of placeholder atom/box to contain final mdat header */ + size_t placeholder_offset; + + uint8_t track_ctr; + /* Audio/Video tracks */ + DARRAY(struct mp4_track) tracks; + /* Special tracks */ + struct mp4_track *chapter_track; +}; + +/* clang-format off */ +// Defined in ISO/IEC 14496-12:2015 Section 8.2.2.1 +const int32_t UNITY_MATRIX[9] = { + 0x00010000, 0, 0, + 0, 0x00010000, 0, + 0, 0, 0x40000000 +}; +/* clang-format on */ + +enum tfhd_flags { + BASE_DATA_OFFSET_PRESENT = 0x000001, + SAMPLE_DESCRIPTION_INDEX_PRESENT = 0x000002, + DEFAULT_SAMPLE_DURATION_PRESENT = 0x000008, + DEFAULT_SAMPLE_SIZE_PRESENT = 0x000010, + DEFAULT_SAMPLE_FLAGS_PRESENT = 0x000020, + DURATION_IS_EMPTY = 0x010000, + DEFAULT_BASE_IS_MOOF = 0x020000, +}; + +enum trun_flags { + DATA_OFFSET_PRESENT = 0x000001, + FIRST_SAMPLE_FLAGS_PRESENT = 0x000004, + SAMPLE_DURATION_PRESENT = 0x000100, + SAMPLE_SIZE_PRESENT = 0x000200, + SAMPLE_FLAGS_PRESENT = 0x000400, + SAMPLE_COMPOSITION_TIME_OFFSETS_PRESENT = 0x000800, +}; + +/* + * ISO Standard structure (big endian so we can't easily use it): + * + * struct sample_flags { + * uint32_t reserved : 4; + * uint32_t is_leading : 2; + * uint32_t sample_depends_on : 2; + * uint32_t sample_is_depended_on : 2; + * uint32_t sample_has_redundancy : 2; + * uint32_t sample_padding_value : 3; + * uint32_t sample_is_non_sync_sample : 1; + * uint32_t sample_degradation_priority : 16; +}; +*/ + +enum sample_flags { + SAMPLE_FLAG_IS_NON_SYNC = 0x00010000, + SAMPLE_FLAG_DEPENDS_YES = 0x01000000, + SAMPLE_FLAG_DEPENDS_NO = 0x02000000, +}; + +#ifndef _WIN32 +static inline size_t min(size_t a, size_t b) +{ + return a < b ? a : b; +} +#endif + +static inline void get_speaker_positions(enum speaker_layout layout, + uint8_t *arr, uint8_t *size, + uint8_t *iso_layout) +{ + switch (layout) { + case SPEAKERS_MONO: + arr[0] = 2; // FC + *size = 1; + *iso_layout = 1; + break; + case SPEAKERS_UNKNOWN: + case SPEAKERS_STEREO: + arr[0] = 0; // FL + arr[1] = 1; // FR + *size = 2; + *iso_layout = 2; + break; + case SPEAKERS_2POINT1: + arr[0] = 0; // FL + arr[1] = 1; // FR + arr[2] = 3; // LFE + *size = 3; + break; + case SPEAKERS_4POINT0: + arr[0] = 0; // FL + arr[1] = 1; // FR + arr[2] = 2; // FC + arr[3] = 10; // RC + *size = 4; + *iso_layout = 4; + break; + case SPEAKERS_4POINT1: + arr[0] = 0; // FL + arr[1] = 1; // FR + arr[2] = 2; // FC + arr[3] = 3; // LFE + arr[4] = 10; // RC + *size = 5; + break; + case SPEAKERS_5POINT1: + arr[0] = 0; // FL + arr[1] = 1; // FR + arr[2] = 2; // FC + arr[3] = 3; // LFE + arr[4] = 8; // RL + arr[5] = 9; // RR + *size = 6; + break; + case SPEAKERS_7POINT1: + arr[0] = 0; // FL + arr[1] = 1; // FR + arr[2] = 2; // FC + arr[3] = 3; // LFE + arr[4] = 8; // RL + arr[5] = 9; // RR + arr[6] = 13; // SL + arr[7] = 14; // SR + *size = 8; + *iso_layout = 12; + break; + } +} + +static inline void get_colour_information(obs_encoder_t *enc, uint16_t *pri, + uint16_t *trc, uint16_t *spc, + uint8_t *full_range) +{ + video_t *video = obs_encoder_video(enc); + const struct video_output_info *info = video_output_get_info(video); + + *full_range = info->range == VIDEO_RANGE_FULL ? 1 : 0; + + switch (info->colorspace) { + case VIDEO_CS_601: + *pri = 6; // OBSCOL_PRI_SMPTE170M + *trc = 6; + *spc = 6; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + *pri = 1; // OBSCOL_PRI_BT709 + *trc = 1; + *spc = 1; + break; + case VIDEO_CS_SRGB: + *pri = 1; // OBSCOL_PRI_BT709 + *trc = 13; // OBSCOL_TRC_IEC61966_2_1 + *spc = 1; // OBSCOL_PRI_BT709 + break; + case VIDEO_CS_2100_PQ: + *pri = 9; // OBSCOL_PRI_BT2020 + *trc = 16; // OBSCOL_TRC_SMPTE2084 + *spc = 9; // OBSCOL_SPC_BT2020_NCL + break; + case VIDEO_CS_2100_HLG: + *pri = 9; // OBSCOL_PRI_BT2020 + *trc = 18; // OBSCOL_TRC_ARIB_STD_B67 + *spc = 9; // OBSCOL_SPC_BT2020_NCL + } +} + +/* Chapter stubs (from libavformat/movenc.c) */ + +static const uint8_t TEXT_STUB_HEADER[] = { + // TextSampleEntry + 0x00, 0x00, 0x00, 0x01, // displayFlags + 0x00, 0x00, // horizontal + vertical justification + 0x00, 0x00, 0x00, 0x00, // bgColourRed/Green/Blue/Alpha + // BoxRecord + 0x00, 0x00, 0x00, 0x00, // defTextBoxTop/Left + 0x00, 0x00, 0x00, 0x00, // defTextBoxBottom/Right + // StyleRecord + 0x00, 0x00, 0x00, 0x00, // startChar + endChar + 0x00, 0x01, // fontID + 0x00, 0x00, // fontStyleFlags + fontSize + 0x00, 0x00, 0x00, 0x00, // fgColourRed/Green/Blue/Alpha + // FontTableBox + 0x00, 0x00, 0x00, 0x0D, // box size + 'f', 't', 'a', 'b', // box atom name + 0x00, 0x01, // entry count + // FontRecord + 0x00, 0x01, // font ID + 0x00, // font name length +}; + +/* clang-format off */ +static const char CHAPTER_PKT_FOOTER[12] = { + 0x00, 0x00, 0x00, 0x0C, + 'e', 'n', 'c', 'd', + 0x00, 0x00, 0x01, 0x00 +}; +/* clang-format on */ diff --git a/plugins/obs-outputs/mp4-mux.c b/plugins/obs-outputs/mp4-mux.c new file mode 100644 index 00000000000000..3d9d0ed07259a9 --- /dev/null +++ b/plugins/obs-outputs/mp4-mux.c @@ -0,0 +1,2843 @@ +/****************************************************************************** + Copyright (C) 2024 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "mp4-mux-internal.h" + +#include "rtmp-hevc.h" +#include "rtmp-av1.h" + +#include +#include +#include +#include +#include +#include + +#include + +/* + * (Mostly) compliant MP4 muxer for fun and profit. + * Based on ISO/IEC 14496-12 and FFmpeg's libavformat/movenc.c ([L]GPL) + * + * Specification section numbers are noted where applicable. + * Standard identifier is included if not referring to ISO/IEC 14496-12. + */ + +#define do_log(level, format, ...) \ + blog(level, "[mp4 muxer: '%s'] " format, \ + obs_output_get_name(mux->output), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +/* Helper to overwrite placeholder size and return total size. */ +static inline size_t write_box_size(struct serializer *s, int64_t start) +{ + int64_t end = serializer_get_pos(s); + size_t size = end - start; + + serializer_seek(s, start, SERIALIZE_SEEK_START); + s_wb32(s, (uint32_t)size); + serializer_seek(s, end, SERIALIZE_SEEK_START); + + return size; +} + +/// 4.2 Box header with size and char[4] name +static inline void write_box(struct serializer *s, const size_t size, + const char name[4]) +{ + if (size <= UINT32_MAX) { + s_wb32(s, (uint32_t)size); // size + s_write(s, name, 4); // boxtype + } else { + s_wb32(s, 1); // size + s_write(s, name, 4); // boxtype + s_wb64(s, size); // largesize + } +} + +/// 4.2 FullBox extended header with u8 version and u24 flags +static inline void write_fullbox(struct serializer *s, const size_t size, + const char name[4], uint8_t version, + uint32_t flags) +{ + write_box(s, size, name); + s_w8(s, version); + s_wb24(s, flags); +} + +/// 4.3 File Type Box +static size_t mp4_write_ftyp(struct mp4_mux *mux, bool fragmented) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "ftyp"); + + const char *major_brand = "isom"; + /* Following FFmpeg's example, when using negative CTS the major brand + * needs to be either iso4 or iso6 depending on whether the file is + * currently fragmented. */ + if (mux->flags & MP4_USE_NEGATIVE_CTS) + major_brand = fragmented ? "iso6" : "iso4"; + + s_write(s, major_brand, 4); // major brand + s_wb32(s, 512); // minor version + + // minor brands (first one matches major brand) + s_write(s, major_brand, 4); + + /* Write isom base brand if it's not the major brand */ + if (strcmp(major_brand, "isom") != 0) + s_write(s, "isom", 4); + + /* Avoid adding newer brand (iso6) unless necessary, use "obs1" brand + * as a placeholder to maintain ftyp box size. */ + if (fragmented && strcmp(major_brand, "iso6") != 0) + s_write(s, "iso6", 4); + else + s_write(s, "obs1", 4); + + s_write(s, "iso2", 4); + + /* Include H.264 brand if used */ + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *track = &mux->tracks.array[i]; + if (track->type == TRACK_VIDEO) { + if (track->codec == CODEC_H264) + s_write(s, "avc1", 4); + break; + } + } + + /* General MP4 brannd */ + s_write(s, "mp41", 4); + + return write_box_size(s, start); +} + +/// 8.1.2 Free Space Box +static size_t mp4_write_free(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + /* Write a 16-byte free box, so it can be replaced with a 64-bit size + * box header (u32 + char[4] + u64) */ + s_wb32(s, 16); + s_write(s, "free", 4); + s_wb64(s, 0); + + return 16; +} + +/// 8.2.2 Movie Header Box +static size_t mp4_write_mvhd(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + size_t start = serializer_get_pos(s); + + /* Use primary video track as the baseline for duration */ + uint64_t duration = 0; + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *track = &mux->tracks.array[i]; + if (track->type == TRACK_VIDEO) { + duration = util_mul_div64(track->duration, 1000, + track->timebase_den); + break; + } + } + + write_fullbox(s, 0, "mvhd", 0, 0); + + if (duration > UINT32_MAX || mux->creation_time > UINT32_MAX) { + s_wb64(s, mux->creation_time); // creation time + s_wb64(s, mux->creation_time); // modification time + s_wb32(s, 1000); // timescale + s_wb64(s, duration); // duration (0 for fragmented) + } else { + s_wb32(s, (uint32_t)mux->creation_time); // creation time + s_wb32(s, (uint32_t)mux->creation_time); // modification time + s_wb32(s, 1000); // timescale + s_wb32(s, (uint32_t)duration); // duration (0 for fragmented) + } + + s_wb32(s, 0x00010000); // rate, 16.16 fixed float (1 << 16) + s_wb16(s, 0x0100); // volume + + s_wb16(s, 0); // reserved + s_wb32(s, 0); // reserved + s_wb32(s, 0); // reserved + + // Matrix + for (int i = 0; i < 9; i++) + s_wb32(s, UNITY_MATRIX[i]); + + // pre_defined + s_wb32(s, 0); + s_wb32(s, 0); + s_wb32(s, 0); + s_wb32(s, 0); + s_wb32(s, 0); + s_wb32(s, 0); + + s_wb32(s, mux->track_ctr + 1); // next_track_ID + + return write_box_size(s, start); +} + +/// 8.3.2 Track Header Box +static size_t mp4_write_tkhd(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + size_t start = serializer_get_pos(s); + + uint64_t duration = + util_mul_div64(track->duration, 1000, track->timebase_den); + + /* Flags are 0x1 (enabled) | 0x2 (in movie) */ + static const uint32_t flags = 0x1 | 0x2; + write_fullbox(s, 0, "tkhd", 0, flags); + + if (duration > UINT32_MAX || mux->creation_time > UINT32_MAX) { + s_wb64(s, mux->creation_time); // creation time + s_wb64(s, mux->creation_time); // modification time + s_wb32(s, track->track_id); // track_id + s_wb32(s, 0); // reserved + s_wb64(s, duration); // duration in movie timescale + } else { + s_wb32(s, (uint32_t)mux->creation_time); // creation time + s_wb32(s, (uint32_t)mux->creation_time); // modification time + s_wb32(s, track->track_id); // track_id + s_wb32(s, 0); // reserved + s_wb32(s, (uint32_t)duration); // duration in movie timescale + } + + s_wb32(s, 0); // reserved + s_wb32(s, 0); // reserved + s_wb16(s, 0); // layer + s_wb16(s, track->type == TRACK_AUDIO ? 1 : 0); // alternate group + s_wb16(s, track->type == TRACK_AUDIO ? 0x100 : 0); // volume + s_wb16(s, 0); // reserved + + // Matrix (predefined) + for (int i = 0; i < 9; i++) + s_wb32(s, UNITY_MATRIX[i]); + + if (track->type == TRACK_AUDIO) { + s_wb32(s, 0); // width + s_wb32(s, 0); // height + } else { + /* width/height are fixed point 16.16, so we just shift the + * integer to the upper 16 bits */ + uint32_t width = obs_encoder_get_width(track->encoder); + s_wb32(s, width << 16); + uint32_t height = obs_encoder_get_height(track->encoder); + s_wb32(s, height << 16); + } + + return write_box_size(s, start); +} + +/// 8.4.2 Media Header Box +static size_t mp4_write_mdhd(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + + size_t size = 32; + uint8_t version = 0; + uint64_t duration = track->duration; + uint32_t timescale = track->timescale; + + if (track->type == TRACK_VIDEO) { + /* Update to track timescale */ + duration = util_mul_div64(duration, track->timescale, + track->timebase_den); + } + + /* use 64-bit duration if necessary */ + if (duration > UINT32_MAX || mux->creation_time > UINT32_MAX) { + size = 44; + version = 1; + } + + write_fullbox(s, size, "mdhd", version, 0); + + if (version == 1) { + s_wb64(s, mux->creation_time); // creation time + s_wb64(s, mux->creation_time); // modification time + s_wb32(s, timescale); // timescale + s_wb64(s, (uint32_t)duration); // duration + } else { + s_wb32(s, (uint32_t)mux->creation_time); // creation time + s_wb32(s, (uint32_t)mux->creation_time); // modification time + s_wb32(s, timescale); // timescale + s_wb32(s, (uint32_t)duration); // duration + } + + s_wb16(s, 21956); // language (undefined) + s_wb16(s, 0); // pre_defined + + return size; +} + +/// 8.4.3 Handler Reference Box +static size_t mp4_write_hdlr(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "hdlr", 0, 0); + + s_wb32(s, 0); // pre_defined + + // handler_type + if (track->type == TRACK_VIDEO) + s_write(s, "vide", 4); + else if (track->type == TRACK_CHAPTERS) + s_write(s, "text", 4); + else + s_write(s, "soun", 4); + + s_wb32(s, 0); // reserved + s_wb32(s, 0); // reserved + s_wb32(s, 0); // reserved + + // name (utf-8 string, null terminated) + if (track->type == TRACK_VIDEO) + s_write(s, "OBS Video Handler", 18); + else if (track->type == TRACK_CHAPTERS) + s_write(s, "OBS Chapter Handler", 20); + else + s_write(s, "OBS Audio Handler", 18); + + return write_box_size(s, start); +} + +/// 12.1.2 Video media header +static size_t mp4_write_vmhd(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + /* Flags is always 1 */ + write_fullbox(s, 20, "vmhd", 0, 1); + + s_wb16(s, 0); // graphicsmode + s_wb16(s, 0); // opcolor r + s_wb16(s, 0); // opcolor g + s_wb16(s, 0); // opcolor b + + return 16; +} + +/// 12.2.2 Sound media header +static size_t mp4_write_smhd(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + write_fullbox(s, 16, "smhd", 0, 0); + + s_wb16(s, 0); // balance + s_wb16(s, 0); // reserved + + return 16; +} + +/// (QTFF/Apple) Text media information atom +static size_t mp4_write_qt_text(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "text"); + + /* Identity matrix, note that it's not fixed point 16.16 */ + s_wb16(s, 0x01); + s_wb32(s, 0x00); + s_wb32(s, 0x00); + s_wb32(s, 0x00); + s_wb32(s, 0x01); + s_wb32(s, 0x00); + s_wb32(s, 0x00); + s_wb32(s, 0x00); + s_wb32(s, 0x00004000); + /* Seemingly undocumented */ + s_wb16(s, 0x0000); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Base media info atom +static size_t mp4_write_gmin(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "gmin", 0, 0); + + s_wb16(s, 0x40); // graphics mode + s_wb16(s, 0x8000); // opColor r + s_wb16(s, 0x8000); // opColor g + s_wb16(s, 0x8000); // opColor b + s_wb16(s, 0); // balance + s_wb16(s, 0); // reserved + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Base media information header atom +static size_t mp4_write_gmhd(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "gmhd"); + + // gmin + mp4_write_gmin(mux); + // text (QuickTime) + mp4_write_qt_text(mux); + + return write_box_size(s, start); +} + +/// ISO/IEC 14496-15 5.4.2.1 AVCConfigurationBox +static size_t mp4_write_avcC(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + + /* For AVC this is the parsed extra data. */ + uint8_t *header; + size_t size; + + struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, + .timebase_den = 1, + .keyframe = true}; + + if (!obs_encoder_get_extra_data(enc, &header, &size)) + return 0; + + packet.size = obs_parse_avc_header(&packet.data, header, size); + + size_t box_size = packet.size + 8; + write_box(s, box_size, "avcC"); + s_write(s, packet.data, packet.size); + + bfree(packet.data); + return box_size; +} + +/// ISO/IEC 14496-15 8.4.1.1 HEVCConfigurationBox +static size_t mp4_write_hvcC(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + + /* For HEVC this is the parsed extra data. */ + uint8_t *header; + size_t size; + + struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, + .timebase_den = 1, + .keyframe = true}; + + if (!obs_encoder_get_extra_data(enc, &header, &size)) + return 0; + + packet.size = obs_parse_hevc_header(&packet.data, header, size); + + size_t box_size = packet.size + 8; + write_box(s, box_size, "hvcC"); + s_write(s, packet.data, packet.size); + + bfree(packet.data); + return box_size; +} + +/// AV1 ISOBMFF 2.3. AV1 Codec Configuration Box +static size_t mp4_write_av1C(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + + /* For AV1 this is just the parsed extra data. */ + uint8_t *header; + size_t size; + + struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, + .timebase_den = 1, + .keyframe = true}; + + if (!obs_encoder_get_extra_data(enc, &header, &size)) + return 0; + + packet.size = obs_parse_av1_header(&packet.data, header, size); + + size_t box_size = packet.size + 8; + write_box(s, box_size, "av1C"); + s_write(s, packet.data, packet.size); + + bfree(packet.data); + return box_size; +} + +/// 12.1.5 Colour information +static size_t mp4_write_colr(struct mp4_mux *mux, obs_encoder_t *enc) +{ + UNUSED_PARAMETER(enc); + struct serializer *s = mux->serializer; + + write_box(s, 19, "colr"); + + uint8_t full_range = 0; + uint16_t pri, trc, spc; + pri = trc = spc = 0; + get_colour_information(enc, &pri, &trc, &spc, &full_range); + + s_write(s, "nclx", 4); // colour_type + s_wb16(s, pri); // colour_primaries + s_wb16(s, trc); // transfer_characteristics + s_wb16(s, spc); // matrix_coefficiencts + s_w8(s, full_range << 7); // full range flag + 7 reserved bits (0) + + return 19; +} + +/// 12.1.4 Pixel Aspect Ratio +static size_t mp4_write_pasp(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + write_box(s, 16, "pasp"); + + s_wb32(s, 1); // hSpacing + s_wb32(s, 1); // vSpacing + + return 16; +} + +/// 12.1.3 Visual Sample Entry +static inline void mp4_write_visual_sample_entry(struct mp4_mux *mux, + obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + + // SampleEntry Box + s_w8(s, 0); // reserved + s_w8(s, 0); + s_w8(s, 0); + s_w8(s, 0); + s_w8(s, 0); + s_w8(s, 0); + + s_wb16(s, 1); // data_reference_index + + // VisualSampleEntry Box + s_wb16(s, 0); // pre_defined + s_wb16(s, 0); // reserved + s_wb32(s, 0); // pre_defined + s_wb32(s, 0); // pre_defined + s_wb32(s, 0); // pre_defined + + s_wb16(s, (uint16_t)obs_encoder_get_width(enc)); // width + s_wb16(s, (uint16_t)obs_encoder_get_height(enc)); // height + + s_wb32(s, 0x00480000); // horizresolution (predefined) + s_wb32(s, 0x00480000); // vertresolution (predefined) + + s_wb32(s, 0); // reserved + s_wb16(s, 1); // frame_count + + /* Name is fixed 32-bytes and needs to be padded to that length. + * First byte is the length, rest is a string sans NULL terminator. */ + char compressor_name[32] = {0}; + const char *enc_id = obs_encoder_get_id(enc); + if (enc_id) { + size_t len = strlen(enc_id); + if (len > 31) + len = 31; + + compressor_name[0] = (char)len; + memcpy(compressor_name + 1, enc_id, len); + } + s_write(s, compressor_name, sizeof(compressor_name)); // compressorname + + s_wb16(s, 0x0018); // depth + s_wb16(s, -1); // pre_defined +} + +/// 12.1.6 Content light level +static size_t mp4_write_clli(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + + video_t *video = obs_encoder_video(enc); + const struct video_output_info *info = video_output_get_info(video); + + /* Only write box for HDR video */ + if (info->colorspace != VIDEO_CS_2100_PQ && + info->colorspace != VIDEO_CS_2100_HLG) + return 0; + + write_box(s, 12, "clli"); + + float nominal_peak = obs_get_video_hdr_nominal_peak_level(); + + s_wb16(s, (uint16_t)nominal_peak); // max_content_light_level + s_wb16(s, (uint16_t)nominal_peak); // max_pic_average_light_level + + return 12; +} + +/// 12.1.7 Mastering display colour volume +static size_t mp4_write_mdcv(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + + video_t *video = obs_encoder_video(enc); + const struct video_output_info *info = video_output_get_info(video); + + // Only write atom for HDR video + if (info->colorspace != VIDEO_CS_2100_PQ && + info->colorspace != VIDEO_CS_2100_HLG) + return 0; + + write_box(s, 32, "mdcv"); + + float nominal_peak = obs_get_video_hdr_nominal_peak_level(); + uint32_t max_lum = (uint32_t)nominal_peak * 10000; + + /* Note that these values are hardcoded everywhere in OBS, so these are + * just the same as used in our other muxers/encoders. */ + + // 3 x display_primaries (x, y) pairs + s_wb16(s, 13250); + s_wb16(s, 34500); + s_wb16(s, 7500); + s_wb16(s, 3000); + s_wb16(s, 34000); + s_wb16(s, 16000); + + s_wb16(s, 15635); // white_point_x + s_wb16(s, 16450); // white_point_y + s_wb32(s, max_lum); // max_display_mastering_luminance + s_wb32(s, 0); // min_display_mastering_luminance + + return 32; +} + +/// ISO/IEC 14496-15 5.4.2.1 AVCSampleEntry +static size_t mp4_write_avc1(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "avc1"); + + mp4_write_visual_sample_entry(mux, enc); + + // avcC + mp4_write_avcC(mux, enc); + + // colr + mp4_write_colr(mux, enc); + + // pasp + mp4_write_pasp(mux); + + return write_box_size(s, start); +} + +/// ISO/IEC 14496-15 8.4.1.1 HEVCSampleEntry +static size_t mp4_write_hvc1(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "hvc1"); + + mp4_write_visual_sample_entry(mux, enc); + + // avcC + mp4_write_hvcC(mux, enc); + + // colr + mp4_write_colr(mux, enc); + + // clli + mp4_write_clli(mux, enc); + + // mdcv + mp4_write_mdcv(mux, enc); + + // pasp + mp4_write_pasp(mux); + + return write_box_size(s, start); +} + +/// AV1 ISOBMFF 2.2. AV1 Sample Entry +static size_t mp4_write_av01(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "av01"); + + mp4_write_visual_sample_entry(mux, enc); + + // avcC + mp4_write_av1C(mux, enc); + + // colr + mp4_write_colr(mux, enc); + + // clli + mp4_write_clli(mux, enc); + + // mdcv + mp4_write_mdcv(mux, enc); + + // pasp + mp4_write_pasp(mux); + + return write_box_size(s, start); +} + +static inline void put_descr(struct serializer *s, uint8_t tag, size_t size) +{ + int i = 3; + s_w8(s, tag); + for (; i > 0; i--) + s_w8(s, (uint8_t)((size >> (7 * i)) | 0x80)); + s_w8(s, size & 0x7F); +} + +/// ISO/IEC 14496-14 5.6 ESDBox +static size_t mp4_write_esds(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "esds", 0, 0); + + /* Encoder extradata will be used as DecoderSpecificInfo */ + uint8_t *extradata; + size_t extradata_size; + if (!obs_encoder_get_extra_data(track->encoder, &extradata, + &extradata_size)) { + extradata_size = 0; + } + + /// ISO/IEC 14496-1 + + // ES_Descriptor + size_t decoder_specific_info_len = extradata_size ? extradata_size + 5 + : 0; + + put_descr(s, 0x03, 3 + 5 + 13 + decoder_specific_info_len + 5 + 1); + s_wb16(s, track->track_id); + s_w8(s, 0x00); // flags + + // DecoderConfigDescriptor + put_descr(s, 0x04, 13 + decoder_specific_info_len); + s_w8(s, 0x40); // codec tag, 0x40 = AAC + s_w8(s, 0x15); // stream type field (0x15 = audio stream) + + /* When writing the final MOOV this could theoretically be calculated + * based on chunks, but it's not really all that important. */ + uint32_t bitrate = 0; + obs_data_t *settings = obs_encoder_get_settings(track->encoder); + if (settings) { + int64_t enc_bitrate = obs_data_get_int(settings, "bitrate"); + if (enc_bitrate) + bitrate = (uint32_t)(enc_bitrate * 1000); + + obs_data_release(settings); + } + + s_wb24(s, 0); // bufferSizeDB (in bytes) + s_wb32(s, bitrate); // maxbitrate + s_wb32(s, bitrate); // avgBitrate + + // DecoderSpecificInfo + if (extradata_size) { + put_descr(s, 0x05, extradata_size); + s_write(s, extradata, extradata_size); + } + + // SLConfigDescriptor descriptor + put_descr(s, 0x06, 1); + s_w8(s, 0x02); // 0x2 = reserved for MP4, descriptor is empty + + return write_box_size(s, start); +} + +/// 12.2.3 Audio Sample Entry +static inline void mp4_write_audio_sample_entry(struct mp4_mux *mux, + struct mp4_track *track, + uint8_t version) +{ + struct serializer *s = mux->serializer; + + // SampleEntry Box + s_w8(s, 0); // reserved + s_w8(s, 0); + s_w8(s, 0); + s_w8(s, 0); + s_w8(s, 0); + s_w8(s, 0); + + s_wb16(s, 1); // data_reference_index + + // AudioSampleEntry Box + if (version == 1) { + s_wb16(s, 1); // entry_version + s_wb16(s, 0); // reserved + s_wb16(s, 0); // reserved + s_wb16(s, 0); // reserved + } else { + s_wb32(s, 0); // reserved + s_wb32(s, 0); // reserved + } + + audio_t *audio = obs_encoder_audio(track->encoder); + size_t channels = audio_output_get_channels(audio); + uint32_t sample_rate = track->timescale; + bool alac = track->codec == CODEC_ALAC; + + s_wb16(s, (uint32_t)channels); // channelcount + + /* OBS FLAC is currently always 16 bit, ALAC always 24, this may change + * in the futrure and should be handled differently then. + * That being said thoes codecs are self-describing so in most cases it + * shouldn't matter either way. */ + s_wb16(s, alac ? 24 : 16); // samplesize + + s_wb16(s, 0); // pre_defined + s_wb16(s, 0); // reserved + + s_wb32(s, sample_rate << 16); // samplerate +} + +/// 12.2.4 Channel layout +static size_t mp4_write_chnl(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "chnl", 0, 0); + + audio_t *audio = obs_encoder_audio(track->encoder); + const struct audio_output_info *info = audio_output_get_info(audio); + + s_w8(s, 1); // stream_structure (1 = channels) + + /* 5.1 and 4.1 do not have a corresponding ISO layout, so we have to + * write a manually created channel map for those. */ + uint8_t map[8] = {0}; + uint8_t items = 0; + uint8_t defined_layout = 0; + + get_speaker_positions(info->speakers, map, &items, &defined_layout); + + if (!defined_layout) { + warn("No ISO layout available for speaker layout %d, " + "this may not be supported by all applications!", + info->speakers); + s_w8(s, 0); // definedLayout + s_write(s, map, items); // uint8_t speaker_position[count] + } else { + s_w8(s, defined_layout); // definedLayout + s_wb64(s, 0); // ommitedChannelMap + } + + return write_box_size(s, start); +} + +/// ISO/IEC 14496-14 5.6 MP4AudioSampleEntry +static size_t mp4_write_mp4a(struct mp4_mux *mux, struct mp4_track *track, + uint8_t version) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "mp4a"); + + mp4_write_audio_sample_entry(mux, track, version); + + // esds + mp4_write_esds(mux, track); + + /* Write channel layout for version 1 sample entires */ + if (version == 1) + mp4_write_chnl(mux, track); + + return write_box_size(s, start); +} + +/// Encapsulation of FLAC in ISO Base Media File Format 3.3.2 FLAC Specific Box +static size_t mp4_write_dfLa(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + uint8_t *extradata; + size_t extradata_size; + + if (!obs_encoder_get_extra_data(track->encoder, &extradata, + &extradata_size)) + return 0; + + write_fullbox(s, 0, "dfLa", 0, 0); + + /// FLACMetadataBlock + + // LastMetadataBlockFlag (1) | BlockType (0) + s_w8(s, 1 << 7 | 0); + // Length + s_wb24(s, (uint32_t)extradata_size); + // BlockData[Length] + s_write(s, extradata, extradata_size); + + return write_box_size(s, start); +} + +/// Encapsulation of FLAC in ISO Base Media File Format 3.3.1 FLACSampleEntry +static size_t mp4_write_fLaC(struct mp4_mux *mux, struct mp4_track *track, + uint8_t version) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "fLaC"); + + mp4_write_audio_sample_entry(mux, track, version); + + // dfLa + mp4_write_dfLa(mux, track); + + if (version == 1) + mp4_write_chnl(mux, track); + + return write_box_size(s, start); +} + +/// Apple Lossless Format "Magic Cookie" Description - MP4/M4A File +static size_t mp4_write_alac(struct mp4_mux *mux, struct mp4_track *track, + uint8_t version) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + uint8_t *extradata; + size_t extradata_size; + + if (!obs_encoder_get_extra_data(track->encoder, &extradata, + &extradata_size)) + return 0; + + write_box(s, 0, "alac"); + + mp4_write_audio_sample_entry(mux, track, version); + + /* Apple Lossless Magic Cookie */ + s_write(s, extradata, extradata_size); + + if (version == 1) + mp4_write_chnl(mux, track); + + return write_box_size(s, start); +} + +/// ISO/IEC 23003-5 5.1 PCM configuration +static size_t mp4_write_pcmc(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "pcmC", 0, 0); + + s_w8(s, 1); // endianness, 1 = little endian + + // bits per sample + if (track->codec == CODEC_PCM_I16) + s_w8(s, 16); + else if (track->codec == CODEC_PCM_I24) + s_w8(s, 24); + else if (track->codec == CODEC_PCM_F32) + s_w8(s, 32); + + return write_box_size(s, start); +} + +/// ISO/IEC 23003-5 5.1 PCM configuration +static size_t mp4_write_xpcm(struct mp4_mux *mux, struct mp4_track *track, + uint8_t version) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + /* Different box types for floating point and integer PCM*/ + write_box(s, 0, track->codec == CODEC_PCM_F32 ? "fpcm" : "ipcm"); + + mp4_write_audio_sample_entry(mux, track, version); + + /* ChannelLayout (chnl) is required for PCM */ + mp4_write_chnl(mux, track); + + // pcmc + mp4_write_pcmc(mux, track); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Text sample description +static size_t mp4_write_text(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "text", 0, 0); + + s_wb32(s, 1); // number of entries + + /* Preset sample description as used by FFmpeg. */ + s_write(s, &TEXT_STUB_HEADER, sizeof(TEXT_STUB_HEADER)); + + return write_box_size(s, start); +} + +static inline uint32_t rl32(const uint8_t *ptr) +{ + return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0]; +} + +static inline uint16_t rl16(const uint8_t *ptr) +{ + return (ptr[1] << 8) + ptr[0]; +} + +/// Encapsulation of Opus in ISO Base Media File Format 4.3.2 Opus Specific Box +static size_t mp4_write_dOps(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + uint8_t *extradata; + size_t extradata_size; + + if (!obs_encoder_get_extra_data(track->encoder, &extradata, + &extradata_size)) + return 0; + + write_box(s, 0, "dOps"); + s_w8(s, 0); // version + + uint8_t channels = *(extradata + 9); + uint8_t channel_map = *(extradata + 18); + + s_w8(s, channels); // channel count + // OpusHead is little-endian, but MP4 is big-endian, so we have to swap them here + s_wb16(s, rl16(extradata + 10)); // pre-skip + s_wb32(s, rl32(extradata + 12)); // input sample rate + s_wb16(s, rl16(extradata + 16)); // output gain + s_w8(s, channel_map); // channel mapping family + + if (channel_map) + s_write(s, extradata + 19, 2 + channels); + + return write_box_size(s, start); +} + +/// Encapsulation of Opus in ISO Base Media File Format 4.3.1 Sample entry format +static size_t mp4_write_Opus(struct mp4_mux *mux, struct mp4_track *track, + uint8_t version) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "Opus"); + + mp4_write_audio_sample_entry(mux, track, version); + + // dOps + mp4_write_dOps(mux, track); + + if (version == 1) + mp4_write_chnl(mux, track); + + return write_box_size(s, start); +} + +/// 8.5.2 Sample Description Box +static size_t mp4_write_stsd(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + /* Anything but mono or stereo technically requires v1, + * but in practice that doesn't appear to matter. */ + uint8_t version = 0; + + if (track->type == TRACK_AUDIO) { + audio_t *audio = obs_encoder_audio(track->encoder); + version = audio_output_get_channels(audio) > 2 ? 1 : 0; + } + + write_fullbox(s, 0, "stsd", version, 0); + + s_wb32(s, 1); // entry_count + + // codec specific boxes + if (track->type == TRACK_VIDEO) { + if (track->codec == CODEC_H264) + mp4_write_avc1(mux, track->encoder); + else if (track->codec == CODEC_HEVC) + mp4_write_hvc1(mux, track->encoder); + else if (track->codec == CODEC_AV1) + mp4_write_av01(mux, track->encoder); + } else if (track->type == TRACK_AUDIO) { + if (track->codec == CODEC_AAC) + mp4_write_mp4a(mux, track, version); + else if (track->codec == CODEC_OPUS) + mp4_write_Opus(mux, track, version); + else if (track->codec == CODEC_FLAC) + mp4_write_fLaC(mux, track, version); + else if (track->codec == CODEC_ALAC) + mp4_write_alac(mux, track, version); + else if (track->codec == CODEC_PCM_I16 || + track->codec == CODEC_PCM_I24 || + track->codec == CODEC_PCM_F32) + mp4_write_xpcm(mux, track, version); + } else if (track->type == TRACK_CHAPTERS) { + mp4_write_text(mux); + } + + return write_box_size(s, start); +} + +/// 8.6.1.2 Decoding Time to Sample Box +static size_t mp4_write_stts(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + + if (fragmented) { + write_fullbox(s, 16, "stts", 0, 0); + s_wb32(s, 0); // entry_count + return 16; + } + + int64_t start = serializer_get_pos(s); + struct sample_delta *arr = track->deltas.array; + size_t num = track->deltas.num; + + write_fullbox(s, 0, "stts", 0, 0); + + s_wb32(s, (uint32_t)num); // entry_count + + for (size_t idx = 0; idx < num; idx++) { + struct sample_delta *smp = &arr[idx]; + + uint64_t delta = util_mul_div64(smp->delta, track->timescale, + track->timebase_den); + + s_wb32(s, smp->count); // sample_count + s_wb32(s, (uint32_t)delta); // sample_delta + } + + return write_box_size(s, start); +} + +/// 8.6.2 Sync Sample Box +static size_t mp4_write_stss(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + uint32_t num = (uint32_t)track->sync_samples.num; + + if (!num) + return 0; + + /* 16 byte FullBox header + 4-bytes (u32) per sync sample */ + uint32_t size = 16 + 4 * num; + + write_fullbox(s, size, "stss", 0, 0); + s_wb32(s, num); // entry_count + + for (size_t idx = 0; idx < num; idx++) + s_wb32(s, track->sync_samples.array[idx]); // sample_number + + return size; +} + +/// 8.6.1.3 Composition Time to Sample Box +static size_t mp4_write_ctts(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + uint32_t num = (uint32_t)track->offsets.num; + + uint8_t version = mux->flags & MP4_USE_NEGATIVE_CTS ? 1 : 0; + + /* 16 byte FullBox header + 8-bytes (u32+u32/i32) per offset entry */ + uint32_t size = 16 + 8 * num; + write_fullbox(s, size, "ctts", version, 0); + + s_wb32(s, num); // entry_count + + for (size_t idx = 0; idx < num; idx++) { + int64_t offset = (int64_t)track->offsets.array[idx].offset * + (int64_t)track->timescale / + (int64_t)track->timebase_den; + + s_wb32(s, track->offsets.array[idx].count); // sample_count + s_wb32(s, (uint32_t)offset); // sample_offset + } + + return size; +} + +/// 8.7.4 Sample To Chunk Box +static size_t mp4_write_stsc(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + + if (fragmented) { + write_fullbox(s, 16, "stsc", 0, 0); + s_wb32(s, 0); // entry_count + return 16; + } + + struct chunk *arr = track->chunks.array; + size_t arr_num = track->chunks.num; + + /* Compress into array with counter for repeating chunk sizes */ + DARRAY(struct chunk_run { + uint32_t first; + uint32_t samples; + }) chunk_runs; + + da_init(chunk_runs); + + for (size_t idx = 0; idx < arr_num; idx++) { + struct chunk *chk = &arr[idx]; + + if (!chunk_runs.num || + chunk_runs.array[chunk_runs.num - 1].samples != + chk->samples) { + struct chunk_run *cr = da_push_back_new(chunk_runs); + cr->samples = chk->samples; + cr->first = (uint32_t)idx + 1; // ISO-BMFF is 1-indexed + } + } + + uint32_t num = (uint32_t)chunk_runs.num; + + /* 16 byte FullBox header + 12-bytes (u32+u32+u32) per chunk run */ + uint32_t size = 16 + 12 * num; + write_fullbox(s, size, "stsc", 0, 0); + + s_wb32(s, num); // entry_count + + for (size_t idx = 0; idx < num; idx++) { + struct chunk_run *cr = &chunk_runs.array[idx]; + s_wb32(s, cr->first); // first_chunk + s_wb32(s, cr->samples); // samples_per_chunk + s_wb32(s, 1); // sample_description_index + } + + da_free(chunk_runs); + + return size; +} + +/// 8.7.3 Sample Size Boxes +static size_t mp4_write_stsz(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + + if (fragmented) { + write_fullbox(s, 20, "stsz", 0, 0); + s_wb32(s, 0); // sample_size + s_wb32(s, 0); // sample_count + + return 20; + } + + int64_t start = serializer_get_pos(s); + + /* This should only ever happen when recording > 24 hours of + * 48 kHz PCM audio or 828 days of 60 FPS video. */ + if (track->samples > UINT32_MAX) { + warn("Track %u has too many samples, its duration may not be " + "read correctly. Remuxing the file to another format such " + "as MKV may be required.", + track->track_id); + } + + write_fullbox(s, 0, "stsz", 0, 0); + + if (track->sample_size) { + /* Fixed size samples mean we don't need an array */ + s_wb32(s, track->sample_size); // sample_size + s_wb32(s, (uint32_t)track->samples); // sample_count + } else { + s_wb32(s, 0); // sample_size + s_wb32(s, (uint32_t)track->sample_sizes.num); // sample_count + + for (size_t idx = 0; idx < track->sample_sizes.num; idx++) { + s_wb32(s, track->sample_sizes.array[idx]); // entry_size + } + } + + return write_box_size(s, start); +} + +/// 8.7.5 Chunk Offset Box +static size_t mp4_write_stco(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + + if (fragmented) { + write_fullbox(s, 16, "stco", 0, 0); + s_wb32(s, 0); // entry_count + return 16; + } + + struct chunk *arr = track->chunks.array; + uint32_t num = (uint32_t)track->chunks.num; + + uint64_t last_off = arr[num - 1].offset; + uint32_t size; + bool co64 = last_off > UINT32_MAX; + + /* When using 64-bit offsets we write 8-bytes (u64) per chunk, + * otherwise 4-bytes (u32). */ + if (co64) { + size = 16 + 8 * num; + write_fullbox(s, size, "co64", 0, 0); + } else { + size = 16 + 4 * num; + write_fullbox(s, size, "stco", 0, 0); + } + + s_wb32(s, num); // entry_count + + for (size_t idx = 0; idx < num; idx++) { + if (co64) + s_wb64(s, arr[idx].offset); // chunk_offset + else + s_wb32(s, (uint32_t)arr[idx].offset); // chunk_offset + } + + return size; +} + +/// 8.9.3 Sample Group Description Box +static size_t mp4_write_sgpd_aac(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + int64_t start = serializer_get_pos(s); + write_fullbox(s, 0, "sgpd", 1, 0); + + s_write(s, "roll", 4); // grouping_tpye + s_wb32(s, 2); // default_length (i16) + + s_wb32(s, 1); // entry_count + + // AudioRollRecoveryEntry + s_wb16(s, -1); // roll_distance + + return write_box_size(s, start); +} + +/// 8.9.2 Sample to Group Box +static size_t mp4_write_sbgp_aac(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + + int64_t start = serializer_get_pos(s); + write_fullbox(s, 0, "sbgp", 0, 0); + + /// 10.1 AudioRollRecoveryEntry + s_write(s, "roll", 4); // grouping_tpye + + s_wb32(s, 1); // entry_count + + s_wb32(s, (uint32_t)track->samples); // sample_count + s_wb32(s, 1); // group_description_index + + return write_box_size(s, start); +} + +static size_t mp4_write_sbgp_sbgp_opus(struct mp4_mux *mux, + struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + /// 8.9.3 Sample Group Description Box + write_fullbox(s, 0, "sgpd", 1, 0); + + s_write(s, "roll", 4); // grouping_tpye + s_wb32(s, 2); // default_length (i16) + + /* Opus requires 80 ms of preroll, which at 48 kHz is 3840 PCM samples */ + const int64_t opus_preroll = 3840; + + /* Compute the preroll samples (should be 4, each being 20 ms) */ + uint16_t preroll_count = 0; + int64_t preroll_remaining = opus_preroll; + + for (size_t i = 0; i < track->deltas.num && preroll_remaining > 0; + i++) { + for (uint32_t j = 0; + j < track->deltas.array[i].count && preroll_remaining > 0; + j++) { + preroll_remaining -= track->deltas.array[i].delta; + preroll_count++; + } + } + + s_wb32(s, 1); // entry_count + /// 10.1 AudioRollRecoveryEntry + s_wb16(s, -preroll_count); // roll_distance + + size_t size_sgpd = write_box_size(s, start); + + /* --------------- */ + + /// 8.9.2 Sample to Group Box + start = serializer_get_pos(s); + write_fullbox(s, 0, "sbgp", 0, 0); + + s_write(s, "roll", 4); // grouping_tpye + s_wb32(s, 2); // entry_count + + // entry 0 + s_wb32(s, preroll_count); // sample_count + s_wb32(s, 0); // group_description_index + // entry 1 + s_wb32(s, (uint32_t)track->samples - preroll_count); // sample_count + s_wb32(s, 1); // group_description_index + + return size_sgpd + write_box_size(s, start); +} + +/// 8.5.1 Sample Table Box +static size_t mp4_write_stbl(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "stbl"); + + // stsd + mp4_write_stsd(mux, track); + + // stts + mp4_write_stts(mux, track, fragmented); + + // stss (non-fragmented only) + if (track->type == TRACK_VIDEO && !fragmented) + mp4_write_stss(mux, track); + + // ctts (non-fragmented only) + if (track->needs_ctts && !fragmented) + mp4_write_ctts(mux, track); + + // stsc + mp4_write_stsc(mux, track, fragmented); + + // stsz + mp4_write_stsz(mux, track, fragmented); + + // stco + mp4_write_stco(mux, track, fragmented); + + if (!fragmented) { + /* AAC and Opus require a pre-roll to get correct decoder + * output, sgpd and sbgp are used to create a "roll" group. */ + if (track->codec == CODEC_AAC) { + // sgpd + mp4_write_sgpd_aac(mux); + // sbgp + mp4_write_sbgp_aac(mux, track); + } else if (track->codec == CODEC_OPUS) { + // sgpd + sbgp + mp4_write_sbgp_sbgp_opus(mux, track); + } + } + + return write_box_size(s, start); +} + +/// 8.7.2.2 DataEntryUrlBox +static size_t mp4_write_url(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "url ", 0, 1); + + /* empty, flag 1 means data is in this file */ + + return write_box_size(s, start); +} + +/// 8.7.2 Data Reference Box +static size_t mp4_write_dref(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "dref ", 0, 0); + + s_wb32(s, 1); // entry_count + + mp4_write_url(mux); + + return write_box_size(s, start); +} + +/// 8.7.1 Data Information Box +static size_t mp4_write_dinf(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "dinf"); + + mp4_write_dref(mux); + + return write_box_size(s, start); +} + +/// 8.4.4 Media Information Box +static size_t mp4_write_minf(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "minf"); + + // vmhd/smhd/gmhd + if (track->type == TRACK_VIDEO) + mp4_write_vmhd(mux); + else if (track->type == TRACK_CHAPTERS) + mp4_write_gmhd(mux); + else + mp4_write_smhd(mux); + + // dinf, unnecessary but mandatory + mp4_write_dinf(mux); + + // stbl + mp4_write_stbl(mux, track, fragmented); + + return write_box_size(s, start); +} + +/// 8.4.1 Media Box +static size_t mp4_write_mdia(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "mdia"); + + // mdhd + mp4_write_mdhd(mux, track); + + // hdlr + mp4_write_hdlr(mux, track); + + // minf + mp4_write_minf(mux, track, fragmented); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) User data atom +static size_t mp4_write_udta_atom(struct mp4_mux *mux, const char tag[4], + const char *val) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, tag); + s_write(s, val, strlen(val)); + + return write_box_size(s, start); +} + +/// 8.10.1 User Data Box +static size_t mp4_write_track_udta(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "udta"); + + /* Our udta box contains QuickTime format user data atoms, which are + * simple key-value pairs. Some are prefixed with 0xa9. */ + + const char *name = obs_encoder_get_name(track->encoder); + if (name) + mp4_write_udta_atom(mux, "name", name); + + if (mux->flags & MP4_WRITE_ENCODER_INFO) { + const char *id = obs_encoder_get_id(track->encoder); + if (name) + mp4_write_udta_atom(mux, "\251enc", id); + + obs_data_t *settings = obs_encoder_get_settings(track->encoder); + if (settings) { + const char *json = + obs_data_get_json_with_defaults(settings); + mp4_write_udta_atom(mux, "json", json); + obs_data_release(settings); + } + } + + return write_box_size(s, start); +} + +/// 8.6.6 Edit List Box +static size_t mp4_write_elst(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "elst", 0, 0); + + s_wb32(s, 1); // entry count + + uint64_t duration = + util_mul_div64(track->duration, 1000, track->timebase_den); + uint64_t delay = 0; + + if (track->type == TRACK_VIDEO && + !(mux->flags & MP4_USE_NEGATIVE_CTS)) { + /* Compensate for frame-reordering delay (for example, when + * using b-frames). */ + int64_t dts_offset = 0; + + if (track->offsets.num) { + struct sample_offset sample = track->offsets.array[0]; + dts_offset = sample.offset; + } else if (track->packets.size) { + /* If no offset data exists yet (i.e. when writing the + * incomplete moov in a fragmented file) use the raw + * data from the current queued packets instead. */ + struct encoder_packet pkt; + deque_peek_front(&track->packets, &pkt, sizeof(pkt)); + dts_offset = pkt.pts - pkt.dts; + } + + delay = util_mul_div64(dts_offset, track->timescale, + track->timebase_den); + } else if (track->type == TRACK_AUDIO && track->first_pts < 0) { + delay = util_mul_div64(llabs(track->first_pts), + track->timescale, track->timebase_den); + /* Subtract priming delay from total duration */ + duration -= util_mul_div64(delay, 1000, track->timescale); + } + + s_wb32(s, (uint32_t)duration); // segment_duration (movie timescale) + s_wb32(s, (uint32_t)delay); // media_time (track timescale) + s_wb32(s, 1 << 16); // media_rate + + return write_box_size(s, start); +} + +/// 8.6.5 Edit Box +static size_t mp4_write_edts(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "edts"); + + mp4_write_elst(mux, track); + + return write_box_size(s, start); +} + +/// 8.3.3.2 TrackReferenceTypeBox +static size_t mp4_write_chap(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + /// QTFF/Apple chapter track reference + write_box(s, 0, "chap"); + + s_wb32(s, mux->chapter_track->track_id); + + return write_box_size(s, start); +} + +/// 8.3.3 Track Reference Box +static size_t mp4_write_tref(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "tref"); + + mp4_write_chap(mux); + + return write_box_size(s, start); +} + +/// 8.3.1 Track Box +static size_t mp4_write_trak(struct mp4_mux *mux, struct mp4_track *track, + bool fragmented) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "trak"); + + // tkhd + mp4_write_tkhd(mux, track); + + // edts + mp4_write_edts(mux, track); + + // tref + if (mux->chapter_track && track->type != TRACK_CHAPTERS) + mp4_write_tref(mux); + + // mdia + mp4_write_mdia(mux, track, fragmented); + + // udta (audio track name mainly) + mp4_write_track_udta(mux, track); + + return write_box_size(s, start); +} + +/// 8.8.3 Track Extends Box +static size_t mp4_write_trex(struct mp4_mux *mux, uint32_t track_id) +{ + struct serializer *s = mux->serializer; + + write_fullbox(s, 32, "trex", 0, 0); + + s_wb32(s, track_id); // track_ID + s_wb32(s, 1); // default_sample_description_index + s_wb32(s, 0); // default_sample_duration + s_wb32(s, 0); // default_sample_size + s_wb32(s, 0); // default_sample_flags + + return 32; +} + +/// 8.8.1 Movie Extends Box +static size_t mp4_write_mvex(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "mvex"); + + for (size_t track_id = 0; track_id < mux->tracks.num; track_id++) + mp4_write_trex(mux, (uint32_t)(track_id + 1)); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Undocumented QuickTime/iTunes metadata handler +static size_t mp4_write_itunes_hdlr(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + write_fullbox(s, 33, "hdlr", 0, 0); + + s_wb32(s, 0); // pre_defined + s_write(s, "mdir", 4); // handler_type + + // reserved + s_write(s, "appl", 4); + s_wb32(s, 0); + s_wb32(s, 0); + + s_w8(s, 0); // name (NULL) + + return 33; +} + +/// (QTFF/Apple) Data atom +static size_t mp4_write_data_atom(struct mp4_mux *mux, const char *data) +{ + struct serializer *s = mux->serializer; + + size_t len = strlen(data); + uint32_t size = 16 + (uint32_t)len; + + write_box(s, size, "data"); + + s_wb32(s, 1); // type, 1 = utf-8 string + s_wb32(s, 0); // locale, 0 = default + s_write(s, data, len); + + return size; +} + +/// (QTFF/Apple) Metadata item atom +static size_t mp4_write_ilst_item_atom(struct mp4_mux *mux, const char name[4], + const char *value) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, name); + + mp4_write_data_atom(mux, value); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Metadata item list atom +static size_t mp4_write_ilst(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + struct dstr value = {0}; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "ilst"); + + /* Encoder name */ + dstr_cat(&value, "OBS Studio ("); + dstr_cat(&value, obs_get_version_string()); + dstr_cat(&value, ")"); + /* Some QuickTime keys are prefixed with 0xa9 */ + mp4_write_ilst_item_atom(mux, "\251too", value.array); + + dstr_free(&value); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Key value metadata handler +static size_t mp4_write_mdta_hdlr(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + write_fullbox(s, 33, "hdlr", 0, 0); + + s_wb32(s, 0); // pre_defined + s_write(s, "mdta", 4); // handler_type + + // reserved + s_wb32(s, 0); + s_wb32(s, 0); + s_wb32(s, 0); + + s_w8(s, 0); // name (NULL) + return 33; +} + +/// (QTFF/Apple) Metadata item keys atom +static size_t mp4_write_mdta_keys(struct mp4_mux *mux, obs_data_t *meta) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "keys", 0, 0); + + uint32_t count = 0; + int64_t count_pos = serializer_get_pos(s); + s_wb32(s, count); // count + + obs_data_item_t *item = obs_data_first(meta); + + for (; item != NULL; obs_data_item_next(&item)) { + const char *name = obs_data_item_get_name(item); + size_t len = strlen(name); + + /* name is key type, can be udta or mdta */ + write_box(s, len + 8, "mdta"); + s_write(s, name, len); // key name + + count++; + } + + int64_t end = serializer_get_pos(s); + + /* Overwrite count with correct value */ + serializer_seek(s, count_pos, SERIALIZE_SEEK_START); + s_wb32(s, count); + serializer_seek(s, end, SERIALIZE_SEEK_START); + + return write_box_size(s, start); +} + +/// (QTFF/Apple) Metadata item atom, but name is an index instead +static inline void write_key_entry(struct mp4_mux *mux, obs_data_item_t *item, + uint32_t idx) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + s_wb32(s, 0); // size + s_wb32(s, idx); // index + + mp4_write_data_atom(mux, obs_data_item_get_string(item)); + + write_box_size(s, start); +} + +/// (QTFF/Apple) Metadata item list atom +static size_t mp4_write_mdta_ilst(struct mp4_mux *mux, obs_data_t *meta) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "ilst"); + + /* indices start with 1 */ + uint32_t key_idx = 1; + + obs_data_item_t *item = obs_data_first(meta); + + for (; item != NULL; obs_data_item_next(&item)) { + write_key_entry(mux, item, key_idx); + key_idx++; + } + + return write_box_size(s, start); +} + +static void mp4_write_mdta_kv(struct mp4_mux *mux) +{ + struct dstr value = {0}; + + obs_data_t *meta = obs_data_create(); + + dstr_cat(&value, "OBS Studio ("); + dstr_cat(&value, obs_get_version_string()); + dstr_cat(&value, ")"); + + // ToDo figure out what else we could put in here for fun and profit :) + obs_data_set_string(meta, "tool", value.array); + + /* Write keys */ + mp4_write_mdta_keys(mux, meta); + /* Write values */ + mp4_write_mdta_ilst(mux, meta); + + obs_data_release(meta); + dstr_free(&value); +} + +/// 8.11.1 The Meta box +static size_t mp4_write_meta(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_fullbox(s, 0, "meta", 0, 0); + + if (mux->flags & MP4_USE_MDTA_KEY_VALUE) { + mp4_write_mdta_hdlr(mux); + mp4_write_mdta_kv(mux); + } else { + mp4_write_itunes_hdlr(mux); + mp4_write_ilst(mux); + } + + return write_box_size(s, start); +} + +/// 8.10.1 User Data Box +static size_t mp4_write_udta(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "udta"); + + /* Normally metadata would be directly in the moov, but since this is + * Apple/QTFF format metadata it is inside udta. */ + + // meta + mp4_write_meta(mux); + + return write_box_size(s, start); +} + +/// Movie Box (8.2.1) +static size_t mp4_write_moov(struct mp4_mux *mux, bool fragmented) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "moov"); + + mp4_write_mvhd(mux); + + // trak(s) + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *track = &mux->tracks.array[i]; + mp4_write_trak(mux, track, fragmented); + } + + if (!fragmented && mux->chapter_track) + mp4_write_trak(mux, mux->chapter_track, false); + + // mvex + if (fragmented) + mp4_write_mvex(mux); + + // udta (metadata) + mp4_write_udta(mux); + + return write_box_size(s, start); +} + +/* ========================================================================== */ +/* moof (fragment header) stuff */ + +/// 8.8.5 Movie Fragment Header Box +static size_t mp4_write_mfhd(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + write_fullbox(s, 16, "mfhd", 0, 0); + + s_wb32(s, mux->fragments_written); // sequence_number + + return 16; +} + +/// 8.8.7 Track Fragment Header Box +static size_t mp4_write_tfhd(struct mp4_mux *mux, struct mp4_track *track, + size_t moof_start) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + uint32_t flags = BASE_DATA_OFFSET_PRESENT | + DEFAULT_SAMPLE_FLAGS_PRESENT; + + /* Add default size/duration if all samples match. */ + bool durations_match = true; + bool sizes_match = true; + uint32_t duration; + uint32_t sample_size; + + if (track->sample_size) { + duration = 1; + sample_size = track->sample_size; + } else { + duration = track->fragment_samples.array[0].duration; + sample_size = track->fragment_samples.array[0].size; + + for (size_t idx = 1; idx < track->fragment_samples.num; idx++) { + uint32_t frag_duration = + track->fragment_samples.array[idx].duration; + uint32_t frag_size = + track->fragment_samples.array[idx].size; + + durations_match = frag_duration == duration; + sizes_match = frag_size == sample_size; + } + } + + if (durations_match) + flags |= DEFAULT_SAMPLE_DURATION_PRESENT; + if (sizes_match) + flags |= DEFAULT_SAMPLE_SIZE_PRESENT; + + write_fullbox(s, 0, "tfhd", 0, flags); + + s_wb32(s, track->track_id); // track_ID + s_wb64(s, moof_start); // base_data_offset + + // default_sample_duration + if (durations_match) { + if (track->type == TRACK_VIDEO) { + /* Convert duration to track timescale */ + duration = (uint32_t)util_mul_div64( + duration, track->timescale, + track->timebase_den); + } + + s_wb32(s, duration); + } + // default_sample_size + if (sizes_match) + s_wb32(s, sample_size); + // default_sample_flags + if (track->type == TRACK_VIDEO) { + s_wb32(s, SAMPLE_FLAG_DEPENDS_YES | SAMPLE_FLAG_IS_NON_SYNC); + } else { + s_wb32(s, SAMPLE_FLAG_DEPENDS_NO); + } + + return write_box_size(s, start); +} + +/// 8.8.12 Track fragment decode time +static size_t mp4_write_tfdt(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + + write_fullbox(s, 20, "tfdt", 1, 0); + + /* Subtract samples that are not written yet */ + uint64_t duration_written = track->duration; + for (size_t i = 0; i < track->fragment_samples.num; i++) + duration_written -= track->fragment_samples.array[i].duration; + + if (track->type == TRACK_VIDEO) { + /* Convert to track timescale */ + duration_written = util_mul_div64(duration_written, + track->timescale, + track->timebase_den); + } + + s_wb64(s, duration_written); // baseMediaDecodeTime + + return 20; +} + +/// 8.8.8 Track Fragment Run Box +static size_t mp4_write_trun(struct mp4_mux *mux, struct mp4_track *track, + uint32_t moof_size, uint64_t *samples_mdat_offset) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + uint32_t flags = DATA_OFFSET_PRESENT; + + if (!track->sample_size) + flags |= SAMPLE_SIZE_PRESENT; + + if (track->type == TRACK_VIDEO) { + flags |= FIRST_SAMPLE_FLAGS_PRESENT; + flags |= SAMPLE_COMPOSITION_TIME_OFFSETS_PRESENT; + } + + uint8_t version = mux->flags & MP4_USE_NEGATIVE_CTS ? 1 : 0; + + write_fullbox(s, 0, "trun", version, flags); + + /* moof_size + 8 bytes for mdat header + offset into mdat box data */ + size_t data_offset = moof_size + 8 + *samples_mdat_offset; + size_t sample_count = track->fragment_samples.num; + + if (track->sample_size) { + /* Update count based on fixed size */ + size_t total_size = 0; + for (size_t i = 0; i < sample_count; i++) + total_size += track->fragment_samples.array[i].size; + + *samples_mdat_offset += total_size; + sample_count = total_size / track->sample_size; + } + + s_wb32(s, (uint32_t)sample_count); // sample_count + s_wb32(s, (uint32_t)data_offset); // data_offset + + /* If we have a fixed sample size (PCM audio) we only need to write + * the sample count and offset. */ + if (track->sample_size) + return write_box_size(s, start); + + if (track->type == TRACK_VIDEO) + s_wb32(s, SAMPLE_FLAG_DEPENDS_NO); // first_sample_flags + + for (size_t idx = 0; idx < sample_count; idx++) { + struct fragment_sample *smp = + &track->fragment_samples.array[idx]; + + s_wb32(s, smp->size); // sample_size + + if (track->type == TRACK_VIDEO) { + // sample_composition_time_offset + int64_t offset = (int64_t)smp->offset * + (int64_t)track->timescale / + (int64_t)track->timebase_den; + s_wb32(s, (uint32_t)offset); + } + + *samples_mdat_offset += smp->size; + } + + return write_box_size(s, start); +} + +/// 8.8.6 Track Fragment Box +static size_t mp4_write_traf(struct mp4_mux *mux, struct mp4_track *track, + int64_t moof_start, uint32_t moof_size, + uint64_t *samples_mdat_offset) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "traf"); + + // tfhd + mp4_write_tfhd(mux, track, moof_start); + + // tfdt + mp4_write_tfdt(mux, track); + + // trun + mp4_write_trun(mux, track, moof_size, samples_mdat_offset); + + return write_box_size(s, start); +} + +/// 8.8.4 Movie Fragment Box +static size_t mp4_write_moof(struct mp4_mux *mux, uint32_t moof_size, + int64_t moof_start) +{ + struct serializer *s = mux->serializer; + int64_t start = serializer_get_pos(s); + + write_box(s, 0, "moof"); + + mp4_write_mfhd(mux); + + /* Track current mdat offset across tracks */ + uint64_t samples_mdat_offset = 0; + + // traf boxes + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *track = &mux->tracks.array[i]; + /* Skip tracks that do not have any samples */ + if (!track->fragment_samples.num) + continue; + + mp4_write_traf(mux, track, moof_start, moof_size, + &samples_mdat_offset); + } + + return write_box_size(s, start); +} + +/* ========================================================================== */ +/* Chapter packets */ + +static void mp4_create_chapter_pkt(struct encoder_packet *pkt, int64_t dts_usec, + const char *name) +{ + int64_t dts = dts_usec / 1000; // chapter track uses a ms timebase + + pkt->pts = dts; + pkt->dts = dts; + pkt->dts_usec = dts_usec; + pkt->timebase_num = 1; + pkt->timebase_den = 1000; + + /* Serialize with data with ref count */ + struct serializer s; + struct array_output_data ao; + array_output_serializer_init(&s, &ao); + + size_t len = min(strlen(name), UINT16_MAX); + long refs = 1; + + /* encoder_packet refs */ + s_write(&s, &refs, sizeof(refs)); + /* actual packet data */ + s_wb16(&s, (uint16_t)len); + s_write(&s, name, len); + s_write(&s, &CHAPTER_PKT_FOOTER, sizeof(CHAPTER_PKT_FOOTER)); + + pkt->data = (void *)(ao.bytes.array + sizeof(long)); + pkt->size = ao.bytes.num - sizeof(long); +} + +/* ========================================================================== */ +/* Encoder packet processing and fragment writer */ + +static inline int64_t packet_pts_usec(struct encoder_packet *packet) +{ + return packet->pts * 1000000 / packet->timebase_den; +} + +static inline struct encoder_packet *get_pkt_at(struct deque *dq, size_t idx) +{ + return deque_data(dq, idx * sizeof(struct encoder_packet)); +} + +static inline uint64_t get_longest_track_duration(struct mp4_mux *mux) +{ + uint64_t dur = 0; + + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *track = &mux->tracks.array[i]; + uint64_t track_dur = util_mul_div64(track->duration, 1000, + track->timebase_den); + + if (track_dur > dur) + dur = track_dur; + } + + return dur; +} + +static void process_packets(struct mp4_mux *mux, struct mp4_track *track, + uint64_t *mdat_size) +{ + size_t count = track->packets.size / sizeof(struct encoder_packet); + + if (!count) + return; + + /* Only iterate upt to penultimate packet so we can determine duration + * for all processed packets. */ + for (size_t i = 0; i < count - 1; i++) { + struct encoder_packet *pkt = get_pkt_at(&track->packets, i); + + if (mux->next_frag_pts && + packet_pts_usec(pkt) >= mux->next_frag_pts) + break; + + struct encoder_packet *next = + get_pkt_at(&track->packets, i + 1); + + /* Duration is just distance between current and next DTS. */ + uint32_t duration = (uint32_t)(next->dts - pkt->dts); + uint32_t sample_count = 1; + uint32_t size = (uint32_t)pkt->size; + int32_t offset = (int32_t)(pkt->pts - pkt->dts); + + /* When using negative CTS, subtract DTS-PTS offset. */ + if (track->type == TRACK_VIDEO && + mux->flags & MP4_USE_NEGATIVE_CTS) { + if (!track->offsets.num) + track->dts_offset = offset; + + offset -= track->dts_offset; + } + + /* Create temporary sample information for moof */ + struct fragment_sample *smp = + da_push_back_new(track->fragment_samples); + smp->size = size; + smp->offset = offset; + smp->duration = duration; + + *mdat_size += size; + + /* Update global sample information for full moov */ + track->duration += duration; + + if (track->sample_size) { + /* Adjust duration/count for fixed sample size */ + sample_count = size / track->sample_size; + duration = 1; + } + + if (!track->samples) + track->first_pts = pkt->pts; + + track->samples += sample_count; + + /* If delta (duration) matche sprevious, increment counter, + * otherwise create a new entry. */ + if (track->deltas.num == 0 || + track->deltas.array[track->deltas.num - 1].delta != + duration) { + struct sample_delta *new = + da_push_back_new(track->deltas); + new->delta = duration; + new->count = sample_count; + } else { + track->deltas.array[track->deltas.num - 1].count += + sample_count; + } + + if (!track->sample_size) + da_push_back(track->sample_sizes, &size); + + if (track->type != TRACK_VIDEO) + continue; + + if (pkt->keyframe) + da_push_back(track->sync_samples, &track->samples); + + /* Only require ctts box if offet is non-zero */ + if (offset && !track->needs_ctts) + track->needs_ctts = true; + + /* If dts-pts offset matche sprevious, increment counter, + * otherwise create a new entry. */ + if (track->offsets.num == 0 || + track->offsets.array[track->offsets.num - 1].offset != + offset) { + struct sample_offset *new = + da_push_back_new(track->offsets); + new->offset = offset; + new->count = 1; + } else { + track->offsets.array[track->offsets.num - 1].count += 1; + } + } +} + +/* Write track data to file */ +static void write_packets(struct mp4_mux *mux, struct mp4_track *track) +{ + struct serializer *s = mux->serializer; + + size_t count = track->packets.size / sizeof(struct encoder_packet); + if (!count) + return; + + struct chunk *chk = da_push_back_new(track->chunks); + chk->offset = serializer_get_pos(s); + chk->samples = (uint32_t)track->fragment_samples.num; + + for (size_t i = 0; i < track->fragment_samples.num; i++) { + struct encoder_packet pkt; + deque_pop_front(&track->packets, &pkt, + sizeof(struct encoder_packet)); + s_write(s, pkt.data, pkt.size); + obs_encoder_packet_release(&pkt); + } + + chk->size = (uint32_t)(serializer_get_pos(s) - chk->offset); + + /* Fixup sample count for fixed-size codecs */ + if (track->sample_size) + chk->samples = chk->size / track->sample_size; + + da_clear(track->fragment_samples); +} + +static void mp4_flush_fragment(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + // Write file header if not already done + if (!mux->fragments_written) { + mp4_write_ftyp(mux, true); + /* Placeholder to write mdat header during soft-remux */ + mux->placeholder_offset = serializer_get_pos(s); + mp4_write_free(mux); + } + + // Array output as temporary buffer to avoid sending seeks to disk + struct serializer as; + struct array_output_data aod; + array_output_serializer_init(&as, &aod); + mux->serializer = &as; + + // Write initial incomplete moov (because fragmentation) + if (!mux->fragments_written) { + mp4_write_moov(mux, true); + s_write(s, aod.bytes.array, aod.bytes.num); + array_output_serializer_reset(&aod); + } + + mux->fragments_written++; + + /* --------------------------------------------------------- */ + /* Analyse packets and create fragment moof. */ + + uint64_t mdat_size = 8; + + for (size_t idx = 0; idx < mux->tracks.num; idx++) { + struct mp4_track *track = &mux->tracks.array[idx]; + process_packets(mux, track, &mdat_size); + } + + if (!mux->next_frag_pts && mux->chapter_track) { + // Create dummy chapter marker at the end so duration is correct + uint64_t duration = get_longest_track_duration(mux); + struct encoder_packet pkt; + mp4_create_chapter_pkt(&pkt, (int64_t)duration * 1000, "Dummy"); + deque_push_back(&mux->chapter_track->packets, &pkt, + sizeof(struct encoder_packet)); + + process_packets(mux, mux->chapter_track, &mdat_size); + } + + // write moof once to get size + int64_t moof_start = serializer_get_pos(s); + size_t moof_size = mp4_write_moof(mux, 0, moof_start); + array_output_serializer_reset(&aod); + + // write moof again with known size + mp4_write_moof(mux, (uint32_t)moof_size, moof_start); + + // Write to output and restore real serializer + s_write(s, aod.bytes.array, aod.bytes.num); + mux->serializer = s; + array_output_serializer_free(&aod); + + /* --------------------------------------------------------- */ + /* Write audio and video samples (in chunks). Also update */ + /* global chunk and sample information for final moov. */ + + if (mdat_size > UINT32_MAX) { + s_wb32(s, 1); + s_write(s, "mdat", 4); + s_wb64(s, mdat_size + 8); + } else { + s_wb32(s, (uint32_t)mdat_size); + s_write(s, "mdat", 4); + } + + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *track = &mux->tracks.array[i]; + write_packets(mux, track); + } + + /* Only write chapter packets on final flush. */ + if (!mux->next_frag_pts && mux->chapter_track) + write_packets(mux, mux->chapter_track); + + mux->next_frag_pts = 0; +} + +/* ========================================================================== */ +/* Track object functions */ + +static inline void track_insert_packet(struct mp4_track *track, + struct encoder_packet *pkt) +{ + int64_t pts_usec = packet_pts_usec(pkt); + if (pts_usec > track->last_pts_usec) + track->last_pts_usec = pts_usec; + + deque_push_back(&track->packets, pkt, sizeof(struct encoder_packet)); +} + +static inline uint32_t get_sample_size(struct mp4_track *track) +{ + audio_t *audio = obs_encoder_audio(track->encoder); + if (!audio) + return 0; + + const struct audio_output_info *info = audio_output_get_info(audio); + uint32_t channels = get_audio_channels(info->speakers); + + switch (track->codec) { + case CODEC_PCM_F32: + return channels * 4; // 4 bytes per sample (32-bit) + case CODEC_PCM_I24: + return channels * 3; // 3 bytes per sample (24-bit) + case CODEC_PCM_I16: + return channels * 2; // 2 bytes per sample (16-bit) + default: + return 0; + } +} + +static inline enum mp4_codec get_codec(obs_encoder_t *enc) +{ + const char *codec = obs_encoder_get_codec(enc); + + if (strcmp(codec, "h264") == 0) + return CODEC_H264; + if (strcmp(codec, "hevc") == 0) + return CODEC_HEVC; + if (strcmp(codec, "av1") == 0) + return CODEC_AV1; + if (strcmp(codec, "aac") == 0) + return CODEC_AAC; + if (strcmp(codec, "opus") == 0) + return CODEC_OPUS; + if (strcmp(codec, "flac") == 0) + return CODEC_FLAC; + if (strcmp(codec, "alac") == 0) + return CODEC_ALAC; + if (strcmp(codec, "pcm_s16le") == 0) + return CODEC_PCM_I16; + if (strcmp(codec, "pcm_s24le") == 0) + return CODEC_PCM_I24; + if (strcmp(codec, "pcm_f32le") == 0) + return CODEC_PCM_F32; + + return CODEC_UNKNOWN; +} + +static inline void add_track(struct mp4_mux *mux, obs_encoder_t *enc) +{ + struct mp4_track *track = da_push_back_new(mux->tracks); + + track->type = obs_encoder_get_type(enc) == OBS_ENCODER_VIDEO + ? TRACK_VIDEO + : TRACK_AUDIO; + track->encoder = obs_encoder_get_ref(enc); + track->codec = get_codec(enc); + track->track_id = ++mux->track_ctr; + + /* Set timebase/timescale */ + if (track->type == TRACK_VIDEO) { + video_t *video = obs_encoder_video(enc); + const struct video_output_info *info = + video_output_get_info(video); + track->timebase_num = info->fps_den; + track->timebase_den = info->fps_num; + + track->timescale = track->timebase_den; + /* FFmpeg does this to compensate for non-monotonic timestamps, + * we probably don't need it, but let's stick to what they do + * for maximum compatibility. */ + while (track->timescale < 10000) + track->timescale *= 2; + } else { + uint32_t sample_rate = obs_encoder_get_sample_rate(enc); + /* Opus is always 48 kHz */ + if (track->codec == CODEC_OPUS) + sample_rate = 48000; + track->timebase_num = 1; + track->timebase_den = sample_rate; + track->timescale = sample_rate; + } + + /* Set sample size (if fixed) */ + if (track->type == TRACK_AUDIO) + track->sample_size = get_sample_size(track); +} + +static inline void add_chapter_track(struct mp4_mux *mux) +{ + mux->chapter_track = bzalloc(sizeof(struct mp4_track)); + mux->chapter_track->type = TRACK_CHAPTERS; + mux->chapter_track->codec = CODEC_TEXT; + mux->chapter_track->timescale = 1000; + mux->chapter_track->timebase_num = 1; + mux->chapter_track->timebase_den = 1000; + mux->chapter_track->track_id = ++mux->track_ctr; +} + +static inline void free_packets(struct deque *dq) +{ + size_t num = dq->size / sizeof(struct encoder_packet); + + for (size_t i = 0; i < num; i++) { + struct encoder_packet pkt; + deque_pop_front(dq, &pkt, sizeof(struct encoder_packet)); + obs_encoder_packet_release(&pkt); + } +} + +static inline void free_track(struct mp4_track *track) +{ + if (!track) + return; + + obs_encoder_release(track->encoder); + + free_packets(&track->packets); + deque_free(&track->packets); + + da_free(track->sample_sizes); + da_free(track->chunks); + da_free(track->deltas); + da_free(track->offsets); + da_free(track->sync_samples); + da_free(track->fragment_samples); +} + +/* ===========================================================================*/ +/* API */ + +struct mp4_mux *mp4_mux_create(obs_output_t *output, + struct serializer *serializer, + enum mp4_mux_flags flags) +{ + struct mp4_mux *mux = bzalloc(sizeof(struct mp4_mux)); + + mux->output = output; + mux->serializer = serializer; + mux->flags = flags; + /* Timestamp is based on 1904 rather than 1970. */ + mux->creation_time = time(NULL) + 0x7C25B080; + + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + obs_encoder_t *enc = obs_output_get_video_encoder2(output, i); + if (!enc) + continue; + add_track(mux, enc); + } + + for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { + obs_encoder_t *enc = obs_output_get_audio_encoder(output, i); + if (!enc) + continue; + add_track(mux, enc); + } + + return mux; +} + +void mp4_mux_destroy(struct mp4_mux *mux) +{ + for (size_t i = 0; i < mux->tracks.num; i++) + free_track(&mux->tracks.array[i]); + + free_track(mux->chapter_track); + bfree(mux->chapter_track); + da_free(mux->tracks); + bfree(mux); +} + +bool mp4_mux_submit_packet(struct mp4_mux *mux, struct encoder_packet *pkt) +{ + struct mp4_track *track = NULL; + struct encoder_packet parsed_packet; + enum obs_encoder_type type = pkt->type; + bool fragment_ready = mux->next_frag_pts > 0; + + for (size_t i = 0; i < mux->tracks.num; i++) { + struct mp4_track *tmp = &mux->tracks.array[i]; + + fragment_ready = fragment_ready && + tmp->last_pts_usec >= mux->next_frag_pts; + + if (tmp->encoder == pkt->encoder) + track = tmp; + } + + if (!track) { + warn("Could not find track for packet of type %s with " + "track id %zu!", + type == OBS_ENCODER_VIDEO ? "video" : "audio", + pkt->track_idx); + return false; + } + + /* If all tracks have caught up to the keyframe we want to fragment on, + * flush the current fragment to disk. */ + if (fragment_ready) + mp4_flush_fragment(mux); + + if (type == OBS_ENCODER_AUDIO) { + obs_encoder_packet_ref(&parsed_packet, pkt); + } else { + if (track->codec == CODEC_H264) + obs_parse_avc_packet(&parsed_packet, pkt); + else if (track->codec == CODEC_HEVC) + obs_parse_hevc_packet(&parsed_packet, pkt); + else if (track->codec == CODEC_AV1) + obs_parse_av1_packet(&parsed_packet, pkt); + + /* Set fragmentation PTS if packet is keyframe and PTS > 0 */ + if (parsed_packet.keyframe && parsed_packet.pts > 0) { + mux->next_frag_pts = packet_pts_usec(&parsed_packet); + } + } + + track_insert_packet(track, &parsed_packet); + + return true; +} + +bool mp4_mux_add_chapter(struct mp4_mux *mux, int64_t dts_usec, + const char *name) +{ + if (dts_usec < 0) + return false; + if (!mux->chapter_track) + add_chapter_track(mux); + + /* To work correctly there needs to be a chapter at PTS 0, + * create that here if necessary. */ + if (dts_usec > 0 && mux->chapter_track->packets.size == 0) { + mp4_mux_add_chapter(mux, 0, + obs_module_text("MP4Output.StartChapter")); + } + + /* Create packets that will be muxed on final flush */ + struct encoder_packet pkt; + mp4_create_chapter_pkt(&pkt, dts_usec, name); + track_insert_packet(mux->chapter_track, &pkt); + + return true; +} + +bool mp4_mux_finalise(struct mp4_mux *mux) +{ + struct serializer *s = mux->serializer; + + /* Flush remaining audio/video samples as final fragment. */ + info("Flushing final fragment..."); + + /* Set target PTS to zero to indicate that we want to flush all + * the remaining packets */ + mux->next_frag_pts = 0; + mp4_flush_fragment(mux); + + info("Number of fragments: %u", mux->fragments_written); + + if (mux->flags & MP4_SKIP_FINALISATION) { + warn("Skipping MP4 finalization!"); + return true; + } + + int64_t data_end = serializer_get_pos(s); + + /* ---------------------------------------- */ + /* Write full moov box */ + + /* Use array serializer for moov data as this will do a lot + * of seeks to write size values of variable-size boxes. */ + struct serializer fs; + struct array_output_data ao; + array_output_serializer_init(&fs, &ao); + + mux->serializer = &fs; + + mp4_write_moov(mux, false); + s_write(s, ao.bytes.array, ao.bytes.num); + info("Full moov size: %zu KiB", ao.bytes.num / 1024); + + mux->serializer = s; // restore real serializer + array_output_serializer_free(&ao); + + /* ---------------------------------------- */ + /* Overwrite file header (ftyp + free/moov) */ + + serializer_seek(s, 0, SERIALIZE_SEEK_START); + mp4_write_ftyp(mux, false); + + size_t data_size = data_end - mux->placeholder_offset; + serializer_seek(s, (int64_t)mux->placeholder_offset, + SERIALIZE_SEEK_START); + + /* If data is more than 4 GiB the mdat header becomes 16 bytes, hence + * why we create a 16-byte placeholder "free" box at the start. */ + if (data_size > UINT32_MAX) { + s_wb32(s, 1); // 1 = use "largesize" field instead + s_write(s, "mdat", 4); + s_wb64(s, data_size); // largesize (64-bit) + } else { + s_wb32(s, (uint32_t)data_size); + s_write(s, "mdat", 4); + } + + info("Final mdat size: %zu KiB", data_size / 1024); + return true; +} diff --git a/plugins/obs-outputs/mp4-mux.h b/plugins/obs-outputs/mp4-mux.h new file mode 100644 index 00000000000000..e791b5a0832bbe --- /dev/null +++ b/plugins/obs-outputs/mp4-mux.h @@ -0,0 +1,43 @@ +/****************************************************************************** + Copyright (C) 2024 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include +#include + +struct mp4_mux; + +enum mp4_mux_flags { + /* Uses mdta key/value list for metadata instead of QuickTime keys */ + MP4_USE_MDTA_KEY_VALUE = 1 << 0, + /* Write encoder configuration to trak udat */ + MP4_WRITE_ENCODER_INFO = 1 << 1, + /* Skip "soft-remux" and leave file in fragmented state */ + MP4_SKIP_FINALISATION = 1 << 2, + /* Use negative CTS instead of edit lists */ + MP4_USE_NEGATIVE_CTS = 1 << 3, +}; + +struct mp4_mux *mp4_mux_create(obs_output_t *output, + struct serializer *serializer, + enum mp4_mux_flags flags); +void mp4_mux_destroy(struct mp4_mux *mux); +bool mp4_mux_submit_packet(struct mp4_mux *mux, struct encoder_packet *pkt); +bool mp4_mux_add_chapter(struct mp4_mux *mux, int64_t dts_usec, + const char *name); +bool mp4_mux_finalise(struct mp4_mux *mux); diff --git a/plugins/obs-outputs/mp4-output.c b/plugins/obs-outputs/mp4-output.c new file mode 100644 index 00000000000000..ef3b54423ad090 --- /dev/null +++ b/plugins/obs-outputs/mp4-output.c @@ -0,0 +1,613 @@ +/****************************************************************************** + Copyright (C) 2024 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "mp4-mux.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#define do_log(level, format, ...) \ + blog(level, "[mp4 output: '%s'] " format, \ + obs_output_get_name(out->output), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +struct chapter { + int64_t dts_usec; + char *name; +}; + +struct mp4_output { + obs_output_t *output; + struct dstr path; + + struct serializer serializer; + + volatile bool active; + volatile bool stopping; + uint64_t stop_ts; + + bool allow_overwrite; + uint64_t total_bytes; + + pthread_mutex_t mutex; + + struct mp4_mux *muxer; + int flags; + + int64_t last_dts_usec; + DARRAY(struct chapter) chapters; + + /* File splitting stuff */ + bool split_file_enabled; + bool split_file_ready; + volatile bool manual_split; + + size_t cur_size; + size_t max_size; + + int64_t start_time; + int64_t max_time; + + bool found_video[MAX_OUTPUT_VIDEO_ENCODERS]; + bool found_audio[MAX_OUTPUT_AUDIO_ENCODERS]; + int64_t video_pts_offsets[MAX_OUTPUT_VIDEO_ENCODERS]; + int64_t audio_dts_offsets[MAX_OUTPUT_AUDIO_ENCODERS]; + + /* Buffer for packets while we reinitialise the muxer after splitting */ + DARRAY(struct encoder_packet) split_buffer; +}; + +static inline bool stopping(struct mp4_output *out) +{ + return os_atomic_load_bool(&out->stopping); +} + +static inline bool active(struct mp4_output *out) +{ + return os_atomic_load_bool(&out->active); +} + +static inline int64_t packet_pts_usec(struct encoder_packet *packet) +{ + return packet->pts * 1000000 / packet->timebase_den; +} + +static inline void ts_offset_clear(struct mp4_output *out) +{ + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + out->found_video[i] = false; + out->video_pts_offsets[i] = 0; + } + + for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { + out->found_audio[i] = false; + out->audio_dts_offsets[i] = 0; + } +} + +static inline void ts_offset_update(struct mp4_output *out, + struct encoder_packet *packet) +{ + int64_t *offset; + bool *found; + + if (packet->type == OBS_ENCODER_VIDEO) { + offset = &out->video_pts_offsets[packet->track_idx]; + found = &out->found_video[packet->track_idx]; + } else { + offset = &out->audio_dts_offsets[packet->track_idx]; + found = &out->found_audio[packet->track_idx]; + } + + if (*found) + return; + + *offset = packet->dts; + *found = true; +} + +static const char *mp4_output_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("MP4Output"); +} + +static void mp4_output_destory(void *data) +{ + struct mp4_output *out = data; + + for (size_t i = 0; i < out->chapters.num; i++) + bfree(out->chapters.array[i].name); + da_free(out->chapters); + + pthread_mutex_destroy(&out->mutex); + dstr_free(&out->path); + bfree(out); +} + +static void mp4_add_chapter_proc(void *data, calldata_t *cd) +{ + struct mp4_output *out = data; + struct dstr name = {0}; + + dstr_copy(&name, calldata_string(cd, "chapter_name")); + + if (name.len == 0) { + /* Generate name if none provided. */ + dstr_catf(&name, "%s %zu", + obs_module_text("MP4Output.UnnamedChapter"), + out->chapters.num + 1); + } + + int64_t totalRecordSeconds = out->last_dts_usec / 1000 / 1000; + int seconds = (int)totalRecordSeconds % 60; + int totalMinutes = (int)totalRecordSeconds / 60; + int minutes = totalMinutes % 60; + int hours = totalMinutes / 60; + + info("Adding chapter \"%s\" at %02d:%02d:%02d", name.array, hours, + minutes, seconds); + + pthread_mutex_lock(&out->mutex); + struct chapter *chap = da_push_back_new(out->chapters); + chap->dts_usec = out->last_dts_usec; + chap->name = name.array; + pthread_mutex_unlock(&out->mutex); +} + +static void split_file_proc(void *data, calldata_t *cd) +{ + struct mp4_output *out = data; + + calldata_set_bool(cd, "split_file_enabled", out->split_file_enabled); + if (!out->split_file_enabled) + return; + + os_atomic_set_bool(&out->manual_split, true); +} + +static void *mp4_output_create(obs_data_t *settings, obs_output_t *output) +{ + struct mp4_output *out = bzalloc(sizeof(struct mp4_output)); + out->output = output; + pthread_mutex_init(&out->mutex, NULL); + + signal_handler_t *sh = obs_output_get_signal_handler(output); + signal_handler_add(sh, "void file_changed(string next_file)"); + + proc_handler_t *ph = obs_output_get_proc_handler(output); + proc_handler_add(ph, "void split_file(out bool split_file_enabled)", + split_file_proc, out); + proc_handler_add(ph, "void add_chapter(string chapter_name)", + mp4_add_chapter_proc, out); + + UNUSED_PARAMETER(settings); + return out; +} + +static inline void apply_flag(int *flags, const char *value, int flag_value) +{ + if (atoi(value)) + *flags |= flag_value; + else + *flags &= ~flag_value; +} + +static int parse_custom_options(const char *opts_str) +{ + int flags = MP4_USE_NEGATIVE_CTS; + + struct obs_options opts = obs_parse_options(opts_str); + + for (size_t i = 0; i < opts.count; i++) { + struct obs_option opt = opts.options[i]; + + if (strcmp(opt.name, "skip_soft_remux") == 0) { + apply_flag(&flags, opt.value, MP4_SKIP_FINALISATION); + } else if (strcmp(opt.name, "write_encoder_info") == 0) { + apply_flag(&flags, opt.value, MP4_WRITE_ENCODER_INFO); + } else if (strcmp(opt.name, "use_metadata_tags") == 0) { + apply_flag(&flags, opt.value, MP4_USE_MDTA_KEY_VALUE); + } else if (strcmp(opt.name, "use_negative_cts") == 0) { + apply_flag(&flags, opt.value, MP4_USE_NEGATIVE_CTS); + } else { + blog(LOG_WARNING, "Unknown muxer option: %s = %s", + opt.name, opt.value); + } + } + + obs_free_options(opts); + + return flags; +} + +static bool mp4_output_start(void *data) +{ + struct mp4_output *out = data; + + if (!obs_output_can_begin_data_capture(out->output, 0)) + return false; + if (!obs_output_initialize_encoders(out->output, 0)) + return false; + + os_atomic_set_bool(&out->stopping, false); + + /* get path */ + obs_data_t *settings = obs_output_get_settings(out->output); + const char *path = obs_data_get_string(settings, "path"); + dstr_copy(&out->path, path); + + out->max_time = obs_data_get_int(settings, "max_time_sec") * 1000000LL; + out->max_size = obs_data_get_int(settings, "max_size_mb") * 1024 * 1024; + out->split_file_enabled = obs_data_get_bool(settings, "split_file"); + out->allow_overwrite = obs_data_get_bool(settings, "allow_overwrite"); + out->cur_size = 0; + + /* Allow skipping the remux step for debugging purposes. */ + const char *muxer_settings = + obs_data_get_string(settings, "muxer_settings"); + out->flags = parse_custom_options(muxer_settings); + + obs_data_release(settings); + + if (!buffered_file_serializer_init_defaults(&out->serializer, + out->path.array)) { + warn("Unable to open MP4 file '%s'", out->path.array); + return false; + } + + /* Initialise muxer and start capture */ + out->muxer = mp4_mux_create(out->output, &out->serializer, out->flags); + os_atomic_set_bool(&out->active, true); + obs_output_begin_data_capture(out->output, 0); + + info("Writing Hybrid MP4 file '%s'...", out->path.array); + return true; +} + +static inline bool should_split(struct mp4_output *out, + struct encoder_packet *packet) +{ + /* split at video frame on primary track */ + if (packet->type != OBS_ENCODER_VIDEO || packet->track_idx > 0) + return false; + + /* don't split group of pictures */ + if (!packet->keyframe) + return false; + + if (os_atomic_load_bool(&out->manual_split)) + return true; + + /* reached maximum file size */ + if (out->max_size > 0 && + out->cur_size + (int64_t)packet->size >= out->max_size) + return true; + + /* reached maximum duration */ + if (out->max_time > 0 && + packet->dts_usec - out->start_time >= out->max_time) + return true; + + return false; +} + +static void find_best_filename(struct dstr *path, bool space) +{ + int num = 2; + + if (!os_file_exists(path->array)) + return; + + const char *ext = strrchr(path->array, '.'); + if (!ext) + return; + + size_t extstart = ext - path->array; + struct dstr testpath; + dstr_init_copy_dstr(&testpath, path); + for (;;) { + dstr_resize(&testpath, extstart); + dstr_catf(&testpath, space ? " (%d)" : "_%d", num++); + dstr_cat(&testpath, ext); + + if (!os_file_exists(testpath.array)) { + dstr_free(path); + dstr_init_move(path, &testpath); + break; + } + } +} + +static void generate_filename(struct mp4_output *out, struct dstr *dst, + bool overwrite) +{ + obs_data_t *settings = obs_output_get_settings(out->output); + const char *dir = obs_data_get_string(settings, "directory"); + const char *fmt = obs_data_get_string(settings, "format"); + const char *ext = obs_data_get_string(settings, "extension"); + bool space = obs_data_get_bool(settings, "allow_spaces"); + + char *filename = os_generate_formatted_filename(ext, space, fmt); + + dstr_copy(dst, dir); + dstr_replace(dst, "\\", "/"); + if (dstr_end(dst) != '/') + dstr_cat_ch(dst, '/'); + dstr_cat(dst, filename); + + char *slash = strrchr(dst->array, '/'); + if (slash) { + *slash = 0; + os_mkdirs(dst->array); + *slash = '/'; + } + + if (!overwrite) + find_best_filename(dst, space); + + bfree(filename); + obs_data_release(settings); +} + +static bool change_file(struct mp4_output *out, struct encoder_packet *pkt) +{ + uint64_t start_time = os_gettime_ns(); + + /* finalise file */ + for (size_t i = 0; i < out->chapters.num; i++) { + struct chapter *chap = &out->chapters.array[i]; + mp4_mux_add_chapter(out->muxer, chap->dts_usec, chap->name); + } + + mp4_mux_finalise(out->muxer); + + info("Waiting for file writer to finish..."); + + /* flush/close file and destroy old muxer */ + buffered_file_serializer_free(&out->serializer); + mp4_mux_destroy(out->muxer); + + for (size_t i = 0; i < out->chapters.num; i++) + bfree(out->chapters.array[i].name); + + da_clear(out->chapters); + + info("MP4 file split complete. Finalization took %" PRIu64 " ms.", + (os_gettime_ns() - start_time) / 1000000); + + /* open new file */ + generate_filename(out, &out->path, out->allow_overwrite); + info("Changing output file to '%s'", out->path.array); + + if (!buffered_file_serializer_init_defaults(&out->serializer, + out->path.array)) { + warn("Unable to open MP4 file '%s'", out->path.array); + return false; + } + + out->muxer = mp4_mux_create(out->output, &out->serializer, out->flags); + + calldata_t cd = {0}; + signal_handler_t *sh = obs_output_get_signal_handler(out->output); + calldata_set_string(&cd, "next_file", out->path.array); + signal_handler_signal(sh, "file_changed", &cd); + calldata_free(&cd); + + out->cur_size = 0; + out->start_time = pkt->dts_usec; + ts_offset_clear(out); + + return true; +} + +static void mp4_output_stop(void *data, uint64_t ts) +{ + struct mp4_output *out = data; + out->stop_ts = ts / 1000; + os_atomic_set_bool(&out->stopping, true); +} + +static void mp4_output_actual_stop(struct mp4_output *out, int code) +{ + os_atomic_set_bool(&out->active, false); + + uint64_t start_time = os_gettime_ns(); + + for (size_t i = 0; i < out->chapters.num; i++) { + struct chapter *chap = &out->chapters.array[i]; + mp4_mux_add_chapter(out->muxer, chap->dts_usec, chap->name); + } + + mp4_mux_finalise(out->muxer); + + if (code) { + obs_output_signal_stop(out->output, code); + } else { + obs_output_end_data_capture(out->output); + } + + info("Waiting for file writer to finish..."); + + /* Flush/close output file and destroy muxer */ + buffered_file_serializer_free(&out->serializer); + mp4_mux_destroy(out->muxer); + out->muxer = NULL; + + /* Clear chapter data */ + for (size_t i = 0; i < out->chapters.num; i++) + bfree(out->chapters.array[i].name); + + da_clear(out->chapters); + + info("MP4 file output complete. Finalization took %" PRIu64 " ms.", + (os_gettime_ns() - start_time) / 1000000); +} + +static void push_back_packet(struct mp4_output *out, + struct encoder_packet *packet) +{ + struct encoder_packet pkt; + obs_encoder_packet_ref(&pkt, packet); + da_push_back(out->split_buffer, &pkt); +} + +static inline bool submit_packet(struct mp4_output *out, + struct encoder_packet *pkt) +{ + out->total_bytes += pkt->size; + + if (!out->split_file_enabled) + return mp4_mux_submit_packet(out->muxer, pkt); + + out->cur_size += pkt->size; + + /* Apply DTS/PTS offset local packet copy */ + struct encoder_packet modified = *pkt; + + if (modified.type == OBS_ENCODER_VIDEO) { + modified.dts -= out->video_pts_offsets[modified.track_idx]; + modified.pts -= out->video_pts_offsets[modified.track_idx]; + } else { + modified.dts -= out->audio_dts_offsets[modified.track_idx]; + modified.pts -= out->audio_dts_offsets[modified.track_idx]; + } + + return mp4_mux_submit_packet(out->muxer, &modified); +} + +static void mp4_output_packet(void *data, struct encoder_packet *packet) +{ + struct mp4_output *out = data; + + pthread_mutex_lock(&out->mutex); + + if (!active(out)) + goto unlock; + + if (!packet) { + mp4_output_actual_stop(out, OBS_OUTPUT_ENCODE_ERROR); + goto unlock; + } + + if (stopping(out)) { + if (packet->sys_dts_usec >= (int64_t)out->stop_ts) { + mp4_output_actual_stop(out, 0); + goto unlock; + } + } + + if (out->split_file_enabled) { + if (out->split_buffer.num) { + int64_t pts_usec = packet_pts_usec(packet); + struct encoder_packet *first_pkt = + out->split_buffer.array; + int64_t first_pts_usec = packet_pts_usec(first_pkt); + + if (pts_usec >= first_pts_usec) { + if (packet->type != OBS_ENCODER_AUDIO) { + push_back_packet(out, packet); + goto unlock; + } + + if (!change_file(out, first_pkt)) { + mp4_output_actual_stop( + out, OBS_OUTPUT_ERROR); + goto unlock; + } + out->split_file_ready = true; + } + } else if (should_split(out, packet)) { + push_back_packet(out, packet); + goto unlock; + } + } + + if (out->split_file_ready) { + for (size_t i = 0; i < out->split_buffer.num; i++) { + struct encoder_packet *pkt = + &out->split_buffer.array[i]; + ts_offset_update(out, pkt); + submit_packet(out, pkt); + obs_encoder_packet_release(pkt); + } + + da_free(out->split_buffer); + out->split_file_ready = false; + os_atomic_set_bool(&out->manual_split, false); + } + + if (out->split_file_enabled) + ts_offset_update(out, packet); + + /* Update PTS for chapter markers */ + if (packet->type == OBS_ENCODER_VIDEO && packet->track_idx == 0) + out->last_dts_usec = packet->dts_usec - out->start_time; + + submit_packet(out, packet); + + if (serializer_get_pos(&out->serializer) == -1) + mp4_output_actual_stop(out, OBS_OUTPUT_ERROR); + +unlock: + pthread_mutex_unlock(&out->mutex); +} + +static obs_properties_t *mp4_output_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + + obs_properties_t *props = obs_properties_create(); + + obs_properties_add_text(props, "path", + obs_module_text("MP4Output.FilePath"), + OBS_TEXT_DEFAULT); + obs_properties_add_text(props, "muxer_settings", "muxer_settings", + OBS_TEXT_DEFAULT); + return props; +} + +uint64_t mp4_output_total_bytes(void *data) +{ + struct mp4_output *out = data; + return out->total_bytes; +} + +struct obs_output_info mp4_output_info = { + .id = "mp4_output", + .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | + OBS_OUTPUT_MULTI_TRACK_AV | OBS_OUTPUT_CAN_PAUSE, + .encoded_video_codecs = "h264;hevc;av1", + .encoded_audio_codecs = "aac", + .get_name = mp4_output_name, + .create = mp4_output_create, + .destroy = mp4_output_destory, + .start = mp4_output_start, + .stop = mp4_output_stop, + .encoded_packet = mp4_output_packet, + .get_properties = mp4_output_properties, + .get_total_bytes = mp4_output_total_bytes, +}; diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c index 4d55e63662afc3..23ffa7da6db8f7 100644 --- a/plugins/obs-outputs/obs-outputs.c +++ b/plugins/obs-outputs/obs-outputs.c @@ -15,6 +15,7 @@ MODULE_EXPORT const char *obs_module_description(void) extern struct obs_output_info rtmp_output_info; extern struct obs_output_info null_output_info; extern struct obs_output_info flv_output_info; +extern struct obs_output_info mp4_output_info; #if defined(FTL_FOUND) extern struct obs_output_info ftl_output_info; #endif @@ -65,6 +66,7 @@ bool obs_module_load(void) obs_register_output(&rtmp_output_info); obs_register_output(&null_output_info); obs_register_output(&flv_output_info); + obs_register_output(&mp4_output_info); #if defined(FTL_FOUND) obs_register_output(&ftl_output_info); #endif From 4503f0a0563e24814448ddb4d1fb4c7501708762 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 27 Apr 2024 03:55:47 +0200 Subject: [PATCH 0147/1073] UI: Add Hybrid MP4 to format selection --- UI/data/locale/en-US.ini | 1 + UI/ffmpeg-utils.cpp | 14 ++++++++++++++ UI/obs-app.cpp | 2 ++ UI/window-basic-main-outputs.cpp | 13 +++++++++++-- UI/window-basic-settings.cpp | 2 ++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index b9325190d31380..b0681b67099fb3 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -966,6 +966,7 @@ Basic.Settings.Output.Format.MP4="MPEG-4 (.mp4)" Basic.Settings.Output.Format.MOV="QuickTime (.mov)" Basic.Settings.Output.Format.TS="MPEG-TS (.ts)" Basic.Settings.Output.Format.HLS="HLS (.m3u8 + .ts)" +Basic.Settings.Output.Format.hMP4="Hybrid MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmented MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmented MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmented MOV writes the recording in chunks and does not require the same finalization as traditional MOV files.\nThis ensures the file remains playable even if writing to disk is interrupted, for example, as a result of a BSOD or power loss.\n\nThis may not be compatible with all players and editors. Use File → Remux Recordings to convert the file into a more compatible format if necessary." diff --git a/UI/ffmpeg-utils.cpp b/UI/ffmpeg-utils.cpp index c3cd54eb3f8e33..b8f36cd00ce9f1 100644 --- a/UI/ffmpeg-utils.cpp +++ b/UI/ffmpeg-utils.cpp @@ -185,6 +185,20 @@ static const unordered_map> codec_compat = { "pcm_f32le", #endif }}, + // Not part of FFmpeg, see obs-outputs module + {"hybrid_mp4", + { + "h264", + "hevc", + "av1", + "aac", + "opus", + "alac", + "flac", + "pcm_s16le", + "pcm_s24le", + "pcm_f32le", + }}, {"mov", { "h264", diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 88ee9c66acdc86..853630a02f4cad 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1783,6 +1783,8 @@ string GetFormatExt(const char *container) string ext = container; if (ext == "fragmented_mp4") ext = "mp4"; + if (ext == "hybrid_mp4") + ext = "mp4"; else if (ext == "fragmented_mov") ext = "mov"; else if (ext == "hls") diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index f87e16dcd29ba6..21548c6e261469 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -697,6 +697,9 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) if (!ffmpegOutput) { bool useReplayBuffer = config_get_bool(main->Config(), "SimpleOutput", "RecRB"); + const char *recFormat = config_get_string( + main->Config(), "SimpleOutput", "RecFormat2"); + if (useReplayBuffer) { OBSDataAutoRelease hotkey; const char *str = config_get_string( @@ -728,8 +731,10 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) OBSReplayBufferSaved, this); } + bool use_native = strcmp(recFormat, "hybrid_mp4") == 0; fileOutput = obs_output_create( - "ffmpeg_muxer", "simple_file_output", nullptr, nullptr); + use_native ? "mp4_output" : "ffmpeg_muxer", + "simple_file_output", nullptr, nullptr); if (!fileOutput) throw "Failed to create recording output " "(simple output)"; @@ -1568,6 +1573,8 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) config_get_string(main->Config(), "AdvOut", "RecEncoder"); const char *recAudioEncoder = config_get_string(main->Config(), "AdvOut", "RecAudioEncoder"); + const char *recFormat = + config_get_string(main->Config(), "AdvOut", "RecFormat2"); #ifdef __APPLE__ translate_macvth264_encoder(streamEncoder); translate_macvth264_encoder(recordEncoder); @@ -1623,8 +1630,10 @@ AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_) OBSReplayBufferSaved, this); } + bool native_muxer = strcmp(recFormat, "hybrid_mp4") == 0; fileOutput = obs_output_create( - "ffmpeg_muxer", "adv_file_output", nullptr, nullptr); + native_muxer ? "mp4_output" : "ffmpeg_muxer", + "adv_file_output", nullptr, nullptr); if (!fileOutput) throw "Failed to create recording output " "(advanced output)"; diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 6d1b4ae943f27b..58fba4854dd23c 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -1152,6 +1152,7 @@ void OBSBasicSettings::LoadFormats() ui->simpleOutRecFormat->addItem(FORMAT_STR("MKV"), "mkv"); ui->simpleOutRecFormat->addItem(FORMAT_STR("MP4"), "mp4"); ui->simpleOutRecFormat->addItem(FORMAT_STR("MOV"), "mov"); + ui->simpleOutRecFormat->addItem(FORMAT_STR("hMP4"), "hybrid_mp4"); ui->simpleOutRecFormat->addItem(FORMAT_STR("fMP4"), "fragmented_mp4"); ui->simpleOutRecFormat->addItem(FORMAT_STR("fMOV"), "fragmented_mov"); ui->simpleOutRecFormat->addItem(FORMAT_STR("TS"), "mpegts"); @@ -1160,6 +1161,7 @@ void OBSBasicSettings::LoadFormats() ui->advOutRecFormat->addItem(FORMAT_STR("MKV"), "mkv"); ui->advOutRecFormat->addItem(FORMAT_STR("MP4"), "mp4"); ui->advOutRecFormat->addItem(FORMAT_STR("MOV"), "mov"); + ui->advOutRecFormat->addItem(FORMAT_STR("hMP4"), "hybrid_mp4"); ui->advOutRecFormat->addItem(FORMAT_STR("fMP4"), "fragmented_mp4"); ui->advOutRecFormat->addItem(FORMAT_STR("fMOV"), "fragmented_mov"); ui->advOutRecFormat->addItem(FORMAT_STR("TS"), "mpegts"); From 8ac8118b7f0057790e9133117d9080742c47a5b3 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 2 May 2024 07:04:49 +0200 Subject: [PATCH 0148/1073] UI: Add chapter frontend API and hotkey --- UI/api-interface.cpp | 17 +++++++++++++++++ UI/data/locale/en-US.ini | 1 + UI/obs-frontend-api/obs-frontend-api.cpp | 6 ++++++ UI/obs-frontend-api/obs-frontend-api.h | 1 + UI/obs-frontend-api/obs-frontend-internal.hpp | 1 + UI/window-basic-main.cpp | 18 ++++++++++++++++++ UI/window-basic-main.hpp | 3 ++- 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index e150511b99a8c5..39f260277db305 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -337,6 +337,23 @@ struct OBSStudioAPI : obs_frontend_callbacks { } } + bool obs_frontend_recording_add_chapter(const char *name) override + { + if (!os_atomic_load_bool(&recording_active) || + os_atomic_load_bool(&recording_paused)) + return false; + + proc_handler_t *ph = obs_output_get_proc_handler( + main->outputHandler->fileOutput); + + calldata cd; + calldata_init(&cd); + calldata_set_string(&cd, "chapter_name", name); + bool result = proc_handler_call(ph, "add_chapter", &cd); + calldata_free(&cd); + return result; + } + void obs_frontend_replay_buffer_start(void) override { QMetaObject::invokeMethod(main, "StartReplayBuffer"); diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index b0681b67099fb3..5a9cc0d62fa5e6 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -732,6 +732,7 @@ Basic.Main.StopRecording="Stop Recording" Basic.Main.PauseRecording="Pause Recording" Basic.Main.UnpauseRecording="Unpause Recording" Basic.Main.SplitFile="Split Recording File" +Basic.Main.AddChapterMarker="Add Chapter Marker" Basic.Main.StoppingRecording="Stopping Recording..." Basic.Main.StopReplayBuffer="Stop Replay Buffer" Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..." diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index df753551b10e05..7a98d5b0fdbe3c 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -285,6 +285,12 @@ bool obs_frontend_recording_split_file(void) : false; } +bool obs_frontend_recording_add_chapter(const char *name) +{ + return !!callbacks_valid() ? c->obs_frontend_recording_add_chapter(name) + : false; +} + void obs_frontend_replay_buffer_start(void) { if (callbacks_valid()) diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index a0913e683359f1..063aa4aa184a18 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -190,6 +190,7 @@ EXPORT bool obs_frontend_recording_active(void); EXPORT void obs_frontend_recording_pause(bool pause); EXPORT bool obs_frontend_recording_paused(void); EXPORT bool obs_frontend_recording_split_file(void); +EXPORT bool obs_frontend_recording_add_chapter(const char *name); EXPORT void obs_frontend_replay_buffer_start(void); EXPORT void obs_frontend_replay_buffer_save(void); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index 90a0e06ea9dd5b..1e600619962f15 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -53,6 +53,7 @@ struct obs_frontend_callbacks { virtual void obs_frontend_recording_pause(bool pause) = 0; virtual bool obs_frontend_recording_paused(void) = 0; virtual bool obs_frontend_recording_split_file(void) = 0; + virtual bool obs_frontend_recording_add_chapter(const char *name) = 0; virtual void obs_frontend_replay_buffer_start(void) = 0; virtual void obs_frontend_replay_buffer_save(void) = 0; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 515797990db8bd..f173886e1200cb 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2808,6 +2808,23 @@ void OBSBasic::CreateHotkeys() this); LoadHotkey(splitFileHotkey, "OBSBasic.SplitFile"); + /* Adding chapters is only supported by the native MP4 output */ + const string_view output_id = + obs_output_get_id(outputHandler->fileOutput); + if (output_id == "mp4_output") { + addChapterHotkey = obs_hotkey_register_frontend( + "OBSBasic.AddChapterMarker", + Str("Basic.Main.AddChapterMarker"), + [](void *, obs_hotkey_id, obs_hotkey_t *, + bool pressed) { + if (pressed) + obs_frontend_recording_add_chapter( + nullptr); + }, + this); + LoadHotkey(addChapterHotkey, "OBSBasic.AddChapterMarker"); + } + replayBufHotkeys = obs_hotkey_pair_register_frontend( "OBSBasic.StartReplayBuffer", Str("Basic.Main.StartReplayBuffer"), @@ -2936,6 +2953,7 @@ void OBSBasic::ClearHotkeys() obs_hotkey_pair_unregister(recordingHotkeys); obs_hotkey_pair_unregister(pauseHotkeys); obs_hotkey_unregister(splitFileHotkey); + obs_hotkey_unregister(addChapterHotkey); obs_hotkey_pair_unregister(replayBufHotkeys); obs_hotkey_pair_unregister(vcamHotkeys); obs_hotkey_pair_unregister(togglePreviewHotkeys); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index d941c490e60f26..fc8420a05b3991 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -460,7 +460,8 @@ class OBSBasic : public OBSMainWindow { obs_hotkey_pair_id streamingHotkeys, recordingHotkeys, pauseHotkeys, replayBufHotkeys, vcamHotkeys, togglePreviewHotkeys, contextBarHotkeys; - obs_hotkey_id forceStreamingStopHotkey, splitFileHotkey; + obs_hotkey_id forceStreamingStopHotkey, splitFileHotkey, + addChapterHotkey; void InitDefaultTransitions(); void InitTransition(obs_source_t *transition); From b5b457d7b059397b70f6e3dd09b65e172ad734c3 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 22 May 2024 09:25:38 +0200 Subject: [PATCH 0149/1073] CI: Attest signed Windows build --- .github/workflows/sign-windows.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/sign-windows.yaml b/.github/workflows/sign-windows.yaml index 5003819c20466e..b160a40e5c6dad 100644 --- a/.github/workflows/sign-windows.yaml +++ b/.github/workflows/sign-windows.yaml @@ -52,6 +52,11 @@ jobs: version: ${{ github.ref_name }} channel: ${{ steps.setup.outputs.channel }} + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-path: ${{ github.workspace }}/output/*-x64.zip + - name: Upload Signed Build uses: actions/upload-artifact@v4 with: From dfc842bfc4e9d61d70907f0f3f73ade869282232 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 23 May 2024 07:56:28 +0200 Subject: [PATCH 0150/1073] CI: Update signing workflow commit and permissions --- .github/workflows/push.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 2433cde085358a..ad107e55be2f12 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -207,12 +207,13 @@ jobs: sign-windows-build: name: Windows Signing ✍️ - uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@d7bf65a80b40bec6446dcb4a2f03629fb74cc3f9 + uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@b5b457d7b059397b70f6e3dd09b65e172ad734c3 if: github.repository_owner == 'obsproject' && github.ref_type == 'tag' needs: build-project permissions: contents: 'read' id-token: 'write' + attestations: 'write' secrets: inherit create-release: From 609ddd075e257b3ed0ddcea363a931c68f8b8be9 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 22 May 2024 09:26:08 +0200 Subject: [PATCH 0151/1073] CI: Verify build attestation during patch generation --- .github/actions/windows-patches/action.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml index e9103867eaa1c0..fb87ed5c7575e0 100644 --- a/.github/actions/windows-patches/action.yaml +++ b/.github/actions/windows-patches/action.yaml @@ -33,8 +33,9 @@ runs: run: | # Download OBS release . ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1 - Invoke-External gh release download "${{ inputs.tagName }}" -p "*-Windows.zip" - Expand-Archive -Path "*-Windows.zip" -DestinationPath "${{ github.workspace }}/build" + Invoke-External gh release download "${{ inputs.tagName }}" -p "OBS-Studio-${{ inputs.tagName }}-Windows.zip" + Invoke-External gh attestation verify "OBS-Studio-${{ inputs.tagName }}-Windows.zip" --owner obsproject + Expand-Archive -Path "OBS-Studio-${{ inputs.tagName }}-Windows.zip" -DestinationPath "${{ github.workspace }}/build" - name: Setup bouf shell: pwsh From d813b7837a7b0b4d6f7a6bdfeb8df851edcfdb59 Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Fri, 24 May 2024 17:32:59 -0700 Subject: [PATCH 0152/1073] cmake: Do not write build number file if OBS_BUILD_NUMBER set Allows for configuration and build from a read-only-mounted source dir by setting the build number externally. For example: `cmake -DOBS_BUILD_NUMBER=1` --- cmake/Modules/VersionConfig.cmake | 14 ++++++++------ cmake/common/buildnumber.cmake | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/cmake/Modules/VersionConfig.cmake b/cmake/Modules/VersionConfig.cmake index 36b34f992c5b16..b1d5dd1b977424 100644 --- a/cmake/Modules/VersionConfig.cmake +++ b/cmake/Modules/VersionConfig.cmake @@ -105,12 +105,14 @@ set(BUILD_NUMBER_CACHE CACHE INTERNAL "OBS build number cache file") # Read build number from cache file or manual override -if(NOT DEFINED OBS_BUILD_NUMBER AND EXISTS ${BUILD_NUMBER_CACHE}) - file(READ ${BUILD_NUMBER_CACHE} OBS_BUILD_NUMBER) - math(EXPR OBS_BUILD_NUMBER "${OBS_BUILD_NUMBER}+1") -elseif(NOT DEFINED OBS_BUILD_NUMBER) - set(OBS_BUILD_NUMBER "1") +if(NOT DEFINED OBS_BUILD_NUMBER) + if(EXISTS ${BUILD_NUMBER_CACHE}) + file(READ ${BUILD_NUMBER_CACHE} OBS_BUILD_NUMBER) + math(EXPR OBS_BUILD_NUMBER "${OBS_BUILD_NUMBER}+1") + else() + set(OBS_BUILD_NUMBER "1") + endif() + file(WRITE ${BUILD_NUMBER_CACHE} "${OBS_BUILD_NUMBER}") endif() -file(WRITE ${BUILD_NUMBER_CACHE} "${OBS_BUILD_NUMBER}") message(STATUS "OBS: Application Version: ${OBS_VERSION} - Build Number: ${OBS_BUILD_NUMBER}") diff --git a/cmake/common/buildnumber.cmake b/cmake/common/buildnumber.cmake index 4d509adc12557f..ef60952bb7c9a2 100644 --- a/cmake/common/buildnumber.cmake +++ b/cmake/common/buildnumber.cmake @@ -8,14 +8,16 @@ set(_BUILD_NUMBER_CACHE CACHE INTERNAL "OBS build number cache file") # Read build number from cache file or manual override -if(NOT DEFINED OBS_BUILD_NUMBER AND EXISTS "${_BUILD_NUMBER_CACHE}") - file(READ "${_BUILD_NUMBER_CACHE}" OBS_BUILD_NUMBER) - math(EXPR OBS_BUILD_NUMBER "${OBS_BUILD_NUMBER}+1") -elseif(NOT DEFINED OBS_BUILD_NUMBER) - if("$ENV{CI}" AND "$ENV{GITHUB_RUN_ID}") - set(OBS_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}") +if(NOT DEFINED OBS_BUILD_NUMBER) + if(EXISTS "${_BUILD_NUMBER_CACHE}") + file(READ "${_BUILD_NUMBER_CACHE}" OBS_BUILD_NUMBER) + math(EXPR OBS_BUILD_NUMBER "${OBS_BUILD_NUMBER}+1") else() - set(OBS_BUILD_NUMBER "1") + if("$ENV{CI}" AND "$ENV{GITHUB_RUN_ID}") + set(OBS_BUILD_NUMBER "$ENV{GITHUB_RUN_ID}") + else() + set(OBS_BUILD_NUMBER "1") + endif() endif() + file(WRITE "${_BUILD_NUMBER_CACHE}" "${OBS_BUILD_NUMBER}") endif() -file(WRITE "${_BUILD_NUMBER_CACHE}" "${OBS_BUILD_NUMBER}") From 51fd7fbaff5a39fc27feee9dd30a2adb67c0bf55 Mon Sep 17 00:00:00 2001 From: Tunghohin Date: Fri, 17 May 2024 16:47:49 +0800 Subject: [PATCH 0153/1073] UI: Removed unused friend classes --- UI/auth-twitch.hpp | 2 -- UI/window-basic-main.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/UI/auth-twitch.hpp b/UI/auth-twitch.hpp index 152b2a95730fd7..21d964d2b4393f 100644 --- a/UI/auth-twitch.hpp +++ b/UI/auth-twitch.hpp @@ -13,8 +13,6 @@ class BrowserDock; class TwitchAuth : public OAuthStreamKey { Q_OBJECT - friend class TwitchLogin; - bool uiLoaded = false; std::string name; diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index d941c490e60f26..7853c442d4fd3a 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -191,7 +191,6 @@ class OBSBasic : public OBSMainWindow { friend class ExtraBrowsersModel; friend class ExtraBrowsersDelegate; friend class DeviceCaptureToolbar; - friend class DeviceToolbarPropertiesThread; friend class OBSBasicSourceSelect; friend class OBSYoutubeActions; friend class OBSPermissions; From 9eca7b75252666f2c9790adbce497f52cf72b41f Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 26 May 2024 01:19:36 +0200 Subject: [PATCH 0154/1073] UI: Fix Qt 6.7 checkbox signal deprecations qt/qtbase@3512fb1ec5ff088772170540c4e91b1886fbea45 deprecated the stateChanged signal of QCheckBoxes in favor of a new checkStateChanged signal. The signals are the same, except that now the enum type is passed explicitly (before the enum was passed as an argument but defined as an int). --- UI/properties-view.cpp | 4 ++++ UI/window-basic-settings.cpp | 14 ++++++++++++++ UI/window-basic-settings.hpp | 5 +++++ UI/window-basic-transform.cpp | 6 +++++- UI/window-youtube-actions.cpp | 13 ++++++++++--- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 6f34b14d37a40f..d7d5b3787efbf5 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -273,7 +273,11 @@ QWidget *OBSPropertiesView::AddCheckbox(obs_property_t *prop) QCheckBox *checkbox = new QCheckBox(QT_UTF8(desc)); checkbox->setCheckState(val ? Qt::Checked : Qt::Unchecked); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + return NewWidget(prop, checkbox, &QCheckBox::checkStateChanged); +#else return NewWidget(prop, checkbox, &QCheckBox::stateChanged); +#endif } QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 6d1b4ae943f27b..d89dd6e7af8fb5 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -790,8 +790,13 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) &OBSBasicSettings::SimpleReplayBufferChanged); connect(ui->simpleRBSecMax, &QSpinBox::valueChanged, this, &OBSBasicSettings::SimpleReplayBufferChanged); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + connect(ui->advOutSplitFile, &QCheckBox::checkStateChanged, this, + &OBSBasicSettings::AdvOutSplitFileChanged); +#else connect(ui->advOutSplitFile, &QCheckBox::stateChanged, this, &OBSBasicSettings::AdvOutSplitFileChanged); +#endif connect(ui->advOutSplitFileType, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvOutSplitFileChanged); connect(ui->advReplayBuf, &QCheckBox::toggled, this, @@ -1360,8 +1365,13 @@ void OBSBasicSettings::LoadGeneralSettings() "HideOBSWindowsFromCapture"); ui->hideOBSFromCapture->setChecked(hideWindowFromCapture); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + connect(ui->hideOBSFromCapture, &QCheckBox::checkStateChanged, + this, &OBSBasicSettings::HideOBSWindowWarning); +#else connect(ui->hideOBSFromCapture, &QCheckBox::stateChanged, this, &OBSBasicSettings::HideOBSWindowWarning); +#endif } #endif @@ -4713,7 +4723,11 @@ void OBSBasicSettings::SpeakerLayoutChanged(int idx) UpdateAudioWarnings(); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) +void OBSBasicSettings::HideOBSWindowWarning(Qt::CheckState state) +#else void OBSBasicSettings::HideOBSWindowWarning(int state) +#endif { if (loading || state == Qt::Unchecked) return; diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index c6f550c1443981..e15c259348f389 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -454,7 +454,12 @@ private slots: void on_colorPreset_currentIndexChanged(int idx); void GeneralChanged(); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + void HideOBSWindowWarning(Qt::CheckState state); +#else void HideOBSWindowWarning(int state); +#endif void AudioChanged(); void AudioChangedRestart(); void ReloadAudioSources(); diff --git a/UI/window-basic-transform.cpp b/UI/window-basic-transform.cpp index f0add7ec3d1cb3..472f98d2405a1a 100644 --- a/UI/window-basic-transform.cpp +++ b/UI/window-basic-transform.cpp @@ -71,9 +71,13 @@ OBSBasicTransform::OBSBasicTransform(OBSSceneItem item, OBSBasic *parent) &OBSBasicTransform::OnCropChanged); HookWidget(ui->cropBottom, ISCROLL_CHANGED, &OBSBasicTransform::OnCropChanged); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + HookWidget(ui->cropToBounds, &QCheckBox::checkStateChanged, + &OBSBasicTransform::OnControlChanged); +#else HookWidget(ui->cropToBounds, &QCheckBox::stateChanged, &OBSBasicTransform::OnControlChanged); - +#endif ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true); connect(ui->buttonBox->button(QDialogButtonBox::Reset), diff --git a/UI/window-youtube-actions.cpp b/UI/window-youtube-actions.cpp index e0b27cef1e6e9a..52d905f911f122 100644 --- a/UI/window-youtube-actions.cpp +++ b/UI/window-youtube-actions.cpp @@ -67,10 +67,17 @@ OBSYoutubeActions::OBSYoutubeActions(QWidget *parent, Auth *auth, [](const QString &link) { QDesktopServices::openUrl(link); }); ui->scheduledTime->setVisible(false); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + connect(ui->checkScheduledLater, &QCheckBox::checkStateChanged, this, + [&](Qt::CheckState state) +#else connect(ui->checkScheduledLater, &QCheckBox::stateChanged, this, - [&](int state) { - ui->scheduledTime->setVisible(state); - if (state) { + [&](int state) +#endif + { + const bool checked = (state == Qt::Checked); + ui->scheduledTime->setVisible(checked); + if (checked) { ui->checkAutoStart->setVisible(true); ui->checkAutoStop->setVisible(true); ui->helpAutoStartStop->setVisible(true); From e92010cc11328ee2a3b2fa63ca28fc0c2d42be36 Mon Sep 17 00:00:00 2001 From: KOU_CHANG Date: Sat, 3 Feb 2024 14:48:02 +0900 Subject: [PATCH 0155/1073] rtmp-services: Add sheeta --- plugins/rtmp-services/data/package.json | 4 +- plugins/rtmp-services/data/services.json | 75 ++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index c92a215456c487..46f32ec173a1e7 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 253, + "version": 254, "files": [ { "name": "services.json", - "version": 253 + "version": 254 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index cc56182271f08f..a2e7e738454226 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2828,6 +2828,81 @@ "max audio bitrate": 320, "bframes": 0 } + }, + { + "name": "sheeta", + "common": false, + "more_info_link": "https://partner-support.sheeta.com/hc/ja/articles/4404573942425-%E7%94%9F%E6%94%BE%E9%80%81%E3%81%AE%E9%85%8D%E4%BF%A1%E6%96%B9%E6%B3%95", + "servers": [ + { + "name": "Default", + "url": "rtmp://lsm.sheeta.com:1935/lsm" + } + ], + "protocol": "RTMP", + "supported video codecs": [ + "h264" + ], + "supported audio codecs": [ + "aac" + ], + "recommended": { + "keyint": 1, + "profile": "main", + "supported resolutions": [ + "1920x1080", + "1280x720", + "854x480", + "640x480" + ], + "bitrate matrix": [ + { + "res": "1920x1080", + "fps": 30, + "max bitrate": 6000 + }, + { + "res": "1280x720", + "fps": 30, + "max bitrate": 4700 + }, + { + "res": "854x480", + "fps": 30, + "max bitrate": 3400 + }, + { + "res": "640x480", + "fps": 30, + "max bitrate": 3400 + }, + { + "res": "1920x1080", + "fps": 60, + "max bitrate": 9000 + }, + { + "res": "1280x720", + "fps": 60, + "max bitrate": 7000 + }, + { + "res": "854x480", + "fps": 60, + "max bitrate": 5000 + }, + { + "res": "640x480", + "fps": 60, + "max bitrate": 5000 + } + ], + "max fps": 60, + "max video bitrate": 9000, + "max audio bitrate": 512, + "x264opts": "scenecut=0", + "output": "rtmp_output" + } } ] } From cf4681a9c3f177269c2d7344984497ce2cbaf3f7 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 29 May 2024 08:19:59 +0200 Subject: [PATCH 0156/1073] docs: Document obs_frontend_recording_add_chapter() --- docs/sphinx/reference-frontend-api.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 241ad7e61ca5aa..22af562092b8a8 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -647,6 +647,17 @@ Functions --------------------------------------- +.. function:: bool obs_frontend_recording_add_chapter(const char *name) + + Asks OBS to insert a chapter marker at the current output time into the recording. + + :param name: The name for the chapter, may be *NULL* to use an automatically generated name ("Unnamed " or localized equivalent). + :return: *true* if insertion was successful, *false* if recording is inactive, paused, or if chapter insertion is not supported by the current output. + + .. versionadded:: 30.2 + +--------------------------------------- + .. function:: void obs_frontend_replay_buffer_start(void) Starts the replay buffer. From fc1ab5fcbc8d99296dd36618e1a3c7682a70e65f Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Sat, 25 May 2024 16:58:05 -0700 Subject: [PATCH 0157/1073] obs-ffmpeg: Check correct VA-API codec support Previously, we just checked for H264 regardless of which codec was selected. This mostly worked, but on Fedora they ship AV1 without H264 or HEVC. In that case users wont see AV1 as an option. --- plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c index 2f580f8118b39d..96d08f29f652e0 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c @@ -1074,6 +1074,21 @@ static bool get_device_name_from_pci(struct pci_access *pacc, char *pci_slot, return false; } +static bool vaapi_device_codec_supported(const char *path, + enum codec_type codec) +{ + switch (codec) { + case CODEC_H264: + return vaapi_device_h264_supported(path); + case CODEC_HEVC: + return vaapi_device_hevc_supported(path); + case CODEC_AV1: + return vaapi_device_av1_supported(path); + default: + return false; + } +} + static obs_properties_t *vaapi_properties_internal(enum codec_type codec) { obs_properties_t *props = obs_properties_create(); @@ -1122,7 +1137,7 @@ static obs_properties_t *vaapi_properties_internal(enum codec_type codec) pacc, pci_slot, namebuf, sizeof(namebuf)); - if (!vaapi_device_h264_supported(path)) + if (!vaapi_device_codec_supported(path, codec)) continue; if (!name_found) From 7a870fd9231286ee7af6eceb22a2a865675b07da Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 27 May 2024 18:50:36 +0200 Subject: [PATCH 0158/1073] obs-qsv11: Fix QSV failing on multi-vendor multi-GPU systems Adds index correction present in QSV test binary to Windows encoder initialization as well. This is necessary when the adapter index of the Intel GPU is not the same as the "implementation" index in MFX. --- plugins/obs-qsv11/common_utils_windows.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/obs-qsv11/common_utils_windows.cpp b/plugins/obs-qsv11/common_utils_windows.cpp index 06c91b0c767b69..1b4326f350f05c 100644 --- a/plugins/obs-qsv11/common_utils_windows.cpp +++ b/plugins/obs-qsv11/common_utils_windows.cpp @@ -33,26 +33,31 @@ mfxStatus Initialize(mfxVersion ver, mfxSession *pSession, obs_video_info ovi; obs_get_video_info(&ovi); mfxU32 adapter_idx = ovi.adapter; + mfxU32 idx_adjustment = 0; // Select current adapter - will be iGPU if exists due to adapter reordering if (codec == QSV_CODEC_AV1 && !adapters[adapter_idx].supports_av1) { - for (mfxU32 i = 0; i < 4; i++) { + for (mfxU32 i = 0; i < MAX_ADAPTERS; i++) { + if (!adapters[i].is_intel) { + idx_adjustment++; + continue; + } if (adapters[i].supports_av1) { adapter_idx = i; break; } } } else if (!adapters[adapter_idx].is_intel) { - for (mfxU32 i = 0; i < 4; i++) { + for (mfxU32 i = 0; i < MAX_ADAPTERS; i++) { if (adapters[i].is_intel) { adapter_idx = i; break; } + idx_adjustment++; } } - adapter_index = adapter_idx; - + adapter_idx -= idx_adjustment; mfxStatus sts = MFX_ERR_NONE; mfxVariant impl; From 64f6c30cb1dbef0c92d54ee8016dcb9f8e3c22e7 Mon Sep 17 00:00:00 2001 From: CodeYan01 <65320293+CodeYan01@users.noreply.github.com> Date: Sat, 1 Jun 2024 11:02:31 +0800 Subject: [PATCH 0159/1073] docs: Add return type of obs_source_audio_active --- docs/sphinx/reference-sources.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/reference-sources.rst b/docs/sphinx/reference-sources.rst index af14d0d4666888..6def21e59233bf 100644 --- a/docs/sphinx/reference-sources.rst +++ b/docs/sphinx/reference-sources.rst @@ -1215,8 +1215,8 @@ General Source Functions --------------------- -.. function:: obs_source_set_audio_active(obs_source_t *source, bool active) - obs_source_audio_active(const obs_source_t *source) +.. function:: void obs_source_set_audio_active(obs_source_t *source, bool active) + bool obs_source_audio_active(const obs_source_t *source) Sets/gets the audio active status (controls whether the source is shown in the mixer). From acd4854cedd702030e2493aa63e987f555fe78ba Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 2 Jun 2024 04:29:10 +0200 Subject: [PATCH 0160/1073] obs-outputs: Add native mp4 output to legacy cmake --- plugins/obs-outputs/cmake/legacy.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/obs-outputs/cmake/legacy.cmake b/plugins/obs-outputs/cmake/legacy.cmake index d7e7d64750e6b4..12b474a1e412f9 100644 --- a/plugins/obs-outputs/cmake/legacy.cmake +++ b/plugins/obs-outputs/cmake/legacy.cmake @@ -20,6 +20,10 @@ target_sources( flv-mux.c flv-mux.h flv-output.c + mp4-mux-internal.h + mp4-mux.c + mp4-mux.h + mp4-output.c net-if.c net-if.h null-output.c @@ -51,7 +55,7 @@ if(ENABLE_HEVC) target_sources(obs-outputs PRIVATE rtmp-hevc.c rtmp-hevc.h) endif() -target_link_libraries(obs-outputs PRIVATE OBS::libobs OBS::happy-eyeballs) +target_link_libraries(obs-outputs PRIVATE OBS::libobs OBS::happy-eyeballs OBS::opts-parser) set_target_properties(obs-outputs PROPERTIES FOLDER "plugins" PREFIX "") From 9c427c5f933caf12449532bc79396f8c314ed419 Mon Sep 17 00:00:00 2001 From: CodeYan01 <65320293+CodeYan01@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:41:09 +0800 Subject: [PATCH 0161/1073] docs: Add versionadded for 30.x functions --- docs/sphinx/reference-core.rst | 4 ++++ docs/sphinx/reference-encoders.rst | 13 +++++++++++++ docs/sphinx/reference-frontend-api.rst | 6 ++++++ docs/sphinx/reference-libobs-util-deque.rst | 2 ++ docs/sphinx/reference-modules.rst | 4 +++- docs/sphinx/reference-properties.rst | 4 ++++ docs/sphinx/reference-settings.rst | 2 ++ docs/sphinx/reference-sources.rst | 2 ++ 8 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/reference-core.rst b/docs/sphinx/reference-core.rst index ba986cfc02a6a3..576cd0351a2b92 100644 --- a/docs/sphinx/reference-core.rst +++ b/docs/sphinx/reference-core.rst @@ -544,6 +544,8 @@ Video, Audio, and Graphics Resets all audio monitoring devices. + .. versionadded:: 30.1 + --------------------- .. function:: void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data) @@ -922,3 +924,5 @@ Views .. function:: void obs_view_enum_video_info(obs_view_t *view, bool (*enum_proc)(void *, struct obs_video_info *), void *param) Enumerates all the video info of all mixes that use the specified mix. + + .. versionadded:: 30.1 diff --git a/docs/sphinx/reference-encoders.rst b/docs/sphinx/reference-encoders.rst index a87df0a0c26737..b35a2f30c3d751 100644 --- a/docs/sphinx/reference-encoders.rst +++ b/docs/sphinx/reference-encoders.rst @@ -165,6 +165,7 @@ Encoder Definition Structure (obs_encoder_info) - **OBS_ENCODER_CAP_DEPRECATED** - Encoder is deprecated - **OBS_ENCODER_CAP_ROI** - Encoder supports region of interest feature + .. versionadded:: 30.1 Encoder Packet Structure (encoder_packet) ----------------------------------------- @@ -273,6 +274,8 @@ Encoder Region of Interest Structure (obs_encoder_roi) Encoder region of interest structure. + .. versionadded:: 30.1 + .. member:: uint32_t top uint32_t bottom uint32_t left @@ -549,18 +552,24 @@ General Encoder Functions :return: *true* if adding succeeded, *false* otherwise. + .. versionadded:: 30.1 + --------------------- .. function:: bool obs_encoder_has_roi(obs_encoder_t *encoder) :return: *true* if encoder has ROI regions set, *false* otherwise. + .. versionadded:: 30.1 + --------------------- .. function:: void obs_encoder_clear_roi(obs_encoder_t *encoder) Clear region of interest list, if any. + .. versionadded:: 30.1 + --------------------- .. function:: void obs_encoder_enum_roi(obs_encoder_t *encoder, void (*enum_proc)(void *, struct obs_encoder_roi *), void *param) @@ -569,6 +578,8 @@ General Encoder Functions **Note:** If the encoder has scaling enabled the struct passed to the callback will be scaled accordingly. + .. versionadded:: 30.1 + --------------------- .. function:: uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder) @@ -577,6 +588,8 @@ General Encoder Functions :return: Increment/revision of ROI list + .. versionadded:: 30.1 + --------------------- diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 22af562092b8a8..0a88451c2c95da 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -489,6 +489,8 @@ Functions :return: *true* if the dock was added, *false* if the id was already used + .. versionadded:: 30.0 + --------------------------------------- .. function:: void obs_frontend_remove_dock(const char *id) @@ -497,6 +499,8 @@ Functions :param id: Unique identifier of the dock to remove. + .. versionadded:: 30.0 + --------------------------------------- .. function:: bool obs_frontend_add_custom_qdock(const char *id, void *dock) @@ -511,6 +515,8 @@ Functions :return: *true* if the dock was added, *false* if the id was already used + .. versionadded:: 30.0 + --------------------------------------- .. function:: void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) diff --git a/docs/sphinx/reference-libobs-util-deque.rst b/docs/sphinx/reference-libobs-util-deque.rst index fdbce90c6b82b2..f6cec26a6d08d3 100644 --- a/docs/sphinx/reference-libobs-util-deque.rst +++ b/docs/sphinx/reference-libobs-util-deque.rst @@ -8,6 +8,8 @@ as data is pushed to the front or back. #include +.. versionadded:: 30.1 + Deque Structure (struct deque) -------------------------------------------- diff --git a/docs/sphinx/reference-modules.rst b/docs/sphinx/reference-modules.rst index 6b5d7a7747144c..b799355dc41107 100644 --- a/docs/sphinx/reference-modules.rst +++ b/docs/sphinx/reference-modules.rst @@ -271,13 +271,15 @@ plugin modules. --------------------- -.. function:: void *obs_add_safe_module(const char *name) +.. function:: void obs_add_safe_module(const char *name) Adds a *name* to the list of modules allowed to load in Safe Mode. If the list is empty, all modules are allowed. :param name: The name of the module (filename sans extension). + .. versionadded:: 30.0 + --------------------- .. function:: void obs_module_failure_info_free(struct obs_module_failure_info *mfi) diff --git a/docs/sphinx/reference-properties.rst b/docs/sphinx/reference-properties.rst index 554a2348384a0d..1081dd1c6df767 100644 --- a/docs/sphinx/reference-properties.rst +++ b/docs/sphinx/reference-properties.rst @@ -209,6 +209,8 @@ Property Object Functions - **OBS_COMBO_TYPE_LIST** - Not editable. Displayed as combo box. - **OBS_COMBO_TYPE_RADIO** - Not editable. Displayed as radio buttons. + .. versionadded:: 30.0 + :param format: Can be one of the following values: - **OBS_COMBO_FORMAT_INT** - Integer list @@ -217,6 +219,8 @@ Property Object Functions - **OBS_COMBO_FORMAT_STRING** - String list - **OBS_COMBO_FORMAT_BOOL** - Boolean list + .. versionadded:: 30.0 + :return: The property Important Related Functions: diff --git a/docs/sphinx/reference-settings.rst b/docs/sphinx/reference-settings.rst index e15be3a2702de5..dcb2ed63089f09 100644 --- a/docs/sphinx/reference-settings.rst +++ b/docs/sphinx/reference-settings.rst @@ -304,6 +304,8 @@ inappropriate or invalid. :return: An incremented reference to a data array object. Release with :c:func:`obs_data_array_release()`. + .. versionadded:: 30.1 + --------------------- diff --git a/docs/sphinx/reference-sources.rst b/docs/sphinx/reference-sources.rst index 6def21e59233bf..bac280dc6b9262 100644 --- a/docs/sphinx/reference-sources.rst +++ b/docs/sphinx/reference-sources.rst @@ -661,6 +661,8 @@ The following signals are defined for every source type: Called when a filter has been added to the source. + .. versionadded:: 30.0 + **filter_remove** (ptr source, ptr filter) Called when a filter has been removed from the source. From 17d654fcfcea5a5402aa0517cfe579fff07da644 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Thu, 30 May 2024 17:33:39 -0500 Subject: [PATCH 0162/1073] UI: Make status bar record output a weak ref This changes the status bar record output from a strong reference to a weak one. --- UI/window-basic-status-bar.cpp | 2 +- UI/window-basic-status-bar.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp index bb4255480e1814..9ec29190aa1f6a 100644 --- a/UI/window-basic-status-bar.cpp +++ b/UI/window-basic-status-bar.cpp @@ -561,7 +561,7 @@ void OBSBasicStatusBar::StreamStopped() void OBSBasicStatusBar::RecordingStarted(obs_output_t *output) { - recordOutput = output; + recordOutput = OBSGetWeakRef(output); Activate(); } diff --git a/UI/window-basic-status-bar.hpp b/UI/window-basic-status-bar.hpp index d0c645ad88a317..38be9e520cb5d7 100644 --- a/UI/window-basic-status-bar.hpp +++ b/UI/window-basic-status-bar.hpp @@ -29,7 +29,7 @@ class OBSBasicStatusBar : public QStatusBar { OBSWeakOutputAutoRelease streamOutput; std::vector streamSigs; - obs_output_t *recordOutput = nullptr; + OBSWeakOutputAutoRelease recordOutput; bool active = false; bool overloadedNotify = true; bool streamPauseIconToggle = false; From 52ae5fc4bd518c67edd8ab94121ace05d9076892 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sat, 20 Apr 2024 19:58:20 -0400 Subject: [PATCH 0163/1073] UI: Update volume meter appearance --- UI/data/themes/Yami.obt | 94 ++++++++++++++++++-- UI/volume-control.cpp | 167 +++++++++++++++++++++++++++--------- UI/volume-control.hpp | 13 ++- libobs/obs-audio-controls.c | 7 +- libobs/obs-audio-controls.h | 4 + 5 files changed, 237 insertions(+), 48 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index e6056dcd631cae..cf9230b772e7ab 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -129,6 +129,10 @@ --spinbox_button_height: calc(var(--input_height) - 2px); + --volume_slider: calc(calc(6px + var(--font_base_value)) / 2); + --volume_slider_box: calc(var(--volume_slider) * 4); + --volume_slider_label: calc(var(--volume_slider) * 6); + --scrollbar_size: 12px; /* Inputs / Controls */ @@ -295,15 +299,10 @@ SourceTree QWidget { /* Misc */ -QAbstractItemView, -QStackedWidget#stackedMixerArea QWidget { +QAbstractItemView { background-color: var(--bg_base); } -QStackedWidget#stackedMixerArea QScrollBar { - background-color: var(--grey6); -} - QToolTip { background-color: var(--bg_base); color: var(--text); @@ -1210,6 +1209,89 @@ QSlider::handle:disabled { background-color: var(--button_bg_down); } +#stackedMixerArea { + border: none; + padding: 0px; + border-bottom: 1px solid #3c404b; +} + +VolControl #volLabel { + padding: var(--padding_base) 0px var(--padding_base); + text-align: center; + font-size: var(--font_base); + color: var(--text_muted); +} + +/* Horizontal Mixer */ +#hMixerScrollArea VolControl { + padding: 0px var(--padding_large); + border-bottom: 1px solid var(--border_color); +} + +#hMixerScrollArea VolControl QSlider { + margin: 0px 0px; +} + +#hMixerScrollArea VolControl QSlider::groove:horizontal { + background: var(--bg_window); + height: var(--volume_slider); +} + +#hMixerScrollArea VolControl QPushButton { + margin-right: var(--padding_xlarge); +} + +/* Vertical Mixer */ +#stackedMixerArea QScrollBar:vertical { + border-left: 1px solid var(--border_color); +} + +#vMixerScrollArea VolControl { + padding: var(--padding_large) 0px var(--padding_base); + border-right: 1px solid var(--border_color); +} + +#vMixerScrollArea VolControl QSlider { + width: var(--volume_slider_box); +} + +#vMixerScrollArea VolControl #volLabel { + padding: var(--padding_base) 0px var(--padding_base); + min-width: var(--volume_slider_label); + max-width: var(--volume_slider_label); + margin-right: 0; + text-align: center; +} + +#vMixerScrollArea VolControl QSlider::groove:vertical { + background: var(--bg_window); + width: var(--volume_slider); +} + +#vMixerScrollArea VolControl #volMeterFrame { + padding: var(--padding_large) var(--padding_xlarge); +} + +#vMixerScrollArea VolControl QLabel { + padding: 0px var(--padding_large); +} + +#vMixerScrollArea VolControl QPushButton { + margin-right: var(--padding_xlarge); +} + +#vMixerScrollArea VolControl MuteCheckBox { + margin-left: var(--padding_xlarge); +} + +VolControl { + background: var(--bg_base); +} + +VolumeMeter { + background: transparent; +} + VolumeMeter { qproperty-backgroundNominalColor: var(--green5); qproperty-backgroundWarningColor: var(--yellow5); diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index a8d29f3ca18951..06cb587edcfd2b 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -246,6 +246,9 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) volLabel = new QLabel(); mute = new MuteCheckBox(); + volLabel->setObjectName("volLabel"); + volLabel->setAlignment(Qt::AlignCenter); + QString sourceName = obs_source_get_name(source); setObjectName(sourceName); @@ -262,24 +265,28 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) } QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->setContentsMargins(4, 4, 4, 4); - mainLayout->setSpacing(2); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); if (vertical) { QHBoxLayout *nameLayout = new QHBoxLayout; QHBoxLayout *controlLayout = new QHBoxLayout; QHBoxLayout *volLayout = new QHBoxLayout; + QFrame *meterFrame = new QFrame; QHBoxLayout *meterLayout = new QHBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, true); slider = new VolumeSlider(obs_fader, Qt::Vertical); slider->setLayoutDirection(Qt::LeftToRight); + slider->setDisplayTicks(true); nameLayout->setAlignment(Qt::AlignCenter); meterLayout->setAlignment(Qt::AlignCenter); controlLayout->setAlignment(Qt::AlignCenter); volLayout->setAlignment(Qt::AlignCenter); + meterFrame->setObjectName("volMeterFrame"); + nameLayout->setContentsMargins(0, 0, 0, 0); nameLayout->setSpacing(0); nameLayout->addWidget(nameLabel); @@ -287,28 +294,38 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) controlLayout->setContentsMargins(0, 0, 0, 0); controlLayout->setSpacing(0); + controlLayout->setAlignment(mute, Qt::AlignVCenter); + // Add Headphone (audio monitoring) widget here + controlLayout->addWidget(mute); + controlLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum)); + if (showConfig) { controlLayout->addWidget(config); controlLayout->setAlignment(config, Qt::AlignVCenter); } - controlLayout->addItem(new QSpacerItem(3, 0)); - // Add Headphone (audio monitoring) widget here - controlLayout->addWidget(mute); - controlLayout->setAlignment(mute, Qt::AlignVCenter); - meterLayout->setContentsMargins(0, 0, 0, 0); meterLayout->setSpacing(0); - meterLayout->addWidget(volMeter); meterLayout->addWidget(slider); + meterLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum)); + meterLayout->addWidget(volMeter); + + meterFrame->setLayout(meterLayout); volLayout->setContentsMargins(0, 0, 0, 0); volLayout->setSpacing(0); volLayout->addWidget(volLabel); + volLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum)); mainLayout->addItem(nameLayout); mainLayout->addItem(volLayout); - mainLayout->addItem(meterLayout); + mainLayout->addWidget(meterFrame); mainLayout->addItem(controlLayout); volMeter->setFocusProxy(slider); @@ -316,17 +333,22 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) // Default size can cause clipping of long names in vertical layout. QFont font = nameLabel->font(); QFontInfo info(font); - font.setPointSizeF(0.8 * info.pointSizeF()); nameLabel->setFont(font); setMaximumWidth(110); } else { QHBoxLayout *textLayout = new QHBoxLayout; + QFrame *meterFrame = new QFrame; + QHBoxLayout *meterLayout = new QHBoxLayout; QHBoxLayout *botLayout = new QHBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, false); + volMeter->setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred); + slider = new VolumeSlider(obs_fader, Qt::Horizontal); slider->setLayoutDirection(Qt::LeftToRight); + slider->setDisplayTicks(true); textLayout->setContentsMargins(0, 0, 0, 0); textLayout->addWidget(nameLabel); @@ -334,20 +356,28 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) textLayout->setAlignment(nameLabel, Qt::AlignLeft); textLayout->setAlignment(volLabel, Qt::AlignRight); + meterFrame->setObjectName("volMeterFrame"); + meterFrame->setLayout(meterLayout); + + meterLayout->setContentsMargins(0, 0, 0, 0); + meterLayout->setSpacing(0); + + if (showConfig) { + meterLayout->addWidget(config); + meterLayout->setAlignment(config, Qt::AlignVCenter); + } + meterLayout->addWidget(volMeter); + botLayout->setContentsMargins(0, 0, 0, 0); - botLayout->setSpacing(5); - botLayout->addWidget(slider); + botLayout->setSpacing(0); botLayout->addWidget(mute); + botLayout->addWidget(slider); + botLayout->setAlignment(slider, Qt::AlignVCenter); botLayout->setAlignment(mute, Qt::AlignVCenter); - if (showConfig) { - botLayout->addWidget(config); - botLayout->setAlignment(config, Qt::AlignVCenter); - } - mainLayout->addItem(textLayout); - mainLayout->addWidget(volMeter); + mainLayout->addWidget(meterFrame); mainLayout->addItem(botLayout); volMeter->setFocusProxy(slider); @@ -915,6 +945,10 @@ inline void VolumeMeter::doLayout() { QMutexLocker locker(&dataMutex); + if (displayNrAudioChannels) { + int meterSize = std::floor(22 / displayNrAudioChannels); + setMeterThickness(std::clamp(meterSize, 3, 7)); + } recalculateLayout = false; tickFont = font(); @@ -928,14 +962,14 @@ inline void VolumeMeter::doLayout() // and a few pixels before the fader. QRect scaleBounds = metrics.boundingRect("-88"); setMinimumSize(displayNrAudioChannels * (meterThickness + 1) - - 1 + 4 + scaleBounds.width() + 2, - 130); + 1 + 10 + scaleBounds.width() + 2, + 100); } else { // Each meter channel is meterThickness pixels high, plus one pixel // between channels, but not after the last. // Add 4 pixels for ticks, and space high enough to hold our label in // this font, presuming that digits don't have descenders. - setMinimumSize(130, + setMinimumSize(100, displayNrAudioChannels * (meterThickness + 1) - 1 + 4 + metrics.capHeight()); } @@ -1085,14 +1119,6 @@ void VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width) painter.drawLine(position, y, position, y + 2); } - - // Draw minor tick lines. - painter.setPen(minorTickColor); - for (int i = 0; i >= minimumLevel; i--) { - int position = int(x + width - (i * scale) - 1); - if (i % 5 != 0) - painter.drawLine(position, y, position, y + 1); - } } void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height) @@ -1110,24 +1136,16 @@ void VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height) // Center the number on the tick, but don't overflow if (i == 0) { - painter.drawText(x + 6, position + metrics.capHeight(), + painter.drawText(x + 10, position + metrics.capHeight(), str); } else { - painter.drawText(x + 4, + painter.drawText(x + 8, position + (metrics.capHeight() / 2), str); } painter.drawLine(x, position, x + 2, position); } - - // Draw minor tick lines. - painter.setPen(minorTickColor); - for (int i = 0; i >= minimumLevel; i--) { - int position = y + int(i * scale) + METER_PADDING; - if (i % 5 != 0) - painter.drawLine(x, position, x + 1, position); - } } #define CLIP_FLASH_DURATION_MS 1000 @@ -1518,6 +1536,77 @@ VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, fad = fader; } +bool VolumeSlider::getDisplayTicks() const +{ + return displayTicks; +} + +void VolumeSlider::setDisplayTicks(bool display) +{ + displayTicks = display; +} + +void VolumeSlider::paintEvent(QPaintEvent *event) +{ + if (!getDisplayTicks()) { + QSlider::paintEvent(event); + return; + } + + QPainter painter(this); + QColor *tickColor = new QColor; + tickColor->setRgb(91, 98, 115, 255); + + obs_fader_conversion_t fader_db_to_def = obs_fader_db_to_def(fad); + + QStyleOptionSlider opt; + initStyleOption(&opt); + + QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, + QStyle::SC_SliderGroove, this); + QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, + QStyle::SC_SliderHandle, this); + + if (orientation() == Qt::Horizontal) { + const int sliderWidth = groove.width() - handle.width(); + + float tickLength = groove.height() * 1.5; + tickLength = std::max((int)tickLength + groove.height(), + 8 + groove.height()); + + float yPos = groove.center().y() - (tickLength / 2) + 1; + + for (int db = -10; db >= -90; db -= 10) { + float tickValue = fader_db_to_def(db); + + float xPos = groove.left() + (tickValue * sliderWidth) + + (handle.width() / 2); + painter.fillRect(xPos, yPos, 1, tickLength, *tickColor); + } + } + + if (orientation() == Qt::Vertical) { + const int sliderHeight = groove.height() - handle.height(); + + float tickLength = groove.width() * 1.5; + tickLength = std::max((int)tickLength + groove.width(), + 8 + groove.width()); + + float xPos = groove.center().x() - (tickLength / 2) + 1; + + for (int db = -10; db >= -96; db -= 10) { + float tickValue = fader_db_to_def(db); + + float yPos = groove.height() + groove.top() - + (tickValue * sliderHeight) - + (handle.height() / 2); + painter.fillRect(xPos, yPos, tickLength, 1, *tickColor); + } + } + + QSlider::paintEvent(event); +} + VolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w) : QAccessibleWidget(w) { diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 5a3efd5de2bf7d..7b520e79a2571e 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -12,6 +12,7 @@ class QPushButton; class VolumeMeterTimer; +class VolumeSlider; class VolumeMeter : public QWidget { Q_OBJECT @@ -276,7 +277,7 @@ class VolumeSlider; class MuteCheckBox; class OBSSourceLabel; -class VolControl : public QWidget { +class VolControl : public QFrame { Q_OBJECT private: @@ -342,6 +343,16 @@ class VolumeSlider : public AbsoluteSlider { VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr); VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr); + + bool getDisplayTicks() const; + void setDisplayTicks(bool display); + +private: + bool displayTicks = false; + QColor tickColor; + +protected: + virtual void paintEvent(QPaintEvent *event) override; }; class VolumeAccessibleInterface : public QAccessibleWidget { diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index ed85b676080065..e47322b3169098 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -36,8 +36,6 @@ along with this program. If not, see . #define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x))) -typedef float (*obs_fader_conversion_t)(const float val); - struct fader_cb { obs_fader_changed_t callback; void *param; @@ -943,3 +941,8 @@ float obs_db_to_mul(float db) { return db_to_mul(db); } + +obs_fader_conversion_t obs_fader_db_to_def(obs_fader_t *fader) +{ + return fader->db_to_def; +} diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index 9346061c25025a..3254712191dfcd 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -282,6 +282,10 @@ EXPORT void obs_volmeter_remove_callback(obs_volmeter_t *volmeter, EXPORT float obs_mul_to_db(float mul); EXPORT float obs_db_to_mul(float db); +typedef float (*obs_fader_conversion_t)(const float val); + +EXPORT obs_fader_conversion_t obs_fader_db_to_def(obs_fader_t *fader); + #ifdef __cplusplus } #endif From b9de99a10391df13e627e0d5951c569b91b93895 Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Wed, 15 May 2024 22:13:24 -0400 Subject: [PATCH 0164/1073] obs-webrtc: Increase RtcpNackResponder size Before we used the default value set by libdatachannel. At higher bitrates the cache would be undersized. Because of the undersized cache we wouldn't have enough NACK history to fix the stream. --- plugins/obs-webrtc/whip-output.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index eb271af0f22bab..48639c667e12cb 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -21,6 +21,9 @@ const uint8_t audio_payload_type = 111; const char *video_mid = "1"; const uint8_t video_payload_type = 96; +// ~3 seconds of 8.5 Megabit video +const int video_nack_buffer_size = 4000; + WHIPOutput::WHIPOutput(obs_data_t *, obs_output_t *output) : output(output), endpoint_url(), @@ -181,7 +184,8 @@ void WHIPOutput::ConfigureVideoTrack(std::string media_stream_id, video_sr_reporter = std::make_shared(rtp_config); packetizer->addToChain(video_sr_reporter); - packetizer->addToChain(std::make_shared()); + packetizer->addToChain(std::make_shared( + video_nack_buffer_size)); video_track = peer_connection->addTrack(video_description); video_track->setMediaHandler(packetizer); From dff4dd9acfe4a7ebee778127c503393a2d91ee17 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 23 May 2024 01:04:16 +0200 Subject: [PATCH 0165/1073] CI: Run PVS-Studio analysis on Windows --- .../windows-analysis/Invoke-External.ps1 | 40 +++++ .github/actions/windows-analysis/Logger.ps1 | 149 ++++++++++++++++++ .github/actions/windows-analysis/action.yaml | 90 +++++++++++ .../actions/windows-analysis/obs.pvsconfig | 10 ++ .github/workflows/analyze-project.yaml | 39 +++++ .github/workflows/scheduled.yaml | 8 + 6 files changed, 336 insertions(+) create mode 100644 .github/actions/windows-analysis/Invoke-External.ps1 create mode 100644 .github/actions/windows-analysis/Logger.ps1 create mode 100644 .github/actions/windows-analysis/action.yaml create mode 100644 .github/actions/windows-analysis/obs.pvsconfig create mode 100644 .github/workflows/analyze-project.yaml diff --git a/.github/actions/windows-analysis/Invoke-External.ps1 b/.github/actions/windows-analysis/Invoke-External.ps1 new file mode 100644 index 00000000000000..d1bfb758515d2e --- /dev/null +++ b/.github/actions/windows-analysis/Invoke-External.ps1 @@ -0,0 +1,40 @@ +function Invoke-External { + <# + .SYNOPSIS + Invokes a non-PowerShell command. + .DESCRIPTION + Runs a non-PowerShell command, and captures its return code. + Throws an exception if the command returns non-zero. + .EXAMPLE + Invoke-External 7z x $MyArchive + #> + + if ( $args.Count -eq 0 ) { + throw 'Invoke-External called without arguments.' + } + + if ( ! ( Test-Path function:Log-Information ) ) { + . $PSScriptRoot/Logger.ps1 + } + + $Command = $args[0] + $CommandArgs = @() + + if ( $args.Count -gt 1) { + $CommandArgs = $args[1..($args.Count - 1)] + } + + $_EAP = $ErrorActionPreference + $ErrorActionPreference = "Continue" + + Log-Debug "Invoke-External: ${Command} ${CommandArgs}" + + & $command $commandArgs + $Result = $LASTEXITCODE + + $ErrorActionPreference = $_EAP + + if ( $Result -ne 0 ) { + throw "${Command} ${CommandArgs} exited with non-zero code ${Result}." + } +} diff --git a/.github/actions/windows-analysis/Logger.ps1 b/.github/actions/windows-analysis/Logger.ps1 new file mode 100644 index 00000000000000..61a81e515237d0 --- /dev/null +++ b/.github/actions/windows-analysis/Logger.ps1 @@ -0,0 +1,149 @@ +function Log-Debug { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Debug $m + } + } +} + +function Log-Verbose { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Verbose $m + } + } +} + +function Log-Warning { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Warning $m + } + } +} + +function Log-Error { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + foreach($m in $Message) { + Write-Error $m + } + } +} + +function Log-Information { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + if ( ! ( $script:Quiet ) ) { + $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) + $Icon = ' =>' + + foreach($m in $Message) { + Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) " + Write-Host "${m}" + } + } + } +} + +function Log-Group { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + [string[]] $Message + ) + + Process { + if ( $Env:CI -ne $null ) { + if ( $script:LogGroup ) { + Write-Output '::endgroup::' + $script:LogGroup = $false + } + + if ( $Message.count -ge 1 ) { + Write-Output "::group::$($Message -join ' ')" + $script:LogGroup = $true + } + } else { + if ( $Message.count -ge 1 ) { + Log-Information $Message + } + } + } +} + +function Log-Status { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + if ( ! ( $script:Quiet ) ) { + $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' }) + $Icon = ' >' + + foreach($m in $Message) { + Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) " + Write-Host "${m}" + } + } + } +} + +function Log-Output { + [CmdletBinding()] + param( + [Parameter(Mandatory,ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [string[]] $Message + ) + + Process { + if ( ! ( $script:Quiet ) ) { + $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) + $Icon = '' + + foreach($m in $Message) { + Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}" + } + } + } +} + +$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5 diff --git a/.github/actions/windows-analysis/action.yaml b/.github/actions/windows-analysis/action.yaml new file mode 100644 index 00000000000000..1ac0ea8bc0b37f --- /dev/null +++ b/.github/actions/windows-analysis/action.yaml @@ -0,0 +1,90 @@ +name: Run PVS-Studio Analysis +inputs: + pvsUsername: + description: PVS-Studio License Username + required: true + pvsKey: + description: PVS-Studio License Key + required: true + target: + description: Build Target + required: true + config: + description: Build Configuration + required: true +runs: + using: composite + steps: + - name: Setup PVS-Studio + shell: pwsh + run: | + choco install pvs-studio --version=7.30.81185.980 -y --no-progress + + - name: Activate PVS-Studio + shell: pwsh + run: | + . ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1 + Invoke-External "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" credentials -u ${{ inputs.pvsUsername }} -n ${{ inputs.pvsKey }} + + - name: Run PVS-Studio Analysis + shell: pwsh + run: | + [flags()] Enum PVSErrorCodes { + Success = 0 + AnalyzerCrash = 1 + GenericError = 2 + InvalidCommandLine = 4 + FileNotFound = 8 + ConfigurationNotFound = 16 + InvalidProject = 32 + InvalidExtension = 64 + LicenseInvalid = 128 + CodeErrorsFound = 256 + SuppressionFailed = 512 + LicenseExpiringSoon = 1024 + } + + $pvsParams = @( + "--progress" + "--disableLicenseExpirationCheck" + "-p", "${{ inputs.target }}" + "-c", "${{ inputs.config }}" + "-t", "${{ github.workspace }}\build_x64\obs-studio.sln" + "-o", "${{ github.workspace }}\analysis.plog" + "-C", "${env:GITHUB_ACTION_PATH}\obs.pvsconfig" + ) + & "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" @pvsParams + + # Success and CodeErrorsFound are fine as error codes, we only care if it is anything but those + $pvs_result = $LASTEXITCODE -band (-bnot [PVSErrorCodes]::CodeErrorsFound) + if ($pvs_result -ne 0) { + Write-Output "PVS-Studio Errors: $([PVSErrorCodes]$pvs_result)" + } + exit $pvs_result + + - name: Convert Analysis to SARIF + shell: pwsh + run: | + . ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1 + + $conversionParams = @( + "-a", "GA:1,2", + "-d", "V1042,Renew" + "-t", "Sarif" + "${{ github.workspace }}\analysis.plog" + ) + Invoke-External "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" @conversionParams + + - name: Upload PVS-Studio Logs + uses: actions/upload-artifact@v4 + with: + name: 'pvs-analysis-log' + path: | + ${{ github.workspace }}/analysis.plog + ${{ github.workspace }}/analysis.plog.sarif + + - name: Upload PVS-Studio Report + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: "${{ github.workspace }}/analysis.plog.sarif" + category: 'PVS-Studio (Windows)' diff --git a/.github/actions/windows-analysis/obs.pvsconfig b/.github/actions/windows-analysis/obs.pvsconfig new file mode 100644 index 00000000000000..0c525ec5a57262 --- /dev/null +++ b/.github/actions/windows-analysis/obs.pvsconfig @@ -0,0 +1,10 @@ +//V_EXCLUDE_PATH */.deps/* +//V_EXCLUDE_PATH */blake2/* +//V_EXCLUDE_PATH */json11/* +//V_EXCLUDE_PATH */simde/* +//V_EXCLUDE_PATH */w32-pthreads/* +//V_EXCLUDE_PATH */plugins/obs-browser/* +//V_EXCLUDE_PATH */plugins/obs-outputs/ftl-sdk/* +//V_EXCLUDE_PATH */plugins/obs-websocket/* +//V_EXCLUDE_PATH */plugins/win-dshow/libdshowcapture/* +//V_EXCLUDE_PATH *_autogen/* diff --git a/.github/workflows/analyze-project.yaml b/.github/workflows/analyze-project.yaml new file mode 100644 index 00000000000000..4eb7ae92d69713 --- /dev/null +++ b/.github/workflows/analyze-project.yaml @@ -0,0 +1,39 @@ +name: Analyze Project +on: + workflow_call: +jobs: + windows: + name: Windows 🪟 (PVS-Studio) + runs-on: windows-2022 + defaults: + run: + shell: pwsh + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Build OBS Studio 🧱 + uses: ./.github/actions/build-obs + env: + TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }} + TWITCH_HASH: ${{ secrets.TWITCH_HASH }} + RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }} + RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }} + YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }} + YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }} + YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }} + YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }} + GPU_PRIORITY_VAL: ${{ secrets.GPU_PRIORITY_VAL }} + with: + target: x64 + config: Debug + + - name: Run PVS-Studio Analysis + uses: ./.github/actions/windows-analysis + with: + pvsUsername: ${{ secrets.PVS_NAME }} + pvsKey: ${{ secrets.PVS_KEY }} + target: x64 + config: Debug diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml index 84c247bac8f9a1..68ecac274a7664 100644 --- a/.github/workflows/scheduled.yaml +++ b/.github/workflows/scheduled.yaml @@ -88,6 +88,14 @@ jobs: needs: cache-cleanup secrets: inherit + analyze-project: + name: Analyze 🔬 + uses: ./.github/workflows/analyze-project.yaml + needs: cache-cleanup + secrets: inherit + permissions: + security-events: write + upload-language-files: name: Upload Language Files 🌐 if: github.repository_owner == 'obsproject' && github.ref_name == 'master' From 432a2a8e9c2f5be81cdd9d10540b22bb44b41832 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 23 May 2024 01:30:41 +0200 Subject: [PATCH 0166/1073] README.rst: Add SAST Tools section --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 49f68fb578c66e..584cf5c76d9ca3 100644 --- a/README.rst +++ b/README.rst @@ -64,3 +64,9 @@ Contributing you fully understand -- bad advice is worse than no advice. When it comes to something that you don't fully know or understand, please defer to the official help or official channels. + + +SAST Tools +---------- + +`PVS-Studio `_ - static analyzer for C, C++, C#, and Java code. From 976b200254c4fc86345e5768ebd1de69cf8e74fe Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 23 Nov 2023 14:30:10 +0100 Subject: [PATCH 0167/1073] UI: Add custom server support for Twitch --- UI/data/locale/en-US.ini | 2 ++ UI/forms/OBSBasicSettings.ui | 45 ++++++++++++++--------- UI/window-basic-settings-stream.cpp | 56 ++++++++++++++++++++++++++--- UI/window-basic-settings.cpp | 1 + UI/window-basic-settings.hpp | 3 ++ 5 files changed, 87 insertions(+), 20 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 5a9cc0d62fa5e6..2b6b1fc1282f00 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -957,6 +957,8 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximum Video Bitrate: %1 kbp Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximum Audio Bitrate: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximum Resolution: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximum FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Specify Custom Server..." +Basic.Settings.Stream.ServiceCustomServer="Custom Server" # basic mode 'output' settings Basic.Settings.Output="Output" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index cf0e4fbd5114f8..504c48ed9a1f92 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1271,6 +1271,19 @@
+ + + Basic.Settings.Stream.ServiceCustomServer + + + serviceCustomServer + + + + + + + Basic.AutoConfig.StreamPage.StreamKey @@ -1283,7 +1296,7 @@ - + @@ -1334,7 +1347,7 @@ - + Qt::Horizontal @@ -1347,7 +1360,7 @@ - + 8 @@ -1390,21 +1403,21 @@
- + Basic.Settings.Stream.BandwidthTestMode - + Basic.Settings.Stream.Custom.UseAuthentication - + Basic.Settings.Stream.Custom.Username @@ -1414,10 +1427,10 @@ - + - + Basic.Settings.Stream.Custom.Password @@ -1427,7 +1440,7 @@ - + @@ -1459,10 +1472,10 @@ - + - + Basic.Settings.Stream.TTVAddon @@ -1472,14 +1485,14 @@ - + Basic.Settings.Stream.IgnoreRecommended - + @@ -1492,7 +1505,7 @@ - + @@ -1516,14 +1529,14 @@ - + Basic.AutoConfig.StreamPage.ConnectedAccount - + diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index e28c5b189a22ae..04cc3a3bb17fb9 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "window-basic-settings.hpp" #include "obs-frontend-api.h" @@ -20,6 +21,13 @@ #include "youtube-api-wrappers.hpp" #endif +static const QUuid &CustomServerUUID() +{ + static const QUuid uuid = QUuid::fromString( + QT_UTF8("{241da255-70f2-4bbb-bef7-509695bf8e65}")); + return uuid; +} + struct QCef; struct QCefCookieManager; @@ -108,6 +116,8 @@ void OBSBasicSettings::LoadStream1Settings() const char *service = obs_data_get_string(settings, "service"); const char *server = obs_data_get_string(settings, "server"); const char *key = obs_data_get_string(settings, "key"); + bool use_custom_server = + obs_data_get_bool(settings, "using_custom_server"); protocol = QT_UTF8(obs_service_get_protocol(service_obj)); const char *bearer_token = obs_data_get_string(settings, "bearer_token"); @@ -148,7 +158,13 @@ void OBSBasicSettings::LoadStream1Settings() UpdateServerList(); if (is_rtmp_common) { - int idx = ui->server->findData(server); + int idx = -1; + if (use_custom_server) { + idx = ui->server->findData(CustomServerUUID()); + } else { + idx = ui->server->findData(QString::fromUtf8(server)); + } + if (idx == -1) { if (server && *server) ui->server->insertItem(0, server, server); @@ -157,6 +173,9 @@ void OBSBasicSettings::LoadStream1Settings() ui->server->setCurrentIndex(idx); } + if (use_custom_server) + ui->serviceCustomServer->setText(server); + if (is_whip) ui->key->setText(bearer_token); else @@ -223,9 +242,19 @@ void OBSBasicSettings::SaveStream1Settings() obs_data_set_string(settings, "service", QT_TO_UTF8(ui->service->currentText())); obs_data_set_string(settings, "protocol", QT_TO_UTF8(protocol)); - obs_data_set_string( - settings, "server", - QT_TO_UTF8(ui->server->currentData().toString())); + if (ui->server->currentData() == CustomServerUUID()) { + obs_data_set_bool(settings, "using_custom_server", + true); + + obs_data_set_string( + settings, "server", + QT_TO_UTF8(ui->serviceCustomServer->text())); + } else { + obs_data_set_string( + settings, "server", + QT_TO_UTF8( + ui->server->currentData().toString())); + } } else { obs_data_set_string( settings, "server", @@ -675,6 +704,12 @@ void OBSBasicSettings::UpdateServerList() ui->server->addItem(name, server); } + if (serviceName == "Twitch") { + ui->server->addItem( + QTStr("Basic.Settings.Stream.SpecifyCustomServer"), + CustomServerUUID()); + } + obs_properties_destroy(props); } @@ -887,6 +922,19 @@ void OBSBasicSettings::on_useAuth_toggled() ui->authPwWidget->setVisible(use_auth); } +bool OBSBasicSettings::IsCustomServer() +{ + return ui->server->currentData() == QVariant{CustomServerUUID()}; +} + +void OBSBasicSettings::on_server_currentIndexChanged(int /*index*/) +{ + auto server_is_custom = IsCustomServer(); + + ui->serviceCustomServerLabel->setVisible(server_is_custom); + ui->serviceCustomServer->setVisible(server_is_custom); +} + void OBSBasicSettings::UpdateVodTrackSetting() { bool enableForCustomServer = config_get_bool( diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 3a719d130c495e..775d1309877b1b 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -411,6 +411,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->service, COMBO_CHANGED, STREAM1_CHANGED); HookWidget(ui->server, COMBO_CHANGED, STREAM1_CHANGED); HookWidget(ui->customServer, EDIT_CHANGED, STREAM1_CHANGED); + HookWidget(ui->serviceCustomServer, EDIT_CHANGED, STREAM1_CHANGED); HookWidget(ui->key, EDIT_CHANGED, STREAM1_CHANGED); HookWidget(ui->bandwidthTestEnable, CHECK_CHANGED, STREAM1_CHANGED); HookWidget(ui->twitchAddonDropdown, COMBO_CHANGED, STREAM1_CHANGED); diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index e15c259348f389..51830c63fe6cdb 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -296,6 +296,8 @@ class OBSBasicSettings : public QDialog { /* Appearance */ void InitAppearancePage(); + bool IsCustomServer(); + private slots: void RecreateOutputResolutionWidget(); bool UpdateResFPSLimits(); @@ -306,6 +308,7 @@ private slots: void on_disconnectAccount_clicked(); void on_useStreamKey_clicked(); void on_useAuth_toggled(); + void on_server_currentIndexChanged(int index); void on_hotkeyFilterReset_clicked(); void on_hotkeyFilterSearch_textChanged(const QString text); From 43a1b309940b7bb7a92fdcd019bd011ef762621f Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 10 May 2024 17:27:13 +0200 Subject: [PATCH 0168/1073] UI: Increase size of log buffer Dumping config json from nlohmann requires more than 4096 bytes --- UI/obs-app.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 853630a02f4cad..c1ba6d6a314147 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -386,7 +386,7 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg, static void do_log(int log_level, const char *msg, va_list args, void *param) { fstream &logFile = *static_cast(param); - char str[4096]; + char str[8192]; #ifndef _WIN32 va_list args2; From c8950900c36fd6d0bd014eb6fd24da829a63033d Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Apr 2024 15:41:36 +0200 Subject: [PATCH 0169/1073] UI: Add eRTMP Multitrack Video Output --- UI/CMakeLists.txt | 18 + UI/api-interface.cpp | 10 + UI/cmake/legacy.cmake | 23 + UI/cmake/os-freebsd.cmake | 2 + UI/cmake/os-linux.cmake | 2 + UI/cmake/os-macos.cmake | 2 + UI/cmake/os-windows.cmake | 2 + UI/data/locale/en-US.ini | 27 + UI/goliveapi-censoredjson.cpp | 89 +++ UI/goliveapi-censoredjson.hpp | 12 + UI/goliveapi-network.cpp | 146 +++++ UI/goliveapi-network.hpp | 16 + UI/goliveapi-postdata.cpp | 47 ++ UI/goliveapi-postdata.hpp | 12 + UI/models/multitrack-video.hpp | 323 +++++++++++ UI/multitrack-video-error.cpp | 46 ++ UI/multitrack-video-error.hpp | 22 + UI/multitrack-video-output.cpp | 950 +++++++++++++++++++++++++++++++ UI/multitrack-video-output.hpp | 79 +++ UI/qt-helpers.cpp | 10 + UI/qt-helpers.hpp | 46 ++ UI/system-info-macos.mm | 6 + UI/system-info-posix.cpp | 6 + UI/system-info-windows.cpp | 278 +++++++++ UI/system-info.hpp | 5 + UI/window-basic-auto-config.cpp | 2 + UI/window-basic-main-outputs.cpp | 506 ++++++++++++---- UI/window-basic-main-outputs.hpp | 28 +- UI/window-basic-main.cpp | 150 +++-- UI/window-basic-main.hpp | 3 + UI/window-basic-settings.cpp | 6 + UI/window-basic-settings.hpp | 2 + 32 files changed, 2721 insertions(+), 155 deletions(-) create mode 100644 UI/goliveapi-censoredjson.cpp create mode 100644 UI/goliveapi-censoredjson.hpp create mode 100644 UI/goliveapi-network.cpp create mode 100644 UI/goliveapi-network.hpp create mode 100644 UI/goliveapi-postdata.cpp create mode 100644 UI/goliveapi-postdata.hpp create mode 100644 UI/models/multitrack-video.hpp create mode 100644 UI/multitrack-video-error.cpp create mode 100644 UI/multitrack-video-error.hpp create mode 100644 UI/multitrack-video-output.cpp create mode 100644 UI/multitrack-video-output.hpp create mode 100644 UI/qt-helpers.cpp create mode 100644 UI/qt-helpers.hpp create mode 100644 UI/system-info-macos.mm create mode 100644 UI/system-info-posix.cpp create mode 100644 UI/system-info-windows.cpp create mode 100644 UI/system-info.hpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index d060dd96b0f9b6..035644ca0e4f72 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -84,6 +84,24 @@ target_sources( ui-validation.cpp ui-validation.hpp) +target_sources( + obs-studio + PRIVATE # cmake-format: sortable + goliveapi-censoredjson.cpp + goliveapi-censoredjson.hpp + goliveapi-network.cpp + goliveapi-network.hpp + goliveapi-postdata.cpp + goliveapi-postdata.hpp + models/multitrack-video.hpp + multitrack-video-error.cpp + multitrack-video-error.hpp + multitrack-video-output.cpp + multitrack-video-output.hpp + qt-helpers.cpp + qt-helpers.hpp + system-info.hpp) + if(OS_WINDOWS) include(cmake/os-windows.cmake) elseif(OS_MACOS) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 39f260277db305..12f03604963ec0 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -482,6 +482,16 @@ struct OBSStudioAPI : obs_frontend_callbacks { obs_output_t *obs_frontend_get_streaming_output(void) override { + auto multitrackVideo = + main->outputHandler->multitrackVideo.get(); + auto mtvOutput = + multitrackVideo + ? obs_output_get_ref( + multitrackVideo->StreamingOutput()) + : nullptr; + if (mtvOutput) + return mtvOutput; + OBSOutput output = main->outputHandler->streamOutput.Get(); return obs_output_get_ref(output); } diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 586b1149f36b3c..5351f16f626d67 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -280,6 +280,23 @@ target_sources( window-remux.cpp window-remux.hpp) +target_sources( + obs + PRIVATE # cmake-format: sortable + goliveapi-censoredjson.cpp + goliveapi-censoredjson.hpp + goliveapi-network.cpp + goliveapi-network.hpp + goliveapi-postdata.cpp + goliveapi-postdata.hpp + multitrack-video-error.cpp + multitrack-video-error.hpp + multitrack-video-output.cpp + multitrack-video-output.hpp + qt-helpers.cpp + qt-helpers.hpp + system-info.hpp) + target_sources(obs PRIVATE importers/importers.cpp importers/importers.hpp importers/classic.cpp importers/sl.cpp importers/studio.cpp importers/xsplit.cpp) @@ -366,6 +383,8 @@ if(OS_WINDOWS) win-update/updater/manifest.hpp ${CMAKE_BINARY_DIR}/obs.rc) + target_sources(obs PRIVATE system-info-windows.cpp) + find_package(MbedTLS) target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2 Detours::Detours) @@ -426,6 +445,8 @@ elseif(OS_MACOS) target_sources(obs PRIVATE platform-osx.mm) target_sources(obs PRIVATE forms/OBSPermissions.ui window-permissions.cpp window-permissions.hpp) + target_sources(obs PRIVATE system-info-macos.mm) + if(ENABLE_WHATSNEW) find_library(SECURITY Security) find_package(nlohmann_json REQUIRED) @@ -462,6 +483,8 @@ elseif(OS_POSIX) target_sources(obs PRIVATE platform-x11.cpp) target_link_libraries(obs PRIVATE Qt::GuiPrivate Qt::DBus) + target_sources(obs PRIVATE system-info-posix.cpp) + target_compile_definitions(obs PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" "$<$:LINUX_PORTABLE>") if(TARGET obspython) diff --git a/UI/cmake/os-freebsd.cmake b/UI/cmake/os-freebsd.cmake index 6ad52b1c417ce9..323600567c7a56 100644 --- a/UI/cmake/os-freebsd.cmake +++ b/UI/cmake/os-freebsd.cmake @@ -2,6 +2,8 @@ target_sources(obs-studio PRIVATE platform-x11.cpp) target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}") target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate procstat) +target_sources(obs-studio PRIVATE system-info-posix.cpp) + if(TARGET OBS::python) find_package(Python REQUIRED COMPONENTS Interpreter Development) target_link_libraries(obs-studio PRIVATE Python::Python) diff --git a/UI/cmake/os-linux.cmake b/UI/cmake/os-linux.cmake index 37a02268fb8e00..0ffb441e4c7473 100644 --- a/UI/cmake/os-linux.cmake +++ b/UI/cmake/os-linux.cmake @@ -2,6 +2,8 @@ target_sources(obs-studio PRIVATE platform-x11.cpp) target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}") target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate Qt::DBus) +target_sources(obs-studio PRIVATE system-info-posix.cpp) + if(TARGET OBS::python) find_package(Python REQUIRED COMPONENTS Interpreter Development) target_link_libraries(obs-studio PRIVATE Python::Python) diff --git a/UI/cmake/os-macos.cmake b/UI/cmake/os-macos.cmake index d9354ec4db6467..5003e0b6231e9f 100644 --- a/UI/cmake/os-macos.cmake +++ b/UI/cmake/os-macos.cmake @@ -3,6 +3,8 @@ include(cmake/feature-sparkle.cmake) target_sources(obs-studio PRIVATE platform-osx.mm forms/OBSPermissions.ui window-permissions.cpp window-permissions.hpp) target_compile_options(obs-studio PRIVATE -Wno-quoted-include-in-framework-header -Wno-comma) +target_sources(obs-studio PRIVATE system-info-macos.mm) + set_source_files_properties(platform-osx.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3) diff --git a/UI/cmake/os-windows.cmake b/UI/cmake/os-windows.cmake index 35180a37eda75e..83ced77f09ec4b 100644 --- a/UI/cmake/os-windows.cmake +++ b/UI/cmake/os-windows.cmake @@ -33,6 +33,8 @@ target_sources( win-dll-blocklist.c win-update/updater/manifest.hpp) +target_sources(obs-studio PRIVATE system-info-windows.cpp) + target_link_libraries(obs-studio PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::MbedTLS nlohmann_json::nlohmann_json Detours::Detours) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 2b6b1fc1282f00..8a24e7265d7cf7 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -721,6 +721,7 @@ Basic.Main.Scenes="Scenes" Basic.Main.Sources="Sources" Basic.Main.Source="Source" Basic.Main.Controls="Controls" +Basic.Main.PreparingStream="Preparing..." Basic.Main.Connecting="Connecting..." Basic.Main.StartRecording="Start Recording" Basic.Main.StartReplayBuffer="Start Replay Buffer" @@ -1543,3 +1544,29 @@ YouTube.Errors.rateLimitExceeded="You are sending messages too quickly." # Browser Dock YouTube.DocksRemoval.Title="Clear Legacy YouTube Browser Docks" YouTube.DocksRemoval.Text="These browser docks will be removed as deprecated:\n\n%1\nUse \"Docks/YouTube Live Control Room\" instead." + +# MultitrackVideo +ConfigDownload.WarningMessageTitle="Warning" +FailedToStartStream.MissingConfigURL="No config URL available for the current service" +FailedToStartStream.NoCustomRTMPURLInSettings="Custom RTMP URL not specified" +FailedToStartStream.InvalidCustomConfig="Invalid custom config" +FailedToStartStream.FailedToCreateMultitrackVideoService="Failed to create multitrack video service" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video rtmp output" +FailedToStartStream.EncoderNotAvailable="NVENC not available.\n\nFailed to find encoder type '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Failed to create video encoder '%1' (type: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get obs video info while creating encoder '%1' (type: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Failed to create audio encoder" +FailedToStartStream.NoRTMPURLInConfig="Config does not contain stream target RTMP(S) URL" +FailedToStartStream.FallbackToDefault="Starting the stream using %1 failed; do you want to retry using single encode settings?" +FailedToStartStream.ConfigRequestFailed="Could not fetch config from %1

HTTP error: %2" +FailedToStartStream.WarningUnknownStatus="Received unknown status value '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nDo you want to continue streaming without %1?" +FailedToStartStream.WarningRetry="\n

\nDo you want to continue streaming?" +FailedToStartStream.MissingEncoderConfigs="Go live config did not include encoder configurations" +FailedToStartStream.StatusMissingHTML="Go live request returned an unspecified error" +FailedToStartStream.NoConfigSupplied="Missing config" +MultitrackVideo.Info="%1 automatically optimizes your settings to encode and send multiple video qualities. Selecting this option will send %2 information about your computer and software setup." +MultitrackVideo.IncompatibleSettings.Title="Incompatible Settings" +MultitrackVideo.IncompatibleSettings.Text="%1 is not currently compatible with:\n\n%2\nTo continue streaming with %1, disable incompatible settings:\n\n%3\nand Start Streaming again." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Disable for this stream and Start Streaming" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Update Settings and Start Streaming" diff --git a/UI/goliveapi-censoredjson.cpp b/UI/goliveapi-censoredjson.cpp new file mode 100644 index 00000000000000..fed481912fdffb --- /dev/null +++ b/UI/goliveapi-censoredjson.cpp @@ -0,0 +1,89 @@ +#include "goliveapi-censoredjson.hpp" +#include +#include + +void censorRecurse(obs_data_t *); +void censorRecurseArray(obs_data_array_t *); + +void censorRecurse(obs_data_t *data) +{ + // if we found what we came to censor, censor it + const char *a = obs_data_get_string(data, "authentication"); + if (a && *a) { + obs_data_set_string(data, "authentication", "CENSORED"); + } + + // recurse to child objects and arrays + obs_data_item_t *item = obs_data_first(data); + for (; item != NULL; obs_data_item_next(&item)) { + enum obs_data_type typ = obs_data_item_gettype(item); + + if (typ == OBS_DATA_OBJECT) { + obs_data_t *child_data = obs_data_item_get_obj(item); + censorRecurse(child_data); + obs_data_release(child_data); + } else if (typ == OBS_DATA_ARRAY) { + obs_data_array_t *child_array = + obs_data_item_get_array(item); + censorRecurseArray(child_array); + obs_data_array_release(child_array); + } + } +} + +void censorRecurseArray(obs_data_array_t *array) +{ + const size_t sz = obs_data_array_count(array); + for (size_t i = 0; i < sz; i++) { + obs_data_t *item = obs_data_array_item(array, i); + censorRecurse(item); + obs_data_release(item); + } +} + +QString censoredJson(obs_data_t *data, bool pretty) +{ + if (!data) { + return ""; + } + + // Ugly clone via JSON write/read + const char *j = obs_data_get_json(data); + obs_data_t *clone = obs_data_create_from_json(j); + + // Censor our copy + censorRecurse(clone); + + // Turn our copy into JSON + QString s = pretty ? obs_data_get_json_pretty(clone) + : obs_data_get_json(clone); + + // Eliminate our copy + obs_data_release(clone); + + return s; +} + +using json = nlohmann::json; + +void censorRecurse(json &data) +{ + if (!data.is_structured()) + return; + + auto it = data.find("authentication"); + if (it != data.end() && it->is_string()) { + *it = "CENSORED"; + } + + for (auto &child : data) { + censorRecurse(child); + } +} + +QString censoredJson(json data, bool pretty) +{ + censorRecurse(data); + + return QString::fromStdString(data.dump(pretty ? 4 : -1)); +} diff --git a/UI/goliveapi-censoredjson.hpp b/UI/goliveapi-censoredjson.hpp new file mode 100644 index 00000000000000..20f141c7f44df5 --- /dev/null +++ b/UI/goliveapi-censoredjson.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +/** + * Returns the input serialized to JSON, but any non-empty "authorization" + * properties have their values replaced by "CENSORED". + */ +QString censoredJson(obs_data_t *data, bool pretty = false); +QString censoredJson(nlohmann::json data, bool pretty = false); diff --git a/UI/goliveapi-network.cpp b/UI/goliveapi-network.cpp new file mode 100644 index 00000000000000..34b751eb47a06a --- /dev/null +++ b/UI/goliveapi-network.cpp @@ -0,0 +1,146 @@ +#include "goliveapi-network.hpp" +#include "goliveapi-censoredjson.hpp" + +#include +#include +#include +#include "multitrack-video-error.hpp" + +#include +#include +#include +#include + +#include + +using json = nlohmann::json; + +Qt::ConnectionType BlockingConnectionTypeFor(QObject *object); + +void HandleGoLiveApiErrors(QWidget *parent, const json &raw_json, + const GoLiveApi::Config &config) +{ + using GoLiveApi::StatusResult; + + if (!config.status) + return; + + auto &status = *config.status; + if (status.result == StatusResult::Success) + return; + + auto warn_continue = [&](QString message) { + bool ret = false; + QMetaObject::invokeMethod( + parent, + [=] { + QMessageBox mb(parent); + mb.setIcon(QMessageBox::Warning); + mb.setWindowTitle(QTStr( + "ConfigDownload.WarningMessageTitle")); + mb.setTextFormat(Qt::RichText); + mb.setText( + message + + QTStr("FailedToStartStream.WarningRetry")); + mb.setStandardButtons( + QMessageBox::StandardButton::Yes | + QMessageBox::StandardButton::No); + return mb.exec() == + QMessageBox::StandardButton::No; + }, + BlockingConnectionTypeFor(parent), &ret); + if (ret) + throw MultitrackVideoError::cancel(); + }; + + auto missing_html = [] { + return QTStr("FailedToStartStream.StatusMissingHTML") + .toStdString(); + }; + + if (status.result == StatusResult::Unknown) { + return warn_continue( + QTStr("FailedToStartStream.WarningUnknownStatus") + .arg(raw_json["status"]["result"] + .dump() + .c_str())); + + } else if (status.result == StatusResult::Warning) { + if (config.encoder_configurations.empty()) { + throw MultitrackVideoError::warning( + status.html_en_us.value_or(missing_html()) + .c_str()); + } + + return warn_continue( + status.html_en_us.value_or(missing_html()).c_str()); + } else if (status.result == StatusResult::Error) { + throw MultitrackVideoError::critical( + status.html_en_us.value_or(missing_html()).c_str()); + } +} + +GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, + const GoLiveApi::PostData &post_data, + const QString &multitrack_video_name) +{ + json post_data_json = post_data; + blog(LOG_INFO, "Go live POST data: %s", + censoredJson(post_data_json).toUtf8().constData()); + + if (url.isEmpty()) + throw MultitrackVideoError::critical( + QTStr("FailedToStartStream.MissingConfigURL")); + + std::string encodeConfigText; + std::string libraryError; + + std::vector headers; + headers.push_back("Content-Type: application/json"); + bool encodeConfigDownloadedOk = GetRemoteFile( + url.toLocal8Bit(), encodeConfigText, + libraryError, // out params + nullptr, + nullptr, // out params (response code and content type) + "POST", post_data_json.dump().c_str(), headers, + nullptr, // signature + 5); // timeout in seconds + + if (!encodeConfigDownloadedOk) + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.ConfigRequestFailed") + .arg(url, libraryError.c_str())); + try { + auto data = json::parse(encodeConfigText); + blog(LOG_INFO, "Go live response data: %s", + censoredJson(data, true).toUtf8().constData()); + GoLiveApi::Config config = data; + HandleGoLiveApiErrors(parent, data, config); + return config; + + } catch (const json::exception &e) { + blog(LOG_INFO, "Failed to parse go live config: %s", e.what()); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.FallbackToDefault") + .arg(multitrack_video_name)); + } +} + +QString MultitrackVideoAutoConfigURL(obs_service_t *service) +{ + static const QString url = [service]() -> QString { + auto args = qApp->arguments(); + for (int i = 0; i < args.length() - 1; i++) { + if (args[i] == "--config-url" && + args.length() > (i + 1)) { + return args[i + 1]; + } + } + OBSDataAutoRelease settings = obs_service_get_settings(service); + return obs_data_get_string( + settings, "multitrack_video_configuration_url"); + }(); + + blog(LOG_INFO, "Go live URL: %s", url.toUtf8().constData()); + return url; +} diff --git a/UI/goliveapi-network.hpp b/UI/goliveapi-network.hpp new file mode 100644 index 00000000000000..2fd3def43c8cd2 --- /dev/null +++ b/UI/goliveapi-network.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "models/multitrack-video.hpp" + +/** Returns either GO_LIVE_API_PRODUCTION_URL or a command line override. */ +QString MultitrackVideoAutoConfigURL(obs_service_t *service); + +class QWidget; + +GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, + const GoLiveApi::PostData &post_data, + const QString &multitrack_video_name); diff --git a/UI/goliveapi-postdata.cpp b/UI/goliveapi-postdata.cpp new file mode 100644 index 00000000000000..090fec75d4fb4e --- /dev/null +++ b/UI/goliveapi-postdata.cpp @@ -0,0 +1,47 @@ +#include "goliveapi-postdata.hpp" + +#include + +#include "system-info.hpp" + +#include "models/multitrack-video.hpp" + +GoLiveApi::PostData +constructGoLivePost(QString streamKey, + const std::optional &maximum_aggregate_bitrate, + const std::optional &maximum_video_tracks, + bool vod_track_enabled) +{ + GoLiveApi::PostData post_data{}; + post_data.service = "IVS"; + post_data.schema_version = "2023-05-10"; + post_data.authentication = streamKey.toStdString(); + + system_info(post_data.capabilities); + + auto &client = post_data.capabilities.client; + + client.name = "obs-studio"; + client.version = obs_get_version_string(); + client.vod_track_audio = vod_track_enabled; + + obs_video_info ovi; + if (obs_get_video_info(&ovi)) { + client.width = ovi.output_width; + client.height = ovi.output_height; + client.fps_numerator = ovi.fps_num; + client.fps_denominator = ovi.fps_den; + + client.canvas_width = ovi.base_width; + client.canvas_height = ovi.base_height; + } + + auto &preferences = post_data.preferences; + if (maximum_aggregate_bitrate.has_value()) + preferences.maximum_aggregate_bitrate = + maximum_aggregate_bitrate.value(); + if (maximum_video_tracks.has_value()) + preferences.maximum_video_tracks = maximum_video_tracks.value(); + + return post_data; +} diff --git a/UI/goliveapi-postdata.hpp b/UI/goliveapi-postdata.hpp new file mode 100644 index 00000000000000..fbb671d9b88daf --- /dev/null +++ b/UI/goliveapi-postdata.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include "models/multitrack-video.hpp" + +GoLiveApi::PostData +constructGoLivePost(QString streamKey, + const std::optional &maximum_aggregate_bitrate, + const std::optional &maximum_video_tracks, + bool vod_track_enabled); diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp new file mode 100644 index 00000000000000..2acccf87c953c8 --- /dev/null +++ b/UI/models/multitrack-video.hpp @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2024 Ruwen Hahn + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include +#include + +#include + +#include + +/* From whatsnew.hpp */ +#ifndef NLOHMANN_DEFINE_TYPE_INTRUSIVE +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json &nlohmann_json_j, \ + const Type &nlohmann_json_t) \ + { \ + NLOHMANN_JSON_EXPAND( \ + NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \ + } \ + friend void from_json(const nlohmann::json &nlohmann_json_j, \ + Type &nlohmann_json_t) \ + { \ + NLOHMANN_JSON_EXPAND( \ + NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) \ + } +#endif + +#ifndef NLOHMANN_JSON_FROM_WITH_DEFAULT +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) \ + nlohmann_json_t.v1 = \ + nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); +#endif + +#ifndef NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json &nlohmann_json_j, \ + const Type &nlohmann_json_t) \ + { \ + NLOHMANN_JSON_EXPAND( \ + NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \ + } \ + friend void from_json(const nlohmann::json &nlohmann_json_j, \ + Type &nlohmann_json_t) \ + { \ + Type nlohmann_json_default_obj; \ + NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE( \ + NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) \ + } + +#endif + +/* + * Support for (de-)serialising std::optional + * From https://github.com/nlohmann/json/issues/1749#issuecomment-1731266676 + * whatsnew.hpp's version doesn't seem to work here + */ +template struct nlohmann::adl_serializer> { + static void from_json(const json &j, std::optional &opt) + { + if (j.is_null()) { + opt = std::nullopt; + } else { + opt = j.get(); + } + } + static void to_json(json &json, std::optional t) + { + if (t) { + json = *t; + } else { + json = nullptr; + } + } +}; + +NLOHMANN_JSON_SERIALIZE_ENUM(obs_scale_type, + { + {OBS_SCALE_DISABLE, "OBS_SCALE_DISABLE"}, + {OBS_SCALE_POINT, "OBS_SCALE_POINT"}, + {OBS_SCALE_BICUBIC, "OBS_SCALE_BICUBIC"}, + {OBS_SCALE_BILINEAR, "OBS_SCALE_BILINEAR"}, + {OBS_SCALE_LANCZOS, "OBS_SCALE_LANCZOS"}, + {OBS_SCALE_AREA, "OBS_SCALE_AREA"}, + }) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(media_frames_per_second, numerator, + denominator) + +namespace GoLiveApi { +using std::string; +using std::optional; +using json = nlohmann::json; + +struct Client { + string name = "obs-studio"; + string version; + bool vod_track_audio; + uint32_t width; + uint32_t height; + uint32_t fps_numerator; + uint32_t fps_denominator; + uint32_t canvas_width; + uint32_t canvas_height; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version, vod_track_audio, + width, height, fps_numerator, + fps_denominator, canvas_width, + canvas_height) +}; + +struct Cpu { + int32_t physical_cores; + int32_t logical_cores; + optional speed; + optional name; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Cpu, physical_cores, logical_cores, + speed, name) +}; + +struct Memory { + uint64_t total; + uint64_t free; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Memory, total, free) +}; + +struct Gpu { + string model; + uint32_t vendor_id; + uint32_t device_id; + uint64_t dedicated_video_memory; + uint64_t shared_system_memory; + string luid; + optional driver_version; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Gpu, model, vendor_id, device_id, + dedicated_video_memory, + shared_system_memory, luid, + driver_version) +}; + +struct GamingFeatures { + optional game_bar_enabled; + optional game_dvr_allowed; + optional game_dvr_enabled; + optional game_dvr_bg_recording; + optional game_mode_enabled; + optional hags_enabled; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(GamingFeatures, game_bar_enabled, + game_dvr_allowed, game_dvr_enabled, + game_dvr_bg_recording, game_mode_enabled, + hags_enabled) +}; + +struct System { + string version; + string name; + int build; + string release; + int revision; + int bits; + bool arm; + bool armEmulation; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(System, version, name, build, release, + revision, bits, arm, armEmulation) +}; + +struct Capabilities { + Client client; + Cpu cpu; + Memory memory; + optional gaming_features; + System system; + optional> gpu; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, client, cpu, memory, + gaming_features, system, gpu) +}; + +struct Preferences { + optional maximum_aggregate_bitrate; + optional maximum_video_tracks; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate, + maximum_video_tracks) +}; + +struct PostData { + string service = "IVS"; + string schema_version = "2023-05-10"; + string authentication; + + Capabilities capabilities; + Preferences preferences; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(PostData, service, schema_version, + authentication, capabilities, + preferences) +}; + +// Config Response + +struct Meta { + string service; + string schema_version; + string config_id; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Meta, service, schema_version, config_id) +}; + +enum struct StatusResult { + Unknown, + Success, + Warning, + Error, +}; + +NLOHMANN_JSON_SERIALIZE_ENUM(StatusResult, + { + {StatusResult::Unknown, nullptr}, + {StatusResult::Success, "success"}, + {StatusResult::Warning, "warning"}, + {StatusResult::Error, "error"}, + }) + +struct Status { + StatusResult result = StatusResult::Unknown; + optional html_en_us; + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Status, result, html_en_us) +}; + +struct IngestEndpoint { + string protocol; + string url_template; + optional authentication; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IngestEndpoint, protocol, + url_template, + authentication) +}; + +struct VideoEncoderConfiguration { + string type; + uint32_t width; + uint32_t height; + uint32_t bitrate; + optional framerate; + optional gpu_scale_type; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VideoEncoderConfiguration, + type, width, height, + bitrate, framerate, + gpu_scale_type) +}; + +struct AudioEncoderConfiguration { + string codec; + uint32_t track_id; + uint32_t channels; + uint32_t bitrate; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(AudioEncoderConfiguration, codec, + track_id, channels, bitrate) +}; + +template struct EncoderConfiguration { + T config; + json data; + + friend void to_json(nlohmann::json &nlohmann_json_j, + const EncoderConfiguration &nlohmann_json_t) + { + nlohmann_json_j = nlohmann_json_t.data; + to_json(nlohmann_json_j, nlohmann_json_t.config); + } + friend void from_json(const nlohmann::json &nlohmann_json_j, + EncoderConfiguration &nlohmann_json_t) + { + nlohmann_json_t.data = nlohmann_json_j; + nlohmann_json_j.get_to(nlohmann_json_t.config); + } +}; + +struct AudioConfigurations { + std::vector> live; + std::vector> vod; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live, + vod) +}; + +struct Config { + Meta meta; + optional status; + std::vector ingest_endpoints; + std::vector> + encoder_configurations; + AudioConfigurations audio_configurations; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, meta, status, + ingest_endpoints, + encoder_configurations, + audio_configurations) +}; +} // namespace GoLiveApi diff --git a/UI/multitrack-video-error.cpp b/UI/multitrack-video-error.cpp new file mode 100644 index 00000000000000..036a80f89fdca6 --- /dev/null +++ b/UI/multitrack-video-error.cpp @@ -0,0 +1,46 @@ +#include "multitrack-video-error.hpp" + +#include +#include "obs-app.hpp" + +MultitrackVideoError MultitrackVideoError::critical(QString error) +{ + return {Type::Critical, error}; +} + +MultitrackVideoError MultitrackVideoError::warning(QString error) +{ + return {Type::Warning, error}; +} + +MultitrackVideoError MultitrackVideoError::cancel() +{ + return {Type::Cancel, {}}; +} + +bool MultitrackVideoError::ShowDialog( + QWidget *parent, const QString &multitrack_video_name) const +{ + QMessageBox mb(parent); + mb.setTextFormat(Qt::RichText); + mb.setWindowTitle(QTStr("Output.StartStreamFailed")); + + if (type == Type::Warning) { + mb.setText( + error + + QTStr("FailedToStartStream.WarningRetryNonMultitrackVideo") + .arg(multitrack_video_name)); + mb.setIcon(QMessageBox::Warning); + mb.setStandardButtons(QMessageBox::StandardButton::Yes | + QMessageBox::StandardButton::No); + return mb.exec() == QMessageBox::StandardButton::Yes; + } else if (type == Type::Critical) { + mb.setText(error); + mb.setIcon(QMessageBox::Critical); + mb.setStandardButtons( + QMessageBox::StandardButton::Ok); // cannot continue + mb.exec(); + } + + return false; +} diff --git a/UI/multitrack-video-error.hpp b/UI/multitrack-video-error.hpp new file mode 100644 index 00000000000000..cbffb42548372b --- /dev/null +++ b/UI/multitrack-video-error.hpp @@ -0,0 +1,22 @@ +#pragma once +#include + +class QWidget; + +struct MultitrackVideoError { + static MultitrackVideoError critical(QString error); + static MultitrackVideoError warning(QString error); + static MultitrackVideoError cancel(); + + bool ShowDialog(QWidget *parent, + const QString &multitrack_video_name) const; + + enum struct Type { + Critical, + Warning, + Cancel, + }; + + const Type type; + const QString error; +}; diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp new file mode 100644 index 00000000000000..2acc64c0caabfb --- /dev/null +++ b/UI/multitrack-video-output.cpp @@ -0,0 +1,950 @@ +#include "multitrack-video-output.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "system-info.hpp" +#include "goliveapi-postdata.hpp" +#include "goliveapi-network.hpp" +#include "multitrack-video-error.hpp" +#include "qt-helpers.hpp" +#include "models/multitrack-video.hpp" + +Qt::ConnectionType BlockingConnectionTypeFor(QObject *object) +{ + return object->thread() == QThread::currentThread() + ? Qt::DirectConnection + : Qt::BlockingQueuedConnection; +} + +bool MultitrackVideoDeveloperModeEnabled() +{ + static bool developer_mode = [] { + auto args = qApp->arguments(); + for (const auto &arg : args) { + if (arg == "--enable-multitrack-video-dev") { + return true; + } + } + return false; + }(); + return developer_mode; +} + +static OBSServiceAutoRelease +create_service(const GoLiveApi::Config &go_live_config, + const std::optional &rtmp_url, + const QString &in_stream_key) +{ + const char *url = nullptr; + QString stream_key = in_stream_key; + + const auto &ingest_endpoints = go_live_config.ingest_endpoints; + + for (auto &endpoint : ingest_endpoints) { + if (qstrnicmp("RTMP", endpoint.protocol.c_str(), 4)) + continue; + + url = endpoint.url_template.c_str(); + if (endpoint.authentication && + !endpoint.authentication->empty()) { + blog(LOG_INFO, + "Using stream key supplied by autoconfig"); + stream_key = QString::fromStdString( + *endpoint.authentication); + } + break; + } + + if (rtmp_url.has_value()) { + // Despite being set by user, it was set to a "" + if (rtmp_url->empty()) { + throw MultitrackVideoError::warning(QTStr( + "FailedToStartStream.NoCustomRTMPURLInSettings")); + } + + url = rtmp_url->c_str(); + blog(LOG_INFO, "Using custom RTMP URL: '%s'", url); + } else { + if (!url) { + blog(LOG_ERROR, "No RTMP URL in go live config"); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.NoRTMPURLInConfig")); + } + + blog(LOG_INFO, "Using URL template: '%s'", url); + } + + DStr str; + dstr_cat(str, url); + + // dstr_find does not protect against null, and dstr_cat will + // not initialize str if cat'ing with a null url + if (!dstr_is_empty(str)) { + auto found = dstr_find(str, "/{stream_key}"); + if (found) + dstr_remove(str, found - str->array, + str->len - (found - str->array)); + } + + QUrl parsed_url{url}; + QUrlQuery parsed_query{parsed_url}; + + if (!go_live_config.meta.config_id.empty()) { + parsed_query.addQueryItem( + "obsConfigId", + QString::fromStdString(go_live_config.meta.config_id)); + } + + auto key_with_param = stream_key; + if (!parsed_query.isEmpty()) + key_with_param += "?" + parsed_query.toString(); + + OBSDataAutoRelease settings = obs_data_create(); + obs_data_set_string(settings, "server", str->array); + obs_data_set_string(settings, "key", + key_with_param.toUtf8().constData()); + + auto service = obs_service_create( + "rtmp_custom", "multitrack video service", settings, nullptr); + + if (!service) { + blog(LOG_WARNING, "Failed to create multitrack video service"); + throw MultitrackVideoError::warning(QTStr( + "FailedToStartStream.FailedToCreateMultitrackVideoService")); + } + + return service; +} + +static void ensure_directory_exists(std::string &path) +{ + replace(path.begin(), path.end(), '\\', '/'); + + size_t last = path.rfind('/'); + if (last == std::string::npos) + return; + + std::string directory = path.substr(0, last); + os_mkdirs(directory.c_str()); +} + +std::string GetOutputFilename(const std::string &path, const char *format) +{ + std::string strPath; + strPath += path; + + char lastChar = strPath.back(); + if (lastChar != '/' && lastChar != '\\') + strPath += "/"; + + strPath += BPtr{ + os_generate_formatted_filename("flv", false, format)}; + ensure_directory_exists(strPath); + + return strPath; +} + +static OBSOutputAutoRelease create_output() +{ + OBSOutputAutoRelease output = obs_output_create( + "rtmp_output", "rtmp multitrack video", nullptr, nullptr); + + if (!output) { + blog(LOG_ERROR, + "Failed to create multitrack video rtmp output"); + throw MultitrackVideoError::warning(QTStr( + "FailedToStartStream.FailedToCreateMultitrackVideoOutput")); + } + + return output; +} + +static OBSOutputAutoRelease create_recording_output(obs_data_t *settings) +{ + OBSOutputAutoRelease output = obs_output_create( + "flv_output", "flv multitrack video", settings, nullptr); + + if (!output) + blog(LOG_ERROR, "Failed to create multitrack video flv output"); + + return output; +} + +static void adjust_video_encoder_scaling( + const obs_video_info &ovi, obs_encoder_t *video_encoder, + const GoLiveApi::VideoEncoderConfiguration &encoder_config, + size_t encoder_index) +{ + auto requested_width = encoder_config.width; + auto requested_height = encoder_config.height; + + if (ovi.output_width == requested_width || + ovi.output_height == requested_height) + return; + + if (ovi.base_width < requested_width || + ovi.base_height < requested_height) { + blog(LOG_WARNING, + "Requested resolution exceeds canvas/available resolution for encoder %zu: %" PRIu32 + "x%" PRIu32 " > %" PRIu32 "x%" PRIu32, + encoder_index, requested_width, requested_height, + ovi.base_width, ovi.base_height); + } + + obs_encoder_set_scaled_size(video_encoder, requested_width, + requested_height); + obs_encoder_set_gpu_scale_type( + video_encoder, + encoder_config.gpu_scale_type.value_or(OBS_SCALE_BICUBIC)); +} + +static uint32_t closest_divisor(const obs_video_info &ovi, + const media_frames_per_second &target_fps) +{ + auto target = (uint64_t)target_fps.numerator * ovi.fps_den; + auto source = (uint64_t)ovi.fps_num * target_fps.denominator; + return std::max(1u, static_cast(source / target)); +} + +static void adjust_encoder_frame_rate_divisor( + const obs_video_info &ovi, obs_encoder_t *video_encoder, + const GoLiveApi::VideoEncoderConfiguration &encoder_config, + const size_t encoder_index) +{ + if (!encoder_config.framerate) { + blog(LOG_WARNING, "`framerate` not specified for encoder %zu", + encoder_index); + return; + } + media_frames_per_second requested_fps = *encoder_config.framerate; + + if (ovi.fps_num == requested_fps.numerator && + ovi.fps_den == requested_fps.denominator) + return; + + auto divisor = closest_divisor(ovi, requested_fps); + if (divisor <= 1) + return; + + blog(LOG_INFO, "Setting frame rate divisor to %u for encoder %zu", + divisor, encoder_index); + obs_encoder_set_frame_rate_divisor(video_encoder, divisor); +} + +static const std::vector &get_available_encoders() +{ + // encoders are currently only registered during startup, so keeping + // a static vector around shouldn't be a problem + static std::vector available_encoders = [] { + std::vector available_encoders; + for (size_t i = 0;; i++) { + const char *id = nullptr; + if (!obs_enum_encoder_types(i, &id)) + break; + available_encoders.push_back(id); + } + return available_encoders; + }(); + return available_encoders; +} + +static bool encoder_available(const char *type) +{ + auto &encoders = get_available_encoders(); + return std::find_if(std::begin(encoders), std::end(encoders), + [=](const char *encoder) { + return strcmp(type, encoder) == 0; + }) != std::end(encoders); +} + +static OBSEncoderAutoRelease create_video_encoder( + DStr &name_buffer, size_t encoder_index, + const GoLiveApi::EncoderConfiguration< + GoLiveApi::VideoEncoderConfiguration> &encoder_config) +{ + auto encoder_type = encoder_config.config.type.c_str(); + if (!encoder_available(encoder_type)) { + blog(LOG_ERROR, "Encoder type '%s' not available", + encoder_type); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.EncoderNotAvailable") + .arg(encoder_type)); + } + + dstr_printf(name_buffer, "multitrack video video encoder %zu", + encoder_index); + + OBSDataAutoRelease encoder_settings = + obs_data_create_from_json(encoder_config.data.dump().c_str()); + obs_data_set_bool(encoder_settings, "disable_scenecut", true); + + OBSEncoderAutoRelease video_encoder = obs_video_encoder_create( + encoder_type, name_buffer, encoder_settings, nullptr); + if (!video_encoder) { + blog(LOG_ERROR, "Failed to create video encoder '%s'", + name_buffer->array); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.FailedToCreateVideoEncoder") + .arg(name_buffer->array, encoder_type)); + } + obs_encoder_set_video(video_encoder, obs_get_video()); + + obs_video_info ovi; + if (!obs_get_video_info(&ovi)) { + blog(LOG_WARNING, + "Failed to get obs_video_info while creating encoder %zu", + encoder_index); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.FailedToGetOBSVideoInfo") + .arg(name_buffer->array, encoder_type)); + } + + adjust_video_encoder_scaling(ovi, video_encoder, encoder_config.config, + encoder_index); + adjust_encoder_frame_rate_divisor(ovi, video_encoder, + encoder_config.config, encoder_index); + + return video_encoder; +} + +static OBSEncoderAutoRelease create_audio_encoder(const char *name, + const char *audio_encoder_id, + uint32_t audio_bitrate, + size_t mixer_idx) +{ + OBSDataAutoRelease settings = obs_data_create(); + obs_data_set_int(settings, "bitrate", audio_bitrate); + + OBSEncoderAutoRelease audio_encoder = obs_audio_encoder_create( + audio_encoder_id, name, settings, mixer_idx, nullptr); + if (!audio_encoder) { + blog(LOG_ERROR, "Failed to create audio encoder"); + throw MultitrackVideoError::warning(QTStr( + "FailedToStartStream.FailedToCreateAudioEncoder")); + } + obs_encoder_set_audio(audio_encoder, obs_get_audio()); + return audio_encoder; +} + +struct OBSOutputs { + OBSOutputAutoRelease output, recording_output; +}; + +static OBSOutputs +SetupOBSOutput(obs_data_t *dump_stream_to_file_config, + const GoLiveApi::Config &go_live_config, + std::vector &audio_encoders, + std::vector &video_encoders, + const char *audio_encoder_id, + std::optional vod_track_mixer); +static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, + obs_output_t *output, OBSSignal &start, + OBSSignal &stop, OBSSignal &deactivate); + +void MultitrackVideoOutput::PrepareStreaming( + QWidget *parent, const char *service_name, obs_service_t *service, + const std::optional &rtmp_url, const QString &stream_key, + const char *audio_encoder_id, + std::optional maximum_aggregate_bitrate, + std::optional maximum_video_tracks, + std::optional custom_config, + obs_data_t *dump_stream_to_file_config, + std::optional vod_track_mixer) +{ + { + const std::lock_guard current_lock{current_mutex}; + const std::lock_guard current_stream_dump_lock{ + current_stream_dump_mutex}; + if (current || current_stream_dump) { + blog(LOG_WARNING, + "Tried to prepare multitrack video output while it's already active"); + return; + } + } + std::optional go_live_config; + std::optional custom; + bool is_custom_config = custom_config.has_value(); + auto auto_config_url = MultitrackVideoAutoConfigURL(service); + + OBSDataAutoRelease service_settings = obs_service_get_settings(service); + auto multitrack_video_name = + QTStr("Basic.Settings.Stream.MultitrackVideoLabel"); + if (obs_data_has_user_value(service_settings, + "ertmp_multitrack_video_name")) { + multitrack_video_name = obs_data_get_string( + service_settings, "ertmp_multitrack_video_name"); + } + + auto auto_config_url_data = auto_config_url.toUtf8(); + + DStr vod_track_info_storage; + if (vod_track_mixer.has_value()) + dstr_printf(vod_track_info_storage, "Yes (mixer: %zu)", + vod_track_mixer.value()); + + blog(LOG_INFO, + "Preparing enhanced broadcasting stream for:\n" + " custom config: %s\n" + " config url: %s\n" + " settings:\n" + " service: %s\n" + " max aggregate bitrate: %s (%" PRIu32 ")\n" + " max video tracks: %s (%" PRIu32 ")\n" + " custom rtmp url: %s ('%s')\n" + " vod track: %s", + is_custom_config ? "Yes" : "No", + !auto_config_url.isEmpty() ? auto_config_url_data.constData() + : "(null)", + service_name, + maximum_aggregate_bitrate.has_value() ? "Set" : "Auto", + maximum_aggregate_bitrate.value_or(0), + maximum_video_tracks.has_value() ? "Set" : "Auto", + maximum_video_tracks.value_or(0), + rtmp_url.has_value() ? "Yes" : "No", + rtmp_url.has_value() ? rtmp_url->c_str() : "", + vod_track_info_storage->array ? vod_track_info_storage->array + : "No"); + + const bool custom_config_only = + auto_config_url.isEmpty() && + MultitrackVideoDeveloperModeEnabled() && + custom_config.has_value() && + strcmp(obs_service_get_id(service), "rtmp_custom") == 0; + + if (!custom_config_only) { + auto go_live_post = constructGoLivePost( + stream_key, maximum_aggregate_bitrate, + maximum_video_tracks, vod_track_mixer.has_value()); + + go_live_config = DownloadGoLiveConfig(parent, auto_config_url, + go_live_post, + multitrack_video_name); + } + + if (custom_config.has_value()) { + GoLiveApi::Config parsed_custom; + try { + parsed_custom = nlohmann::json::parse(*custom_config); + } catch (const nlohmann::json::exception &exception) { + blog(LOG_WARNING, "Failed to parse custom config: %s", + exception.what()); + throw MultitrackVideoError::critical(QTStr( + "FailedToStartStream.InvalidCustomConfig")); + } + + // copy unique ID from go live request + if (go_live_config.has_value()) { + parsed_custom.meta.config_id = + go_live_config->meta.config_id; + blog(LOG_INFO, + "Using config_id from go live config with custom config: %s", + parsed_custom.meta.config_id.c_str()); + } + + nlohmann::json custom_data = parsed_custom; + blog(LOG_INFO, "Using custom go live config: %s", + custom_data.dump(4).c_str()); + + custom.emplace(std::move(parsed_custom)); + } + + if (go_live_config.has_value()) { + blog(LOG_INFO, "Enhanced broadcasting config_id: '%s'", + go_live_config->meta.config_id.c_str()); + } + + if (!go_live_config && !custom) { + blog(LOG_ERROR, + "MultitrackVideoOutput: no config set, this should never happen"); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.NoConfig")); + } + + const auto &output_config = custom ? *custom : *go_live_config; + const auto &service_config = go_live_config ? *go_live_config : *custom; + + auto audio_encoders = std::vector(); + auto video_encoders = std::vector(); + auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config, + audio_encoders, video_encoders, + audio_encoder_id, vod_track_mixer); + auto output = std::move(outputs.output); + auto recording_output = std::move(outputs.recording_output); + if (!output) + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.FallbackToDefault") + .arg(multitrack_video_name)); + + auto multitrack_video_service = + create_service(service_config, rtmp_url, stream_key); + if (!multitrack_video_service) + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.FallbackToDefault") + .arg(multitrack_video_name)); + + obs_output_set_service(output, multitrack_video_service); + + OBSSignal start_streaming; + OBSSignal stop_streaming; + OBSSignal deactivate_stream; + SetupSignalHandlers(false, this, output, start_streaming, + stop_streaming, deactivate_stream); + + if (dump_stream_to_file_config && recording_output) { + OBSSignal start_recording; + OBSSignal stop_recording; + OBSSignal deactivate_recording; + SetupSignalHandlers(true, this, recording_output, + start_recording, stop_recording, + deactivate_recording); + + decltype(video_encoders) recording_video_encoders; + recording_video_encoders.reserve(video_encoders.size()); + for (auto &encoder : video_encoders) { + recording_video_encoders.emplace_back( + obs_encoder_get_ref(encoder)); + } + + decltype(audio_encoders) recording_audio_encoders; + recording_audio_encoders.reserve(audio_encoders.size()); + for (auto &encoder : audio_encoders) { + recording_audio_encoders.emplace_back( + obs_encoder_get_ref(encoder)); + } + + { + const std::lock_guard current_stream_dump_lock{ + current_stream_dump_mutex}; + current_stream_dump.emplace(OBSOutputObjects{ + std::move(recording_output), + std::move(recording_video_encoders), + std::move(recording_audio_encoders), + nullptr, + std::move(start_recording), + std::move(stop_recording), + std::move(deactivate_recording), + }); + } + } + + const std::lock_guard current_lock{current_mutex}; + current.emplace(OBSOutputObjects{ + std::move(output), + std::move(video_encoders), + std::move(audio_encoders), + std::move(multitrack_video_service), + std::move(start_streaming), + std::move(stop_streaming), + std::move(deactivate_stream), + }); +} + +signal_handler_t *MultitrackVideoOutput::StreamingSignalHandler() +{ + const std::lock_guard current_lock{current_mutex}; + return current.has_value() + ? obs_output_get_signal_handler(current->output_) + : nullptr; +} + +void MultitrackVideoOutput::StartedStreaming() +{ + OBSOutputAutoRelease dump_output; + { + const std::lock_guard current_stream_dump_lock{ + current_stream_dump_mutex}; + if (current_stream_dump && current_stream_dump->output_) { + dump_output = obs_output_get_ref( + current_stream_dump->output_); + } + } + + if (!dump_output) + return; + + auto result = obs_output_start(dump_output); + blog(LOG_INFO, "MultitrackVideoOutput: starting recording%s", + result ? "" : " failed"); +} + +void MultitrackVideoOutput::StopStreaming() +{ + OBSOutputAutoRelease current_output; + { + const std::lock_guard current_lock{current_mutex}; + if (current && current->output_) + current_output = obs_output_get_ref(current->output_); + } + if (current_output) + obs_output_stop(current_output); + + OBSOutputAutoRelease dump_output; + { + const std::lock_guard current_stream_dump_lock{ + current_stream_dump_mutex}; + if (current_stream_dump && current_stream_dump->output_) + dump_output = obs_output_get_ref( + current_stream_dump->output_); + } + if (dump_output) + obs_output_stop(dump_output); +} + +bool MultitrackVideoOutput::HandleIncompatibleSettings( + QWidget *parent, config_t *config, obs_service_t *service, + bool &useDelay, bool &enableNewSocketLoop, bool &enableDynBitrate) +{ + QString incompatible_settings; + QString where_to_disable; + QString incompatible_settings_list; + + size_t num = 1; + + auto check_setting = [&](bool setting, const char *name, + const char *section) { + if (!setting) + return; + + incompatible_settings += + QString(" %1. %2\n").arg(num).arg(QTStr(name)); + + where_to_disable += + QString(" %1. [%2 > %3 > %4]\n") + .arg(num) + .arg(QTStr("Settings")) + .arg(QTStr("Basic.Settings.Advanced")) + .arg(QTStr(section)); + + incompatible_settings_list += QString("%1, ").arg(name); + + num += 1; + }; + + check_setting(useDelay, "Basic.Settings.Advanced.StreamDelay", + "Basic.Settings.Advanced.StreamDelay"); +#ifdef _WIN32 + check_setting(enableNewSocketLoop, + "Basic.Settings.Advanced.Network.EnableNewSocketLoop", + "Basic.Settings.Advanced.Network"); +#endif + check_setting(enableDynBitrate, + "Basic.Settings.Output.DynamicBitrate.Beta", + "Basic.Settings.Advanced.Network"); + + if (incompatible_settings.isEmpty()) + return true; + + OBSDataAutoRelease service_settings = obs_service_get_settings(service); + + QMessageBox mb(parent); + mb.setIcon(QMessageBox::Critical); + mb.setWindowTitle(QTStr("MultitrackVideo.IncompatibleSettings.Title")); + mb.setText( + QString(QTStr("MultitrackVideo.IncompatibleSettings.Text")) + .arg(obs_data_get_string(service_settings, + "ertmp_multitrack_video_name")) + .arg(incompatible_settings) + .arg(where_to_disable)); + auto this_stream = mb.addButton( + QTStr("MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming"), + QMessageBox::AcceptRole); + auto all_streams = mb.addButton( + QString(QTStr( + "MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming")), + QMessageBox::AcceptRole); + mb.setStandardButtons(QMessageBox::StandardButton::Cancel); + + mb.exec(); + + const char *action = "cancel"; + if (mb.clickedButton() == this_stream) { + action = "DisableAndStartStreaming"; + } else if (mb.clickedButton() == all_streams) { + action = "UpdateAndStartStreaming"; + } + + blog(LOG_INFO, + "MultitrackVideoOutput: attempted to start stream with incompatible" + "settings (%s); action taken: %s", + incompatible_settings_list.toUtf8().constData(), action); + + if (mb.clickedButton() == this_stream || + mb.clickedButton() == all_streams) { + useDelay = false; + enableNewSocketLoop = false; + enableDynBitrate = false; + useDelay = false; + enableNewSocketLoop = false; + enableDynBitrate = false; + + if (mb.clickedButton() == all_streams) { + config_set_bool(config, "Output", "DelayEnable", false); +#ifdef _WIN32 + config_set_bool(config, "Output", "NewSocketLoopEnable", + false); +#endif + config_set_bool(config, "Output", "DynamicBitrate", + false); + } + + return true; + } + + return false; +} + +static bool +create_video_encoders(const GoLiveApi::Config &go_live_config, + std::vector &video_encoders, + obs_output_t *output, obs_output_t *recording_output) +{ + DStr video_encoder_name_buffer; + obs_encoder_t *first_encoder = nullptr; + if (go_live_config.encoder_configurations.empty()) { + blog(LOG_WARNING, + "MultitrackVideoOutput: Missing video encoder configurations"); + throw MultitrackVideoError::warning( + QTStr("FailedToStartStream.MissingEncoderConfigs")); + } + + for (size_t i = 0; i < go_live_config.encoder_configurations.size(); + i++) { + auto encoder = create_video_encoder( + video_encoder_name_buffer, i, + go_live_config.encoder_configurations[i]); + if (!encoder) + return false; + + if (!first_encoder) + first_encoder = encoder; + else + obs_encoder_group_keyframe_aligned_encoders( + first_encoder, encoder); + + obs_output_set_video_encoder2(output, encoder, i); + if (recording_output) + obs_output_set_video_encoder2(recording_output, encoder, + i); + video_encoders.emplace_back(std::move(encoder)); + } + + return true; +} + +static void +create_audio_encoders(const GoLiveApi::Config &go_live_config, + std::vector &audio_encoders, + obs_output_t *output, obs_output_t *recording_output, + const char *audio_encoder_id, + std::optional vod_track_mixer) +{ + using encoder_configs_type = + decltype(go_live_config.audio_configurations.live); + DStr encoder_name_buffer; + size_t output_encoder_index = 0; + + auto create_encoders = [&](const char *name_prefix, + const encoder_configs_type &configs, + size_t mixer_idx) { + if (configs.empty()) { + blog(LOG_WARNING, + "MultitrackVideoOutput: Missing audio encoder configurations (for '%s')", + name_prefix); + throw MultitrackVideoError::warning(QTStr( + "FailedToStartStream.MissingEncoderConfigs")); + } + + for (size_t i = 0; i < configs.size(); i++) { + dstr_printf(encoder_name_buffer, "%s %zu", name_prefix, + i); + OBSEncoderAutoRelease audio_encoder = + create_audio_encoder(encoder_name_buffer->array, + audio_encoder_id, + configs[i].config.bitrate, + mixer_idx); + obs_output_set_audio_encoder(output, audio_encoder, + output_encoder_index); + if (recording_output) + obs_output_set_audio_encoder( + recording_output, audio_encoder, + output_encoder_index); + output_encoder_index += 1; + audio_encoders.emplace_back(std::move(audio_encoder)); + } + }; + + create_encoders("multitrack video live audio", + go_live_config.audio_configurations.live, 0); + + if (!vod_track_mixer.has_value()) + return; + + create_encoders("multitrack video vod audio", + go_live_config.audio_configurations.vod, + *vod_track_mixer); + + return; +} + +static OBSOutputs +SetupOBSOutput(obs_data_t *dump_stream_to_file_config, + const GoLiveApi::Config &go_live_config, + std::vector &audio_encoders, + std::vector &video_encoders, + const char *audio_encoder_id, + std::optional vod_track_mixer) +{ + + auto output = create_output(); + OBSOutputAutoRelease recording_output; + if (dump_stream_to_file_config) + recording_output = + create_recording_output(dump_stream_to_file_config); + + if (!create_video_encoders(go_live_config, video_encoders, output, + recording_output)) + return {nullptr, nullptr}; + + create_audio_encoders(go_live_config, audio_encoders, output, + recording_output, audio_encoder_id, + vod_track_mixer); + + return {std::move(output), std::move(recording_output)}; +} + +void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, + obs_output_t *output, OBSSignal &start, + OBSSignal &stop, OBSSignal &deactivate) +{ + auto handler = obs_output_get_signal_handler(output); + + if (recording) + start.Connect(handler, "start", RecordingStartHandler, self); + + stop.Connect(handler, "stop", + !recording ? StreamStopHandler : RecordingStopHandler, + self); + + deactivate.Connect(handler, "deactivate", + !recording ? StreamDeactivateHandler + : RecordingDeactivateHandler, + self); +} + +std::optional +MultitrackVideoOutput::take_current() +{ + const std::lock_guard current_lock{current_mutex}; + auto val = std::move(current); + current.reset(); + return val; +} + +std::optional +MultitrackVideoOutput::take_current_stream_dump() +{ + const std::lock_guard current_stream_dump_lock{ + current_stream_dump_mutex}; + auto val = std::move(current_stream_dump); + current_stream_dump.reset(); + return val; +} + +void MultitrackVideoOutput::ReleaseOnMainThread( + std::optional objects) +{ + + if (!objects.has_value()) + return; + + QMetaObject::invokeMethod( + QApplication::instance()->thread(), + [objects = std::move(objects)] {}, Qt::QueuedConnection); +} + +void StreamStopHandler(void *arg, calldata_t *params) +{ + auto self = static_cast(arg); + + OBSOutputAutoRelease stream_dump_output; + { + const std::lock_guard current_stream_dump_lock{ + self->current_stream_dump_mutex}; + if (self->current_stream_dump && + self->current_stream_dump->output_) + stream_dump_output = obs_output_get_ref( + self->current_stream_dump->output_); + } + if (stream_dump_output) + obs_output_stop(stream_dump_output); + + if (obs_output_active(static_cast( + calldata_ptr(params, "output")))) + return; + + MultitrackVideoOutput::ReleaseOnMainThread(self->take_current()); +} + +void StreamDeactivateHandler(void *arg, calldata_t *params) +{ + auto self = static_cast(arg); + + if (obs_output_reconnecting(static_cast( + calldata_ptr(params, "output")))) + return; + + MultitrackVideoOutput::ReleaseOnMainThread(self->take_current()); +} + +void RecordingStartHandler(void * /* arg */, calldata_t * /* data */) +{ + blog(LOG_INFO, "MultitrackVideoOutput: recording started"); +} + +void RecordingStopHandler(void *arg, calldata_t *params) +{ + auto self = static_cast(arg); + blog(LOG_INFO, "MultitrackVideoOutput: recording stopped"); + + if (obs_output_active(static_cast( + calldata_ptr(params, "output")))) + return; + + MultitrackVideoOutput::ReleaseOnMainThread( + self->take_current_stream_dump()); +} + +void RecordingDeactivateHandler(void *arg, calldata_t * /*data*/) +{ + auto self = static_cast(arg); + MultitrackVideoOutput::ReleaseOnMainThread( + self->take_current_stream_dump()); +} diff --git a/UI/multitrack-video-output.hpp b/UI/multitrack-video-output.hpp new file mode 100644 index 00000000000000..586432429e2285 --- /dev/null +++ b/UI/multitrack-video-output.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define NOMINMAX + +class QString; + +void StreamStopHandler(void *arg, calldata_t *data); +void StreamDeactivateHandler(void *arg, calldata_t *data); + +void RecordingStartHandler(void *arg, calldata_t *data); +void RecordingStopHandler(void *arg, calldata_t *data); +void RecordingDeactivateHandler(void *arg, calldata_t *data); + +bool MultitrackVideoDeveloperModeEnabled(); + +struct MultitrackVideoOutput { +public: + void PrepareStreaming(QWidget *parent, const char *service_name, + obs_service_t *service, + const std::optional &rtmp_url, + const QString &stream_key, + const char *audio_encoder_id, + std::optional maximum_aggregate_bitrate, + std::optional maximum_video_tracks, + std::optional custom_config, + obs_data_t *dump_stream_to_file_config, + std::optional vod_track_mixer); + signal_handler_t *StreamingSignalHandler(); + void StartedStreaming(); + void StopStreaming(); + bool HandleIncompatibleSettings(QWidget *parent, config_t *config, + obs_service_t *service, bool &useDelay, + bool &enableNewSocketLoop, + bool &enableDynBitrate); + + OBSOutputAutoRelease StreamingOutput() + { + const std::lock_guard current_lock{current_mutex}; + return current ? obs_output_get_ref(current->output_) : nullptr; + } + +private: + struct OBSOutputObjects { + OBSOutputAutoRelease output_; + std::vector video_encoders_; + std::vector audio_encoders_; + OBSServiceAutoRelease multitrack_video_service_; + OBSSignal start_signal, stop_signal, deactivate_signal; + }; + + std::optional take_current(); + std::optional take_current_stream_dump(); + + static void + ReleaseOnMainThread(std::optional objects); + + std::mutex current_mutex; + std::optional current; + + std::mutex current_stream_dump_mutex; + std::optional current_stream_dump; + + friend void StreamStopHandler(void *arg, calldata_t *data); + friend void StreamDeactivateHandler(void *arg, calldata_t *data); + friend void RecordingStartHandler(void *arg, calldata_t *data); + friend void RecordingStopHandler(void *arg, calldata_t *data); + friend void RecordingDeactivateHandler(void *arg, calldata_t *data); +}; diff --git a/UI/qt-helpers.cpp b/UI/qt-helpers.cpp new file mode 100644 index 00000000000000..ad76bd5a1bbd27 --- /dev/null +++ b/UI/qt-helpers.cpp @@ -0,0 +1,10 @@ +#include "qt-helpers.hpp" + +QFuture CreateFuture() +{ + QPromise promise; + auto future = promise.future(); + promise.start(); + promise.finish(); + return future; +} diff --git a/UI/qt-helpers.hpp b/UI/qt-helpers.hpp new file mode 100644 index 00000000000000..c0d7328c08215d --- /dev/null +++ b/UI/qt-helpers.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +template struct FutureHolder { + std::function cancelAll; + QFuture future; +}; + +QFuture CreateFuture(); + +template inline QFuture PreventFutureDeadlock(QFuture future) +{ + /* + * QFutures deadlock if there are continuations on the same thread that + * need to wait for the previous continuation to finish, see + * https://github.com/qt/qtbase/commit/59e21a536f7f81625216dc7a621e7be59919da33 + * + * related bugs: + * https://bugreports.qt.io/browse/QTBUG-119406 + * https://bugreports.qt.io/browse/QTBUG-119103 + * https://bugreports.qt.io/browse/QTBUG-117918 + * https://bugreports.qt.io/browse/QTBUG-119579 + * https://bugreports.qt.io/browse/QTBUG-119810 + * @RytoEX's summary: + * QTBUG-119406 and QTBUG-119103 affect Qt 6.6.0 and are fixed in Qt 6.6.2 and 6.7.0+. + * QTBUG-119579 and QTBUG-119810 affect Qt 6.6.1 and are fixed in Qt 6.6.2 and 6.7.0+. + * QTBUG-117918 is the only strange one that seems to possibly affect all Qt 6.x versions + * until 6.6.2, but only in Debug builds. + * + * To fix this, move relevant QFutures to another thread before resuming + * on main thread for affected Qt versions + */ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) && \ + (QT_VERSION < QT_VERSION_CHECK(6, 6, 2)) + if (future.isFinished()) { + return future; + } + + return future.then(QtFuture::Launch::Async, [](T val) { return val; }); +#else + return future; +#endif +} diff --git a/UI/system-info-macos.mm b/UI/system-info-macos.mm new file mode 100644 index 00000000000000..0c81e4cfb84c19 --- /dev/null +++ b/UI/system-info-macos.mm @@ -0,0 +1,6 @@ +#include "system-info.hpp" + +void system_info(GoLiveApi::Capabilities &capabilities) +{ + UNUSED_PARAMETER(capabilities); +} diff --git a/UI/system-info-posix.cpp b/UI/system-info-posix.cpp new file mode 100644 index 00000000000000..00affc68eccba5 --- /dev/null +++ b/UI/system-info-posix.cpp @@ -0,0 +1,6 @@ +#include "system-info.hpp" + +void system_info(GoLiveApi::Capabilities &capabilities) +{ + UNUSED_PARAMETER(capabilities); +} diff --git a/UI/system-info-windows.cpp b/UI/system-info-windows.cpp new file mode 100644 index 00000000000000..565e6d52189b56 --- /dev/null +++ b/UI/system-info-windows.cpp @@ -0,0 +1,278 @@ +#include "system-info.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +static std::optional> system_gpu_data() +{ + ComPtr factory; + ComPtr adapter; + HRESULT hr; + UINT i; + + hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory)); + if (FAILED(hr)) + return std::nullopt; + + std::vector adapter_info; + + DStr luid_buffer; + for (i = 0; factory->EnumAdapters1(i, adapter.Assign()) == S_OK; ++i) { + DXGI_ADAPTER_DESC desc; + char name[512] = ""; + char driver_version[512] = ""; + + hr = adapter->GetDesc(&desc); + if (FAILED(hr)) + continue; + + /* ignore Microsoft's 'basic' renderer' */ + if (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c) + continue; + + os_wcs_to_utf8(desc.Description, 0, name, sizeof(name)); + + GoLiveApi::Gpu data; + data.model = name; + + data.vendor_id = desc.VendorId; + data.device_id = desc.DeviceId; + + data.dedicated_video_memory = desc.DedicatedVideoMemory; + data.shared_system_memory = desc.SharedSystemMemory; + + dstr_printf(luid_buffer, "luid_0x%08X_0x%08X", + desc.AdapterLuid.HighPart, + desc.AdapterLuid.LowPart); + data.luid = luid_buffer->array; + + /* driver version */ + LARGE_INTEGER umd; + hr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), + &umd); + if (SUCCEEDED(hr)) { + const uint64_t version = umd.QuadPart; + const uint16_t aa = (version >> 48) & 0xffff; + const uint16_t bb = (version >> 32) & 0xffff; + const uint16_t ccccc = (version >> 16) & 0xffff; + const uint16_t ddddd = version & 0xffff; + snprintf(driver_version, sizeof(driver_version), + "%" PRIu16 ".%" PRIu16 ".%" PRIu16 ".%" PRIu16, + aa, bb, ccccc, ddddd); + data.driver_version = driver_version; + } + + adapter_info.push_back(data); + } + + return adapter_info; +} + +static void get_processor_info(char **name, DWORD *speed) +{ + HKEY key; + wchar_t data[1024]; + DWORD size; + LSTATUS status; + + memset(data, 0, sizeof(data)); + + status = RegOpenKeyW( + HKEY_LOCAL_MACHINE, + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &key); + if (status != ERROR_SUCCESS) + return; + + size = sizeof(data); + status = RegQueryValueExW(key, L"ProcessorNameString", NULL, NULL, + (LPBYTE)data, &size); + if (status == ERROR_SUCCESS) { + os_wcs_to_utf8_ptr(data, 0, name); + } else { + *name = 0; + } + + size = sizeof(*speed); + status = RegQueryValueExW(key, L"~MHz", NULL, NULL, (LPBYTE)speed, + &size); + if (status != ERROR_SUCCESS) + *speed = 0; + + RegCloseKey(key); +} + +#define WIN10_GAME_BAR_REG_KEY \ + L"Software\\Microsoft\\Windows\\CurrentVersion\\GameDVR" +#define WIN10_GAME_DVR_POLICY_REG_KEY \ + L"SOFTWARE\\Policies\\Microsoft\\Windows\\GameDVR" +#define WIN10_GAME_DVR_REG_KEY L"System\\GameConfigStore" +#define WIN10_GAME_MODE_REG_KEY L"Software\\Microsoft\\GameBar" +#define WIN10_HAGS_REG_KEY \ + L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers" + +static std::optional +get_gaming_features_data(const win_version_info &ver) +{ + uint32_t win_ver = (ver.major << 8) | ver.minor; + if (win_ver < 0xA00) + return std::nullopt; + + GoLiveApi::GamingFeatures gaming_features; + + struct feature_mapping_s { + std::optional *field; + HKEY hkey; + LPCWSTR sub_key; + LPCWSTR value_name; + LPCWSTR backup_value_name; + bool non_existence_is_false; + DWORD disabled_value; + }; + struct feature_mapping_s features[] = { + {&gaming_features.game_bar_enabled, HKEY_CURRENT_USER, + WIN10_GAME_BAR_REG_KEY, L"AppCaptureEnabled", 0, false, 0}, + {&gaming_features.game_dvr_allowed, HKEY_CURRENT_USER, + WIN10_GAME_DVR_POLICY_REG_KEY, L"AllowGameDVR", 0, false, 0}, + {&gaming_features.game_dvr_enabled, HKEY_CURRENT_USER, + WIN10_GAME_DVR_REG_KEY, L"GameDVR_Enabled", 0, false, 0}, + {&gaming_features.game_dvr_bg_recording, HKEY_CURRENT_USER, + WIN10_GAME_BAR_REG_KEY, L"HistoricalCaptureEnabled", 0, false, + 0}, + {&gaming_features.game_mode_enabled, HKEY_CURRENT_USER, + WIN10_GAME_MODE_REG_KEY, L"AutoGameModeEnabled", + L"AllowAutoGameMode", false, 0}, + {&gaming_features.hags_enabled, HKEY_LOCAL_MACHINE, + WIN10_HAGS_REG_KEY, L"HwSchMode", 0, true, 1}}; + + for (int i = 0; i < sizeof(features) / sizeof(*features); ++i) { + struct reg_dword info; + + get_reg_dword(features[i].hkey, features[i].sub_key, + features[i].value_name, &info); + + if (info.status != ERROR_SUCCESS && + features[i].backup_value_name) { + get_reg_dword(features[i].hkey, features[i].sub_key, + features[i].backup_value_name, &info); + } + + if (info.status == ERROR_SUCCESS) { + *features[i].field = info.return_value != + features[i].disabled_value; + } else if (features[i].non_existence_is_false) { + *features[i].field = false; + } + } + + return gaming_features; +} + +static inline bool get_reg_sz(HKEY key, const wchar_t *val, wchar_t *buf, + DWORD size) +{ + const LSTATUS status = + RegGetValueW(key, NULL, val, RRF_RT_REG_SZ, NULL, buf, &size); + return status == ERROR_SUCCESS; +} + +#define MAX_SZ_LEN 256 +#define WINVER_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" + +static char win_release_id[MAX_SZ_LEN] = "unavailable"; + +static inline void get_reg_ver(struct win_version_info *ver) +{ + HKEY key; + DWORD size, dw_val; + LSTATUS status; + wchar_t str[MAX_SZ_LEN]; + + status = RegOpenKeyW(HKEY_LOCAL_MACHINE, WINVER_REG_KEY, &key); + if (status != ERROR_SUCCESS) + return; + + size = sizeof(dw_val); + + status = RegQueryValueExW(key, L"CurrentMajorVersionNumber", NULL, NULL, + (LPBYTE)&dw_val, &size); + if (status == ERROR_SUCCESS) + ver->major = (int)dw_val; + + status = RegQueryValueExW(key, L"CurrentMinorVersionNumber", NULL, NULL, + (LPBYTE)&dw_val, &size); + if (status == ERROR_SUCCESS) + ver->minor = (int)dw_val; + + status = RegQueryValueExW(key, L"UBR", NULL, NULL, (LPBYTE)&dw_val, + &size); + if (status == ERROR_SUCCESS) + ver->revis = (int)dw_val; + + if (get_reg_sz(key, L"CurrentBuildNumber", str, sizeof(str))) { + ver->build = wcstol(str, NULL, 10); + } + + const wchar_t *release_key = ver->build > 19041 ? L"DisplayVersion" + : L"ReleaseId"; + if (get_reg_sz(key, release_key, str, sizeof(str))) { + os_wcs_to_utf8(str, 0, win_release_id, MAX_SZ_LEN); + } + + RegCloseKey(key); +} + +void system_info(GoLiveApi::Capabilities &capabilities) +{ + char tmpstr[1024]; + + capabilities.gpu = system_gpu_data(); + + { + auto &cpu_data = capabilities.cpu; + cpu_data.physical_cores = os_get_physical_cores(); + cpu_data.logical_cores = os_get_logical_cores(); + DWORD processorSpeed; + char *processorName; + get_processor_info(&processorName, &processorSpeed); + if (processorSpeed) + cpu_data.speed = processorSpeed; + if (processorName) + cpu_data.name = processorName; + bfree(processorName); + } + + { + auto &memory_data = capabilities.memory; + memory_data.total = os_get_sys_total_size(); + memory_data.free = os_get_sys_free_size(); + } + + struct win_version_info ver; + get_win_ver(&ver); + get_reg_ver(&ver); + + // Gaming features + capabilities.gaming_features = get_gaming_features_data(ver); + + { + auto &system_data = capabilities.system; + + snprintf(tmpstr, sizeof(tmpstr), "%d.%d", ver.major, ver.minor); + + system_data.version = tmpstr; + system_data.name = "Windows"; + system_data.build = ver.build; + system_data.release = win_release_id; + system_data.revision = ver.revis; + system_data.bits = is_64_bit_windows() ? 64 : 32; + system_data.arm = is_arm64_windows(); + system_data.armEmulation = os_get_emulation_status(); + } +} diff --git a/UI/system-info.hpp b/UI/system-info.hpp new file mode 100644 index 00000000000000..231b23ab23057f --- /dev/null +++ b/UI/system-info.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "models/multitrack-video.hpp" + +void system_info(GoLiveApi::Capabilities &capabilities); diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index f992c071912186..e06e65eca29358 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -3,6 +3,8 @@ #include +#include + #include "window-basic-auto-config.hpp" #include "window-basic-main.hpp" #include "qt-wrappers.hpp" diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 21548c6e261469..b9a7e3d8ac8f26 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1,8 +1,11 @@ #include #include +#include #include +#include #include "qt-wrappers.hpp" #include "audio-encoders.hpp" +#include "multitrack-video-error.hpp" #include "window-basic-main.hpp" #include "window-basic-main-outputs.hpp" #include "window-basic-vcam.hpp" @@ -67,6 +70,7 @@ static void OBSStopStreaming(void *data, calldata_t *params) output->streamingActive = false; output->delayActive = false; + output->multitrackVideoActive = false; os_atomic_set_bool(&streaming_active, false); QMetaObject::invokeMethod(output->main, "StreamingStop", Q_ARG(int, code), @@ -300,6 +304,18 @@ inline BasicOutputHandler::BasicOutputHandler(OBSBasic *main_) : main(main_) deactivateVirtualCam.Connect(signal, "deactivate", OBSDeactivateVirtualCam, this); } + + auto multitrack_enabled = config_get_bool(main->Config(), "Stream1", + "EnableMultitrackVideo"); + if (!config_has_user_value(main->Config(), "Stream1", + "EnableMultitrackVideo")) { + auto service = main_->GetService(); + OBSDataAutoRelease settings = obs_service_get_settings(service); + multitrack_enabled = obs_data_has_user_value( + settings, "multitrack_video_configuration_url"); + } + if (multitrack_enabled) + multitrackVideo = make_unique(); } extern void log_vcam_changed(const VCamConfig &config, bool starting); @@ -501,9 +517,11 @@ struct SimpleOutput : BasicOutputHandler { void UpdateRecording(); bool ConfigureRecording(bool useReplayBuffer); + bool IsVodTrackEnabled(obs_service_t *service); void SetupVodTrack(obs_service_t *service); - virtual bool SetupStreaming(obs_service_t *service) override; + virtual FutureHolder + SetupStreaming(obs_service_t *service) override; virtual bool StartStreaming(obs_service_t *service) override; virtual bool StartRecording() override; virtual bool StartReplayBuffer() override; @@ -1100,7 +1118,7 @@ const char *FindAudioEncoderFromCodec(const char *type) return nullptr; } -bool SimpleOutput::SetupStreaming(obs_service_t *service) +FutureHolder SimpleOutput::SetupStreaming(obs_service_t *service) { if (!Active()) SetupOutputs(); @@ -1113,46 +1131,72 @@ bool SimpleOutput::SetupStreaming(obs_service_t *service) const char *type = GetStreamOutputType(service); if (!type) - return false; - - /* XXX: this is messy and disgusting and should be refactored */ - if (outputType != type) { - streamDelayStarting.Disconnect(); - streamStopping.Disconnect(); - startStreaming.Disconnect(); - stopStreaming.Disconnect(); - - streamOutput = obs_output_create(type, "simple_stream", nullptr, - nullptr); - if (!streamOutput) { - blog(LOG_WARNING, - "Creation of stream output type '%s' " - "failed!", - type); - return false; - } - - streamDelayStarting.Connect( - obs_output_get_signal_handler(streamOutput), "starting", - OBSStreamStarting, this); - streamStopping.Connect( - obs_output_get_signal_handler(streamOutput), "stopping", - OBSStreamStopping, this); - - startStreaming.Connect( - obs_output_get_signal_handler(streamOutput), "start", - OBSStartStreaming, this); - stopStreaming.Connect( - obs_output_get_signal_handler(streamOutput), "stop", - OBSStopStreaming, this); + return {[] {}, CreateFuture().then([] { return false; })}; + + auto audio_bitrate = GetAudioBitrate(); + auto vod_track_mixer = IsVodTrackEnabled(service) ? std::optional{1} + : std::nullopt; + + auto holder = SetupMultitrackVideo( + service, GetSimpleAACEncoderForBitrate(audio_bitrate), + vod_track_mixer); + auto future = + PreventFutureDeadlock(holder.future) + .then(main, [&](std::optional + multitrackVideoResult) { + if (multitrackVideoResult.has_value()) + return multitrackVideoResult.value(); + + /* XXX: this is messy and disgusting and should be refactored */ + if (outputType != type) { + streamDelayStarting.Disconnect(); + streamStopping.Disconnect(); + startStreaming.Disconnect(); + stopStreaming.Disconnect(); + + streamOutput = obs_output_create( + type, "simple_stream", nullptr, + nullptr); + if (!streamOutput) { + blog(LOG_WARNING, + "Creation of stream output type '%s' " + "failed!", + type); + return false; + } - outputType = type; - } + streamDelayStarting.Connect( + obs_output_get_signal_handler( + streamOutput), + "starting", OBSStreamStarting, + this); + streamStopping.Connect( + obs_output_get_signal_handler( + streamOutput), + "stopping", OBSStreamStopping, + this); + + startStreaming.Connect( + obs_output_get_signal_handler( + streamOutput), + "start", OBSStartStreaming, + this); + stopStreaming.Connect( + obs_output_get_signal_handler( + streamOutput), + "stop", OBSStopStreaming, this); + + outputType = type; + } - obs_output_set_video_encoder(streamOutput, videoStreaming); - obs_output_set_audio_encoder(streamOutput, audioStreaming, 0); - obs_output_set_service(streamOutput, service); - return true; + obs_output_set_video_encoder(streamOutput, + videoStreaming); + obs_output_set_audio_encoder(streamOutput, + audioStreaming, 0); + obs_output_set_service(streamOutput, service); + return true; + }); + return {holder.cancelAll, future}; } static inline bool ServiceSupportsVodTrack(const char *service); @@ -1174,7 +1218,7 @@ static void clear_archive_encoder(obs_output_t *output, obs_output_set_audio_encoder(output, nullptr, 1); } -void SimpleOutput::SetupVodTrack(obs_service_t *service) +bool SimpleOutput::IsVodTrackEnabled(obs_service_t *service) { bool advanced = config_get_bool(main->Config(), "SimpleOutput", "UseAdvanced"); @@ -1188,11 +1232,14 @@ void SimpleOutput::SetupVodTrack(obs_service_t *service) const char *id = obs_service_get_id(service); if (strcmp(id, "rtmp_custom") == 0) - enable = enableForCustomServer ? enable : false; + return enableForCustomServer ? enable : false; else - enable = advanced && enable && ServiceSupportsVodTrack(name); + return advanced && enable && ServiceSupportsVodTrack(name); +} - if (enable) +void SimpleOutput::SetupVodTrack(obs_service_t *service) +{ + if (IsVodTrackEnabled(service)) obs_output_set_audio_encoder(streamOutput, audioArchive, 1); else clear_archive_encoder(streamOutput, SIMPLE_ARCHIVE_NAME); @@ -1219,10 +1266,20 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) "NewSocketLoopEnable"); bool enableLowLatencyMode = config_get_bool(main->Config(), "Output", "LowLatencyEnable"); +#else + bool enableNewSocketLoop = false; #endif bool enableDynBitrate = config_get_bool(main->Config(), "Output", "DynamicBitrate"); + if (multitrackVideo && multitrackVideoActive && + !multitrackVideo->HandleIncompatibleSettings( + main, main->Config(), service, useDelay, + enableNewSocketLoop, enableDynBitrate)) { + multitrackVideoActive = false; + return false; + } + OBSDataAutoRelease settings = obs_data_create(); obs_data_set_string(settings, "bind_ip", bindIP); obs_data_set_string(settings, "ip_family", ipFamily); @@ -1233,6 +1290,10 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) enableLowLatencyMode); #endif obs_data_set_bool(settings, "dyn_bitrate", enableDynBitrate); + + auto streamOutput = + StreamingOutput(); // shadowing is sort of bad, but also convenient + obs_output_update(streamOutput, settings); if (!reconnect) @@ -1243,12 +1304,18 @@ bool SimpleOutput::StartStreaming(obs_service_t *service) obs_output_set_reconnect_settings(streamOutput, maxRetries, retryDelay); - SetupVodTrack(service); + if (!multitrackVideo || !multitrackVideoActive) + SetupVodTrack(service); if (obs_output_start(streamOutput)) { + if (multitrackVideo && multitrackVideoActive) + multitrackVideo->StartedStreaming(); return true; } + if (multitrackVideo && multitrackVideoActive) + multitrackVideoActive = false; + const char *error = obs_output_get_last_error(streamOutput); bool hasLastError = error && *error; if (hasLastError) @@ -1437,10 +1504,13 @@ bool SimpleOutput::StartReplayBuffer() void SimpleOutput::StopStreaming(bool force) { - if (force) - obs_output_force_stop(streamOutput); + auto output = StreamingOutput(); + if (force && output) + obs_output_force_stop(output); + else if (multitrackVideo && multitrackVideoActive) + multitrackVideo->StopStreaming(); else - obs_output_stop(streamOutput); + obs_output_stop(output); } void SimpleOutput::StopRecording(bool force) @@ -1461,7 +1531,7 @@ void SimpleOutput::StopReplayBuffer(bool force) bool SimpleOutput::StreamingActive() const { - return obs_output_active(streamOutput); + return obs_output_active(StreamingOutput()); } bool SimpleOutput::RecordingActive() const @@ -1497,6 +1567,7 @@ struct AdvancedOutput : BasicOutputHandler { inline void UpdateAudioSettings(); virtual void Update() override; + inline std::optional VodTrackMixerIdx(obs_service_t *service); inline void SetupVodTrack(obs_service_t *service); inline void SetupStreaming(); @@ -1505,7 +1576,8 @@ struct AdvancedOutput : BasicOutputHandler { void SetupOutputs() override; int GetAudioBitrate(size_t i, const char *id) const; - virtual bool SetupStreaming(obs_service_t *service) override; + virtual FutureHolder + SetupStreaming(obs_service_t *service) override; virtual bool StartStreaming(obs_service_t *service) override; virtual bool StartRecording() override; virtual bool StartReplayBuffer() override; @@ -2148,7 +2220,8 @@ int AdvancedOutput::GetAudioBitrate(size_t i, const char *id) const return FindClosestAvailableAudioBitrate(id, bitrate); } -inline void AdvancedOutput::SetupVodTrack(obs_service_t *service) +inline std::optional +AdvancedOutput::VodTrackMixerIdx(obs_service_t *service) { int streamTrackIndex = config_get_int(main->Config(), "AdvOut", "TrackIndex"); @@ -2169,13 +2242,21 @@ inline void AdvancedOutput::SetupVodTrack(obs_service_t *service) if (!ServiceSupportsVodTrack(service)) vodTrackEnabled = false; } + if (vodTrackEnabled && streamTrackIndex != vodTrackIndex) + return {vodTrackIndex - 1}; + return std::nullopt; +} + +inline void AdvancedOutput::SetupVodTrack(obs_service_t *service) +{ + if (VodTrackMixerIdx(service).has_value()) obs_output_set_audio_encoder(streamOutput, streamArchiveEnc, 1); else clear_archive_encoder(streamOutput, ADV_ARCHIVE_NAME); } -bool AdvancedOutput::SetupStreaming(obs_service_t *service) +FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) { int multiTrackAudioMixes = config_get_int(main->Config(), "AdvOut", "StreamMultiTrackAudioMixes"); @@ -2201,55 +2282,88 @@ bool AdvancedOutput::SetupStreaming(obs_service_t *service) const char *type = GetStreamOutputType(service); if (!type) - return false; + return {[] {}, CreateFuture().then(main, [] { return false; })}; - /* XXX: this is messy and disgusting and should be refactored */ - if (outputType != type) { - streamDelayStarting.Disconnect(); - streamStopping.Disconnect(); - startStreaming.Disconnect(); - stopStreaming.Disconnect(); + const char *audio_encoder_id = + config_get_string(main->Config(), "AdvOut", "AudioEncoder"); - streamOutput = - obs_output_create(type, "adv_stream", nullptr, nullptr); - if (!streamOutput) { - blog(LOG_WARNING, - "Creation of stream output type '%s' " - "failed!", - type); - return false; - } + auto holder = SetupMultitrackVideo(service, audio_encoder_id, + VodTrackMixerIdx(service)); + auto future = + PreventFutureDeadlock(holder.future) + .then(main, [&](std::optional + multitrackVideoResult) { + if (multitrackVideoResult.has_value()) + return multitrackVideoResult.value(); + + /* XXX: this is messy and disgusting and should be refactored */ + if (outputType != type) { + streamDelayStarting.Disconnect(); + streamStopping.Disconnect(); + startStreaming.Disconnect(); + stopStreaming.Disconnect(); + + streamOutput = obs_output_create( + type, "adv_stream", nullptr, + nullptr); + if (!streamOutput) { + blog(LOG_WARNING, + "Creation of stream output type '%s' " + "failed!", + type); + return false; + } - streamDelayStarting.Connect( - obs_output_get_signal_handler(streamOutput), "starting", - OBSStreamStarting, this); - streamStopping.Connect( - obs_output_get_signal_handler(streamOutput), "stopping", - OBSStreamStopping, this); + streamDelayStarting.Connect( + obs_output_get_signal_handler( + streamOutput), + "starting", OBSStreamStarting, + this); + streamStopping.Connect( + obs_output_get_signal_handler( + streamOutput), + "stopping", OBSStreamStopping, + this); + + startStreaming.Connect( + obs_output_get_signal_handler( + streamOutput), + "start", OBSStartStreaming, + this); + stopStreaming.Connect( + obs_output_get_signal_handler( + streamOutput), + "stop", OBSStopStreaming, this); + + outputType = type; + } - startStreaming.Connect( - obs_output_get_signal_handler(streamOutput), "start", - OBSStartStreaming, this); - stopStreaming.Connect( - obs_output_get_signal_handler(streamOutput), "stop", - OBSStopStreaming, this); + obs_output_set_video_encoder(streamOutput, + videoStreaming); + obs_output_set_audio_encoder(streamOutput, + streamAudioEnc, 0); - outputType = type; - } + if (!is_multitrack_output) { + obs_output_set_audio_encoder( + streamOutput, streamAudioEnc, + 0); + } else { + for (int i = 0; i < MAX_AUDIO_MIXES; + i++) { + if ((multiTrackAudioMixes & + (1 << i)) != 0) { + obs_output_set_audio_encoder( + streamOutput, + streamTrack[i], + idx); + idx++; + } + } + } - obs_output_set_video_encoder(streamOutput, videoStreaming); - if (!is_multitrack_output) { - obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0); - } else { - for (int i = 0; i < MAX_AUDIO_MIXES; i++) { - if ((multiTrackAudioMixes & (1 << i)) != 0) { - obs_output_set_audio_encoder( - streamOutput, streamTrack[i], idx); - idx++; - } - } - } - return true; + return true; + }); + return {holder.cancelAll, future}; } bool AdvancedOutput::StartStreaming(obs_service_t *service) @@ -2273,10 +2387,20 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) "NewSocketLoopEnable"); bool enableLowLatencyMode = config_get_bool(main->Config(), "Output", "LowLatencyEnable"); +#else + bool enableNewSocketLoop = false; #endif bool enableDynBitrate = config_get_bool(main->Config(), "Output", "DynamicBitrate"); + if (multitrackVideo && multitrackVideoActive && + !multitrackVideo->HandleIncompatibleSettings( + main, main->Config(), service, useDelay, + enableNewSocketLoop, enableDynBitrate)) { + multitrackVideoActive = false; + return false; + } + bool is_rtmp = false; obs_service_t *service_obj = main->GetService(); const char *protocol = obs_service_get_protocol(service_obj); @@ -2296,6 +2420,10 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) enableLowLatencyMode); #endif obs_data_set_bool(settings, "dyn_bitrate", enableDynBitrate); + + auto streamOutput = + StreamingOutput(); // shadowing is sort of bad, but also convenient + obs_output_update(streamOutput, settings); if (!reconnect) @@ -2309,9 +2437,14 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service) SetupVodTrack(service); } if (obs_output_start(streamOutput)) { + if (multitrackVideo && multitrackVideoActive) + multitrackVideo->StartedStreaming(); return true; } + if (multitrackVideo && multitrackVideoActive) + multitrackVideoActive = false; + const char *error = obs_output_get_last_error(streamOutput); bool hasLastError = error && *error; if (hasLastError) @@ -2341,7 +2474,7 @@ bool AdvancedOutput::StartRecording() if (!ffmpegOutput) { UpdateRecordingSettings(); } - } else if (!obs_output_active(streamOutput)) { + } else if (!obs_output_active(StreamingOutput())) { UpdateStreamSettings(); } @@ -2440,7 +2573,7 @@ bool AdvancedOutput::StartReplayBuffer() if (!useStreamEncoder) { if (!ffmpegOutput) UpdateRecordingSettings(); - } else if (!obs_output_active(streamOutput)) { + } else if (!obs_output_active(StreamingOutput())) { UpdateStreamSettings(); } @@ -2504,10 +2637,13 @@ bool AdvancedOutput::StartReplayBuffer() void AdvancedOutput::StopStreaming(bool force) { - if (force) - obs_output_force_stop(streamOutput); + auto output = StreamingOutput(); + if (force && output) + obs_output_force_stop(output); + else if (multitrackVideo && multitrackVideoActive) + multitrackVideo->StopStreaming(); else - obs_output_stop(streamOutput); + obs_output_stop(output); } void AdvancedOutput::StopRecording(bool force) @@ -2528,7 +2664,7 @@ void AdvancedOutput::StopReplayBuffer(bool force) bool AdvancedOutput::StreamingActive() const { - return obs_output_active(streamOutput); + return obs_output_active(StreamingOutput()); } bool AdvancedOutput::RecordingActive() const @@ -2563,6 +2699,174 @@ std::string BasicOutputHandler::GetRecordingFilename( return dst; } +extern std::string DeserializeConfigText(const char *text); + +FutureHolder> +BasicOutputHandler::SetupMultitrackVideo(obs_service_t *service, + std::string audio_encoder_id, + std::optional vod_track_mixer) +{ + if (!multitrackVideo) + return {[] {}, CreateFuture().then([] { + return std::optional{std::nullopt}; + })}; + + multitrackVideoActive = false; + + streamDelayStarting.Disconnect(); + streamStopping.Disconnect(); + startStreaming.Disconnect(); + stopStreaming.Disconnect(); + + bool is_custom = + strncmp("rtmp_custom", obs_service_get_type(service), 11) == 0; + + std::optional custom_config = std::nullopt; + if (config_get_bool(main->Config(), "Stream1", + "MultitrackVideoConfigOverrideEnabled")) + custom_config = DeserializeConfigText( + config_get_string(main->Config(), "Stream1", + "MultitrackVideoConfigOverride")); + + OBSDataAutoRelease settings = obs_service_get_settings(service); + QString key = obs_data_get_string(settings, "key"); + + const char *service_name = ""; + if (is_custom && obs_data_has_user_value(settings, "service_name")) { + service_name = obs_data_get_string(settings, "service_name"); + } else if (!is_custom) { + service_name = obs_data_get_string(settings, "service"); + } + + std::optional custom_rtmp_url; + auto server = obs_data_get_string(settings, "server"); + if (strcmp(server, "auto") != 0) { + custom_rtmp_url = server; + } + + auto service_custom_server = + obs_data_get_bool(settings, "using_custom_server"); + if (custom_rtmp_url.has_value()) { + blog(LOG_INFO, "Using %sserver '%s'", + service_custom_server ? "custom " : "", + custom_rtmp_url->c_str()); + } + + auto maximum_aggregate_bitrate = + config_get_bool(main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrateAuto") + ? std::nullopt + : std::make_optional(config_get_int( + main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrate")); + + auto maximum_video_tracks = + config_get_bool(main->Config(), "Stream1", + "MultitrackVideoMaximumVideoTracksAuto") + ? std::nullopt + : std::make_optional(config_get_int( + main->Config(), "Stream1", + "MultitrackVideoMaximumVideoTracks")); + + auto stream_dump_config = GenerateMultitrackVideoStreamDumpConfig(); + + auto firstFuture = CreateFuture().then( + QThreadPool::globalInstance(), + [=, multitrackVideo = multitrackVideo.get(), + service_name = std::string{service_name}, + service = OBSService{service}, + stream_dump_config = std::move(stream_dump_config)]() + -> std::optional { + try { + multitrackVideo->PrepareStreaming( + main, service_name.c_str(), service, + custom_rtmp_url, key, + audio_encoder_id.c_str(), + maximum_aggregate_bitrate, + maximum_video_tracks, custom_config, + stream_dump_config, vod_track_mixer); + } catch (const MultitrackVideoError &error) { + return error; + } + return std::nullopt; + }); + + auto secondFuture = firstFuture.then( + main, + [&, service = OBSService{service}]( + std::optional error) + -> std::optional { + if (error) { + OBSDataAutoRelease service_settings = + obs_service_get_settings(service); + auto multitrack_video_name = QTStr( + "Basic.Settings.Stream.MultitrackVideoLabel"); + if (obs_data_has_user_value( + service_settings, + "multitrack_video_name")) { + multitrack_video_name = + obs_data_get_string( + service_settings, + "multitrack_video_name"); + } + + multitrackVideoActive = false; + if (!error->ShowDialog(main, + multitrack_video_name)) + return false; + return std::nullopt; + } + + multitrackVideoActive = true; + + auto signal_handler = + multitrackVideo->StreamingSignalHandler(); + + streamDelayStarting.Connect(signal_handler, "starting", + OBSStreamStarting, this); + streamStopping.Connect(signal_handler, "stopping", + OBSStreamStopping, this); + + startStreaming.Connect(signal_handler, "start", + OBSStartStreaming, this); + stopStreaming.Connect(signal_handler, "stop", + OBSStopStreaming, this); + return true; + }); + + return {[=]() mutable { firstFuture.cancel(); }, + PreventFutureDeadlock(secondFuture)}; +} + +OBSDataAutoRelease BasicOutputHandler::GenerateMultitrackVideoStreamDumpConfig() +{ + auto stream_dump_enabled = config_get_bool( + main->Config(), "Stream1", "MultitrackVideoStreamDumpEnabled"); + + if (!stream_dump_enabled) + return nullptr; + + const char *path = + config_get_string(main->Config(), "SimpleOutput", "FilePath"); + bool noSpace = config_get_bool(main->Config(), "SimpleOutput", + "FileNameWithoutSpace"); + const char *filenameFormat = config_get_string(main->Config(), "Output", + "FilenameFormatting"); + bool overwriteIfExists = + config_get_bool(main->Config(), "Output", "OverwriteIfExists"); + + string f; + + OBSDataAutoRelease settings = obs_data_create(); + f = GetFormatString(filenameFormat, nullptr, nullptr); + string strPath = GetRecordingFilename(path, "flv", noSpace, + overwriteIfExists, f.c_str(), + // never remux stream dump + false); + obs_data_set_string(settings, "path", strPath.c_str()); + return settings; +} + BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main) { return new SimpleOutput(main); diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp index 0850f00a972721..5dd86df461f9c1 100644 --- a/UI/window-basic-main-outputs.hpp +++ b/UI/window-basic-main-outputs.hpp @@ -1,7 +1,13 @@ #pragma once +#include #include +#include + +#include "qt-helpers.hpp" +#include "multitrack-video-output.hpp" + class OBSBasic; struct BasicOutputHandler { @@ -16,6 +22,17 @@ struct BasicOutputHandler { bool virtualCamActive = false; OBSBasic *main; + std::unique_ptr multitrackVideo; + bool multitrackVideoActive = false; + + OBSOutputAutoRelease StreamingOutput() const + { + return (multitrackVideo && multitrackVideoActive) + ? multitrackVideo->StreamingOutput() + : OBSOutputAutoRelease{ + obs_output_get_ref(streamOutput)}; + } + obs_view_t *virtualCamView = nullptr; video_t *virtualCamVideo = nullptr; obs_scene_t *vCamSourceScene = nullptr; @@ -46,7 +63,7 @@ struct BasicOutputHandler { virtual ~BasicOutputHandler(){}; - virtual bool SetupStreaming(obs_service_t *service) = 0; + virtual FutureHolder SetupStreaming(obs_service_t *service) = 0; virtual bool StartStreaming(obs_service_t *service) = 0; virtual bool StartRecording() = 0; virtual bool StartReplayBuffer() { return false; } @@ -70,7 +87,8 @@ struct BasicOutputHandler { inline bool Active() const { return streamingActive || recordingActive || delayActive || - replayBufferActive || virtualCamActive; + replayBufferActive || virtualCamActive || + multitrackVideoActive; } protected: @@ -79,6 +97,12 @@ struct BasicOutputHandler { const char *container, bool noSpace, bool overwrite, const char *format, bool ffmpeg); + + FutureHolder> + SetupMultitrackVideo(obs_service_t *service, + std::string audio_encoder_id, + std::optional vod_track_mixer); + OBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig(); }; BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index f173886e1200cb..ce74a8b6553c54 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1615,6 +1615,13 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_bool(basicConfig, "Stream1", "IgnoreRecommended", false); + config_set_default_bool(basicConfig, "Stream1", "EnableMultitrackVideo", + true); + config_set_default_bool(basicConfig, "Stream1", + "MultitrackVideoMaximumAggregateBitrateAuto", + true); + config_set_default_bool(basicConfig, "Stream1", + "MultitrackVideoMaximumVideoTracksAuto", true); config_set_default_string(basicConfig, "SimpleOutput", "FilePath", GetDefaultVideoSavePath().c_str()); @@ -1953,7 +1960,8 @@ void OBSBasic::ResetOutputs() const char *mode = config_get_string(basicConfig, "Output", "Mode"); bool advOut = astrcmpi(mode, "Advanced") == 0; - if (!outputHandler || !outputHandler->Active()) { + if ((!outputHandler || !outputHandler->Active()) && + startStreamingFuture.future.isFinished()) { outputHandler.reset(); outputHandler.reset(advOut ? CreateAdvancedOutputHandler(this) : CreateSimpleOutputHandler(this)); @@ -5097,6 +5105,22 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { + if (!startStreamingFuture.future.isFinished() && + !startStreamingFuture.future.isCanceled()) { + startStreamingFuture.future.onCanceled( + this, [basic = QPointer{this}] { + if (basic) + basic->close(); + }); + startStreamingFuture.cancelAll(); + event->ignore(); + return; + } else if (startStreamingFuture.future.isCanceled() && + !startStreamingFuture.future.isFinished()) { + event->ignore(); + return; + } + /* Do not close window if inside of a temporary event loop because we * could be inside of an Auth::LoadUI call. Keep trying once per * second until we've exit any known sub-loops. */ @@ -7016,68 +7040,88 @@ void OBSBasic::StartStreaming() } } - if (!outputHandler->SetupStreaming(service)) { - DisplayStreamStartError(); - return; - } - - if (api) - api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTING); - - SaveProject(); + auto setStreamText = [&](const QString &text) { + ui->streamButton->setText(text); + if (sysTrayStream) + sysTrayStream->setText(text); + }; ui->streamButton->setEnabled(false); ui->streamButton->setChecked(false); - ui->streamButton->setText(QTStr("Basic.Main.Connecting")); ui->broadcastButton->setChecked(false); - - if (sysTrayStream) { + if (sysTrayStream) sysTrayStream->setEnabled(false); - sysTrayStream->setText(ui->streamButton->text()); - } - if (!outputHandler->StartStreaming(service)) { - DisplayStreamStartError(); - return; - } + setStreamText(QTStr("Basic.Main.PreparingStream")); - if (!autoStartBroadcast) { - ui->broadcastButton->setText( - QTStr("Basic.Main.StartBroadcast")); - ui->broadcastButton->setProperty("broadcastState", "ready"); - ui->broadcastButton->style()->unpolish(ui->broadcastButton); - ui->broadcastButton->style()->polish(ui->broadcastButton); - // well, we need to disable button while stream is not active - ui->broadcastButton->setEnabled(false); - } else { - if (!autoStopBroadcast) { - ui->broadcastButton->setText( - QTStr("Basic.Main.StopBroadcast")); - } else { - ui->broadcastButton->setText( - QTStr("Basic.Main.AutoStopEnabled")); - ui->broadcastButton->setEnabled(false); - } - ui->broadcastButton->setProperty("broadcastState", "active"); - ui->broadcastButton->style()->unpolish(ui->broadcastButton); - ui->broadcastButton->style()->polish(ui->broadcastButton); - broadcastActive = true; - } + auto holder = outputHandler->SetupStreaming(service); + auto future = holder.future.then( + this, [&, setStreamText](bool setupStreamingResult) { + if (!setupStreamingResult) { + DisplayStreamStartError(); + return; + } - bool recordWhenStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); - if (recordWhenStreaming) - StartRecording(); + if (api) + api->on_event( + OBS_FRONTEND_EVENT_STREAMING_STARTING); - bool replayBufferWhileStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming"); - if (replayBufferWhileStreaming) - StartReplayBuffer(); + SaveProject(); + + setStreamText(QTStr("Basic.Main.Connecting")); + + if (!outputHandler->StartStreaming(service)) { + DisplayStreamStartError(); + return; + } + + if (!autoStartBroadcast) { + ui->broadcastButton->setText( + QTStr("Basic.Main.StartBroadcast")); + ui->broadcastButton->setProperty( + "broadcastState", "ready"); + ui->broadcastButton->style()->unpolish( + ui->broadcastButton); + ui->broadcastButton->style()->polish( + ui->broadcastButton); + // well, we need to disable button while stream is not active + ui->broadcastButton->setEnabled(false); + } else { + if (!autoStopBroadcast) { + ui->broadcastButton->setText(QTStr( + "Basic.Main.StopBroadcast")); + } else { + ui->broadcastButton->setText(QTStr( + "Basic.Main.AutoStopEnabled")); + ui->broadcastButton->setEnabled(false); + } + ui->broadcastButton->setProperty( + "broadcastState", "active"); + ui->broadcastButton->style()->unpolish( + ui->broadcastButton); + ui->broadcastButton->style()->polish( + ui->broadcastButton); + broadcastActive = true; + } + + bool recordWhenStreaming = config_get_bool( + GetGlobalConfig(), "BasicWindow", + "RecordWhenStreaming"); + if (recordWhenStreaming) + StartRecording(); + + bool replayBufferWhileStreaming = config_get_bool( + GetGlobalConfig(), "BasicWindow", + "ReplayBufferWhileStreaming"); + if (replayBufferWhileStreaming) + StartReplayBuffer(); #ifdef YOUTUBE_ENABLED - if (!autoStartBroadcast) - OBSBasic::ShowYouTubeAutoStartWarning(); + if (!autoStartBroadcast) + OBSBasic::ShowYouTubeAutoStartWarning(); #endif + }); + startStreamingFuture = {holder.cancelAll, future}; } void OBSBasic::BroadcastButtonClicked() @@ -7447,10 +7491,12 @@ void OBSBasic::StreamDelayStopping(int sec) void OBSBasic::StreamingStart() { + OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); + ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); ui->streamButton->setChecked(true); - ui->statusbar->StreamStarted(outputHandler->streamOutput); + ui->statusbar->StreamStarted(output); if (sysTrayStream) { sysTrayStream->setText(ui->streamButton->text()); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 68b7248c36d8d6..64d237446f5b5c 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include "auth-base.hpp" #include "log-viewer.hpp" #include "undo-stack-obs.hpp" +#include "qt-helpers.hpp" #include @@ -284,6 +286,7 @@ class OBSBasic : public OBSMainWindow { OBSService service; std::unique_ptr outputHandler; + FutureHolder startStreamingFuture; bool streamingStopping = false; bool recordingStopping = false; bool replayBufferStopping = false; diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 775d1309877b1b..f61c02e2afceb1 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -1068,6 +1068,12 @@ void OBSBasicSettings::SaveSpinBox(QSpinBox *widget, const char *section, config_set_int(main->Config(), section, value, widget->value()); } +std::string DeserializeConfigText(const char *value) +{ + OBSDataAutoRelease data = obs_data_create_from_json(value); + return obs_data_get_string(data, "text"); +} + void OBSBasicSettings::SaveGroupBox(QGroupBox *widget, const char *section, const char *value) { diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 51830c63fe6cdb..03d55a26394a1f 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -70,6 +70,8 @@ public slots: } }; +std::string DeserializeConfigText(const char *value); + class OBSBasicSettings : public QDialog { Q_OBJECT Q_PROPERTY(QIcon generalIcon READ GetGeneralIcon WRITE SetGeneralIcon From 3f1362f7e7864df8fcac5d2b1aa97be9343b9e71 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Apr 2024 16:23:02 +0200 Subject: [PATCH 0170/1073] UI: Add Multitrack Video settings --- UI/data/locale/en-US.ini | 11 + UI/forms/OBSBasicSettings.ui | 906 ++++++++++++++++++---------- UI/window-basic-settings-stream.cpp | 104 +++- UI/window-basic-settings.cpp | 134 ++++ UI/window-basic-settings.hpp | 5 +- 5 files changed, 827 insertions(+), 333 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 8a24e7265d7cf7..c956325778673a 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -935,6 +935,7 @@ Basic.Settings.Appearance.General.NoVariant="No Styles Available" # basic mode 'stream' settings Basic.Settings.Stream="Stream" +Basic.Settings.Stream.Destination="Destination" Basic.Settings.Stream.Custom.UseAuthentication="Use authentication" Basic.Settings.Stream.Custom.Username="Username" Basic.Settings.Stream.Custom.Password="Password" @@ -960,6 +961,16 @@ Basic.Settings.Stream.Recommended.MaxResolution="Maximum Resolution: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximum FPS: %1" Basic.Settings.Stream.SpecifyCustomServer="Specify Custom Server..." Basic.Settings.Stream.ServiceCustomServer="Custom Server" +Basic.Settings.Stream.EnableMultitrackVideo="Enable %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximum Streaming Bandwidth" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Auto" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maximum Video Tracks" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Auto" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Enable stream dump to FLV (uses simple recording file settings)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Config Override (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Enable Config Override" +Basic.Settings.Stream.MultitrackVideoLabel="Multitrack Video" +Basic.Settings.Stream.AdvancedOptions="Advanced Options" # basic mode 'output' settings Basic.Settings.Output="Output" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 504c48ed9a1f92..a144805d3dac4b 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1204,366 +1204,614 @@
- - - - QFormLayout::AllNonFixedFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + - - - - Basic.AutoConfig.StreamPage.Server - - - - - - - - 0 - 0 - - - - 1 - - - + + + + + + 0 + 0 + + + + Basic.Settings.Stream.Destination + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - 0 + 9 - 0 + 2 - 0 + 9 - 0 + 9 - - + + + + Basic.AutoConfig.StreamPage.Server + + + + + + + + 0 + 0 + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + Basic.Settings.Stream.ServiceCustomServer + + + serviceCustomServer + + + + + + + + + + Basic.AutoConfig.StreamPage.StreamKey + + + true + + + key + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + QLineEdit::Password + + + + + + + Show + + + + + + + + + + -4 + + + Basic.AutoConfig.StreamPage.GetStreamKey + + + + + + + + + + Qt::Horizontal + + + + 170 + 0 + + + + + + + + + + PointingHandCursor + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + Basic.AutoConfig.StreamPage.ConnectedAccount + + + + + + + 8 + + + 7 + + + 7 + + + + + font-weight: bold + + + Auth.LoadingChannel.Title + + + + + + + Basic.AutoConfig.StreamPage.DisconnectAccount + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + Basic.Settings.Stream.Custom.UseAuthentication + + + + + + + Basic.Settings.Stream.Custom.Username + + + authUsername + + + + + + + + + + Basic.Settings.Stream.Custom.Password + + + authPw + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLineEdit::Password + + + + + + + Show + + + + + - - + + + + + + 0 + 0 + + + + Basic.Settings.Stream.MultitrackVideoLabel + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - 0 + 9 - 0 + 2 - 0 + 9 - 0 + 9 - - + + + + Basic.Settings.Stream.EnableMultitrackVideo + + + + + + + MultitrackVideo.Info + + + Qt::RichText + + + true + + + true + + + + + + + Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate + + + + + + + + + Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto + + + QSizePolicy::Minimum, QSizePolicy::Minimum + + + + + + + QSizePolicy::MinimumExpanding, QSizePolicy::Minimum + + + 500 + + + 1000000 + + + 8000 + + + + + + + + + Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks + + + + + + + + + Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto + + + QSizePolicy::Minimum, QSizePolicy::Minimum + + + + + + + QSizePolicy::MinimumExpanding, QSizePolicy::Minimum + + + 0 + + + 100 + + + 0 + + + + + + + + + Basic.Settings.Stream.MultitrackVideoStreamDumpEnable + + + + + + + Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable + + + + + + + Basic.Settings.Stream.MultitrackVideoConfigOverride + + + + + + + QSizePolicy::Preferred, QSizePolicy::MinimumExpanding + + - - - - - - Basic.Settings.Stream.ServiceCustomServer - - - serviceCustomServer - - - - - - - - - - Basic.AutoConfig.StreamPage.StreamKey - - - true - - - key - - - - - - - - 0 + + + + + + 0 + 0 + - - 0 - - - 0 - - - 0 + + Basic.Settings.Stream.AdvancedOptions - - - - - - - - - - QLineEdit::Password - - - - - - - Show - - - - - - - - - - -4 - - - Basic.AutoConfig.StreamPage.GetStreamKey - - - - - -
- - - - Qt::Horizontal - - - - 170 - 8 - - - - - - - - 8 - - - 7 - - - 7 - - - - - font-weight: bold - - - Auth.LoadingChannel.Title - - - - - - - Basic.AutoConfig.StreamPage.DisconnectAccount + + + QFormLayout::AllNonFixedFieldsGrow - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Basic.Settings.Stream.BandwidthTestMode - - - - - - - Basic.Settings.Stream.Custom.UseAuthentication - - - - - - - Basic.Settings.Stream.Custom.Username - - - authUsername - - - - - - - - - - Basic.Settings.Stream.Custom.Password - - - authPw - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QLineEdit::Password - - - - - - - Show - - - - - - - - - - - - - Basic.Settings.Stream.TTVAddon - - - twitchAddonDropdown - - - - - - - Basic.Settings.Stream.IgnoreRecommended - - - - - - - - - - Qt::RichText - - - true - - - - - - - - - Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced - - - - - - - Qt::Horizontal - - - - 40 - 20 - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - Basic.AutoConfig.StreamPage.ConnectedAccount - - - - - - - - - PointingHandCursor + + 9 - - Basic.AutoConfig.StreamPage.ConnectAccount + + 2 - - - - - - Qt::Horizontal + + 9 - - - 40 - 20 - + + 9 - - - - -
+ + + + Basic.Settings.Stream.TTVAddon + + + twitchAddonDropdown + + + + + + + + + + Qt::Horizontal + + + + 170 + 0 + + + + + + + + Basic.Settings.Stream.BandwidthTestMode + + + + + + + Basic.Settings.Stream.IgnoreRecommended + + + + + + + + + + Qt::RichText + + + true + + + +
+ + + + diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 04cc3a3bb17fb9..8a5f41c00a1de0 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -45,7 +45,7 @@ enum class Section : int { StreamKey, }; -inline bool OBSBasicSettings::IsCustomService() const +bool OBSBasicSettings::IsCustomService() const { return ui->service->currentData().toInt() == (int)ListOpt::Custom; } @@ -96,6 +96,16 @@ void OBSBasicSettings::InitStreamPage() &OBSBasicSettings::DisplayEnforceWarning); connect(ui->ignoreRecommended, &QCheckBox::toggled, this, &OBSBasicSettings::UpdateResFPSLimits); + + connect(ui->enableMultitrackVideo, &QCheckBox::toggled, this, + &OBSBasicSettings::UpdateMultitrackVideo); + connect(ui->multitrackVideoMaximumAggregateBitrateAuto, + &QCheckBox::toggled, this, + &OBSBasicSettings::UpdateMultitrackVideo); + connect(ui->multitrackVideoMaximumVideoTracksAuto, &QCheckBox::toggled, + this, &OBSBasicSettings::UpdateMultitrackVideo); + connect(ui->multitrackVideoConfigOverrideEnable, &QCheckBox::toggled, + this, &OBSBasicSettings::UpdateMultitrackVideo); } void OBSBasicSettings::LoadStream1Settings() @@ -155,6 +165,44 @@ void OBSBasicSettings::LoadStream1Settings() ui->twitchAddonDropdown->setCurrentIndex(idx); } + ui->enableMultitrackVideo->setChecked(config_get_bool( + main->Config(), "Stream1", "EnableMultitrackVideo")); + + ui->multitrackVideoMaximumAggregateBitrateAuto->setChecked( + config_get_bool(main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrateAuto")); + if (config_has_user_value(main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrate")) { + ui->multitrackVideoMaximumAggregateBitrate->setValue( + config_get_int( + main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrate")); + } + + ui->multitrackVideoMaximumVideoTracksAuto->setChecked( + config_get_bool(main->Config(), "Stream1", + "MultitrackVideoMaximumVideoTracksAuto")); + if (config_has_user_value(main->Config(), "Stream1", + "MultitrackVideoMaximumVideoTracks")) + ui->multitrackVideoMaximumVideoTracks->setValue( + config_get_int(main->Config(), "Stream1", + "MultitrackVideoMaximumVideoTracks")); + + ui->multitrackVideoStreamDumpEnable->setChecked(config_get_bool( + main->Config(), "Stream1", "MultitrackVideoStreamDumpEnabled")); + + ui->multitrackVideoConfigOverrideEnable->setChecked( + config_get_bool(main->Config(), "Stream1", + "MultitrackVideoConfigOverrideEnabled")); + if (config_has_user_value(main->Config(), "Stream1", + "MultitrackVideoConfigOverride")) + ui->multitrackVideoConfigOverride->setPlainText( + DeserializeConfigText( + config_get_string( + main->Config(), "Stream1", + "MultitrackVideoConfigOverride")) + .c_str()); + UpdateServerList(); if (is_rtmp_common) { @@ -187,6 +235,7 @@ void OBSBasicSettings::LoadStream1Settings() UpdateMoreInfoLink(); UpdateVodTrackSetting(); UpdateServiceRecommendations(); + UpdateMultitrackVideo(); bool streamActive = obs_frontend_streaming_active(); ui->streamPage->setEnabled(!streamActive); @@ -315,6 +364,49 @@ void OBSBasicSettings::SaveStream1Settings() } SaveCheckBox(ui->ignoreRecommended, "Stream1", "IgnoreRecommended"); + + auto oldMultitrackVideoSetting = config_get_bool( + main->Config(), "Stream1", "EnableMultitrackVideo"); + + if (!IsCustomService()) { + OBSDataAutoRelease settings = obs_data_create(); + obs_data_set_string(settings, "service", + QT_TO_UTF8(ui->service->currentText())); + OBSServiceAutoRelease temp_service = obs_service_create_private( + "rtmp_common", "auto config query service", settings); + settings = obs_service_get_settings(temp_service); + auto available = obs_data_has_user_value( + settings, "multitrack_video_configuration_url"); + + if (available) { + SaveCheckBox(ui->enableMultitrackVideo, "Stream1", + "EnableMultitrackVideo"); + } else { + config_remove_value(main->Config(), "Stream1", + "EnableMultitrackVideo"); + } + } else { + SaveCheckBox(ui->enableMultitrackVideo, "Stream1", + "EnableMultitrackVideo"); + } + SaveCheckBox(ui->multitrackVideoMaximumAggregateBitrateAuto, "Stream1", + "MultitrackVideoMaximumAggregateBitrateAuto"); + SaveSpinBox(ui->multitrackVideoMaximumAggregateBitrate, "Stream1", + "MultitrackVideoMaximumAggregateBitrate"); + SaveCheckBox(ui->multitrackVideoMaximumVideoTracksAuto, "Stream1", + "MultitrackVideoMaximumVideoTracksAuto"); + SaveSpinBox(ui->multitrackVideoMaximumVideoTracks, "Stream1", + "MultitrackVideoMaximumVideoTracks"); + SaveCheckBox(ui->multitrackVideoStreamDumpEnable, "Stream1", + "MultitrackVideoStreamDumpEnabled"); + SaveCheckBox(ui->multitrackVideoConfigOverrideEnable, "Stream1", + "MultitrackVideoConfigOverrideEnabled"); + SaveText(ui->multitrackVideoConfigOverride, "Stream1", + "MultitrackVideoConfigOverride"); + + if (oldMultitrackVideoSetting != ui->enableMultitrackVideo->isChecked()) + main->ResetOutputs(); + SwapMultiTrack(QT_TO_UTF8(protocol)); } @@ -555,6 +647,7 @@ void OBSBasicSettings::on_service_currentIndexChanged(int idx) protocol = FindProtocol(); UpdateAdvNetworkGroup(); + UpdateMultitrackVideo(); if (ServiceSupportsCodecCheck() && UpdateResFPSLimits()) { lastServiceIdx = idx; @@ -576,6 +669,7 @@ void OBSBasicSettings::on_customServer_textChanged(const QString &) protocol = FindProtocol(); UpdateAdvNetworkGroup(); + UpdateMultitrackVideo(); if (ServiceSupportsCodecCheck()) lastCustomServer = ui->customServer->text(); @@ -596,6 +690,10 @@ void OBSBasicSettings::ServiceChanged(bool resetFields) if (resetFields || lastService != service.c_str()) { reset_service_ui_fields(ui.get(), service, loading); + + ui->enableMultitrackVideo->setChecked(config_get_bool( + main->Config(), "Stream1", "EnableMultitrackVideo")); + UpdateMultitrackVideo(); } ui->useAuth->setVisible(custom); @@ -605,8 +703,8 @@ void OBSBasicSettings::ServiceChanged(bool resetFields) ui->authPwWidget->setVisible(custom); if (custom || whip) { - ui->streamkeyPageLayout->insertRow(1, ui->serverLabel, - ui->serverStackedWidget); + ui->destinationLayout->insertRow(1, ui->serverLabel, + ui->serverStackedWidget); ui->serverStackedWidget->setCurrentIndex(1); ui->serverStackedWidget->setVisible(true); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index f61c02e2afceb1..fc241576e29f4f 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -337,6 +337,7 @@ void RestrictResetBitrates(initializer_list boxes, int maxbitrate); #define GROUP_CHANGED &QGroupBox::toggled #define SCROLL_CHANGED &QSpinBox::valueChanged #define DSCROLL_CHANGED &QDoubleSpinBox::valueChanged +#define TEXT_CHANGED &QPlainTextEdit::textChanged #define GENERAL_CHANGED &OBSBasicSettings::GeneralChanged #define STREAM1_CHANGED &OBSBasicSettings::Stream1Changed @@ -419,6 +420,14 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->authUsername, EDIT_CHANGED, STREAM1_CHANGED); HookWidget(ui->authPw, EDIT_CHANGED, STREAM1_CHANGED); HookWidget(ui->ignoreRecommended, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->enableMultitrackVideo, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoMaximumAggregateBitrateAuto, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoMaximumAggregateBitrate, SCROLL_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoMaximumVideoTracksAuto, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoMaximumVideoTracks, SCROLL_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoStreamDumpEnable, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoConfigOverrideEnable, CHECK_CHANGED, STREAM1_CHANGED); + HookWidget(ui->multitrackVideoConfigOverride, TEXT_CHANGED, STREAM1_CHANGED); HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutputPath, EDIT_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleNoSpace, CHECK_CHANGED, OUTPUTS_CHANGED); @@ -1068,6 +1077,21 @@ void OBSBasicSettings::SaveSpinBox(QSpinBox *widget, const char *section, config_set_int(main->Config(), section, value, widget->value()); } +void OBSBasicSettings::SaveText(QPlainTextEdit *widget, const char *section, + const char *value) +{ + if (!WidgetChanged(widget)) + return; + + auto utf8 = widget->toPlainText().toUtf8(); + + OBSDataAutoRelease safe_text = obs_data_create(); + obs_data_set_string(safe_text, "text", utf8.constData()); + + config_set_string(main->Config(), section, value, + obs_data_get_json(safe_text)); +} + std::string DeserializeConfigText(const char *value) { OBSDataAutoRelease data = obs_data_create_from_json(value); @@ -6253,6 +6277,116 @@ void OBSBasicSettings::UpdateAdvNetworkGroup() #endif } +extern bool MultitrackVideoDeveloperModeEnabled(); + +void OBSBasicSettings::UpdateMultitrackVideo() +{ + // Technically, it should currently be safe to toggle multitrackVideo + // while not streaming (recording should be irrelevant), but practically + // output settings aren't currently being tracked with that degree of + // flexibility, so just disable everything while outputs are active. + auto toggle_available = !main->Active(); + + // FIXME: protocol is not updated properly for WHIP; what do? + auto available = protocol.startsWith("RTMP"); + + if (available && !IsCustomService()) { + OBSDataAutoRelease settings = obs_data_create(); + obs_data_set_string(settings, "service", + QT_TO_UTF8(ui->service->currentText())); + OBSServiceAutoRelease temp_service = obs_service_create_private( + "rtmp_common", "auto config query service", settings); + settings = obs_service_get_settings(temp_service); + available = obs_data_has_user_value( + settings, "multitrack_video_configuration_url"); + if (!available && ui->enableMultitrackVideo->isChecked()) + ui->enableMultitrackVideo->setChecked(false); + } + + ui->multitrackVideoGroupBox->setVisible(available); + + ui->enableMultitrackVideo->setEnabled(toggle_available); + + ui->multitrackVideoMaximumAggregateBitrateLabel->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked()); + ui->multitrackVideoMaximumAggregateBitrateAuto->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked()); + ui->multitrackVideoMaximumAggregateBitrate->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked() && + !ui->multitrackVideoMaximumAggregateBitrateAuto->isChecked()); + + ui->multitrackVideoMaximumVideoTracksLabel->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked()); + ui->multitrackVideoMaximumVideoTracksAuto->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked()); + ui->multitrackVideoMaximumVideoTracks->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked() && + !ui->multitrackVideoMaximumVideoTracksAuto->isChecked()); + + ui->multitrackVideoStreamDumpEnable->setVisible( + available && MultitrackVideoDeveloperModeEnabled()); + ui->multitrackVideoConfigOverrideEnable->setVisible( + available && MultitrackVideoDeveloperModeEnabled()); + ui->multitrackVideoConfigOverrideLabel->setVisible( + available && MultitrackVideoDeveloperModeEnabled()); + ui->multitrackVideoConfigOverride->setVisible( + available && MultitrackVideoDeveloperModeEnabled()); + + ui->multitrackVideoStreamDumpEnable->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked()); + ui->multitrackVideoConfigOverrideEnable->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked()); + ui->multitrackVideoConfigOverrideLabel->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked() && + ui->multitrackVideoConfigOverrideEnable->isChecked()); + ui->multitrackVideoConfigOverride->setEnabled( + toggle_available && ui->enableMultitrackVideo->isChecked() && + ui->multitrackVideoConfigOverrideEnable->isChecked()); + + if (available) { + OBSDataAutoRelease settings; + { + auto service_name = ui->service->currentText(); + auto custom_server = ui->customServer->text().trimmed(); + + obs_properties_t *props = + obs_get_service_properties("rtmp_common"); + obs_property_t *service = + obs_properties_get(props, "service"); + + settings = obs_data_create(); + + obs_data_set_string(settings, "service", + QT_TO_UTF8(service_name)); + obs_property_modified(service, settings); + + obs_properties_destroy(props); + } + + auto multitrack_video_name = + QTStr("Basic.Settings.Stream.MultitrackVideoLabel"); + if (obs_data_has_user_value(settings, "multitrack_video_name")) + multitrack_video_name = obs_data_get_string( + settings, "multitrack_video_name"); + + ui->enableMultitrackVideo->setText( + QTStr("Basic.Settings.Stream.EnableMultitrackVideo") + .arg(multitrack_video_name)); + + if (obs_data_has_user_value(settings, + "multitrack_video_disclaimer")) { + ui->multitrackVideoInfo->setVisible(true); + ui->multitrackVideoInfo->setText(obs_data_get_string( + settings, "multitrack_video_disclaimer")); + } else { + ui->multitrackVideoInfo->setText( + QTStr("MultitrackVideo.Info") + .arg(multitrack_video_name, + ui->service->currentText())); + } + } +} + void OBSBasicSettings::SimpleStreamAudioEncoderChanged() { PopulateSimpleBitrates( diff --git a/UI/window-basic-settings.hpp b/UI/window-basic-settings.hpp index 03d55a26394a1f..846b800146ead9 100644 --- a/UI/window-basic-settings.hpp +++ b/UI/window-basic-settings.hpp @@ -195,6 +195,8 @@ class OBSBasicSettings : public QDialog { const char *value); void SaveSpinBox(QSpinBox *widget, const char *section, const char *value); + void SaveText(QPlainTextEdit *widget, const char *section, + const char *value); void SaveFormat(QComboBox *combo); void SaveEncoder(QComboBox *combo, const char *section, const char *value); @@ -275,7 +277,7 @@ class OBSBasicSettings : public QDialog { /* stream */ void InitStreamPage(); - inline bool IsCustomService() const; + bool IsCustomService() const; inline bool IsWHIP() const; void LoadServices(bool showAll); void OnOAuthStreamKeyConnected(); @@ -301,6 +303,7 @@ class OBSBasicSettings : public QDialog { bool IsCustomServer(); private slots: + void UpdateMultitrackVideo(); void RecreateOutputResolutionWidget(); bool UpdateResFPSLimits(); void DisplayEnforceWarning(bool checked); From 2007b4b844c6df7f2879b1cb66268337137e5d16 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Apr 2024 16:27:37 +0200 Subject: [PATCH 0171/1073] UI: Add Multitrack Video support for auto config --- UI/data/locale/en-US.ini | 3 + UI/forms/AutoConfigStreamPage.ui | 197 +++++++++++++++------------ UI/window-basic-auto-config-test.cpp | 74 +++++++++- UI/window-basic-auto-config.cpp | 171 ++++++++++++++++++++++- UI/window-basic-auto-config.hpp | 14 ++ 5 files changed, 368 insertions(+), 91 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index c956325778673a..3f8238c77c1ac5 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -115,6 +115,7 @@ MixerToolbarMenu="Audio Mixer Menu" SceneFilters="Open Scene Filters" List="List" Grid="Grid" +Automatic="Automatic" # warning for plugin load failures PluginsFailedToLoad.Title="Plugin Load Error" @@ -224,6 +225,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Prefer hardware encoding" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware Encoding eliminates most CPU usage, but may require more bitrate to obtain the same level of quality." Basic.AutoConfig.StreamPage.StreamWarning.Title="Stream warning" Basic.AutoConfig.StreamPage.StreamWarning.Text="The bandwidth test is about to stream randomized video data without audio to your channel. If you're able, it's recommended to temporarily turn off saving videos of streams and set the stream to private until after the test has completed. Continue?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Test %1" Basic.AutoConfig.TestPage="Final Results" Basic.AutoConfig.TestPage.SubTitle.Testing="The program is now executing a set of tests to estimate the ideal settings" Basic.AutoConfig.TestPage.SubTitle.Complete="Testing complete" @@ -242,6 +244,7 @@ Basic.AutoConfig.TestPage.Result.Header="The program has determined that these e Basic.AutoConfig.TestPage.Result.Footer="To use these settings, click Apply Settings. To reconfigure the wizard and try again, click Back. To manually configure settings yourself, click Cancel and open Settings." Basic.AutoConfig.Info="The auto-configuration wizard will determine the best settings based on your computer specs and internet speed." Basic.AutoConfig.RunAnytime="This can be run at any time by going to the Tools menu." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Streaming (Scaled) Resolution" # stats Basic.Stats="Stats" diff --git a/UI/forms/AutoConfigStreamPage.ui b/UI/forms/AutoConfigStreamPage.ui index b52f4d02a40528..241292c6d8e0b8 100644 --- a/UI/forms/AutoConfigStreamPage.ui +++ b/UI/forms/AutoConfigStreamPage.ui @@ -330,7 +330,73 @@ - + + + + PointingHandCursor + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + + + + Basic.AutoConfig.StreamPage.ConnectedAccount + + + + + + + 7 + + + 7 + + + + + + true + + + + Auth.LoadingChannel.Title + + + + + + + Basic.AutoConfig.StreamPage.DisconnectAccount + + + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced + + + + Basic.Settings.Output.VideoBitrate @@ -340,7 +406,7 @@ - + @@ -356,7 +422,7 @@ - + Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip @@ -369,7 +435,7 @@ - + Basic.AutoConfig.StreamPage.PerformBandwidthTest @@ -379,7 +445,49 @@ - + + + + Basic.AutoConfig.StreamPage.UseMultitrackVideo + + + true + + + + + + + MultitrackVideo.Info + + + Qt::RichText + + + true + + + true + + + QSizePolicy::MinimumExpanding, QSizePolicy::Minimum + + + + + + + Qt::Vertical + + + + 6 + 6 + + + + + BandwidthTest.Region @@ -416,85 +524,6 @@ - - - - PointingHandCursor - - - Basic.AutoConfig.StreamPage.ConnectAccount - - - - - - - Qt::Vertical - - - - 6 - 6 - - - - - - - - 7 - - - 7 - - - - - - true - - - - Auth.LoadingChannel.Title - - - - - - - Basic.AutoConfig.StreamPage.DisconnectAccount - - - - - - - - - Basic.AutoConfig.StreamPage.ConnectedAccount - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced - - - diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index 7d7c4278ede573..90487578305234 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -280,6 +280,34 @@ void AutoConfigTestPage::TestBandwidthThread() servers.resize(2); } + if (!wiz->serviceConfigServers.empty()) { + if (wiz->service == AutoConfig::Service::Twitch && + wiz->twitchAuto) { + // servers from Twitch service config replace the "auto" entry + servers.erase(servers.begin()); + } + + for (auto it = std::rbegin(wiz->serviceConfigServers); + it != std::rend(wiz->serviceConfigServers); it++) { + auto same_server = std::find_if( + std::begin(servers), std::end(servers), + [&](const ServerInfo &si) { + return si.address == it->address; + }); + if (same_server != std::end(servers)) + servers.erase(same_server); + servers.emplace(std::begin(servers), it->name.c_str(), + it->address.c_str()); + } + + if (wiz->service == AutoConfig::Service::Twitch && + wiz->twitchAuto) { + // see above, only test 3 servers + // rtmps urls are currently counted as separate servers + servers.resize(3); + } + } + /* -----------------------------------*/ /* apply service settings */ @@ -287,6 +315,11 @@ void AutoConfigTestPage::TestBandwidthThread() obs_service_apply_encoder_settings(service, vencoder_settings, aencoder_settings); + if (wiz->multitrackVideo.testSuccessful) { + obs_data_set_int(vencoder_settings, "bitrate", + wiz->startingBitrate); + } + /* -----------------------------------*/ /* create output */ @@ -823,6 +856,10 @@ bool AutoConfigTestPage::TestSoftwareEncoding() upperBitrate /= 100; } + if (wiz->testMultitrackVideo && wiz->multitrackVideo.testSuccessful && + !wiz->multitrackVideo.bitrate.has_value()) + wiz->multitrackVideo.bitrate = wiz->idealBitrate; + if (wiz->idealBitrate > upperBitrate) wiz->idealBitrate = upperBitrate; @@ -1080,6 +1117,11 @@ void AutoConfigTestPage::FinalizeResults() OBSDataAutoRelease service_settings = obs_data_create(); OBSDataAutoRelease vencoder_settings = obs_data_create(); + if (wiz->testMultitrackVideo && + wiz->multitrackVideo.testSuccessful && + !wiz->multitrackVideo.bitrate.has_value()) + wiz->multitrackVideo.bitrate = wiz->idealBitrate; + obs_data_set_int(vencoder_settings, "bitrate", wiz->idealBitrate); @@ -1121,12 +1163,30 @@ void AutoConfigTestPage::FinalizeResults() form->addRow(newLabel("Basic.AutoConfig.StreamPage.Server"), new QLabel(wiz->serverName.c_str(), ui->finishPage)); - form->addRow(newLabel("Basic.Settings.Output.VideoBitrate"), - new QLabel(QString::number(wiz->idealBitrate), - ui->finishPage)); - form->addRow(newLabel(TEST_RESULT_SE), - new QLabel(encName(wiz->streamingEncoder), - ui->finishPage)); + form->addRow( + newLabel("Basic.Settings.Stream.MultitrackVideoLabel"), + newLabel(wiz->multitrackVideo.testSuccessful ? "Yes" + : "No")); + + if (wiz->multitrackVideo.testSuccessful) { + form->addRow( + newLabel("Basic.Settings.Output.VideoBitrate"), + newLabel("Automatic")); + form->addRow(newLabel(TEST_RESULT_SE), + newLabel("Automatic")); + form->addRow( + newLabel( + "Basic.AutoConfig.TestPage.Result.StreamingResolution"), + newLabel("Automatic")); + } else { + form->addRow( + newLabel("Basic.Settings.Output.VideoBitrate"), + new QLabel(QString::number(wiz->idealBitrate), + ui->finishPage)); + form->addRow(newLabel(TEST_RESULT_SE), + new QLabel(encName(wiz->streamingEncoder), + ui->finishPage)); + } } QString baseRes = @@ -1168,6 +1228,8 @@ void AutoConfigTestPage::FinalizeResults() new QLabel(scaleRes, ui->finishPage)); form->addRow(newLabel("Basic.Settings.Video.FPS"), new QLabel(fpsStr, ui->finishPage)); + + // FIXME: form layout is super squished, probably need to set proper sizepolicy on all widgets? } #define STARTING_SEPARATOR \ diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index e06e65eca29358..da2ecb0cdc705b 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -11,6 +11,10 @@ #include "obs-app.hpp" #include "url-push-button.hpp" +#include "goliveapi-postdata.hpp" +#include "goliveapi-network.hpp" +#include "multitrack-video-error.hpp" + #include "ui_AutoConfigStartPage.h" #include "ui_AutoConfigVideoPage.h" #include "ui_AutoConfigStreamPage.h" @@ -262,6 +266,7 @@ AutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) ui->bitrate->setVisible(false); ui->connectAccount2->setVisible(false); ui->disconnectAccount->setVisible(false); + ui->useMultitrackVideo->setVisible(false); ui->connectedAccountLabel->setVisible(false); ui->connectedAccountText->setVisible(false); @@ -412,6 +417,76 @@ bool AutoConfigStreamPage::validatePage() wiz->service = AutoConfig::Service::Other; } + if (wiz->service == AutoConfig::Service::Twitch) { + wiz->testMultitrackVideo = ui->useMultitrackVideo->isChecked(); + + auto postData = + constructGoLivePost(QString::fromStdString(wiz->key), + std::nullopt, std::nullopt, false); + + OBSDataAutoRelease service_settings = + obs_service_get_settings(service); + auto multitrack_video_name = + QTStr("Basic.Settings.Stream.MultitrackVideoLabel"); + if (obs_data_has_user_value(service_settings, + "multitrack_video_name")) { + multitrack_video_name = obs_data_get_string( + service_settings, "multitrack_video_name"); + } + + try { + auto config = DownloadGoLiveConfig( + this, MultitrackVideoAutoConfigURL(service), + postData, multitrack_video_name); + + for (const auto &endpoint : config.ingest_endpoints) { + if (qstrnicmp("RTMP", endpoint.protocol.c_str(), + 4) != 0) + continue; + + std::string address = endpoint.url_template; + auto pos = address.find("/{stream_key}"); + if (pos != address.npos) + address.erase(pos); + + wiz->serviceConfigServers.push_back( + {address, address}); + } + + int multitrackVideoBitrate = 0; + for (auto &encoder_config : + config.encoder_configurations) { + multitrackVideoBitrate += + encoder_config.config.bitrate; + } + + // grab a streamkey from the go live config if we can + for (auto &endpoint : config.ingest_endpoints) { + const char *p = endpoint.protocol.c_str(); + const char *auth = + endpoint.authentication + ? endpoint.authentication + ->c_str() + : nullptr; + if (qstrnicmp("RTMP", p, 4) == 0 && auth && + *auth) { + wiz->key = auth; + break; + } + } + + if (multitrackVideoBitrate > 0) { + wiz->startingBitrate = multitrackVideoBitrate; + wiz->idealBitrate = multitrackVideoBitrate; + wiz->multitrackVideo.targetBitrate = + multitrackVideoBitrate; + wiz->multitrackVideo.testSuccessful = true; + } + } catch (const MultitrackVideoError & /*err*/) { + // FIXME: do something sensible + } + } + if (wiz->service != AutoConfig::Service::Twitch && wiz->service != AutoConfig::Service::YouTube && wiz->bandwidthTest) { @@ -563,6 +638,22 @@ void AutoConfigStreamPage::on_useStreamKey_clicked() UpdateCompleted(); } +void AutoConfigStreamPage::on_preferHardware_clicked() +{ + auto *main = OBSBasic::Get(); + bool multitrackVideoEnabled = + config_has_user_value(main->Config(), "Stream1", + "EnableMultitrackVideo") + ? config_get_bool(main->Config(), "Stream1", + "EnableMultitrackVideo") + : true; + + ui->useMultitrackVideo->setEnabled(ui->preferHardware->isChecked()); + ui->multitrackVideoInfo->setEnabled(ui->preferHardware->isChecked()); + ui->useMultitrackVideo->setChecked(ui->preferHardware->isChecked() && + multitrackVideoEnabled); +} + static inline bool is_auth_service(const std::string &service) { return Auth::AuthType(service) != Auth::Type::None; @@ -629,6 +720,48 @@ void AutoConfigStreamPage::ServiceChanged() bool testBandwidth = ui->doBandwidthTest->isChecked(); bool custom = IsCustomService(); + bool ertmp_multitrack_video_available = service == "Twitch"; + + bool custom_disclaimer = false; + auto multitrack_video_name = + QTStr("Basic.Settings.Stream.MultitrackVideoLabel"); + if (!custom) { + OBSDataAutoRelease service_settings = obs_data_create(); + obs_data_set_string(service_settings, "service", + service.c_str()); + OBSServiceAutoRelease obs_service = + obs_service_create("rtmp_common", "temp service", + service_settings, nullptr); + + if (obs_data_has_user_value(service_settings, + "multitrack_video_name")) { + multitrack_video_name = obs_data_get_string( + service_settings, "multitrack_video_name"); + } + + if (obs_data_has_user_value(service_settings, + "multitrack_video_disclaimer")) { + ui->multitrackVideoInfo->setText(obs_data_get_string( + service_settings, + "multitrack_video_disclaimer")); + custom_disclaimer = true; + } + } + + if (!custom_disclaimer) { + ui->multitrackVideoInfo->setText( + QTStr("MultitrackVideo.Info") + .arg(multitrack_video_name, service.c_str())); + } + + ui->multitrackVideoInfo->setVisible(ertmp_multitrack_video_available); + ui->useMultitrackVideo->setVisible(ertmp_multitrack_video_available); + ui->useMultitrackVideo->setText( + QTStr("Basic.AutoConfig.StreamPage.UseMultitrackVideo") + .arg(multitrack_video_name)); + ui->multitrackVideoInfo->setEnabled(wiz->hardwareEncodingAvailable); + ui->useMultitrackVideo->setEnabled(wiz->hardwareEncodingAvailable); + reset_service_ui_fields(service); /* Test three closest servers if "Auto" is available for Twitch */ @@ -979,12 +1112,21 @@ AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent) if (!key.empty()) streamPage->ui->key->setText(key.c_str()); + TestHardwareEncoding(); + int bitrate = config_get_int(main->Config(), "SimpleOutput", "VBitrate"); + bool multitrackVideoEnabled = + config_has_user_value(main->Config(), "Stream1", + "EnableMultitrackVideo") + ? config_get_bool(main->Config(), "Stream1", + "EnableMultitrackVideo") + : true; streamPage->ui->bitrate->setValue(bitrate); + streamPage->ui->useMultitrackVideo->setChecked( + hardwareEncodingAvailable && multitrackVideoEnabled); streamPage->ServiceChanged(); - TestHardwareEncoding(); if (!hardwareEncodingAvailable) { delete streamPage->ui->preferHardware; streamPage->ui->preferHardware = nullptr; @@ -1144,6 +1286,33 @@ void AutoConfig::SaveStreamSettings() config_set_string(main->Config(), "SimpleOutput", "StreamEncoder", GetEncoderId(streamingEncoder)); config_remove_value(main->Config(), "SimpleOutput", "UseAdvanced"); + + config_set_bool(main->Config(), "Stream1", "EnableMultitrackVideo", + multitrackVideo.testSuccessful); + + if (multitrackVideo.targetBitrate.has_value()) + config_set_int(main->Config(), "Stream1", + "MultitrackVideoTargetBitrate", + *multitrackVideo.targetBitrate); + else + config_remove_value(main->Config(), "Stream1", + "MultitrackVideoTargetBitrate"); + + if (multitrackVideo.bitrate.has_value() && + multitrackVideo.targetBitrate.has_value() && + (static_cast(*multitrackVideo.bitrate) / + *multitrackVideo.targetBitrate) >= 0.90) { + config_set_bool(main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrateAuto", + true); + } else if (multitrackVideo.bitrate.has_value()) { + config_set_bool(main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrateAuto", + false); + config_set_int(main->Config(), "Stream1", + "MultitrackVideoMaximumAggregateBitrate", + *multitrackVideo.bitrate); + } } void AutoConfig::SaveSettings() diff --git a/UI/window-basic-auto-config.hpp b/UI/window-basic-auto-config.hpp index 5d966c7956e272..2f93c540b25a14 100644 --- a/UI/window-basic-auto-config.hpp +++ b/UI/window-basic-auto-config.hpp @@ -12,6 +12,7 @@ #include #include #include +#include class Ui_AutoConfigStartPage; class Ui_AutoConfigVideoPage; @@ -64,6 +65,11 @@ class AutoConfig : public QWizard { fps60, }; + struct StreamServer { + std::string name; + std::string address; + }; + static inline const char *GetEncoderId(Encoder enc); AutoConfigStreamPage *streamPage = nullptr; @@ -75,6 +81,11 @@ class AutoConfig : public QWizard { Type type = Type::Streaming; FPSType fpsType = FPSType::PreferHighFPS; int idealBitrate = 2500; + struct { + std::optional targetBitrate; + std::optional bitrate; + bool testSuccessful = false; + } multitrackVideo; int baseResolutionCX = 1920; int baseResolutionCY = 1080; int idealResolutionCX = 1280; @@ -84,6 +95,7 @@ class AutoConfig : public QWizard { std::string serviceName; std::string serverName; std::string server; + std::vector serviceConfigServers; std::string key; bool hardwareEncodingAvailable = false; @@ -95,6 +107,7 @@ class AutoConfig : public QWizard { int startingBitrate = 2500; bool customServer = false; bool bandwidthTest = false; + bool testMultitrackVideo = false; bool testRegions = true; bool twitchAuto = false; bool regionUS = true; @@ -195,6 +208,7 @@ public slots: void on_connectAccount_clicked(); void on_disconnectAccount_clicked(); void on_useStreamKey_clicked(); + void on_preferHardware_clicked(); void ServiceChanged(); void UpdateKeyLink(); void UpdateMoreInfoLink(); From 42a3f903a9b4a1bef7d0ec05dedaca426f63ffa3 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 17 Apr 2024 13:21:13 +0200 Subject: [PATCH 0172/1073] UI: Add `OBSPropertiesView::SetDisabled` Disables all children of the properties view. --- UI/properties-view.cpp | 7 +++++++ UI/properties-view.hpp | 2 ++ 2 files changed, 9 insertions(+) diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index d7d5b3787efbf5..1f9658d06aed76 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -245,6 +245,13 @@ OBSPropertiesView::OBSPropertiesView(OBSData settings_, const char *type_, Qt::QueuedConnection); } +void OBSPropertiesView::SetDisabled(bool disabled) +{ + for (auto child : findChildren()) { + child->setDisabled(disabled); + } +} + void OBSPropertiesView::resizeEvent(QResizeEvent *event) { emit PropertiesResized(); diff --git a/UI/properties-view.hpp b/UI/properties-view.hpp index 06ce853f0a2224..42d9b36b89cdb4 100644 --- a/UI/properties-view.hpp +++ b/UI/properties-view.hpp @@ -208,6 +208,8 @@ public slots: RefreshProperties(); } + void SetDisabled(bool disabled); + #define Def_IsObject(type) \ inline bool IsObject(obs_##type##_t *type) const \ { \ From 1aa81117d6ba8c012dc5ac8c2159681d7720e927 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 17 Apr 2024 14:30:54 +0200 Subject: [PATCH 0173/1073] UI: Add output settings indicators for eRTMP multitrack video --- UI/data/locale/en-US.ini | 1 + UI/data/themes/Yami.obt | 5 ++ UI/forms/OBSBasicSettings.ui | 19 +++++ UI/window-basic-settings.cpp | 133 +++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 3f8238c77c1ac5..28a5e28545d3d8 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -864,6 +864,7 @@ Basic.MainMenu.Help.About="&About" Basic.Settings.ProgramRestart="The program must be restarted for these settings to take effect." Basic.Settings.ConfirmTitle="Confirm Changes" Basic.Settings.Confirm="You have unsaved changes. Save changes?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 is controlling some of your stream settings" # basic mode 'general' settings Basic.Settings.General="General" diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index cf9230b772e7ab..91b48ddc4a2df9 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -1376,6 +1376,11 @@ QLabel#errorLabel { font-weight: bold; } +QFrame [themeID="notice"] { + background: var(--bg_preview); + border-radius: var(--border_radius); +} + /* About dialog */ * [themeID="aboutName"] { diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index a144805d3dac4b..45af7e598cb486 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1846,6 +1846,25 @@ 0 + + + + notice + + + + + + Basic.Settings.MultitrackVideoDisabledSettings + + + Qt::AlignCenter + + + + + + diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index fc241576e29f4f..82cd64dcccbb0b 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -896,6 +896,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) obs_properties_destroy(ppts); + ui->multitrackVideoNoticeBox->setVisible(false); + InitStreamPage(); InitAppearancePage(); LoadSettings(false); @@ -4662,6 +4664,8 @@ void OBSBasicSettings::OutputsChanged() outputsChanged = true; sender()->setProperty("changed", QVariant(true)); EnableApplyButton(true); + + UpdateMultitrackVideo(); } } @@ -5420,6 +5424,7 @@ void OBSBasicSettings::SimpleRecordingQualityChanged() ui->simpleOutRecFormat->setVisible(!losslessQuality); ui->simpleOutRecFormatLabel->setVisible(!losslessQuality); + UpdateMultitrackVideo(); SimpleRecordingEncoderChanged(); SimpleReplayBufferChanged(); } @@ -6343,6 +6348,114 @@ void OBSBasicSettings::UpdateMultitrackVideo() toggle_available && ui->enableMultitrackVideo->isChecked() && ui->multitrackVideoConfigOverrideEnable->isChecked()); + auto update_simple_output_settings = [&](bool mtv_enabled) { + auto recording_uses_stream_encoder = + ui->simpleOutRecQuality->currentData().toString() == + "Stream"; + mtv_enabled = mtv_enabled && !recording_uses_stream_encoder; + + ui->simpleOutputVBitrateLabel->setDisabled(mtv_enabled); + ui->simpleOutputVBitrate->setDisabled(mtv_enabled); + + ui->simpleOutputABitrateLabel->setDisabled(mtv_enabled); + ui->simpleOutputABitrate->setDisabled(mtv_enabled); + + ui->simpleOutStrEncoderLabel->setDisabled(mtv_enabled); + ui->simpleOutStrEncoder->setDisabled(mtv_enabled); + + ui->simpleOutPresetLabel->setDisabled(mtv_enabled); + ui->simpleOutPreset->setDisabled(mtv_enabled); + + ui->simpleOutCustomLabel->setDisabled(mtv_enabled); + ui->simpleOutCustom->setDisabled(mtv_enabled); + + ui->simpleOutStrAEncoderLabel->setDisabled(mtv_enabled); + ui->simpleOutStrAEncoder->setDisabled(mtv_enabled); + }; + + auto update_advanced_output_settings = [&](bool mtv_enabled) { + auto recording_uses_stream_video_encoder = + ui->advOutRecEncoder->currentText() == + TEXT_USE_STREAM_ENC; + auto recording_uses_stream_audio_encoder = + ui->advOutRecAEncoder->currentData() == "none"; + auto disable_video = mtv_enabled && + !recording_uses_stream_video_encoder; + auto disable_audio = mtv_enabled && + !recording_uses_stream_audio_encoder; + + ui->advOutAEncLabel->setDisabled(disable_audio); + ui->advOutAEncoder->setDisabled(disable_audio); + + ui->advOutEncLabel->setDisabled(disable_video); + ui->advOutEncoder->setDisabled(disable_video); + + ui->advOutUseRescale->setDisabled(disable_video); + ui->advOutRescale->setDisabled(disable_video); + ui->advOutRescaleFilter->setDisabled(disable_video); + + if (streamEncoderProps) + streamEncoderProps->SetDisabled(disable_video); + }; + + auto update_advanced_output_audio_tracks = [&](bool mtv_enabled) { + auto vod_track_enabled = vodTrackCheckbox && + vodTrackCheckbox->isChecked(); + + auto vod_track_idx_enabled = [&](size_t idx) { + return vod_track_enabled && vodTrack[idx] && + vodTrack[idx]->isChecked(); + }; + + auto track1_warning_visible = mtv_enabled && + (ui->advOutTrack1->isChecked() || + vod_track_idx_enabled(1)); + auto track1_disabled = track1_warning_visible && + !ui->advOutRecTrack1->isChecked(); + ui->advOutTrack1BitrateLabel->setDisabled(track1_disabled); + ui->advOutTrack1Bitrate->setDisabled(track1_disabled); + + auto track2_warning_visible = mtv_enabled && + (ui->advOutTrack2->isChecked() || + vod_track_idx_enabled(2)); + auto track2_disabled = track2_warning_visible && + !ui->advOutRecTrack2->isChecked(); + ui->advOutTrack2BitrateLabel->setDisabled(track2_disabled); + ui->advOutTrack2Bitrate->setDisabled(track2_disabled); + + auto track3_warning_visible = mtv_enabled && + (ui->advOutTrack3->isChecked() || + vod_track_idx_enabled(3)); + auto track3_disabled = track3_warning_visible && + !ui->advOutRecTrack3->isChecked(); + ui->advOutTrack3BitrateLabel->setDisabled(track3_disabled); + ui->advOutTrack3Bitrate->setDisabled(track3_disabled); + + auto track4_warning_visible = mtv_enabled && + (ui->advOutTrack4->isChecked() || + vod_track_idx_enabled(4)); + auto track4_disabled = track4_warning_visible && + !ui->advOutRecTrack4->isChecked(); + ui->advOutTrack4BitrateLabel->setDisabled(track4_disabled); + ui->advOutTrack4Bitrate->setDisabled(track4_disabled); + + auto track5_warning_visible = mtv_enabled && + (ui->advOutTrack5->isChecked() || + vod_track_idx_enabled(5)); + auto track5_disabled = track5_warning_visible && + !ui->advOutRecTrack5->isChecked(); + ui->advOutTrack5BitrateLabel->setDisabled(track5_disabled); + ui->advOutTrack5Bitrate->setDisabled(track5_disabled); + + auto track6_warning_visible = mtv_enabled && + (ui->advOutTrack6->isChecked() || + vod_track_idx_enabled(6)); + auto track6_disabled = track6_warning_visible && + !ui->advOutRecTrack6->isChecked(); + ui->advOutTrack6BitrateLabel->setDisabled(track6_disabled); + ui->advOutTrack6Bitrate->setDisabled(track6_disabled); + }; + if (available) { OBSDataAutoRelease settings; { @@ -6384,6 +6497,26 @@ void OBSBasicSettings::UpdateMultitrackVideo() .arg(multitrack_video_name, ui->service->currentText())); } + + auto disabled_text = + QTStr("Basic.Settings.MultitrackVideoDisabledSettings") + .arg(ui->service->currentText()) + .arg(multitrack_video_name); + + ui->multitrackVideoNotice->setText(disabled_text); + + auto mtv_enabled = ui->enableMultitrackVideo->isChecked(); + ui->multitrackVideoNoticeBox->setVisible(mtv_enabled); + + update_simple_output_settings(mtv_enabled); + update_advanced_output_settings(mtv_enabled); + update_advanced_output_audio_tracks(mtv_enabled); + } else { + ui->multitrackVideoNoticeBox->setVisible(false); + + update_simple_output_settings(false); + update_advanced_output_settings(false); + update_advanced_output_audio_tracks(false); } } From 29994afe7fe3c1fca99e80be7db92725bfdd9a8c Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 1 Jun 2024 18:42:34 +0200 Subject: [PATCH 0174/1073] UI: Retain existing last output resolution --- UI/window-basic-main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index ce74a8b6553c54..9f72c621cfc4f9 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1306,6 +1306,15 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file) vcamConfig.source = obs_data_get_string(obj, "source"); } + if (obs_data_has_user_value(data, "resolution")) { + OBSDataAutoRelease res = obs_data_get_obj(data, "resolution"); + if (obs_data_has_user_value(res, "x") && + obs_data_has_user_value(res, "y")) { + lastOutputResolution = {obs_data_get_int(res, "x"), + obs_data_get_int(res, "y")}; + } + } + /* ---------------------- */ if (api) From cb024a069680ade75e9ff54178866d7a58db1a09 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 3 Apr 2024 15:14:29 +0200 Subject: [PATCH 0175/1073] UI: Use connection with modeSwitch button --- UI/window-basic-main-transitions.cpp | 4 ++-- UI/window-basic-main.cpp | 6 ++++++ UI/window-basic-main.hpp | 9 +++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index edf3baf9ffa08e..60d1943206fa43 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -976,7 +976,7 @@ int OBSBasic::GetTbarPosition() return tBar->value(); } -void OBSBasic::on_modeSwitch_clicked() +void OBSBasic::TogglePreviewProgramMode() { SetPreviewProgramMode(!IsPreviewProgramMode()); } @@ -1607,8 +1607,8 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) if (IsPreviewProgramMode() == enabled) return; - ui->modeSwitch->setChecked(enabled); os_atomic_set_bool(&previewProgramMode, enabled); + emit PreviewProgramModeChanged(enabled); if (IsPreviewProgramMode()) { if (!previewEnabled) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 9f72c621cfc4f9..798d71f634e7e2 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -334,6 +334,12 @@ OBSBasic::OBSBasic(QWidget *parent) ui->previewDisabledWidget->setVisible(false); ui->broadcastButton->setVisible(false); + /* Setup Studio Mode button connections */ + connect(this, &OBSBasic::PreviewProgramModeChanged, ui->modeSwitch, + &QAbstractButton::setChecked); + connect(ui->modeSwitch, &QAbstractButton::clicked, this, + &OBSBasic::TogglePreviewProgramMode); + startingDockLayout = saveState(); statsDock = new OBSDock(); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 64d237446f5b5c..9bbd58f049ec9b 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -1177,8 +1177,6 @@ private slots: void ShowTransitionProperties(); void HideTransitionProperties(); - void on_modeSwitch_clicked(); - // Source Context Buttons void on_sourcePropertiesButton_clicked(); void on_sourceFiltersButton_clicked(); @@ -1236,6 +1234,9 @@ private slots: void RepairOldExtraDockName(); void RepairCustomExtraDockName(); + /* Studio Mode toggle slot */ + void TogglePreviewProgramMode(); + public slots: void on_actionResetTransform_triggered(); @@ -1249,6 +1250,10 @@ public slots: void UpdateContextBarDeferred(bool force = false); void UpdateContextBarVisibility(); +signals: + /* Studio Mode signal */ + void PreviewProgramModeChanged(bool enabled); + private: std::unique_ptr ui; From 909327533616f5c981dd1908d6a8f0bb6c9915d7 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 3 Apr 2024 17:08:14 +0200 Subject: [PATCH 0176/1073] UI: Track recording state in OBSBasic Avoid using controls dock buttons for recording states. Use signals and OBSBasic member variables instead. --- UI/window-basic-main.cpp | 70 +++++++++++++++++++++++++++++++--------- UI/window-basic-main.hpp | 11 +++++++ 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 798d71f634e7e2..6878965609a9df 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -340,6 +340,30 @@ OBSBasic::OBSBasic(QWidget *parent) connect(ui->modeSwitch, &QAbstractButton::clicked, this, &OBSBasic::TogglePreviewProgramMode); + /* Set up recording connections */ + connect( + this, &OBSBasic::RecordingStarted, this, + [this]() { + this->recordingStarted = true; + this->recordingPaused = false; + }, + Qt::DirectConnection); + connect( + this, &OBSBasic::RecordingPaused, this, + [this]() { this->recordingPaused = true; }, + Qt::DirectConnection); + connect( + this, &OBSBasic::RecordingUnpaused, this, + [this]() { this->recordingPaused = false; }, + Qt::DirectConnection); + connect( + this, &OBSBasic::RecordingStopped, this, + [this]() { + this->recordingStarted = false; + this->recordingPaused = false; + }, + Qt::DirectConnection); + startingDockLayout = saveState(); statsDock = new OBSDock(); @@ -1994,6 +2018,8 @@ void OBSBasic::ResetOutputs() if (sysTrayReplayBuffer) sysTrayReplayBuffer->setEnabled( !!outputHandler->replayBuffer); + + UpdateIsRecordingPausable(); } else { outputHandler->Update(); } @@ -2802,10 +2828,10 @@ void OBSBasic::CreateHotkeys() "OBSBasic.StartRecording", Str("Basic.Main.StartRecording"), "OBSBasic.StopRecording", Str("Basic.Main.StopRecording"), MAKE_CALLBACK(!basic.outputHandler->RecordingActive() && - !basic.ui->recordButton->isChecked(), + !basic.recordingStarted, basic.StartRecording, "Starting recording"), MAKE_CALLBACK(basic.outputHandler->RecordingActive() && - basic.ui->recordButton->isChecked(), + basic.recordingStarted, basic.StopRecording, "Stopping recording"), this, this); LoadHotkeyPair(recordingHotkeys, "OBSBasic.StartRecording", @@ -2814,9 +2840,11 @@ void OBSBasic::CreateHotkeys() pauseHotkeys = obs_hotkey_pair_register_frontend( "OBSBasic.PauseRecording", Str("Basic.Main.PauseRecording"), "OBSBasic.UnpauseRecording", Str("Basic.Main.UnpauseRecording"), - MAKE_CALLBACK(basic.pause && !basic.pause->isChecked(), + MAKE_CALLBACK(basic.isRecordingPausable && + !basic.recordingPaused, basic.PauseRecording, "Pausing recording"), - MAKE_CALLBACK(basic.pause && basic.pause->isChecked(), + MAKE_CALLBACK(basic.isRecordingPausable && + basic.recordingPaused, basic.UnpauseRecording, "Unpausing recording"), this, this); LoadHotkeyPair(pauseHotkeys, "OBSBasic.PauseRecording", @@ -7795,6 +7823,7 @@ void OBSBasic::StopRecording() void OBSBasic::RecordingStart() { ui->statusbar->RecordingStarted(outputHandler->fileOutput); + emit RecordingStarted(); ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); ui->recordButton->setChecked(true); @@ -7817,6 +7846,7 @@ void OBSBasic::RecordingStart() void OBSBasic::RecordingStop(int code, QString last_error) { ui->statusbar->RecordingStopped(); + emit RecordingStopped(); ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); ui->recordButton->setChecked(false); @@ -10793,7 +10823,8 @@ void OBSBasic::UpdatePatronJson(const QString &text, const QString &error) void OBSBasic::PauseRecording() { - if (!pause || !outputHandler || !outputHandler->fileOutput || + if (!isRecordingPausable || !outputHandler || + !outputHandler->fileOutput || os_atomic_load_bool(&recording_paused)) return; @@ -10802,6 +10833,7 @@ void OBSBasic::PauseRecording() if (obs_output_pause(output, true)) { os_atomic_set_bool(&recording_paused, true); + emit RecordingPaused(); pause->setAccessibleName(QTStr("Basic.Main.UnpauseRecording")); pause->setToolTip(QTStr("Basic.Main.UnpauseRecording")); pause->blockSignals(true); @@ -10839,7 +10871,8 @@ void OBSBasic::PauseRecording() void OBSBasic::UnpauseRecording() { - if (!pause || !outputHandler || !outputHandler->fileOutput || + if (!isRecordingPausable || !outputHandler || + !outputHandler->fileOutput || !os_atomic_load_bool(&recording_paused)) return; @@ -10848,6 +10881,7 @@ void OBSBasic::UnpauseRecording() if (obs_output_pause(output, false)) { os_atomic_set_bool(&recording_paused, false); + emit RecordingUnpaused(); pause->setAccessibleName(QTStr("Basic.Main.PauseRecording")); pause->setToolTip(QTStr("Basic.Main.PauseRecording")); pause->blockSignals(true); @@ -10882,7 +10916,8 @@ void OBSBasic::UnpauseRecording() void OBSBasic::PauseToggled() { - if (!pause || !outputHandler || !outputHandler->fileOutput) + if (!isRecordingPausable || !outputHandler || + !outputHandler->fileOutput) return; obs_output_t *output = outputHandler->fileOutput; @@ -10894,16 +10929,11 @@ void OBSBasic::PauseToggled() UnpauseRecording(); } -void OBSBasic::UpdatePause(bool activate) +void OBSBasic::UpdateIsRecordingPausable() { - if (!activate || !outputHandler || !outputHandler->RecordingActive()) { - pause.reset(); - return; - } - const char *mode = config_get_string(basicConfig, "Output", "Mode"); bool adv = astrcmpi(mode, "Advanced") == 0; - bool shared; + bool shared = true; if (adv) { const char *recType = @@ -10923,7 +10953,17 @@ void OBSBasic::UpdatePause(bool activate) shared = strcmp(quality, "Stream") == 0; } - if (!shared) { + isRecordingPausable = !shared; +} + +void OBSBasic::UpdatePause(bool activate) +{ + if (!activate || !outputHandler || !outputHandler->RecordingActive()) { + pause.reset(); + return; + } + + if (isRecordingPausable) { pause.reset(new QPushButton()); pause->setAccessibleName(QTStr("Basic.Main.PauseRecording")); pause->setToolTip(QTStr("Basic.Main.PauseRecording")); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 9bbd58f049ec9b..0e3b514d6e729e 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -677,6 +677,10 @@ class OBSBasic : public OBSMainWindow { void UpdatePreviewOverflowSettings(); + bool recordingStarted = false; + bool isRecordingPausable = false; + bool recordingPaused = false; + bool restartingVCam = false; public slots: @@ -881,6 +885,7 @@ private slots: void AutoRemux(QString input, bool no_show = false); + void UpdateIsRecordingPausable(); void UpdatePause(bool activate = true); void UpdateReplayBuffer(bool activate = true); @@ -1251,6 +1256,12 @@ public slots: void UpdateContextBarVisibility(); signals: + /* Recording signals */ + void RecordingStarted(); + void RecordingPaused(); + void RecordingUnpaused(); + void RecordingStopped(); + /* Studio Mode signal */ void PreviewProgramModeChanged(bool enabled); From d23b65a2bf99922e3b377f292ab8e14e68268fa7 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 6 Apr 2024 15:37:34 +0200 Subject: [PATCH 0177/1073] UI: Track streaming state in OBSBasic Avoid using controls dock buttons for streaming state. Use signals and OBSBasic member variables instead. --- UI/window-basic-main.cpp | 25 +++++++++++++++++++++++-- UI/window-basic-main.hpp | 7 +++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 6878965609a9df..d7535e5b03b71b 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -340,6 +340,20 @@ OBSBasic::OBSBasic(QWidget *parent) connect(ui->modeSwitch, &QAbstractButton::clicked, this, &OBSBasic::TogglePreviewProgramMode); + /* Set up streaming connections */ + connect( + this, &OBSBasic::StreamingStarting, this, + [this] { this->streamingStarting = true; }, + Qt::DirectConnection); + connect( + this, &OBSBasic::StreamingStarted, this, + [this] { this->streamingStarting = false; }, + Qt::DirectConnection); + connect( + this, &OBSBasic::StreamingStopped, this, + [this] { this->streamingStarting = false; }, + Qt::DirectConnection); + /* Set up recording connections */ connect( this, &OBSBasic::RecordingStarted, this, @@ -2803,10 +2817,10 @@ void OBSBasic::CreateHotkeys() "OBSBasic.StartStreaming", Str("Basic.Main.StartStreaming"), "OBSBasic.StopStreaming", Str("Basic.Main.StopStreaming"), MAKE_CALLBACK(!basic.outputHandler->StreamingActive() && - basic.ui->streamButton->isEnabled(), + !basic.streamingStarting, basic.StartStreaming, "Starting stream"), MAKE_CALLBACK(basic.outputHandler->StreamingActive() && - basic.ui->streamButton->isEnabled(), + !basic.streamingStarting, basic.StopStreaming, "Stopping stream"), this, this); LoadHotkeyPair(streamingHotkeys, "OBSBasic.StartStreaming", @@ -6929,6 +6943,8 @@ void OBSBasic::DisplayStreamStartError() QString message = !outputHandler->lastError.empty() ? QTStr(outputHandler->lastError.c_str()) : QTStr("Output.StartFailedGeneric"); + + emit StreamingStopped(); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); ui->streamButton->setChecked(false); @@ -7089,6 +7105,7 @@ void OBSBasic::StartStreaming() sysTrayStream->setText(text); }; + emit StreamingStarting(); ui->streamButton->setEnabled(false); ui->streamButton->setChecked(false); ui->broadcastButton->setChecked(false); @@ -7481,6 +7498,7 @@ void OBSBasic::ForceStopStreaming() void OBSBasic::StreamDelayStarting(int sec) { + emit StreamingStarted(); ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); ui->streamButton->setChecked(true); @@ -7507,6 +7525,7 @@ void OBSBasic::StreamDelayStarting(int sec) void OBSBasic::StreamDelayStopping(int sec) { + emit StreamingStopped(); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); ui->streamButton->setChecked(false); @@ -7534,6 +7553,7 @@ void OBSBasic::StreamDelayStopping(int sec) void OBSBasic::StreamingStart() { + emit StreamingStarted(); OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); @@ -7638,6 +7658,7 @@ void OBSBasic::StreamingStop(int code, QString last_error) ui->statusbar->StreamStopped(); + emit StreamingStopped(); ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); ui->streamButton->setChecked(false); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 0e3b514d6e729e..a478c49ff47548 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -677,6 +677,8 @@ class OBSBasic : public OBSMainWindow { void UpdatePreviewOverflowSettings(); + bool streamingStarting = false; + bool recordingStarted = false; bool isRecordingPausable = false; bool recordingPaused = false; @@ -1256,6 +1258,11 @@ public slots: void UpdateContextBarVisibility(); signals: + /* Streaming signals */ + void StreamingStarting(); + void StreamingStarted(); + void StreamingStopped(); + /* Recording signals */ void RecordingStarted(); void RecordingPaused(); From 178205257e8a31f43e243788b41d7c5531294621 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 6 Apr 2024 16:14:36 +0200 Subject: [PATCH 0178/1073] UI: Set system tray text explicitly Avoid using controls dock buttons as a source for text for system tray elements. --- UI/window-basic-main.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index d7535e5b03b71b..1e823b271be1c8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -6950,7 +6950,7 @@ void OBSBasic::DisplayStreamStartError() ui->streamButton->setChecked(false); if (sysTrayStream) { - sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); sysTrayStream->setEnabled(true); } @@ -7504,7 +7504,7 @@ void OBSBasic::StreamDelayStarting(int sec) ui->streamButton->setChecked(true); if (sysTrayStream) { - sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setText(QTStr("Basic.Main.StopStreaming")); sysTrayStream->setEnabled(true); } @@ -7531,7 +7531,7 @@ void OBSBasic::StreamDelayStopping(int sec) ui->streamButton->setChecked(false); if (sysTrayStream) { - sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); sysTrayStream->setEnabled(true); } @@ -7562,7 +7562,7 @@ void OBSBasic::StreamingStart() ui->statusbar->StreamStarted(output); if (sysTrayStream) { - sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setText(QTStr("Basic.Main.StopStreaming")); sysTrayStream->setEnabled(true); } @@ -7601,7 +7601,7 @@ void OBSBasic::StreamStopping() ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); if (sysTrayStream) - sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setText(QTStr("Basic.Main.StoppingStreaming")); streamingStopping = true; if (api) @@ -7664,7 +7664,7 @@ void OBSBasic::StreamingStop(int code, QString last_error) ui->streamButton->setChecked(false); if (sysTrayStream) { - sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); sysTrayStream->setEnabled(true); } @@ -7824,7 +7824,7 @@ void OBSBasic::RecordStopping() ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); if (sysTrayRecord) - sysTrayRecord->setText(ui->recordButton->text()); + sysTrayRecord->setText(QTStr("Basic.Main.StoppingRecording")); recordingStopping = true; if (api) @@ -7849,7 +7849,7 @@ void OBSBasic::RecordingStart() ui->recordButton->setChecked(true); if (sysTrayRecord) - sysTrayRecord->setText(ui->recordButton->text()); + sysTrayRecord->setText(QTStr("Basic.Main.StopRecording")); recordingStopping = false; if (api) @@ -7872,7 +7872,7 @@ void OBSBasic::RecordingStop(int code, QString last_error) ui->recordButton->setChecked(false); if (sysTrayRecord) - sysTrayRecord->setText(ui->recordButton->text()); + sysTrayRecord->setText(QTStr("Basic.Main.StartRecording")); blog(LOG_INFO, RECORDING_STOP); @@ -8029,7 +8029,7 @@ void OBSBasic::ReplayBufferStopping() if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText( - replayBufferButton->first()->text()); + QTStr("Basic.Main.StoppingReplayBuffer")); replayBufferStopping = true; if (api) @@ -8060,7 +8060,7 @@ void OBSBasic::ReplayBufferStart() if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText( - replayBufferButton->first()->text()); + QTStr("Basic.Main.StopReplayBuffer")); replayBufferStopping = false; if (api) @@ -8121,7 +8121,7 @@ void OBSBasic::ReplayBufferStop(int code) if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText( - replayBufferButton->first()->text()); + QTStr("Basic.Main.StartReplayBuffer")); blog(LOG_INFO, REPLAY_BUFFER_STOP); From eed5578e4c214833cd3c47af1df67b8cb7859a52 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 5 Jun 2024 08:35:21 +0200 Subject: [PATCH 0179/1073] UI: Remove setStreamText lambda Avoid tangling controls dock stream button to OBSBasic with a lambda. --- UI/window-basic-main.cpp | 116 +++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 1e823b271be1c8..130259883468a0 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -7099,12 +7099,6 @@ void OBSBasic::StartStreaming() } } - auto setStreamText = [&](const QString &text) { - ui->streamButton->setText(text); - if (sysTrayStream) - sysTrayStream->setText(text); - }; - emit StreamingStarting(); ui->streamButton->setEnabled(false); ui->streamButton->setChecked(false); @@ -7112,75 +7106,77 @@ void OBSBasic::StartStreaming() if (sysTrayStream) sysTrayStream->setEnabled(false); - setStreamText(QTStr("Basic.Main.PreparingStream")); + ui->streamButton->setText("Basic.Main.PreparingStream"); + if (sysTrayStream) + sysTrayStream->setText("Basic.Main.PreparingStream"); auto holder = outputHandler->SetupStreaming(service); - auto future = holder.future.then( - this, [&, setStreamText](bool setupStreamingResult) { - if (!setupStreamingResult) { - DisplayStreamStartError(); - return; - } + auto future = holder.future.then(this, [&](bool setupStreamingResult) { + if (!setupStreamingResult) { + DisplayStreamStartError(); + return; + } - if (api) - api->on_event( - OBS_FRONTEND_EVENT_STREAMING_STARTING); + if (api) + api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTING); - SaveProject(); + SaveProject(); - setStreamText(QTStr("Basic.Main.Connecting")); + ui->streamButton->setText("Basic.Main.Connecting"); + if (sysTrayStream) + sysTrayStream->setText("Basic.Main.Connecting"); - if (!outputHandler->StartStreaming(service)) { - DisplayStreamStartError(); - return; - } + if (!outputHandler->StartStreaming(service)) { + DisplayStreamStartError(); + return; + } - if (!autoStartBroadcast) { + if (!autoStartBroadcast) { + ui->broadcastButton->setText( + QTStr("Basic.Main.StartBroadcast")); + ui->broadcastButton->setProperty("broadcastState", + "ready"); + ui->broadcastButton->style()->unpolish( + ui->broadcastButton); + ui->broadcastButton->style()->polish( + ui->broadcastButton); + // well, we need to disable button while stream is not active + ui->broadcastButton->setEnabled(false); + } else { + if (!autoStopBroadcast) { ui->broadcastButton->setText( - QTStr("Basic.Main.StartBroadcast")); - ui->broadcastButton->setProperty( - "broadcastState", "ready"); - ui->broadcastButton->style()->unpolish( - ui->broadcastButton); - ui->broadcastButton->style()->polish( - ui->broadcastButton); - // well, we need to disable button while stream is not active - ui->broadcastButton->setEnabled(false); + QTStr("Basic.Main.StopBroadcast")); } else { - if (!autoStopBroadcast) { - ui->broadcastButton->setText(QTStr( - "Basic.Main.StopBroadcast")); - } else { - ui->broadcastButton->setText(QTStr( - "Basic.Main.AutoStopEnabled")); - ui->broadcastButton->setEnabled(false); - } - ui->broadcastButton->setProperty( - "broadcastState", "active"); - ui->broadcastButton->style()->unpolish( - ui->broadcastButton); - ui->broadcastButton->style()->polish( - ui->broadcastButton); - broadcastActive = true; + ui->broadcastButton->setText( + QTStr("Basic.Main.AutoStopEnabled")); + ui->broadcastButton->setEnabled(false); } + ui->broadcastButton->setProperty("broadcastState", + "active"); + ui->broadcastButton->style()->unpolish( + ui->broadcastButton); + ui->broadcastButton->style()->polish( + ui->broadcastButton); + broadcastActive = true; + } - bool recordWhenStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", - "RecordWhenStreaming"); - if (recordWhenStreaming) - StartRecording(); + bool recordWhenStreaming = + config_get_bool(GetGlobalConfig(), "BasicWindow", + "RecordWhenStreaming"); + if (recordWhenStreaming) + StartRecording(); - bool replayBufferWhileStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", - "ReplayBufferWhileStreaming"); - if (replayBufferWhileStreaming) - StartReplayBuffer(); + bool replayBufferWhileStreaming = + config_get_bool(GetGlobalConfig(), "BasicWindow", + "ReplayBufferWhileStreaming"); + if (replayBufferWhileStreaming) + StartReplayBuffer(); #ifdef YOUTUBE_ENABLED - if (!autoStartBroadcast) - OBSBasic::ShowYouTubeAutoStartWarning(); + if (!autoStartBroadcast) + OBSBasic::ShowYouTubeAutoStartWarning(); #endif - }); + }); startStreamingFuture = {holder.cancelAll, future}; } From 511385891c7616b4e4680c6da4d624b2b68923f6 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 3 Apr 2024 14:35:10 +0200 Subject: [PATCH 0180/1073] UI: Separate controls dock from the main window --- UI/basic-controls.cpp | 329 +++++++++++++++++++++++++++++ UI/basic-controls.hpp | 72 +++++++ UI/cmake/legacy.cmake | 6 +- UI/cmake/ui-elements.cmake | 5 +- UI/cmake/ui-qt.cmake | 1 + UI/data/themes/Yami.obt | 8 + UI/forms/OBSBasic.ui | 243 --------------------- UI/forms/OBSBasicControls.ui | 397 ++++++++++++++++++++++++++++++++++ UI/noncheckable-button.hpp | 23 ++ UI/record-button.cpp | 159 -------------- UI/record-button.hpp | 39 ---- UI/window-basic-main.cpp | 399 ++++++++++------------------------- UI/window-basic-main.hpp | 64 ++++-- 13 files changed, 988 insertions(+), 757 deletions(-) create mode 100644 UI/basic-controls.cpp create mode 100644 UI/basic-controls.hpp create mode 100644 UI/forms/OBSBasicControls.ui create mode 100644 UI/noncheckable-button.hpp delete mode 100644 UI/record-button.cpp delete mode 100644 UI/record-button.hpp diff --git a/UI/basic-controls.cpp b/UI/basic-controls.cpp new file mode 100644 index 00000000000000..00145281cc1fe2 --- /dev/null +++ b/UI/basic-controls.cpp @@ -0,0 +1,329 @@ +#include "basic-controls.hpp" + +#include "window-basic-main.hpp" + +OBSBasicControls::OBSBasicControls(OBSBasic *main) + : QFrame(nullptr), + ui(new Ui::OBSBasicControls) +{ + /* Create UI elements */ + ui->setupUi(this); + + streamButtonMenu.reset(new QMenu()); + startStreamAction = + streamButtonMenu->addAction(QTStr("Basic.Main.StartStreaming")); + stopStreamAction = + streamButtonMenu->addAction(QTStr("Basic.Main.StopStreaming")); + QAction *forceStopStreamAction = streamButtonMenu->addAction( + QTStr("Basic.Main.ForceStopStreaming")); + + /* Transfer buttons signals as OBSBasicControls signals */ + connect( + ui->streamButton, &QPushButton::clicked, this, + [this]() { emit this->StreamButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->broadcastButton, &QPushButton::clicked, this, + [this]() { emit this->BroadcastButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->recordButton, &QPushButton::clicked, this, + [this]() { emit this->RecordButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->pauseRecordButton, &QPushButton::clicked, this, + [this]() { emit this->PauseRecordButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->replayBufferButton, &QPushButton::clicked, this, + [this]() { emit this->ReplayBufferButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->saveReplayButton, &QPushButton::clicked, this, + [this]() { emit this->SaveReplayBufferButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->virtualCamButton, &QPushButton::clicked, this, + [this]() { emit this->VirtualCamButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->virtualCamConfigButton, &QPushButton::clicked, this, + [this]() { emit this->VirtualCamConfigButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->modeSwitch, &QPushButton::clicked, this, + [this]() { emit this->StudioModeButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->settingsButton, &QPushButton::clicked, this, + [this]() { emit this->SettingsButtonClicked(); }, + Qt::DirectConnection); + connect( + ui->exitButton, &QPushButton::clicked, this, + [this]() { emit this->ExitButtonClicked(); }, + Qt::DirectConnection); + + /* Transfer menu actions signals as OBSBasicControls signals */ + connect( + startStreamAction.get(), &QAction::triggered, this, + [this]() { emit this->StartStreamMenuActionClicked(); }, + Qt::DirectConnection); + connect( + stopStreamAction.get(), &QAction::triggered, this, + [this]() { emit this->StopStreamMenuActionClicked(); }, + Qt::DirectConnection); + connect( + forceStopStreamAction, &QAction::triggered, this, + [this]() { emit this->ForceStopStreamMenuActionClicked(); }, + Qt::DirectConnection); + + /* Set up default visibilty */ + ui->broadcastButton->setVisible(false); + ui->pauseRecordButton->setVisible(false); + ui->replayBufferButton->setVisible(false); + ui->saveReplayButton->setVisible(false); + ui->virtualCamButton->setVisible(false); + ui->virtualCamConfigButton->setVisible(false); + + /* Set up state update connections */ + connect(main, &OBSBasic::StreamingPreparing, this, + &OBSBasicControls::StreamingPreparing); + connect(main, &OBSBasic::StreamingStarting, this, + &OBSBasicControls::StreamingStarting); + connect(main, &OBSBasic::StreamingStarted, this, + &OBSBasicControls::StreamingStarted); + connect(main, &OBSBasic::StreamingStopping, this, + &OBSBasicControls::StreamingStopping); + connect(main, &OBSBasic::StreamingStopped, this, + &OBSBasicControls::StreamingStopped); + + connect(main, &OBSBasic::BroadcastStreamReady, this, + &OBSBasicControls::BroadcastStreamReady); + connect(main, &OBSBasic::BroadcastStreamActive, this, + &OBSBasicControls::BroadcastStreamActive); + connect(main, &OBSBasic::BroadcastStreamStarted, this, + &OBSBasicControls::BroadcastStreamStarted); + + connect(main, &OBSBasic::RecordingStarted, this, + &OBSBasicControls::RecordingStarted); + connect(main, &OBSBasic::RecordingPaused, this, + &OBSBasicControls::RecordingPaused); + connect(main, &OBSBasic::RecordingUnpaused, this, + &OBSBasicControls::RecordingUnpaused); + connect(main, &OBSBasic::RecordingStopping, this, + &OBSBasicControls::RecordingStopping); + connect(main, &OBSBasic::RecordingStopped, this, + &OBSBasicControls::RecordingStopped); + + connect(main, &OBSBasic::ReplayBufStarted, this, + &OBSBasicControls::ReplayBufferStarted); + connect(main, &OBSBasic::ReplayBufferStopping, this, + &OBSBasicControls::ReplayBufferStopping); + connect(main, &OBSBasic::ReplayBufStopped, this, + &OBSBasicControls::ReplayBufferStopped); + + connect(main, &OBSBasic::VirtualCamStarted, this, + &OBSBasicControls::VirtualCamStarted); + connect(main, &OBSBasic::VirtualCamStopped, this, + &OBSBasicControls::VirtualCamStopped); + + connect(main, &OBSBasic::PreviewProgramModeChanged, this, + &OBSBasicControls::UpdateStudioModeState); + + /* Set up enablement connection */ + connect(main, &OBSBasic::BroadcastFlowEnabled, this, + &OBSBasicControls::EnableBroadcastFlow); + connect(main, &OBSBasic::ReplayBufEnabled, this, + &OBSBasicControls::EnableReplayBufferButtons); + connect(main, &OBSBasic::VirtualCamEnabled, this, + &OBSBasicControls::EnableVirtualCamButtons); +} + +void OBSBasicControls::StreamingPreparing() +{ + ui->streamButton->setEnabled(false); + ui->streamButton->setText(QTStr("Basic.Main.PreparingStream")); +} + +void OBSBasicControls::StreamingStarting(bool broadcastAutoStart) +{ + ui->streamButton->setText(QTStr("Basic.Main.Connecting")); + + if (!broadcastAutoStart) { + // well, we need to disable button while stream is not active + ui->broadcastButton->setEnabled(false); + + ui->broadcastButton->setText( + QTStr("Basic.Main.StartBroadcast")); + + ui->broadcastButton->setProperty("broadcastState", "ready"); + ui->broadcastButton->style()->unpolish(ui->broadcastButton); + ui->broadcastButton->style()->polish(ui->broadcastButton); + } +} + +void OBSBasicControls::StreamingStarted(bool withDelay) +{ + ui->streamButton->setEnabled(true); + ui->streamButton->setChecked(true); + ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); + + if (withDelay) { + ui->streamButton->setMenu(streamButtonMenu.get()); + startStreamAction->setVisible(false); + stopStreamAction->setVisible(true); + } +} + +void OBSBasicControls::StreamingStopping() +{ + ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); +} + +void OBSBasicControls::StreamingStopped(bool withDelay) +{ + ui->streamButton->setEnabled(true); + ui->streamButton->setChecked(false); + ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); + + if (withDelay) { + if (!ui->streamButton->menu()) + ui->streamButton->setMenu(streamButtonMenu.get()); + + startStreamAction->setVisible(true); + stopStreamAction->setVisible(false); + } else { + ui->streamButton->setMenu(nullptr); + } +} + +void OBSBasicControls::BroadcastStreamReady(bool ready) +{ + ui->broadcastButton->setChecked(ready); +} + +void OBSBasicControls::BroadcastStreamActive() +{ + ui->broadcastButton->setEnabled(true); +} + +void OBSBasicControls::BroadcastStreamStarted(bool autoStop) +{ + ui->broadcastButton->setText( + QTStr(autoStop ? "Basic.Main.AutoStopEnabled" + : "Basic.Main.StopBroadcast")); + if (autoStop) + ui->broadcastButton->setEnabled(false); + + ui->broadcastButton->setProperty("broadcastState", "active"); + ui->broadcastButton->style()->unpolish(ui->broadcastButton); + ui->broadcastButton->style()->polish(ui->broadcastButton); +} + +void OBSBasicControls::RecordingStarted(bool pausable) +{ + ui->recordButton->setChecked(true); + ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); + + if (pausable) { + ui->pauseRecordButton->setVisible(pausable); + RecordingUnpaused(); + } +} + +void OBSBasicControls::RecordingPaused() +{ + QString text = QTStr("Basic.Main.UnpauseRecording"); + + ui->pauseRecordButton->setChecked(true); + ui->pauseRecordButton->setAccessibleName(text); + ui->pauseRecordButton->setToolTip(text); + + ui->saveReplayButton->setEnabled(false); +} + +void OBSBasicControls::RecordingUnpaused() +{ + QString text = QTStr("Basic.Main.PauseRecording"); + + ui->pauseRecordButton->setChecked(false); + ui->pauseRecordButton->setAccessibleName(text); + ui->pauseRecordButton->setToolTip(text); + + ui->saveReplayButton->setEnabled(true); +} + +void OBSBasicControls::RecordingStopping() +{ + ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); +} + +void OBSBasicControls::RecordingStopped() +{ + ui->recordButton->setChecked(false); + ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); + + ui->pauseRecordButton->setVisible(false); +} + +void OBSBasicControls::ReplayBufferStarted() +{ + ui->replayBufferButton->setChecked(true); + ui->replayBufferButton->setText(QTStr("Basic.Main.StopReplayBuffer")); + + ui->saveReplayButton->setVisible(true); +} + +void OBSBasicControls::ReplayBufferStopping() +{ + ui->replayBufferButton->setText( + QTStr("Basic.Main.StoppingReplayBuffer")); +} + +void OBSBasicControls::ReplayBufferStopped() +{ + ui->replayBufferButton->setChecked(false); + ui->replayBufferButton->setText(QTStr("Basic.Main.StartReplayBuffer")); + + ui->saveReplayButton->setVisible(false); +} + +void OBSBasicControls::VirtualCamStarted() +{ + ui->virtualCamButton->setChecked(true); + ui->virtualCamButton->setText(QTStr("Basic.Main.StopVirtualCam")); +} + +void OBSBasicControls::VirtualCamStopped() +{ + ui->virtualCamButton->setChecked(false); + ui->virtualCamButton->setText(QTStr("Basic.Main.StartVirtualCam")); +} + +void OBSBasicControls::UpdateStudioModeState(bool enabled) +{ + ui->modeSwitch->setChecked(enabled); +} + +void OBSBasicControls::EnableBroadcastFlow(bool enabled) +{ + ui->broadcastButton->setVisible(enabled); + ui->broadcastButton->setEnabled(enabled); + + ui->broadcastButton->setText(QTStr("Basic.Main.SetupBroadcast")); + + ui->broadcastButton->setProperty("broadcastState", "idle"); + ui->broadcastButton->style()->unpolish(ui->broadcastButton); + ui->broadcastButton->style()->polish(ui->broadcastButton); +} + +void OBSBasicControls::EnableReplayBufferButtons(bool enabled) +{ + ui->replayBufferButton->setVisible(enabled); +} + +void OBSBasicControls::EnableVirtualCamButtons() +{ + ui->virtualCamButton->setVisible(true); + ui->virtualCamConfigButton->setVisible(true); +} diff --git a/UI/basic-controls.hpp b/UI/basic-controls.hpp new file mode 100644 index 00000000000000..6aa6778028ce54 --- /dev/null +++ b/UI/basic-controls.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include +#include +#include + +class OBSBasic; + +#include "ui_OBSBasicControls.h" + +class OBSBasicControls : public QFrame { + Q_OBJECT + + std::unique_ptr ui; + + QScopedPointer streamButtonMenu; + QPointer startStreamAction; + QPointer stopStreamAction; + +private slots: + void StreamingPreparing(); + void StreamingStarting(bool broadcastAutoStart); + void StreamingStarted(bool withDelay); + void StreamingStopping(); + void StreamingStopped(bool withDelay); + + void BroadcastStreamReady(bool ready); + void BroadcastStreamActive(); + void BroadcastStreamStarted(bool autoStop); + + void RecordingStarted(bool pausable); + void RecordingPaused(); + void RecordingUnpaused(); + void RecordingStopping(); + void RecordingStopped(); + + void ReplayBufferStarted(); + void ReplayBufferStopping(); + void ReplayBufferStopped(); + + void VirtualCamStarted(); + void VirtualCamStopped(); + + void UpdateStudioModeState(bool enabled); + + void EnableBroadcastFlow(bool enabled); + void EnableReplayBufferButtons(bool enabled); + void EnableVirtualCamButtons(); + +public: + OBSBasicControls(OBSBasic *main); + inline ~OBSBasicControls() {} + +signals: + void StreamButtonClicked(); + void BroadcastButtonClicked(); + void RecordButtonClicked(); + void PauseRecordButtonClicked(); + void ReplayBufferButtonClicked(); + void SaveReplayBufferButtonClicked(); + void VirtualCamButtonClicked(); + void VirtualCamConfigButtonClicked(); + void StudioModeButtonClicked(); + void SettingsButtonClicked(); + void ExitButtonClicked(); + + void StartStreamMenuActionClicked(); + void StopStreamMenuActionClicked(); + void ForceStopStreamMenuActionClicked(); +}; diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 5351f16f626d67..dc2800464ac60b 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -101,6 +101,7 @@ target_sources( forms/OBSAbout.ui forms/OBSAdvAudio.ui forms/OBSBasic.ui + forms/OBSBasicControls.ui forms/OBSBasicFilters.ui forms/OBSBasicInteraction.ui forms/OBSBasicProperties.ui @@ -167,6 +168,8 @@ target_sources( audio-encoders.cpp audio-encoders.hpp balance-slider.hpp + basic-controls.cpp + basic-controls.hpp clickable-label.hpp double-slider.cpp double-slider.hpp @@ -189,13 +192,12 @@ target_sources( menu-button.cpp menu-button.hpp mute-checkbox.hpp + noncheckable-button.hpp plain-text-edit.cpp plain-text-edit.hpp properties-view.cpp properties-view.hpp properties-view.moc.hpp - record-button.cpp - record-button.hpp remote-text.cpp remote-text.hpp scene-tree.cpp diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index 4b4ccb66e1f27f..d3f6f44ec29aac 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -38,6 +38,8 @@ target_sources( audio-encoders.cpp audio-encoders.hpp balance-slider.hpp + basic-controls.cpp + basic-controls.hpp context-bar-controls.cpp context-bar-controls.hpp focus-list.cpp @@ -55,8 +57,7 @@ target_sources( menu-button.cpp menu-button.hpp mute-checkbox.hpp - record-button.cpp - record-button.hpp + noncheckable-button.hpp remote-text.cpp remote-text.hpp scene-tree.cpp diff --git a/UI/cmake/ui-qt.cmake b/UI/cmake/ui-qt.cmake index 4989d2d1153a5d..6148fda8854924 100644 --- a/UI/cmake/ui-qt.cmake +++ b/UI/cmake/ui-qt.cmake @@ -34,6 +34,7 @@ set(_qt_sources forms/OBSAbout.ui forms/OBSAdvAudio.ui forms/OBSBasic.ui + forms/OBSBasicControls.ui forms/OBSBasicFilters.ui forms/OBSBasicInteraction.ui forms/OBSBasicProperties.ui diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 91b48ddc4a2df9..f9bc6e07f088e9 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -990,6 +990,14 @@ QPushButton[themeID="replayBufferButton"], padding: var(--padding_large); } +#pauseRecordButton, +#saveReplayButton, +#virtualCamConfigButton { + padding: var(--padding_large) var(--padding_large); + width: var(--input_height); + max-width: var(--input_height); +} + /* Primary Control Button Checked Coloring */ #streamButton:!hover:!pressed:checked, #recordButton:!hover:!pressed:checked, diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index a0313b2b4e6549..052d867350099b 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -1399,228 +1399,6 @@ - - - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable - - - Basic.Main.Controls - - - 8 - - - - - 0 - - - 1 - - - 0 - - - 1 - - - 1 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - true - - - - 0 - 0 - - - - - 150 - 0 - - - - Basic.Main.StartStreaming - - - true - - - - - - - true - - - - 0 - 0 - - - - - 150 - 0 - - - - Basic.Main.StartBroadcast - - - true - - - - - - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - Basic.Main.StartRecording - - - true - - - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - Basic.TogglePreviewProgramMode - - - true - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - Settings - - - - - - - - 0 - 0 - - - - - 150 - 0 - - - - Exit - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - @@ -2372,11 +2150,6 @@
window-dock.hpp
1 - - RecordButton - QPushButton -
record-button.hpp
-
@@ -2398,21 +2171,5 @@ - - exitButton - clicked() - OBSBasic - close() - - - 976 - 601 - - - 862 - -11 - - - diff --git a/UI/forms/OBSBasicControls.ui b/UI/forms/OBSBasicControls.ui new file mode 100644 index 00000000000000..c70df6142aa03f --- /dev/null +++ b/UI/forms/OBSBasicControls.ui @@ -0,0 +1,397 @@ + + + OBSBasicControls + + + + 0 + 0 + 318 + 213 + + + + + 0 + + + 1 + + + 0 + + + 1 + + + 1 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + true + + + + 0 + 0 + + + + + 150 + 0 + + + + Basic.Main.StartStreaming + + + true + + + + + + + true + + + + 0 + 0 + + + + + 150 + 0 + + + + Basic.Main.StartBroadcast + + + true + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + Basic.Main.StartRecording + + + true + + + + + + + true + + + + 0 + 0 + + + + Basic.Main.PauseRecording + + + Basic.Main.PauseRecording + + + true + + + pauseIconSmall + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + Basic.Main.StartReplayBuffer + + + true + + + replayBufferButton + + + + + + + true + + + + 0 + 0 + + + + Basic.Main.SaveReplay + + + Basic.Main.SaveReplay + + + replayIconSmall + + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + Basic.Main.StartVirtualCam + + + true + + + vcamButton + + + + + + + true + + + + 0 + 0 + + + + Basic.Main.VirtualCamConfig + + + Basic.Main.VirtualCamConfig + + + configIconSmall + + + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Basic.TogglePreviewProgramMode + + + true + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Settings + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + Exit + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + NonCheckableButton + QPushButton +
noncheckable-button.hpp
+
+
+ + + + +
diff --git a/UI/noncheckable-button.hpp b/UI/noncheckable-button.hpp new file mode 100644 index 00000000000000..71d942ef31d3e0 --- /dev/null +++ b/UI/noncheckable-button.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +/* Button with its checked property not changed when clicked. + * Meant to be used in situations where manually changing the property + * is always preferred. */ +class NonCheckableButton : public QPushButton { + Q_OBJECT + + inline void nextCheckState() override {} + +public: + inline NonCheckableButton(QWidget *parent = nullptr) + : QPushButton(parent) + { + } + inline NonCheckableButton(const QString &text, + QWidget *parent = nullptr) + : QPushButton(text, parent) + { + } +}; diff --git a/UI/record-button.cpp b/UI/record-button.cpp deleted file mode 100644 index a33bbfdec9cbf7..00000000000000 --- a/UI/record-button.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "record-button.hpp" -#include "window-basic-main.hpp" - -void RecordButton::resizeEvent(QResizeEvent *event) -{ - OBSBasic *main = OBSBasic::Get(); - if (!main->pause) - return; - - QSize pauseSize = main->pause->size(); - int height = main->ui->recordButton->size().height(); - - if (pauseSize.height() != height || pauseSize.width() != height) { - main->pause->setMinimumSize(height, height); - main->pause->setMaximumSize(height, height); - } - - event->accept(); -} - -static QWidget *firstWidget(QLayoutItem *item) -{ - auto widget = item->widget(); - if (widget) - return widget; - - auto layout = item->layout(); - if (!layout) - return nullptr; - - auto n = layout->count(); - for (auto i = 0; i < n; i++) { - widget = firstWidget(layout->itemAt(i)); - if (widget) - return widget; - } - return nullptr; -} - -static QWidget *lastWidget(QLayoutItem *item) -{ - auto widget = item->widget(); - if (widget) - return widget; - - auto layout = item->layout(); - if (!layout) - return nullptr; - - for (auto i = layout->count(); i > 0; i--) { - widget = lastWidget(layout->itemAt(i - 1)); - if (widget) - return widget; - } - return nullptr; -} - -static QWidget *getNextWidget(QBoxLayout *container, QLayoutItem *item) -{ - for (auto i = 1, n = container->count(); i < n; i++) { - if (container->itemAt(i - 1) == item) - return firstWidget(container->itemAt(i)); - } - return nullptr; -} - -ControlsSplitButton::ControlsSplitButton(const QString &text, - const QVariant &themeID, - void (OBSBasic::*clicked)()) - : QHBoxLayout() -{ - button.reset(new QPushButton(text)); - button->setCheckable(true); - button->setProperty("themeID", themeID); - - button->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - button->installEventFilter(this); - - OBSBasic *main = OBSBasic::Get(); - connect(button.data(), &QPushButton::clicked, main, clicked); - - addWidget(button.data()); -} - -void ControlsSplitButton::addIcon(const QString &name, const QVariant &themeID, - void (OBSBasic::*clicked)()) -{ - icon.reset(new QPushButton()); - icon->setAccessibleName(name); - icon->setToolTip(name); - icon->setChecked(false); - icon->setProperty("themeID", themeID); - - QSizePolicy sp; - sp.setHeightForWidth(true); - icon->setSizePolicy(sp); - - OBSBasic *main = OBSBasic::Get(); - connect(icon.data(), &QAbstractButton::clicked, main, clicked); - - addWidget(icon.data()); - QWidget::setTabOrder(button.data(), icon.data()); - - auto next = getNextWidget(main->ui->buttonsVLayout, this); - if (next) - QWidget::setTabOrder(icon.data(), next); -} - -void ControlsSplitButton::removeIcon() -{ - icon.reset(); -} - -void ControlsSplitButton::insert(int index) -{ - OBSBasic *main = OBSBasic::Get(); - auto count = main->ui->buttonsVLayout->count(); - if (index < 0) - index = 0; - else if (index > count) - index = count; - - main->ui->buttonsVLayout->insertLayout(index, this); - - QWidget *prev = button.data(); - - if (index > 0) { - prev = lastWidget(main->ui->buttonsVLayout->itemAt(index - 1)); - if (prev) - QWidget::setTabOrder(prev, button.data()); - prev = button.data(); - } - - if (icon) { - QWidget::setTabOrder(button.data(), icon.data()); - prev = icon.data(); - } - - if (index < count) { - auto next = firstWidget( - main->ui->buttonsVLayout->itemAt(index + 1)); - if (next) - QWidget::setTabOrder(prev, next); - } -} - -bool ControlsSplitButton::eventFilter(QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::Resize && icon) { - QSize iconSize = icon->size(); - int height = button->height(); - - if (iconSize.height() != height || iconSize.width() != height) { - icon->setMinimumSize(height, height); - icon->setMaximumSize(height, height); - } - } - return QObject::eventFilter(obj, event); -} diff --git a/UI/record-button.hpp b/UI/record-button.hpp deleted file mode 100644 index ea8d40c7de1aee..00000000000000 --- a/UI/record-button.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include - -class RecordButton : public QPushButton { - Q_OBJECT - -public: - inline RecordButton(QWidget *parent = nullptr) : QPushButton(parent) {} - - virtual void resizeEvent(QResizeEvent *event) override; -}; - -class OBSBasic; - -class ControlsSplitButton : public QHBoxLayout { - Q_OBJECT - -public: - ControlsSplitButton(const QString &text, const QVariant &themeID, - void (OBSBasic::*clicked)()); - - void addIcon(const QString &name, const QVariant &themeID, - void (OBSBasic::*clicked)()); - void removeIcon(); - void insert(int index); - - inline QPushButton *first() { return button.data(); } - inline QPushButton *second() { return icon.data(); } - -protected: - virtual bool eventFilter(QObject *obj, QEvent *event) override; - -private: - QScopedPointer button; - QScopedPointer icon; -}; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 130259883468a0..74e07e540af94b 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -47,6 +47,7 @@ #include "platform.hpp" #include "visibility-item-widget.hpp" #include "item-widget-helpers.hpp" +#include "basic-controls.hpp" #include "window-basic-settings.hpp" #include "window-namedialog.hpp" #include "window-basic-auto-config.hpp" @@ -332,13 +333,6 @@ OBSBasic::OBSBasic(QWidget *parent) ui->setupUi(this); ui->previewDisabledWidget->setVisible(false); - ui->broadcastButton->setVisible(false); - - /* Setup Studio Mode button connections */ - connect(this, &OBSBasic::PreviewProgramModeChanged, ui->modeSwitch, - &QAbstractButton::setChecked); - connect(ui->modeSwitch, &QAbstractButton::clicked, this, - &OBSBasic::TogglePreviewProgramMode); /* Set up streaming connections */ connect( @@ -378,6 +372,52 @@ OBSBasic::OBSBasic(QWidget *parent) }, Qt::DirectConnection); + /* Add controls dock */ + OBSBasicControls *controls = new OBSBasicControls(this); + controlsDock = new OBSDock(this); + controlsDock->setObjectName(QString::fromUtf8("controlsDock")); + controlsDock->setWindowTitle(QTStr("Basic.Main.Controls")); + /* Parenting is done there so controls will be deleted alongside controlsDock */ + controlsDock->setWidget(controls); + addDockWidget(Qt::BottomDockWidgetArea, controlsDock); + + connect(controls, &OBSBasicControls::StreamButtonClicked, this, + &OBSBasic::StreamActionTriggered); + + connect(controls, &OBSBasicControls::StartStreamMenuActionClicked, this, + &OBSBasic::StartStreaming); + connect(controls, &OBSBasicControls::StopStreamMenuActionClicked, this, + &OBSBasic::StopStreaming); + connect(controls, &OBSBasicControls::ForceStopStreamMenuActionClicked, + this, &OBSBasic::ForceStopStreaming); + + connect(controls, &OBSBasicControls::BroadcastButtonClicked, this, + &OBSBasic::BroadcastButtonClicked); + + connect(controls, &OBSBasicControls::RecordButtonClicked, this, + &OBSBasic::RecordActionTriggered); + connect(controls, &OBSBasicControls::PauseRecordButtonClicked, this, + &OBSBasic::RecordPauseToggled); + + connect(controls, &OBSBasicControls::ReplayBufferButtonClicked, this, + &OBSBasic::ReplayBufferActionTriggered); + connect(controls, &OBSBasicControls::SaveReplayBufferButtonClicked, + this, &OBSBasic::ReplayBufferSave); + + connect(controls, &OBSBasicControls::VirtualCamButtonClicked, this, + &OBSBasic::VirtualCamActionTriggered); + connect(controls, &OBSBasicControls::VirtualCamConfigButtonClicked, + this, &OBSBasic::OpenVirtualCamConfig); + + connect(controls, &OBSBasicControls::StudioModeButtonClicked, this, + &OBSBasic::TogglePreviewProgramMode); + + connect(controls, &OBSBasicControls::SettingsButtonClicked, this, + &OBSBasic::on_action_Settings_triggered); + + connect(controls, &OBSBasicControls::ExitButtonClicked, this, + &QMainWindow::close); + startingDockLayout = saveState(); statsDock = new OBSDock(); @@ -518,7 +558,7 @@ OBSBasic::OBSBasic(QWidget *parent) SETUP_DOCK(ui->sourcesDock); SETUP_DOCK(ui->mixerDock); SETUP_DOCK(ui->transitionsDock); - SETUP_DOCK(ui->controlsDock); + SETUP_DOCK(controlsDock); SETUP_DOCK(statsDock); #undef SETUP_DOCK @@ -577,9 +617,6 @@ OBSBasic::OBSBasic(QWidget *parent) connect(ui->scenes, &SceneTree::scenesReordered, []() { OBSProjector::UpdateMultiviewProjectors(); }); - connect(ui->broadcastButton, &QPushButton::clicked, this, - &OBSBasic::BroadcastButtonClicked); - connect(App(), &OBSApp::StyleChanged, this, [this]() { if (api) api->on_event(OBS_FRONTEND_EVENT_THEME_CHANGED); @@ -1985,7 +2022,7 @@ void OBSBasic::InitPrimitives() obs_leave_graphics(); } -void OBSBasic::ReplayBufferClicked() +void OBSBasic::ReplayBufferActionTriggered() { if (outputHandler->ReplayBufferActive()) StopReplayBuffer(); @@ -1993,19 +2030,6 @@ void OBSBasic::ReplayBufferClicked() StartReplayBuffer(); }; -void OBSBasic::AddVCamButton() -{ - vcamButton = new ControlsSplitButton( - QTStr("Basic.Main.StartVirtualCam"), "vcamButton", - &OBSBasic::VCamButtonClicked); - vcamButton->addIcon(QTStr("Basic.Main.VirtualCamConfig"), - QStringLiteral("configIconSmall"), - &OBSBasic::VCamConfigButtonClicked); - vcamButton->insert(2); - vcamButton->first()->setSizePolicy(QSizePolicy::Minimum, - QSizePolicy::Minimum); -} - void OBSBasic::ResetOutputs() { ProfileScope("OBSBasic::ResetOutputs"); @@ -2019,15 +2043,7 @@ void OBSBasic::ResetOutputs() outputHandler.reset(advOut ? CreateAdvancedOutputHandler(this) : CreateSimpleOutputHandler(this)); - delete replayBufferButton; - - if (outputHandler->replayBuffer) { - replayBufferButton = new ControlsSplitButton( - QTStr("Basic.Main.StartReplayBuffer"), - "replayBufferButton", - &OBSBasic::ReplayBufferClicked); - replayBufferButton->insert(2); - } + emit ReplayBufEnabled(outputHandler->replayBuffer); if (sysTrayReplayBuffer) sysTrayReplayBuffer->setEnabled( @@ -2169,7 +2185,7 @@ void OBSBasic::OBSInit() vcamEnabled = (obs_get_output_flags(VIRTUAL_CAM_ID) & OBS_OUTPUT_VIDEO) != 0; if (vcamEnabled) { - AddVCamButton(); + emit VirtualCamEnabled(); } InitBasicConfigDefaults2(); @@ -6945,9 +6961,6 @@ void OBSBasic::DisplayStreamStartError() : QTStr("Output.StartFailedGeneric"); emit StreamingStopped(); - ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); - ui->streamButton->setEnabled(true); - ui->streamButton->setChecked(false); if (sysTrayStream) { sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); @@ -6981,6 +6994,8 @@ void OBSBasic::YouTubeActionDialogOk(const QString &broadcast_id, autoStopBroadcast = autostop; broadcastReady = true; + emit BroadcastStreamReady(broadcastReady); + if (start_now) QMetaObject::invokeMethod(this, "StartStreaming"); } @@ -7023,9 +7038,7 @@ void OBSBasic::YoutubeStreamCheck(const std::string &key) auto item = json["items"][0]; auto status = item["status"]["streamStatus"].string_value(); if (status == "active") { - QMetaObject::invokeMethod(ui->broadcastButton, - "setEnabled", - Q_ARG(bool, true)); + emit BroadcastStreamActive(); break; } else { QThread::sleep(1); @@ -7077,8 +7090,6 @@ void OBSBasic::StartStreaming() if (auth && auth->broadcastFlow()) { if (!broadcastActive && !broadcastReady) { - ui->streamButton->setChecked(false); - QMessageBox no_broadcast(this); no_broadcast.setText(QTStr("Output.NoBroadcast.Text")); QPushButton *SetupBroadcast = no_broadcast.addButton( @@ -7099,16 +7110,12 @@ void OBSBasic::StartStreaming() } } - emit StreamingStarting(); - ui->streamButton->setEnabled(false); - ui->streamButton->setChecked(false); - ui->broadcastButton->setChecked(false); - if (sysTrayStream) - sysTrayStream->setEnabled(false); + emit StreamingPreparing(); - ui->streamButton->setText("Basic.Main.PreparingStream"); - if (sysTrayStream) + if (sysTrayStream) { + sysTrayStream->setEnabled(false); sysTrayStream->setText("Basic.Main.PreparingStream"); + } auto holder = outputHandler->SetupStreaming(service); auto future = holder.future.then(this, [&](bool setupStreamingResult) { @@ -7122,7 +7129,8 @@ void OBSBasic::StartStreaming() SaveProject(); - ui->streamButton->setText("Basic.Main.Connecting"); + emit StreamingStarting(autoStartBroadcast); + if (sysTrayStream) sysTrayStream->setText("Basic.Main.Connecting"); @@ -7131,32 +7139,8 @@ void OBSBasic::StartStreaming() return; } - if (!autoStartBroadcast) { - ui->broadcastButton->setText( - QTStr("Basic.Main.StartBroadcast")); - ui->broadcastButton->setProperty("broadcastState", - "ready"); - ui->broadcastButton->style()->unpolish( - ui->broadcastButton); - ui->broadcastButton->style()->polish( - ui->broadcastButton); - // well, we need to disable button while stream is not active - ui->broadcastButton->setEnabled(false); - } else { - if (!autoStopBroadcast) { - ui->broadcastButton->setText( - QTStr("Basic.Main.StopBroadcast")); - } else { - ui->broadcastButton->setText( - QTStr("Basic.Main.AutoStopEnabled")); - ui->broadcastButton->setEnabled(false); - } - ui->broadcastButton->setProperty("broadcastState", - "active"); - ui->broadcastButton->style()->unpolish( - ui->broadcastButton); - ui->broadcastButton->style()->polish( - ui->broadcastButton); + if (autoStartBroadcast) { + emit BroadcastStreamStarted(autoStopBroadcast); broadcastActive = true; } @@ -7185,8 +7169,6 @@ void OBSBasic::BroadcastButtonClicked() if (!broadcastReady || (!broadcastActive && !outputHandler->StreamingActive())) { SetupBroadcast(); - if (broadcastReady) - ui->broadcastButton->setChecked(true); return; } @@ -7210,7 +7192,6 @@ void OBSBasic::BroadcastButtonClicked() this, QTStr("Output.BroadcastStartFailed"), last_error, true); - ui->broadcastButton->setChecked(false); return; } } @@ -7218,18 +7199,7 @@ void OBSBasic::BroadcastButtonClicked() broadcastActive = true; autoStartBroadcast = true; // and clear the flag - if (!autoStopBroadcast) { - ui->broadcastButton->setText( - QTStr("Basic.Main.StopBroadcast")); - } else { - ui->broadcastButton->setText( - QTStr("Basic.Main.AutoStopEnabled")); - ui->broadcastButton->setEnabled(false); - } - - ui->broadcastButton->setProperty("broadcastState", "active"); - ui->broadcastButton->style()->unpolish(ui->broadcastButton); - ui->broadcastButton->style()->polish(ui->broadcastButton); + emit BroadcastStreamStarted(autoStopBroadcast); } else if (!autoStopBroadcast) { #ifdef YOUTUBE_ENABLED bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", @@ -7241,10 +7211,8 @@ void OBSBasic::BroadcastButtonClicked() QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (button == QMessageBox::No) { - ui->broadcastButton->setChecked(true); + if (button == QMessageBox::No) return; - } } std::shared_ptr ytAuth = @@ -7273,19 +7241,14 @@ void OBSBasic::BroadcastButtonClicked() autoStopBroadcast = true; QMetaObject::invokeMethod(this, "StopStreaming"); + emit BroadcastStreamReady(broadcastReady); SetBroadcastFlowEnabled(true); } } void OBSBasic::SetBroadcastFlowEnabled(bool enabled) { - ui->broadcastButton->setEnabled(enabled); - ui->broadcastButton->setVisible(enabled); - ui->broadcastButton->setChecked(broadcastReady); - ui->broadcastButton->setProperty("broadcastState", "idle"); - ui->broadcastButton->style()->unpolish(ui->broadcastButton); - ui->broadcastButton->style()->polish(ui->broadcastButton); - ui->broadcastButton->setText(QTStr("Basic.Main.SetupBroadcast")); + emit BroadcastFlowEnabled(enabled); } void OBSBasic::SetupBroadcast() @@ -7296,11 +7259,7 @@ void OBSBasic::SetupBroadcast() OBSYoutubeActions dialog(this, auth, broadcastReady); connect(&dialog, &OBSYoutubeActions::ok, this, &OBSBasic::YouTubeActionDialogOk); - int result = dialog.Valid() ? dialog.exec() : QDialog::Rejected; - if (result != QDialog::Accepted) { - if (!broadcastReady) - ui->broadcastButton->setChecked(false); - } + dialog.exec(); } #endif } @@ -7433,6 +7392,8 @@ void OBSBasic::StopStreaming() broadcastReady = false; } + emit BroadcastStreamReady(broadcastReady); + OnDeactivate(); bool recordWhenStreaming = config_get_bool( @@ -7473,6 +7434,8 @@ void OBSBasic::ForceStopStreaming() broadcastReady = false; } + emit BroadcastStreamReady(broadcastReady); + OnDeactivate(); bool recordWhenStreaming = config_get_bool( @@ -7494,26 +7457,13 @@ void OBSBasic::ForceStopStreaming() void OBSBasic::StreamDelayStarting(int sec) { - emit StreamingStarted(); - ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); - ui->streamButton->setEnabled(true); - ui->streamButton->setChecked(true); + emit StreamingStarted(true); if (sysTrayStream) { sysTrayStream->setText(QTStr("Basic.Main.StopStreaming")); sysTrayStream->setEnabled(true); } - if (!startStreamMenu.isNull()) - startStreamMenu->deleteLater(); - - startStreamMenu = new QMenu(); - startStreamMenu->addAction(QTStr("Basic.Main.StopStreaming"), this, - &OBSBasic::StopStreaming); - startStreamMenu->addAction(QTStr("Basic.Main.ForceStopStreaming"), this, - &OBSBasic::ForceStopStreaming); - ui->streamButton->setMenu(startStreamMenu); - ui->statusbar->StreamDelayStarting(sec); OnActivate(); @@ -7521,26 +7471,13 @@ void OBSBasic::StreamDelayStarting(int sec) void OBSBasic::StreamDelayStopping(int sec) { - emit StreamingStopped(); - ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); - ui->streamButton->setEnabled(true); - ui->streamButton->setChecked(false); + emit StreamingStopped(true); if (sysTrayStream) { sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); sysTrayStream->setEnabled(true); } - if (!startStreamMenu.isNull()) - startStreamMenu->deleteLater(); - - startStreamMenu = new QMenu(); - startStreamMenu->addAction(QTStr("Basic.Main.StartStreaming"), this, - &OBSBasic::StartStreaming); - startStreamMenu->addAction(QTStr("Basic.Main.ForceStopStreaming"), this, - &OBSBasic::ForceStopStreaming); - ui->streamButton->setMenu(startStreamMenu); - ui->statusbar->StreamDelayStopping(sec); if (api) @@ -7551,10 +7488,6 @@ void OBSBasic::StreamingStart() { emit StreamingStarted(); OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); - - ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); - ui->streamButton->setEnabled(true); - ui->streamButton->setChecked(true); ui->statusbar->StreamStarted(output); if (sysTrayStream) { @@ -7594,7 +7527,7 @@ void OBSBasic::StreamingStart() void OBSBasic::StreamStopping() { - ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); + emit StreamingStopping(); if (sysTrayStream) sysTrayStream->setText(QTStr("Basic.Main.StoppingStreaming")); @@ -7655,9 +7588,6 @@ void OBSBasic::StreamingStop(int code, QString last_error) ui->statusbar->StreamStopped(); emit StreamingStopped(); - ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); - ui->streamButton->setEnabled(true); - ui->streamButton->setChecked(false); if (sysTrayStream) { sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); @@ -7696,12 +7626,6 @@ void OBSBasic::StreamingStop(int code, QString last_error) QSystemTrayIcon::Warning); } - if (!startStreamMenu.isNull()) { - ui->streamButton->setMenu(nullptr); - startStreamMenu->deleteLater(); - startStreamMenu = nullptr; - } - // Reset broadcast button state/text if (!broadcastActive) SetBroadcastFlowEnabled(auth && auth->broadcastFlow()); @@ -7796,13 +7720,11 @@ void OBSBasic::StartRecording() if (!OutputPathValid()) { OutputPathInvalidMessage(); - ui->recordButton->setChecked(false); return; } if (!IsFFmpegOutputToURL() && LowDiskSpace()) { DiskSpaceMessage(); - ui->recordButton->setChecked(false); return; } @@ -7811,13 +7733,12 @@ void OBSBasic::StartRecording() SaveProject(); - if (!outputHandler->StartRecording()) - ui->recordButton->setChecked(false); + outputHandler->StartRecording(); } void OBSBasic::RecordStopping() { - ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); + emit RecordingStopping(); if (sysTrayRecord) sysTrayRecord->setText(QTStr("Basic.Main.StoppingRecording")); @@ -7840,9 +7761,7 @@ void OBSBasic::StopRecording() void OBSBasic::RecordingStart() { ui->statusbar->RecordingStarted(outputHandler->fileOutput); - emit RecordingStarted(); - ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); - ui->recordButton->setChecked(true); + emit RecordingStarted(isRecordingPausable); if (sysTrayRecord) sysTrayRecord->setText(QTStr("Basic.Main.StopRecording")); @@ -7855,7 +7774,6 @@ void OBSBasic::RecordingStart() diskFullTimer->start(1000); OnActivate(); - UpdatePause(); blog(LOG_INFO, RECORDING_START); } @@ -7864,8 +7782,6 @@ void OBSBasic::RecordingStop(int code, QString last_error) { ui->statusbar->RecordingStopped(); emit RecordingStopped(); - ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); - ui->recordButton->setChecked(false); if (sysTrayRecord) sysTrayRecord->setText(QTStr("Basic.Main.StartRecording")); @@ -7935,7 +7851,6 @@ void OBSBasic::RecordingStop(int code, QString last_error) AutoRemux(outputHandler->lastRecordingPath.c_str()); OnDeactivate(); - UpdatePause(false); } void OBSBasic::RecordingFileChanged(QString lastRecordingPath) @@ -7986,20 +7901,16 @@ void OBSBasic::StartReplayBuffer() if (disableOutputsRef) return; - if (!UIValidation::NoSourcesConfirmation(this)) { - replayBufferButton->first()->setChecked(false); + if (!UIValidation::NoSourcesConfirmation(this)) return; - } if (!OutputPathValid()) { OutputPathInvalidMessage(); - replayBufferButton->first()->setChecked(false); return; } if (LowDiskSpace()) { DiskSpaceMessage(); - replayBufferButton->first()->setChecked(false); return; } @@ -8008,9 +7919,8 @@ void OBSBasic::StartReplayBuffer() SaveProject(); - if (!outputHandler->StartReplayBuffer()) { - replayBufferButton->first()->setChecked(false); - } else if (os_atomic_load_bool(&recording_paused)) { + if (outputHandler->StartReplayBuffer() && + os_atomic_load_bool(&recording_paused)) { ShowReplayBufferPauseWarning(); } } @@ -8020,8 +7930,7 @@ void OBSBasic::ReplayBufferStopping() if (!outputHandler || !outputHandler->replayBuffer) return; - replayBufferButton->first()->setText( - QTStr("Basic.Main.StoppingReplayBuffer")); + emit ReplayBufStopping(); if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText( @@ -8050,9 +7959,7 @@ void OBSBasic::ReplayBufferStart() if (!outputHandler || !outputHandler->replayBuffer) return; - replayBufferButton->first()->setText( - QTStr("Basic.Main.StopReplayBuffer")); - replayBufferButton->first()->setChecked(true); + emit ReplayBufStarted(); if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText( @@ -8063,7 +7970,6 @@ void OBSBasic::ReplayBufferStart() api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED); OnActivate(); - UpdateReplayBuffer(); blog(LOG_INFO, REPLAY_BUFFER_START); } @@ -8111,9 +8017,7 @@ void OBSBasic::ReplayBufferStop(int code) if (!outputHandler || !outputHandler->replayBuffer) return; - replayBufferButton->first()->setText( - QTStr("Basic.Main.StartReplayBuffer")); - replayBufferButton->first()->setChecked(false); + emit ReplayBufStopped(); if (sysTrayReplayBuffer) sysTrayReplayBuffer->setText( @@ -8151,7 +8055,6 @@ void OBSBasic::ReplayBufferStop(int code) api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED); OnDeactivate(); - UpdateReplayBuffer(false); } void OBSBasic::StartVirtualCam() @@ -8165,9 +8068,7 @@ void OBSBasic::StartVirtualCam() SaveProject(); - if (!outputHandler->StartVirtualCam()) { - vcamButton->first()->setChecked(false); - } + outputHandler->StartVirtualCam(); } void OBSBasic::StopVirtualCam() @@ -8188,10 +8089,10 @@ void OBSBasic::OnVirtualCamStart() if (!outputHandler || !outputHandler->virtualCam) return; - vcamButton->first()->setText(QTStr("Basic.Main.StopVirtualCam")); + emit VirtualCamStarted(); + if (sysTrayVirtualCam) sysTrayVirtualCam->setText(QTStr("Basic.Main.StopVirtualCam")); - vcamButton->first()->setChecked(true); if (api) api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED); @@ -8206,10 +8107,10 @@ void OBSBasic::OnVirtualCamStop(int) if (!outputHandler || !outputHandler->virtualCam) return; - vcamButton->first()->setText(QTStr("Basic.Main.StartVirtualCam")); + emit VirtualCamStopped(); + if (sysTrayVirtualCam) sysTrayVirtualCam->setText(QTStr("Basic.Main.StartVirtualCam")); - vcamButton->first()->setChecked(false); if (api) api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED); @@ -8226,7 +8127,7 @@ void OBSBasic::OnVirtualCamStop(int) QTimer::singleShot(100, this, &OBSBasic::RestartingVirtualCam); } -void OBSBasic::on_streamButton_clicked() +void OBSBasic::StreamActionTriggered() { if (outputHandler->StreamingActive()) { bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", @@ -8241,10 +8142,8 @@ void OBSBasic::on_streamButton_clicked() QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (button == QMessageBox::No) { - ui->streamButton->setChecked(true); + if (button == QMessageBox::No) return; - } confirm = false; } @@ -8257,18 +8156,14 @@ void OBSBasic::on_streamButton_clicked() QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (button == QMessageBox::No) { - ui->streamButton->setChecked(true); + if (button == QMessageBox::No) return; - } } StopStreaming(); } else { - if (!UIValidation::NoSourcesConfirmation(this)) { - ui->streamButton->setChecked(false); + if (!UIValidation::NoSourcesConfirmation(this)) return; - } Auth *auth = GetAuth(); @@ -8282,10 +8177,8 @@ void OBSBasic::on_streamButton_clicked() break; case StreamSettingsAction::OpenSettings: on_action_Settings_triggered(); - ui->streamButton->setChecked(false); return; case StreamSettingsAction::Cancel: - ui->streamButton->setChecked(false); return; } @@ -8310,10 +8203,8 @@ void OBSBasic::on_streamButton_clicked() this, QTStr("ConfirmBWTest.Title"), QTStr("ConfirmBWTest.Text")); - if (button == QMessageBox::No) { - ui->streamButton->setChecked(false); + if (button == QMessageBox::No) return; - } } else if (confirm && isVisible()) { QMessageBox::StandardButton button = OBSMessageBox::question( @@ -8322,17 +8213,15 @@ void OBSBasic::on_streamButton_clicked() QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (button == QMessageBox::No) { - ui->streamButton->setChecked(false); + if (button == QMessageBox::No) return; - } } StartStreaming(); } } -void OBSBasic::on_recordButton_clicked() +void OBSBasic::RecordActionTriggered() { if (outputHandler->RecordingActive()) { bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", @@ -8346,37 +8235,31 @@ void OBSBasic::on_recordButton_clicked() QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - if (button == QMessageBox::No) { - ui->recordButton->setChecked(true); + if (button == QMessageBox::No) return; - } } StopRecording(); } else { - if (!UIValidation::NoSourcesConfirmation(this)) { - ui->recordButton->setChecked(false); + if (!UIValidation::NoSourcesConfirmation(this)) return; - } StartRecording(); } } -void OBSBasic::VCamButtonClicked() +void OBSBasic::VirtualCamActionTriggered() { if (outputHandler->VirtualCamActive()) { StopVirtualCam(); } else { - if (!UIValidation::NoSourcesConfirmation(this)) { - vcamButton->first()->setChecked(false); + if (!UIValidation::NoSourcesConfirmation(this)) return; - } StartVirtualCam(); } } -void OBSBasic::VCamConfigButtonClicked() +void OBSBasic::OpenVirtualCamConfig() { OBSBasicVCamConfig dialog(vcamConfig, outputHandler->VirtualCamActive(), this); @@ -8440,11 +8323,6 @@ void OBSBasic::RestartingVirtualCam() restartingVCam = false; } -void OBSBasic::on_settingsButton_clicked() -{ - on_action_Settings_triggered(); -} - void OBSBasic::on_actionHelpPortal_triggered() { QUrl url = QUrl("https://obsproject.com/help", QUrl::TolerantMode); @@ -9735,7 +9613,7 @@ void OBSBasic::on_resetDocks_triggered(bool force) QList docks{ui->scenesDock, ui->sourcesDock, ui->mixerDock, ui->transitionsDock, - ui->controlsDock}; + controlsDock}; QList sizes{cx22_5, cx22_5, mixerSize, cx5, cx21}; @@ -9743,7 +9621,7 @@ void OBSBasic::on_resetDocks_triggered(bool force) ui->sourcesDock->setVisible(true); ui->mixerDock->setVisible(true); ui->transitionsDock->setVisible(true); - ui->controlsDock->setVisible(true); + controlsDock->setVisible(true); statsDock->setVisible(false); statsDock->setFloating(true); @@ -9768,7 +9646,7 @@ void OBSBasic::on_lockDocks_toggled(bool lock) ui->sourcesDock->setFeatures(mainFeatures); ui->mixerDock->setFeatures(mainFeatures); ui->transitionsDock->setFeatures(mainFeatures); - ui->controlsDock->setFeatures(mainFeatures); + controlsDock->setFeatures(mainFeatures); statsDock->setFeatures(features); for (int i = extraDocks.size() - 1; i >= 0; i--) @@ -10071,13 +9949,13 @@ void OBSBasic::SystemTrayInit() &OBSBasic::IconActivated); connect(showHide, &QAction::triggered, this, &OBSBasic::ToggleShowHide); connect(sysTrayStream, &QAction::triggered, this, - &OBSBasic::on_streamButton_clicked); + &OBSBasic::StreamActionTriggered); connect(sysTrayRecord, &QAction::triggered, this, - &OBSBasic::on_recordButton_clicked); + &OBSBasic::RecordActionTriggered); connect(sysTrayReplayBuffer.data(), &QAction::triggered, this, - &OBSBasic::ReplayBufferClicked); + &OBSBasic::ReplayBufferActionTriggered); connect(sysTrayVirtualCam.data(), &QAction::triggered, this, - &OBSBasic::VCamButtonClicked); + &OBSBasic::VirtualCamActionTriggered); connect(exit, &QAction::triggered, this, &OBSBasic::close); } @@ -10851,11 +10729,6 @@ void OBSBasic::PauseRecording() os_atomic_set_bool(&recording_paused, true); emit RecordingPaused(); - pause->setAccessibleName(QTStr("Basic.Main.UnpauseRecording")); - pause->setToolTip(QTStr("Basic.Main.UnpauseRecording")); - pause->blockSignals(true); - pause->setChecked(true); - pause->blockSignals(false); ui->statusbar->RecordingPaused(); @@ -10873,11 +10746,6 @@ void OBSBasic::PauseRecording() trayIconFile)); } - auto replay = replayBufferButton ? replayBufferButton->second() - : nullptr; - if (replay) - replay->setEnabled(false); - if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_PAUSED); @@ -10899,11 +10767,6 @@ void OBSBasic::UnpauseRecording() os_atomic_set_bool(&recording_paused, false); emit RecordingUnpaused(); - pause->setAccessibleName(QTStr("Basic.Main.PauseRecording")); - pause->setToolTip(QTStr("Basic.Main.PauseRecording")); - pause->blockSignals(true); - pause->setChecked(false); - pause->blockSignals(false); ui->statusbar->RecordingUnpaused(); @@ -10921,17 +10784,12 @@ void OBSBasic::UnpauseRecording() trayIconFile)); } - auto replay = replayBufferButton ? replayBufferButton->second() - : nullptr; - if (replay) - replay->setEnabled(true); - if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_UNPAUSED); } } -void OBSBasic::PauseToggled() +void OBSBasic::RecordPauseToggled() { if (!isRecordingPausable || !outputHandler || !outputHandler->fileOutput) @@ -10973,47 +10831,6 @@ void OBSBasic::UpdateIsRecordingPausable() isRecordingPausable = !shared; } -void OBSBasic::UpdatePause(bool activate) -{ - if (!activate || !outputHandler || !outputHandler->RecordingActive()) { - pause.reset(); - return; - } - - if (isRecordingPausable) { - pause.reset(new QPushButton()); - pause->setAccessibleName(QTStr("Basic.Main.PauseRecording")); - pause->setToolTip(QTStr("Basic.Main.PauseRecording")); - pause->setCheckable(true); - pause->setChecked(false); - pause->setProperty("themeID", - QVariant(QStringLiteral("pauseIconSmall"))); - - QSizePolicy sp; - sp.setHeightForWidth(true); - pause->setSizePolicy(sp); - - connect(pause.data(), &QAbstractButton::clicked, this, - &OBSBasic::PauseToggled); - ui->recordingLayout->addWidget(pause.data()); - } else { - pause.reset(); - } -} - -void OBSBasic::UpdateReplayBuffer(bool activate) -{ - if (!activate || !outputHandler || - !outputHandler->ReplayBufferActive()) { - replayBufferButton->removeIcon(); - return; - } - - replayBufferButton->addIcon(QTStr("Basic.Main.SaveReplay"), - QStringLiteral("replayIconSmall"), - &OBSBasic::ReplayBufferSave); -} - #define MBYTE (1024ULL * 1024ULL) #define MBYTES_LEFT_STOP_REC 50ULL #define MAX_BYTES_LEFT (MBYTES_LEFT_STOP_REC * MBYTE) diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index a478c49ff47548..f68075ba3013ee 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -319,13 +319,8 @@ class OBSBasic : public OBSMainWindow { QPointer extraBrowsers; QPointer importer; - QPointer startStreamMenu; - QPointer transitionButton; - QPointer replayBufferButton; - QScopedPointer pause; - QPointer vcamButton; bool vcamEnabled = false; VCamConfig vcamConfig; @@ -546,8 +541,6 @@ class OBSBasic : public OBSMainWindow { void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; - void ReplayBufferClicked(); - bool sysTrayMinimizeToTray(); void EnumDialogs(); @@ -888,8 +881,6 @@ private slots: void AutoRemux(QString input, bool no_show = false); void UpdateIsRecordingPausable(); - void UpdatePause(bool activate = true); - void UpdateReplayBuffer(bool activate = true); bool IsFFmpegOutputToURL() const; bool OutputPathValid(); @@ -933,7 +924,6 @@ private slots: int ResetVideo(); bool ResetAudio(); - void AddVCamButton(); void ResetOutputs(); void RefreshVolumeColors(); @@ -1132,11 +1122,6 @@ private slots: void on_actionScaleCanvas_triggered(); void on_actionScaleOutput_triggered(); - void on_streamButton_clicked(); - void on_recordButton_clicked(); - void VCamButtonClicked(); - void VCamConfigButtonClicked(); - void on_settingsButton_clicked(); void Screenshot(OBSSource source_ = nullptr); void ScreenshotSelectedSource(); void ScreenshotProgram(); @@ -1198,8 +1183,6 @@ private slots: void on_multiviewProjectorWindowed_triggered(); void on_sideDocks_toggled(bool side); - void PauseToggled(); - void logUploadFinished(const QString &text, const QString &error); void crashUploadFinished(const QString &text, const QString &error); void openLogDialog(const QString &text, const bool crash); @@ -1241,6 +1224,23 @@ private slots: void RepairOldExtraDockName(); void RepairCustomExtraDockName(); + /* Stream action (start/stop) slot */ + void StreamActionTriggered(); + + /* Record action (start/stop) slot */ + void RecordActionTriggered(); + + /* Record pause (pause/unpause) slot */ + void RecordPauseToggled(); + + /* Replay Buffer action (start/stop) slot */ + void ReplayBufferActionTriggered(); + + /* Virtual Cam action (start/stop) slots */ + void VirtualCamActionTriggered(); + + void OpenVirtualCamConfig(); + /* Studio Mode toggle slot */ void TogglePreviewProgramMode(); @@ -1259,22 +1259,44 @@ public slots: signals: /* Streaming signals */ - void StreamingStarting(); - void StreamingStarted(); - void StreamingStopped(); + void StreamingPreparing(); + void StreamingStarting(bool broadcastAutoStart); + void StreamingStarted(bool withDelay = false); + void StreamingStopping(); + void StreamingStopped(bool withDelay = false); + + /* Broadcast Flow signals */ + void BroadcastFlowEnabled(bool enabled); + void BroadcastStreamReady(bool ready); + void BroadcastStreamActive(); + void BroadcastStreamStarted(bool autoStop); /* Recording signals */ - void RecordingStarted(); + void RecordingStarted(bool pausable = false); void RecordingPaused(); void RecordingUnpaused(); + void RecordingStopping(); void RecordingStopped(); + /* Replay Buffer signals */ + void ReplayBufEnabled(bool enabled); + void ReplayBufStarted(); + void ReplayBufStopping(); + void ReplayBufStopped(); + + /* Virtual Camera signals */ + void VirtualCamEnabled(); + void VirtualCamStarted(); + void VirtualCamStopped(); + /* Studio Mode signal */ void PreviewProgramModeChanged(bool enabled); private: std::unique_ptr ui; + QPointer controlsDock; + public: /* `undo_s` needs to be declared after `ui` to prevent an uninitialized * warning for `ui` while initializing `undo_s`. */ From 3840ea0275ca27e3403830fe6eaf492e961c5726 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 21 Apr 2024 11:56:33 +0200 Subject: [PATCH 0181/1073] UI: Remove replayBufferButton and vcamButton theme IDs Those IDs were used on buttons that were programatically added to the controls dock, now those buttons are always present inside the UI file with their own object name. The use of theme IDs can be replaced by their object names. --- UI/data/themes/Yami.obt | 10 +++++----- UI/data/themes/Yami_Acri.ovt | 12 ++++++------ UI/forms/OBSBasicControls.ui | 6 ------ 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index f9bc6e07f088e9..51e85a62a88532 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -985,7 +985,7 @@ QDoubleSpinBox::down-arrow { #streamButton, #recordButton, -QPushButton[themeID="replayBufferButton"], +#replayBufferButton, #broadcastButton { padding: var(--padding_large); } @@ -1001,8 +1001,8 @@ QPushButton[themeID="replayBufferButton"], /* Primary Control Button Checked Coloring */ #streamButton:!hover:!pressed:checked, #recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, +#replayBufferButton:!hover:!pressed:checked, +#virtualCamButton:!hover:!pressed:checked, #modeSwitch:!hover:!pressed:checked, #broadcastButton:!hover:!pressed:checked { background: var(--primary); @@ -1011,8 +1011,8 @@ QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, /* Primary Control Button Hover Coloring */ #streamButton:hover:!pressed:checked, #recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, +#replayBufferButton:!pressed:checked, +#virtualCamButton:!pressed:checked, #modeSwitch:hover:!pressed:checked, #broadcastButton:hover:!pressed:checked { background: var(--primary_light); diff --git a/UI/data/themes/Yami_Acri.ovt b/UI/data/themes/Yami_Acri.ovt index d727a804fc7f55..8f87eaebece9ba 100644 --- a/UI/data/themes/Yami_Acri.ovt +++ b/UI/data/themes/Yami_Acri.ovt @@ -147,8 +147,8 @@ QTabBar QToolButton { /* Primary Control Button Checked Coloring */ #streamButton:!hover:!pressed:checked, #recordButton:!hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!hover:!pressed:checked, -QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, +#replayBufferButton:!hover:!pressed:checked, +#virtualCamButton:!hover:!pressed:checked, #modeSwitch:!hover:!pressed:checked, #broadcastButton:!hover:!pressed:checked { background: var(--button_bg_red); @@ -158,8 +158,8 @@ QPushButton[themeID="vcamButton"]:!hover:!pressed:checked, /* Primary Control Button Hover Coloring */ #streamButton:hover:!pressed:checked, #recordButton:hover:!pressed:checked, -QPushButton[themeID="replayBufferButton"]:!pressed:checked, -QPushButton[themeID="vcamButton"]:!pressed:checked, +#replayBufferButton:hover:!pressed:checked, +#virtualCamButton:hover:!pressed:checked, #modeSwitch:hover:!pressed:checked, #broadcastButton:hover:!pressed:checked { background: var(--button_bg_red_hover); @@ -168,8 +168,8 @@ QPushButton[themeID="vcamButton"]:!pressed:checked, /* Primary Control Button Checked + Pressed Coloring */ #streamButton:pressed:checked, #recordButton:pressed:checked, -QPushButton[themeID="replayBufferButton"]:pressed:checked, -QPushButton[themeID="vcamButton"]:pressed:checked, +#replayBufferButton:pressed:checked, +#virtualCamButton:pressed:checked, #modeSwitch:pressed:checked, #broadcastButton:pressed:checked { background: var(--button_bg_red_down); diff --git a/UI/forms/OBSBasicControls.ui b/UI/forms/OBSBasicControls.ui index c70df6142aa03f..1a4ab19103af59 100644 --- a/UI/forms/OBSBasicControls.ui +++ b/UI/forms/OBSBasicControls.ui @@ -207,9 +207,6 @@ true - - replayBufferButton -
@@ -276,9 +273,6 @@ true - - vcamButton - From a2785b7c4049fb660ebbf8083d98bc35b3e5e431 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Sun, 21 Apr 2024 15:57:37 -0400 Subject: [PATCH 0182/1073] UI: Add icons to Controls form buttons --- UI/forms/OBSBasicControls.ui | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/UI/forms/OBSBasicControls.ui b/UI/forms/OBSBasicControls.ui index 1a4ab19103af59..09ae9e500f8834 100644 --- a/UI/forms/OBSBasicControls.ui +++ b/UI/forms/OBSBasicControls.ui @@ -157,6 +157,10 @@ Basic.Main.PauseRecording + + + :/res/images/media-pause.svg:/res/images/media-pause.svg + true @@ -226,6 +230,10 @@ Basic.Main.SaveReplay + + + :/res/images/save.svg:/res/images/save.svg + replayIconSmall @@ -292,6 +300,10 @@ Basic.Main.VirtualCamConfig + + + :/settings/images/settings/general.svg:/settings/images/settings/general.svg + configIconSmall From 1d8c377240dbeb601c8cc3c22bdd1888f685dcb7 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Mon, 27 May 2024 20:32:37 +0200 Subject: [PATCH 0183/1073] cmake,UI: Fix SOVERSION on Linux --- UI/CMakeLists.txt | 7 +++++-- cmake/linux/helpers.cmake | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 035644ca0e4f72..9a77da80f7f7b0 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -115,8 +115,11 @@ endif() foreach(graphics_library IN ITEMS opengl metal d3d11) string(TOUPPER ${graphics_library} graphics_library_U) if(TARGET OBS::libobs-${graphics_library}) - target_compile_definitions(obs-studio - PRIVATE DL_${graphics_library_U}="$") + target_compile_definitions( + obs-studio + PRIVATE + DL_${graphics_library_U}="$<$,TARGET_FILE_NAME,TARGET_SONAME_FILE_NAME>:OBS::libobs-${graphics_library}>" + ) else() target_compile_definitions(obs-studio PRIVATE DL_${graphics_library_U}="") endif() diff --git a/cmake/linux/helpers.cmake b/cmake/linux/helpers.cmake index 01c3402c7b5c2f..7446f7f0e3afb2 100644 --- a/cmake/linux/helpers.cmake +++ b/cmake/linux/helpers.cmake @@ -55,8 +55,8 @@ function(set_target_properties_obs target) elseif(target_type STREQUAL SHARED_LIBRARY) set_target_properties( ${target} - PROPERTIES VERSION ${OBS_VERSION_MAJOR} - SOVERSION ${OBS_VERSION_CANONICAL} + PROPERTIES VERSION ${OBS_VERSION_CANONICAL} + SOVERSION ${OBS_VERSION_MAJOR} BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" INSTALL_RPATH "${OBS_LIBRARY_RPATH}") @@ -98,12 +98,12 @@ function(set_target_properties_obs target) elseif(target_type STREQUAL MODULE_LIBRARY) if(target STREQUAL obs-browser) - set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_VERSION_CANONICAL}) + set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_VERSION_MAJOR}) else() set_target_properties( ${target} PROPERTIES VERSION 0 - SOVERSION ${OBS_VERSION_CANONICAL} + SOVERSION ${OBS_VERSION_MAJOR} BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" INSTALL_RPATH "${OBS_MODULE_RPATH}") endif() From 116107bc86a0ba83aedcb10255bcb018c223fdf7 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 24 May 2024 10:05:26 +0200 Subject: [PATCH 0184/1073] UI: Fix Qt platform override in CMake 3 This override is still needed until Qt minimum version requirement is increased to at least 6.3.0. ui-config.h.in and CMake 2 are modified to avoid USE_XDG redefinition in CMake 3. --- UI/cmake/legacy.cmake | 22 ++++++++++++++-------- UI/cmake/os-linux.cmake | 2 +- UI/obs-app.cpp | 3 ++- UI/ui-config.h.in | 2 -- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index dc2800464ac60b..709e9c7ab485bb 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -504,16 +504,22 @@ elseif(OS_POSIX) target_compile_options(obs PRIVATE -Wno-unqualified-std-cast-call) endif() - if(OS_LINUX AND ENABLE_WHATSNEW) - find_package(MbedTLS) - find_package(nlohmann_json REQUIRED) - if(NOT MBEDTLS_FOUND) - obs_status(FATAL_ERROR "mbedTLS not found, but required for WhatsNew support on Linux") + if(OS_LINUX) + if(USE_XDG) + target_compile_definitions(obs PRIVATE USE_XDG) endif() - target_sources(obs PRIVATE update/crypto-helpers.hpp update/crypto-helpers-mbedtls.cpp update/shared-update.cpp - update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp) - target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2) + if(ENABLE_WHATSNEW) + find_package(MbedTLS) + find_package(nlohmann_json REQUIRED) + if(NOT MBEDTLS_FOUND) + obs_status(FATAL_ERROR "mbedTLS not found, but required for WhatsNew support on Linux") + endif() + + target_sources(obs PRIVATE update/crypto-helpers.hpp update/crypto-helpers-mbedtls.cpp update/shared-update.cpp + update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp) + target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2) + endif() endif() endif() diff --git a/UI/cmake/os-linux.cmake b/UI/cmake/os-linux.cmake index 0ffb441e4c7473..a8c08080f866db 100644 --- a/UI/cmake/os-linux.cmake +++ b/UI/cmake/os-linux.cmake @@ -1,5 +1,5 @@ target_sources(obs-studio PRIVATE platform-x11.cpp) -target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}") +target_compile_definitions(obs-studio PRIVATE USE_XDG OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}") target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate Qt::DBus) target_sources(obs-studio PRIVATE system-info-posix.cpp) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index c1ba6d6a314147..389f7df5258cc5 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1996,7 +1996,8 @@ static int run_program(fstream &logFile, int argc, char *argv[]) if (platform_theme && strcmp(platform_theme, "qt5ct") == 0) unsetenv("QT_QPA_PLATFORMTHEME"); -#if defined(ENABLE_WAYLAND) && defined(USE_XDG) +#if defined(ENABLE_WAYLAND) && defined(USE_XDG) && \ + QT_VERSION < QT_VERSION_CHECK(6, 3, 0) /* NOTE: Qt doesn't use the Wayland platform on GNOME, so we have to * force it using the QT_QPA_PLATFORM env var. It's still possible to * use other QPA platforms using this env var, or the -platform command diff --git a/UI/ui-config.h.in b/UI/ui-config.h.in index 25ec4699d6e07b..bbe00634749b1c 100644 --- a/UI/ui-config.h.in +++ b/UI/ui-config.h.in @@ -1,7 +1,5 @@ #pragma once -#cmakedefine USE_XDG - #define OAUTH_BASE_URL "@OAUTH_BASE_URL@" #define TWITCH_CLIENTID "@TWITCH_CLIENTID@" From 19b8557e80caeced8fd90359d211722f41618715 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 6 Jun 2024 00:43:43 +0200 Subject: [PATCH 0185/1073] CI: Ignore PVS-Studio's license close to expiry error code --- .github/actions/windows-analysis/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/windows-analysis/action.yaml b/.github/actions/windows-analysis/action.yaml index 1ac0ea8bc0b37f..112f4315820295 100644 --- a/.github/actions/windows-analysis/action.yaml +++ b/.github/actions/windows-analysis/action.yaml @@ -55,8 +55,8 @@ runs: ) & "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" @pvsParams - # Success and CodeErrorsFound are fine as error codes, we only care if it is anything but those - $pvs_result = $LASTEXITCODE -band (-bnot [PVSErrorCodes]::CodeErrorsFound) + # Success, LicenseExpiringSoon, and CodeErrorsFound are fine as error codes, we only care if it is anything but those + $pvs_result = $LASTEXITCODE -band (-bnot ([PVSErrorCodes]::CodeErrorsFound -bor [PVSErrorCodes]::LicenseExpiringSoon)) if ($pvs_result -ne 0) { Write-Output "PVS-Studio Errors: $([PVSErrorCodes]$pvs_result)" } From ffdf5416726bcd9974bc311c9f3a81918d79d1f8 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Thu, 6 Jun 2024 00:30:51 +0200 Subject: [PATCH 0186/1073] UI: Fix crash when entering settings We refer to VOD tracks as 1-6 but internally the array is 0-5 so there's an off-by-one error here. --- UI/window-basic-settings.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 82cd64dcccbb0b..02b9d1dc8778f3 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -6403,8 +6403,8 @@ void OBSBasicSettings::UpdateMultitrackVideo() vodTrackCheckbox->isChecked(); auto vod_track_idx_enabled = [&](size_t idx) { - return vod_track_enabled && vodTrack[idx] && - vodTrack[idx]->isChecked(); + return vod_track_enabled && vodTrack[idx - 1] && + vodTrack[idx - 1]->isChecked(); }; auto track1_warning_visible = mtv_enabled && From efd0e6d0bd9db32d07b6ee7cbf9c37c2931f6685 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 6 Jun 2024 00:07:56 +0200 Subject: [PATCH 0187/1073] libobs: Add buffered file serializer to legacy cmake --- libobs/cmake/legacy.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libobs/cmake/legacy.cmake b/libobs/cmake/legacy.cmake index d3f307a57e4d86..9ecaf7a843ac03 100644 --- a/libobs/cmake/legacy.cmake +++ b/libobs/cmake/legacy.cmake @@ -176,6 +176,8 @@ target_sources( util/bitstream.h util/bmem.c util/bmem.h + util/buffered-file-serializer.c + util/buffered-file-serializer.h util/c99defs.h util/cf-lexer.c util/cf-lexer.h From 97a20c478c37b0dc680a67cdd15e4106dbc6ae6d Mon Sep 17 00:00:00 2001 From: Exeldro Date: Thu, 22 Feb 2024 10:34:55 +0100 Subject: [PATCH 0188/1073] obs-filters: Do not load NVVFX on OpenGL --- plugins/obs-filters/obs-filters.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 44fc695ccb9225..7966f6788f9c33 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -84,7 +84,10 @@ bool obs_module_load(void) obs_register_source(&luma_key_filter); obs_register_source(&luma_key_filter_v2); #ifdef LIBNVVFX_ENABLED - if (load_nvvfx()) + obs_enter_graphics(); + const bool direct3d = gs_get_device_type() == GS_DEVICE_DIRECT3D_11; + obs_leave_graphics(); + if (direct3d && load_nvvfx()) obs_register_source(&nvidia_greenscreen_filter_info); #endif return true; From 4aa41ec854d09ef70283bc0fabbb39b45f020aae Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Fri, 12 Apr 2024 14:42:31 -0400 Subject: [PATCH 0189/1073] obs-webrtc: Add Link Header support WHIP/WHEP allows ICE Servers to be specified via Link Headers[0] [0] https://www.ietf.org/archive/id/draft-ietf-wish-whip-13.html#name-stun-turn-server-configurat Co-authored-by: Takeru Ohta --- plugins/obs-webrtc/whip-output.cpp | 93 +++++++++++++++++++++++++++++- plugins/obs-webrtc/whip-output.h | 2 + 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index 48639c667e12cb..935824ea98a664 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -224,8 +224,13 @@ bool WHIPOutput::Init() */ bool WHIPOutput::Setup() { + rtc::Configuration cfg; - peer_connection = std::make_shared(); +#if RTC_VERSION_MAJOR == 0 && RTC_VERSION_MINOR > 20 || RTC_VERSION_MAJOR > 1 + cfg.disableAutoGathering = true; +#endif + + peer_connection = std::make_shared(cfg); peer_connection->onStateChange([this](rtc::PeerConnection::State state) { switch (state) { @@ -283,6 +288,68 @@ bool WHIPOutput::Setup() return true; } +// Given a Link header extract URL/Username/Credential and create rtc::IceServer +// ; username="user"; credential="myPassword"; +// +// https://www.ietf.org/archive/id/draft-ietf-wish-whip-13.html#section-4.4 +void WHIPOutput::ParseLinkHeader(std::string val, + std::vector &iceServers) +{ + std::string url, username, password; + + auto extractUrl = [](std::string input) -> std::string { + auto head = input.find("<") + 1; + auto tail = input.find(">"); + + if (head == std::string::npos || tail == std::string::npos) { + return ""; + } + return input.substr(head, tail - head); + }; + + auto extractValue = [](std::string input) -> std::string { + auto head = input.find("\"") + 1; + auto tail = input.find_last_of("\""); + + if (head == std::string::npos || tail == std::string::npos) { + return ""; + } + return input.substr(head, tail - head); + }; + + while (true) { + std::string token = val; + auto pos = token.find(";"); + if (pos != std::string::npos) { + token = val.substr(0, pos); + } + + if (token.find(" iceServers; + while ((h = curl_easy_nextheader(c, CURLH_HEADER, 0, prev))) { + if (astrcmpi("Link", h->name) == 0) { + auto value = std::string(h->value); + // Parse multiple links separated by ',' + for (auto end = value.find(","); + end != std::string::npos; end = value.find(",")) { + this->ParseLinkHeader(value.substr(0, end), + iceServers); + value = value.substr(end + 1); + } + this->ParseLinkHeader(value, iceServers); + } + prev = h; + } + // If Location header doesn't start with `http` it is a relative URL. // Construct a absolute URL using the host of the effective URL if (last_location_header.find("http") != 0) { @@ -446,6 +532,11 @@ bool WHIPOutput::Connect() return false; } cleanup(); + +#if RTC_VERSION_MAJOR == 0 && RTC_VERSION_MINOR > 20 || RTC_VERSION_MAJOR > 1 + peer_connection->gatherLocalCandidates(iceServers); +#endif + return true; } diff --git a/plugins/obs-webrtc/whip-output.h b/plugins/obs-webrtc/whip-output.h index 0f9a66e9e4d313..88fd433eefc249 100644 --- a/plugins/obs-webrtc/whip-output.h +++ b/plugins/obs-webrtc/whip-output.h @@ -37,6 +37,8 @@ class WHIPOutput { void StartThread(); void SendDelete(); void StopThread(bool signal); + void ParseLinkHeader(std::string linkHeader, + std::vector &iceServers); void Send(void *data, uintptr_t size, uint64_t duration, std::shared_ptr track, From da8fc0d5166e81788cc47ffab0dde4d29b3fb790 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 5 Jun 2024 19:54:31 -0400 Subject: [PATCH 0190/1073] UI: Disable Multitrack Video by default --- UI/window-basic-main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 74e07e540af94b..a931db27a23a0c 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1706,7 +1706,7 @@ bool OBSBasic::InitBasicConfigDefaults() config_set_default_bool(basicConfig, "Stream1", "IgnoreRecommended", false); config_set_default_bool(basicConfig, "Stream1", "EnableMultitrackVideo", - true); + false); config_set_default_bool(basicConfig, "Stream1", "MultitrackVideoMaximumAggregateBitrateAuto", true); From d6b7f7ed4fca99094730d360a2732fb9de42112f Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 5 Jun 2024 20:14:14 -0400 Subject: [PATCH 0191/1073] UI: Remove GPU LUID from system info for Multitrack Video After further review, this is not needed. --- UI/models/multitrack-video.hpp | 4 +--- UI/system-info-windows.cpp | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index 2acccf87c953c8..66c6d9eeb48d11 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -146,13 +146,11 @@ struct Gpu { uint32_t device_id; uint64_t dedicated_video_memory; uint64_t shared_system_memory; - string luid; optional driver_version; NLOHMANN_DEFINE_TYPE_INTRUSIVE(Gpu, model, vendor_id, device_id, dedicated_video_memory, - shared_system_memory, luid, - driver_version) + shared_system_memory, driver_version) }; struct GamingFeatures { diff --git a/UI/system-info-windows.cpp b/UI/system-info-windows.cpp index 565e6d52189b56..557ff75d5ad2b4 100644 --- a/UI/system-info-windows.cpp +++ b/UI/system-info-windows.cpp @@ -23,7 +23,6 @@ static std::optional> system_gpu_data() std::vector adapter_info; - DStr luid_buffer; for (i = 0; factory->EnumAdapters1(i, adapter.Assign()) == S_OK; ++i) { DXGI_ADAPTER_DESC desc; char name[512] = ""; @@ -48,11 +47,6 @@ static std::optional> system_gpu_data() data.dedicated_video_memory = desc.DedicatedVideoMemory; data.shared_system_memory = desc.SharedSystemMemory; - dstr_printf(luid_buffer, "luid_0x%08X_0x%08X", - desc.AdapterLuid.HighPart, - desc.AdapterLuid.LowPart); - data.luid = luid_buffer->array; - /* driver version */ LARGE_INTEGER umd; hr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), From ae5c034accde7ed892420a4c392b75f94bb52ec8 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 6 Jun 2024 00:05:07 -0400 Subject: [PATCH 0192/1073] UI: Fix incompatible settings message when streaming Multitrack Video When streaming with Multitrack Video (Enhanced Broadcasting) and incompatible settings, the warning message was missing parts of the string due to incorrect translation string keys. Additionally, use Unicode arrows to direct users through UI elements. --- UI/multitrack-video-output.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 2acc64c0caabfb..855bfbe29d2caa 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -398,9 +398,9 @@ void MultitrackVideoOutput::PrepareStreaming( auto multitrack_video_name = QTStr("Basic.Settings.Stream.MultitrackVideoLabel"); if (obs_data_has_user_value(service_settings, - "ertmp_multitrack_video_name")) { + "multitrack_video_name")) { multitrack_video_name = obs_data_get_string( - service_settings, "ertmp_multitrack_video_name"); + service_settings, "multitrack_video_name"); } auto auto_config_url_data = auto_config_url.toUtf8(); @@ -637,7 +637,7 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings( QString(" %1. %2\n").arg(num).arg(QTStr(name)); where_to_disable += - QString(" %1. [%2 > %3 > %4]\n") + QString(" %1. [%2 → %3 → %4]\n") .arg(num) .arg(QTStr("Settings")) .arg(QTStr("Basic.Settings.Advanced")) @@ -667,12 +667,11 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings( QMessageBox mb(parent); mb.setIcon(QMessageBox::Critical); mb.setWindowTitle(QTStr("MultitrackVideo.IncompatibleSettings.Title")); - mb.setText( - QString(QTStr("MultitrackVideo.IncompatibleSettings.Text")) - .arg(obs_data_get_string(service_settings, - "ertmp_multitrack_video_name")) - .arg(incompatible_settings) - .arg(where_to_disable)); + mb.setText(QString(QTStr("MultitrackVideo.IncompatibleSettings.Text")) + .arg(obs_data_get_string(service_settings, + "multitrack_video_name")) + .arg(incompatible_settings) + .arg(where_to_disable)); auto this_stream = mb.addButton( QTStr("MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming"), QMessageBox::AcceptRole); From deee0e331ff03e57012cfb6b1da4020c1a38c6f8 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 6 Jun 2024 00:27:06 -0400 Subject: [PATCH 0193/1073] UI: Remove redundant assignments Detected by PVS-Studio. --- UI/multitrack-video-output.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 855bfbe29d2caa..320070c9a6ba8c 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -700,9 +700,6 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings( useDelay = false; enableNewSocketLoop = false; enableDynBitrate = false; - useDelay = false; - enableNewSocketLoop = false; - enableDynBitrate = false; if (mb.clickedButton() == all_streams) { config_set_bool(config, "Output", "DelayEnable", false); From e4ae1492781ea5b7433e5b75ef904e0b79c0133a Mon Sep 17 00:00:00 2001 From: tytan652 Date: Mon, 27 May 2024 20:59:00 +0200 Subject: [PATCH 0194/1073] linux-capture: Fix module prefix with CMake 3 --- plugins/linux-capture/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/linux-capture/CMakeLists.txt b/plugins/linux-capture/CMakeLists.txt index 2f4c039996fceb..04c74a938ad99d 100644 --- a/plugins/linux-capture/CMakeLists.txt +++ b/plugins/linux-capture/CMakeLists.txt @@ -41,5 +41,5 @@ target_link_libraries( xcb::xcb-composite) # cmake-format: off -set_target_properties_obs(linux-capture PROPERTIES FOLDER "plugins") +set_target_properties_obs(linux-capture PROPERTIES FOLDER plugins PREFIX "") # cmake-format: on From 09afd185aa5cd8f6d129b592fac24f45dd54cd5f Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Jan 2024 13:32:32 +0100 Subject: [PATCH 0195/1073] rtmp-services: Add Amazon IVS service --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 46f32ec173a1e7..f05a13c2a13191 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 254, + "version": 255, "files": [ { "name": "services.json", - "version": 254 + "version": 255 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index a2e7e738454226..5c5a717bb5c2f5 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2903,6 +2903,25 @@ "x264opts": "scenecut=0", "output": "rtmp_output" } + }, + { + "name": "Amazon IVS", + "supported video codecs": ["h264"], + "servers": [ + { + "name": "Global (RTMPS, recommended)", + "url": "rtmps://ingest.global-contribute.live-video.net:443/app" + }, + { + "name": "Global (RTMP)", + "url": "rtmp://ingest.global-contribute.live-video.net/app" + } + ], + "multitrack_video_configuration_url": "https://ingest.contribute.live-video.net/api/v3/GetClientConfiguration", + "recommended": { + "keyint": 2, + "x264opts": "scenecut=0" + } } ] } From 5d05d7084e82baff188f0a77f70652ac45c0e4e8 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 6 Jun 2024 15:21:25 -0400 Subject: [PATCH 0196/1073] obs-browser: Update version to 2.23.5 b4f724a - cmake: Set minimum CEF version to 95 --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index 996b5a7bc43d91..0f4f6c654e5803 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 996b5a7bc43d912f1f4992e0032d4f263ac8b060 +Subproject commit 0f4f6c654e5803445dfd1266d8ce72c893acdf26 From f9d86aeac8d04944d75b6acea4f05809ce782313 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 6 Jun 2024 15:50:06 +0200 Subject: [PATCH 0197/1073] UI: Enable custom server entry for Amazon IVS --- UI/window-basic-settings-stream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 8a5f41c00a1de0..881e1d54247f53 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -802,7 +802,7 @@ void OBSBasicSettings::UpdateServerList() ui->server->addItem(name, server); } - if (serviceName == "Twitch") { + if (serviceName == "Twitch" || serviceName == "Amazon IVS") { ui->server->addItem( QTStr("Basic.Settings.Stream.SpecifyCustomServer"), CustomServerUUID()); From 8999d9fef9d5d166f1ef2c4302b9840b7973a26e Mon Sep 17 00:00:00 2001 From: Translation Updater <> Date: Fri, 7 Jun 2024 09:47:58 +0000 Subject: [PATCH 0198/1073] Update translations from Crowdin --- AUTHORS | 163 ++++++--- UI/data/locale/af-ZA.ini | 20 +- UI/data/locale/an-ES.ini | 1 - UI/data/locale/ar-SA.ini | 3 +- UI/data/locale/az-AZ.ini | 43 +++ UI/data/locale/ba-RU.ini | 1 - UI/data/locale/be-BY.ini | 168 ++++++--- UI/data/locale/bg-BG.ini | 19 +- UI/data/locale/bn-BD.ini | 1 - UI/data/locale/ca-ES.ini | 45 ++- UI/data/locale/cs-CZ.ini | 10 +- UI/data/locale/da-DK.ini | 1 - UI/data/locale/de-DE.ini | 142 +++++--- UI/data/locale/el-GR.ini | 1 - UI/data/locale/eo-UY.ini | 1 - UI/data/locale/es-ES.ini | 47 ++- UI/data/locale/et-EE.ini | 1 - UI/data/locale/eu-ES.ini | 8 +- UI/data/locale/fa-IR.ini | 6 +- UI/data/locale/fi-FI.ini | 7 +- UI/data/locale/fil-PH.ini | 1 - UI/data/locale/fr-FR.ini | 33 +- UI/data/locale/gd-GB.ini | 1 - UI/data/locale/gl-ES.ini | 1 - UI/data/locale/he-IL.ini | 27 +- UI/data/locale/hi-IN.ini | 6 +- UI/data/locale/hr-HR.ini | 1 - UI/data/locale/hu-HU.ini | 30 +- UI/data/locale/hy-AM.ini | 1 - UI/data/locale/id-ID.ini | 50 ++- UI/data/locale/it-IT.ini | 38 +- UI/data/locale/ja-JP.ini | 55 ++- UI/data/locale/ka-GE.ini | 1 - UI/data/locale/kab-KAB.ini | 1 - UI/data/locale/kmr-TR.ini | 1 - UI/data/locale/ko-KR.ini | 8 +- UI/data/locale/lt-LT.ini | 2 +- UI/data/locale/lv-LV.ini | 33 +- UI/data/locale/mn-MN.ini | 1 - UI/data/locale/ms-MY.ini | 13 +- UI/data/locale/nb-NO.ini | 28 +- UI/data/locale/nl-NL.ini | 30 +- UI/data/locale/nn-NO.ini | 1 - UI/data/locale/oc-FR.ini | 1 - UI/data/locale/pl-PL.ini | 54 ++- UI/data/locale/pt-BR.ini | 50 +-- UI/data/locale/pt-PT.ini | 60 +-- UI/data/locale/ro-RO.ini | 12 +- UI/data/locale/ru-RU.ini | 31 +- UI/data/locale/si-LK.ini | 1 - UI/data/locale/sk-SK.ini | 6 +- UI/data/locale/sl-SI.ini | 6 +- UI/data/locale/sr-CS.ini | 1 - UI/data/locale/sr-SP.ini | 1 - UI/data/locale/sv-SE.ini | 56 ++- UI/data/locale/szl-PL.ini | 1 - UI/data/locale/ta-IN.ini | 1 - UI/data/locale/th-TH.ini | 19 +- UI/data/locale/tl-PH.ini | 1 - UI/data/locale/tr-TR.ini | 56 +-- UI/data/locale/tt-RU.ini | 343 ++++++++++++++++++ UI/data/locale/ug-CN.ini | 27 +- UI/data/locale/uk-UA.ini | 9 +- UI/data/locale/vi-VN.ini | 70 +++- UI/data/locale/zh-CN.ini | 50 ++- UI/data/locale/zh-TW.ini | 8 +- .../aja-output-ui/data/locale/be-BY.ini | 2 +- .../aja-output-ui/data/locale/bg-BG.ini | 6 + .../aja-output-ui/data/locale/hr-HR.ini | 5 + .../aja-output-ui/data/locale/tt-RU.ini | 1 + .../frontend-tools/data/locale/it-IT.ini | 2 +- .../frontend-tools/data/locale/th-TH.ini | 26 ++ .../frontend-tools/data/locale/tt-RU.ini | 11 + plugins/aja/data/locale/tt-RU.ini | 8 + .../coreaudio-encoder/data/locale/tt-RU.ini | 1 + plugins/decklink/data/locale/tt-RU.ini | 7 + plugins/image-source/data/locale/it-IT.ini | 9 +- plugins/image-source/data/locale/ru-RU.ini | 2 +- plugins/image-source/data/locale/tt-RU.ini | 9 + plugins/linux-alsa/data/locale/th-TH.ini | 2 + plugins/linux-alsa/data/locale/tt-RU.ini | 1 + plugins/linux-capture/data/locale/af-ZA.ini | 2 - plugins/linux-capture/data/locale/ar-SA.ini | 2 - plugins/linux-capture/data/locale/az-AZ.ini | 2 - plugins/linux-capture/data/locale/ba-RU.ini | 1 - plugins/linux-capture/data/locale/be-BY.ini | 8 +- plugins/linux-capture/data/locale/bg-BG.ini | 2 - plugins/linux-capture/data/locale/bn-BD.ini | 2 - plugins/linux-capture/data/locale/ca-ES.ini | 4 +- plugins/linux-capture/data/locale/cs-CZ.ini | 4 +- plugins/linux-capture/data/locale/da-DK.ini | 2 - plugins/linux-capture/data/locale/de-DE.ini | 4 +- plugins/linux-capture/data/locale/el-GR.ini | 2 - plugins/linux-capture/data/locale/es-ES.ini | 4 +- plugins/linux-capture/data/locale/et-EE.ini | 4 +- plugins/linux-capture/data/locale/eu-ES.ini | 2 - plugins/linux-capture/data/locale/fa-IR.ini | 4 +- plugins/linux-capture/data/locale/fi-FI.ini | 4 +- plugins/linux-capture/data/locale/fr-FR.ini | 4 +- plugins/linux-capture/data/locale/gd-GB.ini | 2 - plugins/linux-capture/data/locale/gl-ES.ini | 2 - plugins/linux-capture/data/locale/he-IL.ini | 4 +- plugins/linux-capture/data/locale/hi-IN.ini | 2 - plugins/linux-capture/data/locale/hr-HR.ini | 2 - plugins/linux-capture/data/locale/hu-HU.ini | 4 +- plugins/linux-capture/data/locale/hy-AM.ini | 2 - plugins/linux-capture/data/locale/id-ID.ini | 4 +- plugins/linux-capture/data/locale/it-IT.ini | 4 +- plugins/linux-capture/data/locale/ja-JP.ini | 4 +- plugins/linux-capture/data/locale/ka-GE.ini | 2 - plugins/linux-capture/data/locale/kab-KAB.ini | 2 - plugins/linux-capture/data/locale/kmr-TR.ini | 2 - plugins/linux-capture/data/locale/ko-KR.ini | 4 +- plugins/linux-capture/data/locale/lo-LA.ini | 1 - plugins/linux-capture/data/locale/ms-MY.ini | 3 +- plugins/linux-capture/data/locale/nb-NO.ini | 2 - plugins/linux-capture/data/locale/nl-NL.ini | 4 +- plugins/linux-capture/data/locale/nn-NO.ini | 1 - plugins/linux-capture/data/locale/pl-PL.ini | 4 +- plugins/linux-capture/data/locale/pt-BR.ini | 6 +- plugins/linux-capture/data/locale/pt-PT.ini | 4 +- plugins/linux-capture/data/locale/ro-RO.ini | 2 - plugins/linux-capture/data/locale/ru-RU.ini | 4 +- plugins/linux-capture/data/locale/si-LK.ini | 2 - plugins/linux-capture/data/locale/sk-SK.ini | 4 +- plugins/linux-capture/data/locale/sl-SI.ini | 4 +- plugins/linux-capture/data/locale/sr-CS.ini | 2 - plugins/linux-capture/data/locale/sr-SP.ini | 2 - plugins/linux-capture/data/locale/sv-SE.ini | 4 +- plugins/linux-capture/data/locale/szl-PL.ini | 2 - plugins/linux-capture/data/locale/ta-IN.ini | 1 - plugins/linux-capture/data/locale/tl-PH.ini | 2 - plugins/linux-capture/data/locale/tr-TR.ini | 4 +- plugins/linux-capture/data/locale/tt-RU.ini | 2 + plugins/linux-capture/data/locale/ug-CN.ini | 2 - plugins/linux-capture/data/locale/uk-UA.ini | 4 +- plugins/linux-capture/data/locale/vi-VN.ini | 4 +- plugins/linux-capture/data/locale/zh-CN.ini | 4 +- plugins/linux-capture/data/locale/zh-TW.ini | 4 +- plugins/linux-jack/data/locale/th-TH.ini | 2 + plugins/linux-jack/data/locale/tt-RU.ini | 1 + plugins/linux-pipewire/data/locale/ar-SA.ini | 1 + plugins/linux-pipewire/data/locale/be-BY.ini | 12 + plugins/linux-pipewire/data/locale/ca-ES.ini | 1 + plugins/linux-pipewire/data/locale/cs-CZ.ini | 1 + plugins/linux-pipewire/data/locale/de-DE.ini | 1 + plugins/linux-pipewire/data/locale/es-ES.ini | 1 + plugins/linux-pipewire/data/locale/fa-IR.ini | 1 + plugins/linux-pipewire/data/locale/fi-FI.ini | 1 + plugins/linux-pipewire/data/locale/fr-FR.ini | 1 + plugins/linux-pipewire/data/locale/he-IL.ini | 1 + plugins/linux-pipewire/data/locale/hu-HU.ini | 1 + plugins/linux-pipewire/data/locale/id-ID.ini | 1 + plugins/linux-pipewire/data/locale/it-IT.ini | 1 + plugins/linux-pipewire/data/locale/ja-JP.ini | 1 + plugins/linux-pipewire/data/locale/ko-KR.ini | 1 + plugins/linux-pipewire/data/locale/ms-MY.ini | 7 + plugins/linux-pipewire/data/locale/nl-NL.ini | 1 + plugins/linux-pipewire/data/locale/pl-PL.ini | 1 + plugins/linux-pipewire/data/locale/pt-BR.ini | 1 + plugins/linux-pipewire/data/locale/pt-PT.ini | 1 + plugins/linux-pipewire/data/locale/ru-RU.ini | 1 + plugins/linux-pipewire/data/locale/sk-SK.ini | 1 + plugins/linux-pipewire/data/locale/sv-SE.ini | 1 + plugins/linux-pipewire/data/locale/tr-TR.ini | 1 + plugins/linux-pipewire/data/locale/tt-RU.ini | 5 + plugins/linux-pipewire/data/locale/uk-UA.ini | 1 + plugins/linux-pipewire/data/locale/vi-VN.ini | 1 + plugins/linux-pipewire/data/locale/zh-CN.ini | 1 + plugins/linux-pipewire/data/locale/zh-TW.ini | 1 + .../linux-pulseaudio/data/locale/eu-ES.ini | 1 + .../linux-pulseaudio/data/locale/tt-RU.ini | 1 + plugins/linux-v4l2/data/locale/hu-HU.ini | 2 +- plugins/linux-v4l2/data/locale/tt-RU.ini | 6 + plugins/mac-avcapture/data/locale/ar-SA.ini | 2 + plugins/mac-avcapture/data/locale/be-BY.ini | 16 + plugins/mac-avcapture/data/locale/eu-ES.ini | 3 + plugins/mac-avcapture/data/locale/fi-FI.ini | 4 +- plugins/mac-avcapture/data/locale/hi-IN.ini | 1 + plugins/mac-avcapture/data/locale/it-IT.ini | 2 +- plugins/mac-avcapture/data/locale/ms-MY.ini | 2 + plugins/mac-avcapture/data/locale/pt-BR.ini | 3 +- plugins/mac-avcapture/data/locale/ru-RU.ini | 2 +- plugins/mac-avcapture/data/locale/tt-RU.ini | 8 + plugins/mac-capture/data/locale/ar-SA.ini | 1 + plugins/mac-capture/data/locale/be-BY.ini | 39 ++ plugins/mac-capture/data/locale/de-DE.ini | 4 +- plugins/mac-capture/data/locale/eu-ES.ini | 8 + plugins/mac-capture/data/locale/fi-FI.ini | 1 + plugins/mac-capture/data/locale/ms-MY.ini | 6 + plugins/mac-capture/data/locale/pt-BR.ini | 2 +- plugins/mac-capture/data/locale/pt-PT.ini | 4 +- plugins/mac-capture/data/locale/ru-RU.ini | 2 +- plugins/mac-capture/data/locale/tt-RU.ini | 16 + plugins/mac-syphon/data/locale/tt-RU.ini | 1 + .../mac-videotoolbox/data/locale/be-BY.ini | 19 + .../mac-videotoolbox/data/locale/de-DE.ini | 18 +- .../mac-videotoolbox/data/locale/it-IT.ini | 4 +- .../mac-videotoolbox/data/locale/pt-PT.ini | 2 +- .../mac-videotoolbox/data/locale/tt-RU.ini | 4 + .../src/obs-plugin/data/locale/ar-SA.ini | 2 + .../src/obs-plugin/data/locale/be-BY.ini | 10 +- .../src/obs-plugin/data/locale/de-DE.ini | 2 +- plugins/obs-browser | 2 +- plugins/obs-ffmpeg/data/locale/de-DE.ini | 22 +- plugins/obs-ffmpeg/data/locale/it-IT.ini | 20 +- plugins/obs-ffmpeg/data/locale/kmr-TR.ini | 2 +- plugins/obs-ffmpeg/data/locale/pt-PT.ini | 6 +- plugins/obs-ffmpeg/data/locale/tt-RU.ini | 24 ++ plugins/obs-ffmpeg/data/locale/ug-CN.ini | 5 + plugins/obs-filters/data/locale/be-BY.ini | 2 +- plugins/obs-filters/data/locale/de-DE.ini | 4 +- plugins/obs-filters/data/locale/ms-MY.ini | 2 + plugins/obs-filters/data/locale/pt-BR.ini | 75 ++-- plugins/obs-filters/data/locale/pt-PT.ini | 6 +- plugins/obs-filters/data/locale/ru-RU.ini | 2 +- plugins/obs-filters/data/locale/tt-RU.ini | 20 + plugins/obs-libfdk/data/locale/tt-RU.ini | 1 + plugins/obs-outputs/data/locale/be-BY.ini | 4 + plugins/obs-outputs/data/locale/ca-ES.ini | 4 + plugins/obs-outputs/data/locale/cs-CZ.ini | 4 + plugins/obs-outputs/data/locale/de-DE.ini | 3 + plugins/obs-outputs/data/locale/es-ES.ini | 4 + plugins/obs-outputs/data/locale/fi-FI.ini | 4 + plugins/obs-outputs/data/locale/he-IL.ini | 4 + plugins/obs-outputs/data/locale/hi-IN.ini | 2 +- plugins/obs-outputs/data/locale/hu-HU.ini | 4 + plugins/obs-outputs/data/locale/id-ID.ini | 4 + plugins/obs-outputs/data/locale/it-IT.ini | 4 + plugins/obs-outputs/data/locale/ja-JP.ini | 4 + plugins/obs-outputs/data/locale/ko-KR.ini | 4 + plugins/obs-outputs/data/locale/nl-NL.ini | 3 + plugins/obs-outputs/data/locale/pl-PL.ini | 4 + plugins/obs-outputs/data/locale/pt-BR.ini | 10 +- plugins/obs-outputs/data/locale/pt-PT.ini | 4 + plugins/obs-outputs/data/locale/ru-RU.ini | 4 + plugins/obs-outputs/data/locale/sv-SE.ini | 3 + plugins/obs-outputs/data/locale/th-TH.ini | 5 + plugins/obs-outputs/data/locale/tt-RU.ini | 7 + plugins/obs-outputs/data/locale/uk-UA.ini | 4 + plugins/obs-outputs/data/locale/vi-VN.ini | 4 + plugins/obs-outputs/data/locale/zh-CN.ini | 4 + plugins/obs-outputs/data/locale/zh-TW.ini | 4 + plugins/obs-qsv11/data/locale/af-ZA.ini | 1 - plugins/obs-qsv11/data/locale/ar-SA.ini | 2 +- plugins/obs-qsv11/data/locale/be-BY.ini | 1 - plugins/obs-qsv11/data/locale/bn-BD.ini | 1 - plugins/obs-qsv11/data/locale/ca-ES.ini | 1 - plugins/obs-qsv11/data/locale/cs-CZ.ini | 1 - plugins/obs-qsv11/data/locale/da-DK.ini | 1 - plugins/obs-qsv11/data/locale/de-DE.ini | 5 +- plugins/obs-qsv11/data/locale/el-GR.ini | 1 - plugins/obs-qsv11/data/locale/es-ES.ini | 1 - plugins/obs-qsv11/data/locale/et-EE.ini | 1 - plugins/obs-qsv11/data/locale/eu-ES.ini | 1 - plugins/obs-qsv11/data/locale/fa-IR.ini | 1 - plugins/obs-qsv11/data/locale/fi-FI.ini | 1 - plugins/obs-qsv11/data/locale/fil-PH.ini | 1 - plugins/obs-qsv11/data/locale/fr-FR.ini | 1 - plugins/obs-qsv11/data/locale/gd-GB.ini | 1 - plugins/obs-qsv11/data/locale/gl-ES.ini | 1 - plugins/obs-qsv11/data/locale/he-IL.ini | 1 - plugins/obs-qsv11/data/locale/hi-IN.ini | 1 - plugins/obs-qsv11/data/locale/hu-HU.ini | 1 - plugins/obs-qsv11/data/locale/hy-AM.ini | 1 - plugins/obs-qsv11/data/locale/id-ID.ini | 1 - plugins/obs-qsv11/data/locale/it-IT.ini | 7 +- plugins/obs-qsv11/data/locale/ja-JP.ini | 1 - plugins/obs-qsv11/data/locale/ka-GE.ini | 1 - plugins/obs-qsv11/data/locale/kmr-TR.ini | 1 - plugins/obs-qsv11/data/locale/ko-KR.ini | 1 - plugins/obs-qsv11/data/locale/ms-MY.ini | 1 - plugins/obs-qsv11/data/locale/nb-NO.ini | 1 - plugins/obs-qsv11/data/locale/nl-NL.ini | 1 - plugins/obs-qsv11/data/locale/pl-PL.ini | 1 - plugins/obs-qsv11/data/locale/pt-BR.ini | 1 - plugins/obs-qsv11/data/locale/pt-PT.ini | 3 +- plugins/obs-qsv11/data/locale/ro-RO.ini | 1 - plugins/obs-qsv11/data/locale/ru-RU.ini | 15 +- plugins/obs-qsv11/data/locale/sk-SK.ini | 1 - plugins/obs-qsv11/data/locale/sl-SI.ini | 1 - plugins/obs-qsv11/data/locale/sv-SE.ini | 1 - plugins/obs-qsv11/data/locale/tr-TR.ini | 1 - plugins/obs-qsv11/data/locale/tt-RU.ini | 3 + plugins/obs-qsv11/data/locale/uk-UA.ini | 1 - plugins/obs-qsv11/data/locale/vi-VN.ini | 1 - plugins/obs-qsv11/data/locale/zh-CN.ini | 1 - plugins/obs-qsv11/data/locale/zh-TW.ini | 1 - plugins/obs-text/data/locale/pt-BR.ini | 8 +- plugins/obs-text/data/locale/tt-RU.ini | 15 + plugins/obs-transitions/data/locale/de-DE.ini | 4 +- plugins/obs-transitions/data/locale/hr-HR.ini | 45 ++- plugins/obs-transitions/data/locale/it-IT.ini | 2 +- plugins/obs-transitions/data/locale/sk-SK.ini | 6 +- plugins/obs-transitions/data/locale/tt-RU.ini | 9 + plugins/obs-webrtc/data/locale/ar-SA.ini | 5 + plugins/obs-webrtc/data/locale/de-DE.ini | 1 + plugins/obs-webrtc/data/locale/fa-IR.ini | 2 + plugins/obs-webrtc/data/locale/fi-FI.ini | 2 + plugins/obs-webrtc/data/locale/hi-IN.ini | 1 + plugins/obs-webrtc/data/locale/ka-GE.ini | 1 + plugins/obs-webrtc/data/locale/ms-MY.ini | 2 + plugins/obs-webrtc/data/locale/pt-BR.ini | 2 + plugins/obs-webrtc/data/locale/tr-TR.ini | 2 + plugins/obs-x264/data/locale/ko-KR.ini | 2 +- plugins/obs-x264/data/locale/pt-PT.ini | 2 +- plugins/obs-x264/data/locale/tt-RU.ini | 3 + plugins/rtmp-services/data/locale/be-BY.ini | 2 + plugins/rtmp-services/data/locale/ca-ES.ini | 2 + plugins/rtmp-services/data/locale/cs-CZ.ini | 2 + plugins/rtmp-services/data/locale/de-DE.ini | 2 + plugins/rtmp-services/data/locale/en-GB.ini | 2 +- plugins/rtmp-services/data/locale/es-ES.ini | 2 + plugins/rtmp-services/data/locale/fa-IR.ini | 2 + plugins/rtmp-services/data/locale/fi-FI.ini | 2 + plugins/rtmp-services/data/locale/fr-FR.ini | 2 + plugins/rtmp-services/data/locale/he-IL.ini | 2 + plugins/rtmp-services/data/locale/hu-HU.ini | 2 + plugins/rtmp-services/data/locale/id-ID.ini | 2 + plugins/rtmp-services/data/locale/it-IT.ini | 2 + plugins/rtmp-services/data/locale/ja-JP.ini | 2 + plugins/rtmp-services/data/locale/ko-KR.ini | 2 + plugins/rtmp-services/data/locale/ms-MY.ini | 2 + plugins/rtmp-services/data/locale/nl-NL.ini | 2 + plugins/rtmp-services/data/locale/pl-PL.ini | 2 + plugins/rtmp-services/data/locale/pt-BR.ini | 2 + plugins/rtmp-services/data/locale/pt-PT.ini | 4 +- plugins/rtmp-services/data/locale/ru-RU.ini | 2 + plugins/rtmp-services/data/locale/sk-SK.ini | 2 + plugins/rtmp-services/data/locale/sl-SI.ini | 2 + plugins/rtmp-services/data/locale/sv-SE.ini | 2 + plugins/rtmp-services/data/locale/tr-TR.ini | 2 + plugins/rtmp-services/data/locale/tt-RU.ini | 6 + plugins/rtmp-services/data/locale/uk-UA.ini | 2 + plugins/rtmp-services/data/locale/vi-VN.ini | 2 + plugins/rtmp-services/data/locale/zh-CN.ini | 2 + plugins/rtmp-services/data/locale/zh-TW.ini | 2 + plugins/sndio/data/locale/tt-RU.ini | 1 + plugins/text-freetype2/data/locale/it-IT.ini | 1 + plugins/text-freetype2/data/locale/tt-RU.ini | 9 + plugins/vlc-video/data/locale/tt-RU.ini | 6 + plugins/win-capture/data/locale/ar-SA.ini | 1 + plugins/win-capture/data/locale/be-BY.ini | 2 +- plugins/win-capture/data/locale/de-DE.ini | 10 +- plugins/win-capture/data/locale/hi-IN.ini | 1 + plugins/win-capture/data/locale/id-ID.ini | 2 +- plugins/win-capture/data/locale/it-IT.ini | 2 +- plugins/win-capture/data/locale/ja-JP.ini | 2 +- plugins/win-capture/data/locale/ms-MY.ini | 3 + plugins/win-capture/data/locale/pt-PT.ini | 8 +- plugins/win-capture/data/locale/sv-SE.ini | 2 +- plugins/win-capture/data/locale/tt-RU.ini | 7 + plugins/win-dshow/data/locale/de-DE.ini | 6 +- plugins/win-dshow/data/locale/ja-JP.ini | 4 +- plugins/win-dshow/data/locale/pt-PT.ini | 2 +- plugins/win-dshow/data/locale/tt-RU.ini | 12 + plugins/win-dshow/data/locale/ug-CN.ini | 2 +- plugins/win-wasapi/data/locale/ms-MY.ini | 1 + plugins/win-wasapi/data/locale/tt-RU.ini | 4 + 359 files changed, 2357 insertions(+), 716 deletions(-) create mode 100644 UI/data/locale/tt-RU.ini create mode 100644 UI/frontend-plugins/aja-output-ui/data/locale/bg-BG.ini create mode 100644 UI/frontend-plugins/aja-output-ui/data/locale/tt-RU.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/tt-RU.ini create mode 100644 plugins/aja/data/locale/tt-RU.ini create mode 100644 plugins/coreaudio-encoder/data/locale/tt-RU.ini create mode 100644 plugins/decklink/data/locale/tt-RU.ini create mode 100644 plugins/image-source/data/locale/tt-RU.ini create mode 100644 plugins/linux-alsa/data/locale/tt-RU.ini create mode 100644 plugins/linux-capture/data/locale/tt-RU.ini create mode 100644 plugins/linux-jack/data/locale/th-TH.ini create mode 100644 plugins/linux-jack/data/locale/tt-RU.ini create mode 100644 plugins/linux-pipewire/data/locale/be-BY.ini create mode 100644 plugins/linux-pipewire/data/locale/tt-RU.ini create mode 100644 plugins/linux-pulseaudio/data/locale/tt-RU.ini create mode 100644 plugins/linux-v4l2/data/locale/tt-RU.ini create mode 100644 plugins/mac-avcapture/data/locale/tt-RU.ini create mode 100644 plugins/mac-capture/data/locale/be-BY.ini create mode 100644 plugins/mac-capture/data/locale/tt-RU.ini create mode 100644 plugins/mac-syphon/data/locale/tt-RU.ini create mode 100644 plugins/mac-videotoolbox/data/locale/be-BY.ini create mode 100644 plugins/mac-videotoolbox/data/locale/tt-RU.ini create mode 100644 plugins/obs-ffmpeg/data/locale/tt-RU.ini create mode 100644 plugins/obs-filters/data/locale/tt-RU.ini create mode 100644 plugins/obs-libfdk/data/locale/tt-RU.ini create mode 100644 plugins/obs-outputs/data/locale/tt-RU.ini create mode 100644 plugins/obs-qsv11/data/locale/tt-RU.ini create mode 100644 plugins/obs-text/data/locale/tt-RU.ini create mode 100644 plugins/obs-transitions/data/locale/tt-RU.ini create mode 100644 plugins/obs-webrtc/data/locale/ar-SA.ini create mode 100644 plugins/obs-x264/data/locale/tt-RU.ini create mode 100644 plugins/rtmp-services/data/locale/tt-RU.ini create mode 100644 plugins/sndio/data/locale/tt-RU.ini create mode 100644 plugins/text-freetype2/data/locale/tt-RU.ini create mode 100644 plugins/vlc-video/data/locale/tt-RU.ini create mode 100644 plugins/win-capture/data/locale/tt-RU.ini create mode 100644 plugins/win-dshow/data/locale/tt-RU.ini create mode 100644 plugins/win-wasapi/data/locale/tt-RU.ini diff --git a/AUTHORS b/AUTHORS index 78d009be945a4e..42331da4a9c99b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,10 +7,10 @@ Contributors: jpark37 PatTheMav R1CH - Palana cg2121 - Sebastian Beckmann + Palana derrod + Sebastian Beckmann Ryan Foster tytan652 WizardCM @@ -20,30 +20,31 @@ Contributors: Georges Basile Stavracas Neto Kurt Kartaltepe Rodney - Gol-D-Ace pkv + Gol-D-Ace BtbN - Shaolin tt2468 + Shaolin kc5nra Exeldro Michael Fabian 'Xaymar' Dirks + Warchamp7 VodBox Zachary Lund - Warchamp7 SuslikV Paul Hindt + Ruwen Hahn Vainock CodeYan01 Reboot Martell Malone Ford Smith columbarius + Penwywern dodgepong 田七不甜 Ed Maste HomeWorld - Penwywern Joel Bethke Alex Anderson SCG82 @@ -52,21 +53,21 @@ Contributors: Tommy Vercetti shiina424 Roman Huts + Gale + Service Checker Yuriy Chumak juvester mvji wangshaohui - Gale - Service Checker + Florian Zwoch + jcm JohannMG craftwar sorayuki Dead133 Eric Lindvall - Florian Zwoch Jimi Huotari Maya Venkatraman - jcm Radzaquiel Scratch Socapex @@ -80,15 +81,17 @@ Contributors: bin Chris Developer-Ecosystem-Engineering + Sean DuBois Stéphane Lepin Chip Bradford Ilya Melamed + John Bradley Marvin Scholz Rat - Ruwen Hahn Skyler Lipthay Wim Taymans Arkkis + David Rosca GoaLitiuM Hunter L. Allen Jan Beich @@ -96,7 +99,6 @@ Contributors: Manuel Kroeber Michel Quinn Damerell - Sean DuBois Theodore Dubois Anthony Torres Anton Bershanskiy @@ -105,7 +107,6 @@ Contributors: Chris (Flaeri) Chris Angelico Danni - David Rosca Doug Kelly James Hurley Jess Mayo @@ -115,7 +116,6 @@ Contributors: Tristan Matthews Alexandre Vicenzi Carl Fürstenberg - John Bradley Jonathan Bennett Kilian von Pflugk Neal Gompa @@ -125,6 +125,7 @@ Contributors: Wahaj Dar prgmitchell Aaron Boxer + Aleks Todorov Anry Bilal Elmoussaoui CoDEmanX @@ -154,6 +155,7 @@ Contributors: Bl00drav3n Blackhive Charles Ray Shisler III + Chaturbate Chensiyy Daniel Lopez David Cooper @@ -163,6 +165,7 @@ Contributors: Hernán Jeremiah Senkpiel Jiaxun Yang + John Bowers John R. Bradley Joseph El-Khouri Kasin Sparks @@ -204,7 +207,6 @@ Contributors: Benjamin Schubert Bongalive Brian S. Stephan - Chaturbate Christine Lin Christopher P Yarger Douglas Rhine @@ -217,6 +219,8 @@ Contributors: Hans Petter Selasky Henrik "Henke37" Andersson Ilya Chernikov + Jeremy Woertink + JoHn BoWeRs John Boiles Joshua Rowe Julian Orth @@ -256,6 +260,7 @@ Contributors: Vadim Zhukov Warren Turkal Willy Liu(HQ ENG) + WuLongyue Zulleyy3 Zulleyy3 adray aggresss @@ -274,6 +279,7 @@ Contributors: jpk jw0z96 liu.haibin + moocowsheep notmark shinji3 skwerlman @@ -295,8 +301,8 @@ Contributors: Akagi201 Akihiko Koizuka Alcaros - Aleks Todorov Alex Kosenko + Alex Luccisano Alexander Kozhevin Alexander Uhlmann Alexandre Biny @@ -394,6 +400,7 @@ Contributors: Frank Frank Gehann Frank Löffler + GPattenden Garrett GingerAudio Giorgio Pellero @@ -419,7 +426,6 @@ Contributors: Jason Francis Jeff Ward Jeremy Cole - Jeremy Woertink JetMeta Jkoan Joan Miquel Fuster @@ -435,7 +441,9 @@ Contributors: Juliusz Chroboczek Justin B. Watson K.B.Dharun Krishna + KOU_CHANG Kacper Geisheimer + Kamal Mostafa Kazuki Oishi Kazuki Yamaguchi Keith Packard @@ -447,6 +455,7 @@ Contributors: Kraz3D Lasse Dalegaard Laurin Müller + Len LiamCoal Linnun Lioncash @@ -460,6 +469,7 @@ Contributors: Makeenon Marc Chambers Marcus Rückert + Mariana Mark Sergienko Masato Takahashi Mathias Panzenböck @@ -475,6 +485,7 @@ Contributors: Misutaa Asriel Morgan Rose Morten Bøgeskov + MrMahgu Murnux Nathan-Huckleberry Nick Stockton @@ -503,6 +514,7 @@ Contributors: Ricardo Tavares Richard Fontana Ro + Rob Howell Robert Mader Robert Wittams Robert de Bock @@ -514,6 +526,7 @@ Contributors: Sebastian Ramacher Sefa Eyeoglu Serge Martial Nguetta + Sergio Garcia Murillo Sergo Pogosyan Seth Murphy Seung-Woo Kim @@ -546,13 +559,13 @@ Contributors: Tophicles TotalCaesar659 Translation + Tunghohin Uro Weikardzaena Will Jamieson William Casarin Wooster Wouter - WuLongyue Ying Yin Younes SERRAJ Yves Dionne @@ -618,6 +631,7 @@ Contributors: pkuznetsov poccariswet pongraczgabor87 + powersagitar praveenkumar probonopd qiongzai.sun @@ -660,6 +674,7 @@ Contributors: yalpul yoho zhaolong + наб 张昆 练亮斌 @@ -667,6 +682,7 @@ Translators: Afrikaans: Gideon Wentink (gjwentink) VainockManager + Baker123 Rory Townsend (f3emarketing) Samuel Nthoroane (Samuel_Nthoroane) speedytechdev @@ -699,8 +715,8 @@ Translators: lazerns Vainock doyTech (aldoyh) - waleedmortaja VainockManager + waleedmortaja AiAF (7MR) ibrahim Younis (gr33v) Fahim Sabah (Road_ar) @@ -724,8 +740,8 @@ Translators: Fahad Alshaya (fashaya) azez sh (azezsh) AL-3boud_25 (alaboud257) - Chipsum Hani Sweileh (hno.sweileh) + zefr0x Nasser Bin Laboun (nasserbinlaboun) M24 AL-Zurqan (mohammedamer2443) Awatif Koko (ngamu.zakaria15) @@ -768,6 +784,7 @@ Translators: Azerbaijani: ShahinF27 (Khan27) Mirsahin Mirserifov (mirsahin.mirsarifov) + Magiraliyev Sədi Məmmədov (sedi9816) royal_reyx Şamil Veliyev (TheShamil035) @@ -844,7 +861,7 @@ Translators: Bashishtha Narayan Singh (bashishtha) VainockManager Bulgarian: - Zakxaev68 + Krezzin j3dy Dremski kalmarin @@ -854,8 +871,8 @@ Translators: TraoX_ (Macroguy) capitalrhino g-k-m - DivideByNone VainockManager + DivideByNone Vainock Kayos471 AntiTikTokGang @@ -868,6 +885,7 @@ Translators: Akjo (Akjo03) TwoOneTwo (TwoThreeTwo) Gol D. Ace (goldace) + krlsky Ivan (SKDown) unknowndomain Salif Mehmed (salifm) @@ -966,14 +984,14 @@ Translators: Smpq117 (smpq117) dodgepong WaterOtaku - Dianer Small (smalldianer) + Small Dianer (smalldianer) OYYZ Bob Wei (BobWaver) Haoran Du (bingchuanjuzi) lifeeeeeeeeeeE AperturePenguin Chen99 - Gentry Deng (wordlesswind) + cheriny FaZe Fakay (fazefakay) xtexChooser pluwen @@ -1049,6 +1067,7 @@ Translators: Maky (the.real.maky) Vainock Flicker (galaxybro2411) + Mat GMC7 (MatGMC7) tvoja_stara unknowndomain CroatianTranslator @@ -1058,6 +1077,7 @@ Translators: Jirka 'Venty' Michel (VentyCZ) Vít Loyd Kolomazník (Arocles) Šimon Bartoš (TyphousCrane654) + Sedloman VainockManager Kryštof Černý (panKrysha) multi.flexi @@ -1152,6 +1172,7 @@ Translators: unknowndomain JorRy Julian Meijboom (julianmeijboom) + Joey Slotboom (JoeyS) RyyzQ Mees (MeesJ) Roeland Kussé (roeland) @@ -1287,13 +1308,13 @@ Translators: Benjamin Cambour (lesinfox) Yberion Léo (leeo97one) + Lucatacos RisedSky Pikana BoboopTeam DoK_- leluk MrAngelos6 - Lucatacos EN LYOWH (enlyowh) EGuillemot DarckCrystale @@ -1308,9 +1329,9 @@ Translators: Figurant16 Youtubeur FR│Giaco35 (Giaco35) Wiwok + Kévin (FleuryK) steve_fr Valentin (valentin45) - Kévin (FleuryK) LEMIBANDDEXARI kyllian (tardigradeus) Nicolas Leperlier (leperlier) @@ -1342,10 +1363,10 @@ Translators: tburette Richard Stanway (r1ch) Adrien “GameZone Tv” de Decker (redcraft007) + Zalki Evan Kummel (EvanK) ButterflyOfFire (BoFFire) Aryoll (Naomi_CAST) - Zalki SkytAsul (skytasul) xav blacyre (xav35000) Alex Smotra (smotraalex75) @@ -1435,7 +1456,8 @@ Translators: Luchx MatsMTN Richard Stanway (r1ch) - kinsej + J T (JT4) + Jesper Mahel (kinsej) Palana deexbeam Bumpsy @@ -1444,7 +1466,6 @@ Translators: Geisteskranker Enderdrache LP (enderdrachelp) David (Tulpedublone) - J T (JT4) Fabi135 Prince_of_Raop SlimPlay @@ -1466,6 +1487,7 @@ Translators: Hadi Gamer (hadigamer3131) hakuchi degra78 (degra789) + NotTwerp Greek: swatzniker (dimitrisrtg324) Katerina (katerinaramm) @@ -1536,6 +1558,7 @@ Translators: yoni3D TheOver (upmeboost) unknowndomain + Omer I.S. (omeritzics) yair (5shekel) ghsi אפיק רוזנר (afikr333) @@ -1587,6 +1610,7 @@ Translators: abydosan (abydoshun) Skelly001 Benjamin Otto (Cubebanyasz) + damagepy dodgepong Icelandic: Sigurður óskarsson (sig.oskarsson) @@ -1605,6 +1629,7 @@ Translators: dnpxs (Dnoxs) Deraidos Asahi itoza (azza_su) + Dennisbdhdkdsgslaowhsosvsj Hskdldkd Trousershsjsodhdldheodhdisjsvs (saxwelcoki) VainockManager Irgi Fachrezi (IrgiFachrezi) Jovan Ferryal E. F. (Jovanzers) @@ -1626,18 +1651,19 @@ Translators: Vincenzo Reale (vinx.reale) Sergio Beneduce (sbeneduce) Ruggero Tomaselli (ruggi99) + uman + VainockManager Michele (ScrappyCocco) Albakham (albakham) dodgepong Marco Ciampa (ciampix) Alessandro Sarto (alesarto03) Tommaso Cammelli (tomganguz) (tomganguz) - VainockManager Vainock Christian Mazzola (Alphaaa) Martazza - mauriziopersi Alfonso Scarpino (alfonso.scarpino) + mauriziopersi ScemEnzo M K (michelek.) Edoardo Macrì (edomacri) @@ -1655,7 +1681,6 @@ Translators: Fisherozzo DomenicoDD icovada - Androide Umano (androide012340) Eugenio Tomasella (eugeniotomasella9) Alessio Ilardi (alantheandroid) SkyLion @@ -1669,8 +1694,8 @@ Translators: Matt (NightMat) Flavio Rinaldi (flaviorinaldi) Rodney (derrod) - Lorenzo Giannini (Loweredgames) - Augusto (augustomich) + Loweredgames + Augusto (ForenXis14) Alessandro Iepure (alessandro_iepure) Daniele02_777 Japanese: @@ -1685,10 +1710,12 @@ Translators: Hiroki IWAMA (higamma) Vainock nishi-ruse - kotobato meitel1014 (meitel1234) + kotobato + ラズベリージャム (rpiuser1923) Kanji1113 (kanji1113) nkamae + akawshi Gol D. Ace (goldace) 宮脇丈治 (gogstudio) VainockManager @@ -1698,7 +1725,6 @@ Translators: 神成フィルム (kami00nari) ? (a2y4) GO Channel (georgestv.0129) - akawshi Bob Liu (Akagi201) Tolr chika takada (miyu1224) @@ -1711,10 +1737,10 @@ Translators: Yacine Bouklif (YacineBouklif) Vainock ZiriSut - Slimane Selyan AMIRI (SelyanKab) + Slimane AMIRI (Akselyan) VainockManager Karakalpak: - KarLin - Karakalpak Translators Team (alizhumagaliev) + KarLin - Karakalpak Translators Team (karakalpaklingvo) flower (flower_flower) Mukhit Nurmanov (nurmanov) Zhanserik @@ -1749,18 +1775,19 @@ Translators: Jong Kwon Choi (dailypro) Ra.Workspace (RaWouk) ilsubyeega + SteamB23 (steamb23) 무아MUA (moajikyeongtv1048) rldbs3411 DHtheCreator + 김희석 (hskimse1) hykdem kim (hykdem) 미르냥 (Mirnyang) Russell (crimeroyal) unknowndomain - SteamB23 (steamb23) - 뇽룡 (nyongryong) Charles Wallis (charlestw127) 이승진 (smctgrass) Look Studio (yoon080708) + Hana (momenthana) antome Tristar Corp (Charleslee) bluestar8 @@ -1783,7 +1810,7 @@ Translators: VainockManager Latvian: oskars - Andris Liepiņš (andrisliepins3) + Andris Liepiņš (ahgpuc) Arthur (ArthurLV) Imants Vancāns (Imants565) Mops3005 @@ -1805,6 +1832,7 @@ Translators: Nikita Podvinskij (PODWINSKi) MuscileTurze justas beisinas (idkjhui) + VainockManager dnew (dbot) Pufikas nojusu (nojus.urbutis1) @@ -1815,12 +1843,11 @@ Translators: Vainock justas beisinas (justas.beisinas2008) AquantiuM - VainockManager Baker123 Malay: abuyop (Abuyop) - amsyar ZeRo (amsyarminer555) AnNamir + amsyar ZeRo (amsyarminer555) WeingHong VainockManager Srap Dsign (srapdsign) @@ -1840,11 +1867,11 @@ Translators: Vainock VainockManager Norwegian Bokmal: - Imre Kristoffer Eilertsen (DandelionSprout) + Imre Eilertsen (DandelionSprout) Taesh (magnusmbratteng) flaeri + Oddbjørn Grytdal (OGrytdal) Patrick Williamson (wpatrick59) - Oddbjørn Grytdal (Fooshi) Mats Edvin Aarø (matsedvin) dodgepong LandyLERThERmfLOpi @@ -1872,7 +1899,7 @@ Translators: VainockManager Norwegian Nynorsk: Bjørn I. (bjorni) - Imre Kristoffer Eilertsen (DandelionSprout) + Imre Eilertsen (DandelionSprout) Eiliv Ulvestad (Eiliv) morden LandyLERThERmfLOpi @@ -1947,6 +1974,7 @@ Translators: unknowndomain RytoEX Piteriuz + ThatSteve1721 Hubert Degler (Hubertoos) seba (xseba) adamek2314 @@ -2010,6 +2038,7 @@ Translators: Emanoel Lopes (emanoelopes) Pedro Ricardo (Pedro270707) Guilherme Dias (Darkaiser) + Pedro Brantes (brantes) TFSThiagoBR98 Vainock Bruno Lopes Mattos Luiz (lopestranslate) @@ -2050,6 +2079,7 @@ Translators: JNylson DanielTaqueto Alex Smotra (smotraalex75) + Celso Fernandes (Celsof) lakitoo DJ Matheus Martins (DJMatheusMartins) Tetri Mesquita Neto (tetri) @@ -2059,6 +2089,7 @@ Translators: DevilLorde João (fror) unknowndomain + ThomasLawliet (thomaslawliet) Gersonzao Ramon Gonzalez (ramon200000) Esdras Tarsis (esdrastarsis) @@ -2099,6 +2130,7 @@ Translators: Skellytone Mihai G (babasghenciu) Vainock + Eduard Andrei (28edx) Gol D. Ace (goldace) nkamae Melonoone @@ -2305,6 +2337,7 @@ Translators: MarioMey Adolfo Jayme (fitojb) Gol D. Ace (goldace) + marcrisale Manuel Matías (manuel.matias) 716gjesu Alejandro Alzate Sanchez (alejandroalzate) @@ -2317,6 +2350,7 @@ Translators: Carlos Plata (carlosesgenial33) JaviermbPlays (JavierMB) Gonzalo Lorenzo (Gxnzalo) + VainockManager eemiroj Hector meneses (hector.meneses) henrycontreras @@ -2325,7 +2359,6 @@ Translators: Marcos Vidal (markitos.maki22) Ruben Deig Ramos (rdeigramos) 1 (Ipsumry) - VainockManager makiza1 (micosil_2) Santiago Pereyra (SannttVIII) FloodedVoyage45 @@ -2336,14 +2369,15 @@ Translators: Sr_V Eleazar (MtrElee3) AndMe (accomigt) + Kevin Reyes (Kvrnn) Gabriel Arriagada (ggvbo) ian Vatega (ianvatega) amssusgameplays (willifake052) - marcrisale D E B (D_E_B) Stephen Q (qstephen73) Jaire (corpi.98) Luis Carlos González Morales (luiscarlosgm) + Dominik K. (mezotv) Sigge Stjärnholm (Kladdy) unknowndomain Mahin Rafi Alam Hassan (Rafi13082007) @@ -2370,12 +2404,13 @@ Translators: Sigge Stjärnholm (Kladdy) Laccy IEST (Laccy) Bjorn Astrom (beeanyew) - 0x9fff00 LinusW + 0x9fff00 Kristoffer Grundström (Umeaboy) Gustav Ekner (ekner) Victor Ahlin (VSwede) VainockManager + Johan Andersson (RealIndrit) Vainock Gustav Lövgren (morsgris) Mats Karlsson (matska) @@ -2385,6 +2420,7 @@ Translators: nahkampf dotar (TorstenAugustsson) Gol D. Ace (goldace) + Dennisbdhdkdsgslaowhsosvsj Hskdldkd Trousershsjsodhdldheodhdisjsvs (saxwelcoki) Henrik Mattsson-Mårn (rchk) unknowndomain Axel Aminoff (axel.aminoff) @@ -2419,6 +2455,8 @@ Translators: unknowndomain Naveen Techs (alexonpeace) VainockManager + Tatar: + Әлмәт Ак Арыслан (19082004amir) Telugu: CRTL Amaravati (rrskiran) VainockManager @@ -2436,12 +2474,14 @@ Translators: zero0200 ธีรภัทร์ โยชนา (2RD) PolaX3 + Pawat Chivapruk (pawattoto) Narintorn101 VainockManager sakuhanachan* (sakuhanachanloli) SurachaiJUI Sakia Normal Human (arcanaarcana5) นักลูบคม อันดับ 1 (mrmanyt629) + Worawut Weerawan (nero.exe) PlyKung (plykung) ธีร์ ธีรพล (teraphonk) Vainock @@ -2449,7 +2489,6 @@ Translators: 盛凤阁 (execzero) nongnoobjung (kitcharuk_4) 301-14 สิรวิชญ์ ศรีคําเมือง (Sam_Hanhyongsok2006) - Worawut Weerawan (nero.exe) Rathchaarnon Threeanjuleethaan (rathchaarnon) Yuthana Phanom (uyuth19thai) unknowndomain @@ -2469,21 +2508,23 @@ Translators: Ufuk Furkan Öztürk (UfukFurkanOzturk) Emir (dirt3009) Cemal Dursun (cmldrs) + Kayhan (SwitchAlpha) Savas Tokmak (Laserist) Serhat Öktem (delidolu1adam) Vainock Umut kılıç (kilic190787) Burak‏ Eker‏‏‏‏‏‏‎ (mburakeker) + Nejdet ACAR (nejdetacar) BreezeQS keke_ea furkanbicici - Ektaynot - Brtc (BorATICI) - Nejdet ACAR (nejdetacar) + İsmail Efe Top (Ektaynot) + Bora Atıcı (Brtc) MeskaX VainockManager Murat Karagöz (anemon_1994) Miss.Syzygy + Ömer Faruk Altınova (Cavit19) Görkem Akova (gorkemakova) Doğukan (qreardedisback) Ebubekir Türker (ebubekirtrkr) @@ -2534,6 +2575,7 @@ Translators: Hydroboost unknowndomain Mustafa Arslan (mstfaa) + ulutek Alican Gultekin (Vitaefinis) Fatih Güneş (fatihgunes) Türker Yıldırım (turkeryildirim) @@ -2543,6 +2585,7 @@ Translators: 1 0 (efkanyz) Emre (Khapnols) Uğurcan Sayan (ugurcansayan) + Mert (Memort) chaironeko Huseyin Emre (emreemuzik) Selim Şumlu (maxcoder) @@ -2552,7 +2595,8 @@ Translators: Ihor Hordiichuk (ihor_ck) lxlalexlxl Anatolii Bielibov (anatoly136ua) - Denys (Veydzher) + Veydzher + Marm (Marm_Marm) Vainock Kefir (Kefir2105) Lino Bico (bicolino34) @@ -2562,9 +2606,9 @@ Translators: ROkelbow FOR___REST (konstantinkostenko96) Свиридюк Іван (fl1per) + Дмитро Маркевич (hotr1pak) Gol D. Ace (goldace) Shannar de Kassal (587) - Hotripak (hotr1pak) Andy (anry025) kateryna.roiko Andrey (Fokz) @@ -2576,7 +2620,7 @@ Translators: powerdef Serhii Raboshchuk (Fallenbard) unknowndomain - A A (hran) + 1 (284) skuu Maksym Tymoshyk (maximillian_) MeDustyy @@ -2584,6 +2628,7 @@ Translators: Володимир Родич (vrodych) Mark Chorney (chorneymark2006) D_D + Oleh Hnat (aetrnm) Urdu (Pakistan): Abuzar (a6y3ap) Sheikh Ahmed (sheikhahmed) @@ -2597,12 +2642,13 @@ Translators: VainockManager Uyghur: Abduqadir Abliz (Sahran) + VainockManager Vietnamese: IoeCmcomc (ioecmcomc) Vichy (tnhung2011) Johnny “max20091” Utah (boostyourprogram) - Quang Nguyen (quangnguyen.25) I_am_Vietnam + Quang Nguyen (quangnguyen.25) Hưng Nguyễn (hoyostudio) Drake Strike (phjtieudoc) ngoisaosang @@ -2614,10 +2660,13 @@ Translators: Bach Le (BachWumpus) nguyenthanhphong BIGO - 지혜 (parkjihye98) + Nguyen Vu (songnguxyz) Blog Đào Lê Minh (daoleminh2010) Hà Phi Hùng (haphihungcom) Thanh Doan (thanhcs) + Ngọc Anh TVE (ngocanh.tve) MichaelTr + Bỉ Bỉ Lê (bbkuro) anhduck14 Bùi Xuân Hải (real.xuanhai) Vainock diff --git a/UI/data/locale/af-ZA.ini b/UI/data/locale/af-ZA.ini index 338f5598590776..b997e2fe04e201 100644 --- a/UI/data/locale/af-ZA.ini +++ b/UI/data/locale/af-ZA.ini @@ -21,6 +21,7 @@ Name="Naam" Exit="Verlaat" Mixer="OudioMenger" Browse="Blaai" +DroppedFrames="Geval rame %1 (%2%)" StudioProgramProjector="Volskerem Projektor (Program)" PreviewProjector="Volskerem Projektor (Voorshou)" SceneProjector="Volskerem Projektor (Toneel)" @@ -41,7 +42,8 @@ Untitled="Ongetitelde" New="Nuwe" Duplicate="Dupliseer" Enable="Aktiveer" -DisableOSXVSync="Deaktiveer macOS V-Sync" +DisableOSXVSync="Deaktiveer macOS V-Sync"\nResetOSXVSyncOnExit="\nStel macOS V-Sync terug by uitgang" +HighResourceUsage="Enkodering oorlaai! Oorweeg dit om video-instellings af te skakel of om 'n vinniger enkoderingvoorinstelling te gebruik." Transition="Transiesie" QuickTransitions="Vinnege Transiesie" FadeToBlack="Verdwyn na swart" @@ -54,6 +56,7 @@ Hours="Ure" Minutes="Minute" Seconds="Sekondes" Deprecated="Afgemaai" +ReplayBuffer="Herhaal buffer" Import="Voer in" Export="Stuur uit" Copy="Kopieer" @@ -66,11 +69,13 @@ Back="Terug" Defaults="Verstekke" RestoreDefaults="Verstekke" HideMixer="Versteek in menger" +TransitionOverride="Oorgangsoorskakeling" ShowTransition="Toon oorgang" HideTransition="Versteek oorgang" None="Geen" StudioMode.Preview="Voorskou" StudioMode.PreviewSceneName="Voorskou: %1" +ShowInMultiview="Wys in Multiview" VerticalLayout="Vertikale uitleg" Group="Groep" DoNotShowAgain="Moet dit nie weer toon nie" @@ -78,8 +83,10 @@ Default="(Verstek)" Calculating="Bereken tans…" Fullscreen="Volskerm" Windowed="Venster" +RefreshBrowser="Verfris" AspectRatio="Aspekverhouding %1:%2" LockVolume="Vergrendel volume" +ShowOnStartup="Wys tydens opstart" OpenFile="Open lêer" AddSource="Voeg bron toe" RemoveScene="Verwyder gekose toneel" @@ -94,6 +101,7 @@ MixerToolbarMenu="Oudiomengerkieslys" SceneFilters="Open toneelfilters" List="Lys" Grid="Rooster" +PluginsFailedToLoad.Title="Inprop laai Fout" PluginsFailedToLoad.Text="Die volgende OBS-inprop kon nie laai nie:\n\n%1\nWerk dit by of verwyder hierdie inproppe." AlreadyRunning.Title="OBS loop reeds" AlreadyRunning.Text="OBS loop reeds! Tensy die bedoeling was om dit te doen, moet u asb. enige bestaande instansies van OBS sluit voor u ’n nuwe instansie wil laat loop. Indien OBS in die stelsellaai verklein is, kyk asb. om te sien of dit nog daar loop." @@ -107,11 +115,18 @@ SafeMode.RestartNormal="Wil u OBS in normale modus herbegin?" ChromeOS.Title="Onondersteunde platform" ChromeOS.Text="OBS blyk binne ’n ChromeOS-houer te loop. Hierdie platform word nie ondersteun nie." Wine.Title="Wine bespeur" +Wine.Text="Om OBS in Wine te laat loop word nie ondersteun nie, en baie kenmerke soos vaslegging of toestelbronne sal nie werk nie of slegs in beperkte kapasiteit.

Dit word aanbeveel om eerder 'n oorspronklike weergawe van OBS te laat loop, byvoorbeeld ons Flatpak-weergawe of jou bedryfstelsel se pakkette." DockCloseWarning.Title="Dokbare venster word gesluit" DockCloseWarning.Text="U het pas ’n dokbare venster gesluit. Indien u dit weer wil vertoon kan u die Doks-kieslys in die kieslysbalk gebruik." ExtraBrowsers="Pasgemaakte blaaierdoks" ExtraBrowsers.Info="Voeg doks toe deur ’n naam en URL daaraan te gee, klik dan op Pas toe of Sluit om die doks te open. U kan enige tyd doks toevoeg of verwyder." ExtraBrowsers.DockName="Doknaam" +Auth.Authing.Title="Staaf tans..." +Auth.Authing.Text="Staaf tans met %1, wag asseblief..." +Auth.AuthFailure.Title="Verifikasie mislukking" +Auth.AuthFailure.Text="Kon nie staaf met %1:\n\n%2: %3 nie" +Auth.InvalidScope.Title="Stawing vereis" +Auth.InvalidScope.Text="Die stawingvereistes vir %1 het verander. Sommige kenmerke is dalk nie beskikbaar nie." Auth.LoadingChannel.Title="Laai tans kanaalinligting…" Auth.LoadingChannel.Text="Laai tans kanaalinligting vir %1, wag asb…" Auth.LoadingChannel.Error="Kon nie kanaalinligting kry nie." @@ -121,7 +136,9 @@ Auth.Chat="Klets" Auth.StreamInfo="Stroominligting" TwitchAuth.Stats="Twitch-statiestieke" TwitchAuth.Feed="Twitch-aktiwiteitsvoer" +TwitchAuth.TwoFactorFail.Title="Kon nie stroomsleutel navraag doen nie" TwitchAuth.TwoFactorFail.Text="OBS kon nie aan u Twitch-rekening koppel nie; sorg dat tweestapwaarmerking ingestel is in die Twitch-veiligheidssinstellings, omdat dit vereis word om te kan stroom." +RestreamAuth.Channels="Herstroom kanale" Copy.Filters="Kopieer filters" Paste.Filters="Plak filters" BandwidthTest.Region="Streek" @@ -314,7 +331,6 @@ Basic.MainMenu.Help.Repair="Gaan lêerintegriteit na" Basic.Settings.ConfirmTitle="Bevestig veranderinge" Basic.Settings.Confirm="U het onbewaarde veranderinge. Bewaar verandeirnge?" Basic.Settings.General="Algemeen" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Taal" Basic.Settings.General.Updater="Bywerkings" Basic.Settings.General.UpdateChannel="Bywerkkanaal" diff --git a/UI/data/locale/an-ES.ini b/UI/data/locale/an-ES.ini index 745353a24ee153..11e9cf4984083c 100644 --- a/UI/data/locale/an-ES.ini +++ b/UI/data/locale/an-ES.ini @@ -562,7 +562,6 @@ Basic.Settings.ProgramRestart="Lo programa ha de reiniciar-se pa que esta config Basic.Settings.ConfirmTitle="Confirmar cambios" Basic.Settings.Confirm="I hai cambios sin alzar. Alzar los cambios?" Basic.Settings.General="Cheneral" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.EnableAutoUpdates="Comprebar si i hai actualizacions automaticas a l'inicio" Basic.Settings.General.OpenStatsOnStartup="Ubrir lo dialogo d'estatisticas a l'inicio" diff --git a/UI/data/locale/ar-SA.ini b/UI/data/locale/ar-SA.ini index bfddeb4d0208dc..bd32b5220d0a03 100644 --- a/UI/data/locale/ar-SA.ini +++ b/UI/data/locale/ar-SA.ini @@ -311,7 +311,7 @@ Undo.GroupItems="تجميع العناصر في '%1'" TransitionNameDlg.Text="Text" TransitionNameDlg.Title="اسم تأثير الإنتقال" TitleBar.SafeMode="الوضع الآمن" -TitleBar.PortableMode="الوضع المحمول\n" +TitleBar.PortableMode="الوضع المحمول" TitleBar.Profile="الملف الشخصي" TitleBar.Scenes="المشاهد" NameExists.Title="الاسم موجود بالفعل" @@ -682,7 +682,6 @@ Basic.Settings.ProgramRestart="يجب إعادة تشغيل البرنامج ح Basic.Settings.ConfirmTitle="تأكيد التغييرات" Basic.Settings.Confirm="لديك تغييرات غير محفوظة. هل تريد حفظها?" Basic.Settings.General="عام" -Basic.Settings.General.Theme="السمة" Basic.Settings.General.Language="اللغة" Basic.Settings.General.Updater="التحديثات" Basic.Settings.General.UpdateChannel="قناة التحديث" diff --git a/UI/data/locale/az-AZ.ini b/UI/data/locale/az-AZ.ini index 4bd32f50530fa6..00c37f82ed8f7d 100644 --- a/UI/data/locale/az-AZ.ini +++ b/UI/data/locale/az-AZ.ini @@ -103,10 +103,24 @@ MoveSourceDown="Mənbələri aşağı köçür" SourceProperties="Mənbə özəlliklərini aç" SourceFilters="Mənbə filtirlərini aç" MixerToolbarMenu="Səs Düzənləyici" +SceneFilters="Səhnə Süzgəclərini Aç" +List="Siyahı" +Grid="Tor" +PluginsFailedToLoad.Title="Qoşmanı Yükləmə Səhvi" +PluginsFailedToLoad.Text="Aşağıdakı OBS qoşmaları yüklənə bilmədi:\n\n%1\nLütfən, bu qoşmaları yeniləyin ya da silin." AlreadyRunning.Title="OBS artıq işləyir" AlreadyRunning.Text="OBS artıq işləyir! Bunu etmək istəmirsinizsə, zəhmət olmasa yeni bir nümunəni işlətməyi sınamazdan əvvəl bütün mövcud OBS nümunələrini söndürün. OBS-i sistem sinisinə kiçilməsi üçün tənzimləmisinizsə, zəhmət olmasa işlədiyini görmək üçün yoxlayın." AlreadyRunning.LaunchAnyway="Yenə də başlat" +AutoSafeMode.Title="Təhlükəsiz Rejim" +AutoSafeMode.Text="OBS, son seansınız zamanı düzgün sönmədi.\n\nTəhlükəsiz Rejimdə başlamaq istərdinizmi (üçüncü tərəf qoşmaları, skriptlər və \"WebSocket\"lər qeyri-aktiv olacaqlar)?" +AutoSafeMode.LaunchSafe="Təhlükəsiz Rejimdə icra et" +AutoSafeMode.LaunchNormal="Normal İcra Et" +SafeMode.Restart="OBS-i, Təhlükəsiz Rejimdə (üçüncü tərəf qoşmaları, skriptlər və \"WebSocket\"lər qeyri-aktiv olacaqlar) yenidən başlatmaq istəyirsinizmi?" +SafeMode.RestartNormal="OBS-i, Normal Rejimdə yenidən başlatmaq istəyirsinizmi?" ChromeOS.Title="Dəstəklənməyən platform" +ChromeOS.Text="OBS, görünür, ChromeOS konteynerində işləyir. Bu platforma dəstəklənmir." +Wine.Title="Wine aşkarlandı" +Wine.Text="OBS-i, \"Wine\"da icra etmə dəstəklənmir və yazma ya da qurğu mənbələri kimi bir çox xüsusiyyətlər işləməyəcək ya da yalnız məhdud rejimdə işləyəcək.

Bunun yerinə OBS-in doğma versiyasını icra etmək tövsiyə olunur, örnək üçün bizim Flatpak versiyamızı ya da sizin əməliyyat sisteminizin paketlərini. " DockCloseWarning.Title="Qoşulan pəncərəni bağla" DockCloseWarning.Text="İndicə qoşulan pəncərəni bağladınız. Əgər yenidən göstərmək istəyirsinizsə, Menyu sətrindəki Bax → Yuvalar bölməsinə gedin." ExtraBrowsers="Özəl Səyyah Yuvaları" @@ -168,7 +182,9 @@ Basic.AutoConfig.StreamPage.Service="Xidmət" Basic.AutoConfig.StreamPage.Service.ShowAll="Hamısını Göstər..." Basic.AutoConfig.StreamPage.Service.Custom="Fərdi..." Basic.AutoConfig.StreamPage.StreamKey="Yayım Açarı" +Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: şifrləmə parolunu daxil edin.\nRTMP: xidmət tərəfindən verilən açarı daxil edin.\nSRT: xidmət, streamid (yayım identifikatoru) istifadə edirsə, onu daxil edin." Basic.AutoConfig.StreamPage.EncoderKey="Kodlayıcı açar kodu" +Basic.AutoConfig.StreamPage.BearerToken="Bearer Token (əslliyi yoxlama tokeni)" Basic.AutoConfig.StreamPage.ConnectedAccount="Hesaba qoşuldu" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Bant genişliyi ilə bit sürətini təxmin et (Bir neçə dəqiqə çəkə bilər)" Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Donanım kodlamasına üstünlük ver" @@ -179,6 +195,7 @@ Basic.AutoConfig.TestPage="Final Nəticələri" Basic.AutoConfig.TestPage.SubTitle.Testing="Proqram ən uyğun parametrləri təxmin etmək üçün bir neçə test edir" Basic.AutoConfig.TestPage.SubTitle.Complete="Test tamamlandı" Basic.AutoConfig.TestPage.TestingBandwidth="Bant genişliyi testi aparılır,bu bir neçə dəqiqə ala bilər..." +Basic.AutoConfig.TestPage.TestingBandwidth.NoOutput="Bu xidmətin protokolu üçün çıxış tapılmadı" Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Bağlantı qurulur: %1..." Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Serverə qoşulmaq mümkün olmadı,zəhmət olmasa internet bağlantınızı yoxlayın və yenidən cəhd edin." Basic.AutoConfig.TestPage.TestingBandwidth.Server="Bant genişliyi testi aparılır: %1" @@ -213,6 +230,7 @@ Basic.Stats.Bitrate="Bit sürəti" Basic.Stats.DiskFullIn="Disk dolur (təxmini)" Basic.Stats.ResetStats="Statistikaları sıfırla" ResetUIWarning.Title="İstifadəçi interfeysini sıfırlamaq istədiyinizə əminsiniz?" +ResetUIWarning.Text="İstifadəçi interfeysinin ilkin vəziyyətinə bərpa edilməsi, əlavə bəndləri gizlədəcək. Onların görünməsini istəyirsinizsə, Bəndlər menyusundan çıxarmalısınız.\n\nİstifadəçi interfeysini ilkin vəziyyətinə bərpa etmək istədiyinizə əminsinizmi?" Updater.Title="Yeniləmə mövcuddur" Updater.Text="Yeniləmə mövcuddur:" Updater.UpdateNow="İndi Yenilə" @@ -220,16 +238,41 @@ Updater.RemindMeLater="Daha sonra xatırlat" Updater.Skip="Versiyanı Ötür" Updater.NoUpdatesAvailable.Title="Yeniləmə yoxdur" Updater.NoUpdatesAvailable.Text="Hazırda son yeniləmələr yoxdur" +Updater.BranchNotFound.Title="Yeniləmə Kanalı, Silindi" +Updater.BranchNotFound.Text="Seçdiyiniz yeniləmə kanalı artıq əlçatan deyil, OBS, ilkin vəziyyətinə bərpa edilmişdir." +Updater.RepairButUpdatesAvailable.Title="Tamlıq yoxlaması qeyri-mümkündür" +Updater.RepairButUpdatesAvailable.Text="Fayl tamlığının yoxlanılması ancaq əlçatan ən son versiya üçün mümkündür. OBS quraşdırmanızı yoxlamaq və yeniləmək üçün Kömək → Yeniləmələri Yoxla seçimindən istifadə edin." +Updater.RepairConfirm.Title="Tamlıq Yoxlamasını Təsdiq Et" +Updater.RepairConfirm.Text="Tamlıq yoxlamasının başladılması, OBS quraşdırmanızın zədəli olub-olmadığını skanlayacaq və zədələnmiş/dəyişdirilmiş faylları yenidən endirəcək. Bu bir müddət çəkə bilər.\n\nDavam etmək istəyirsinizmi?" Updater.FailedToLaunch="Yeniləyici başladılmadı" +QuickTransitions.SwapScenes="Keçiddən Sonra Önbaxış/Proqram Səhnələrini Dəyişdir" +QuickTransitions.SwapScenesTT="Keçiddən sonra önbaxış və proqram səhnələrini dəyişdirir (proqramın orijinal səhnəsi hələ mövcuddursa).\nBu, proqramın orijinal səhnəsində edilmiş hər hansı bir dəyişikliyi qaytarmayacaq." QuickTransitions.DuplicateScene="Səhnəni Çoxalt" +QuickTransitions.DuplicateSceneTT="Eyni səhnəni redaktə edərkən, proqram çıxışını dəyişdirmədən mənbələrin çevrilməsini/görünməsini redaktə etməyə imkan verir.\nProqram çıxışını dəyişdirmədən mənbələrin xüsusiyyətlərini redaktə etmək üçün \"Mənbələri Çoxalt\" seçimini aktivləşdirin.\nBu parametrin dəyişdirilməsi, cari proqram səhnəsini ilkin vəziyyətinə bərpa edəcək (hələ varsa)." QuickTransitions.EditProperties="Mənbələri kopyala" +QuickTransitions.EditPropertiesTT="Eyni səhnəni redaktə edərkən, proqram çıxışını dəyişdirmədən mənbələrin xüsusiyyətlərini redaktə etməyə imkan verir.\nBu, yalnız \"Səhnəni Çoxalt\" aktivləşdirilibsə, istifadə oluna bilər.\nBəzi mənbələr (yazma ya da media mənbələri kimi), bunu dəstəkləmir və ayrıca redaktə oluna bilməz.\nBu parametrin dəyişdirilməsi, cari proqram səhnəsini ilkin vəziyyətinə bərpa edəcək (hələ varsa).\n\nXəbərdarlıq: Mənbələr çoxaldılacağından, bu, əlavə sistem ya da video resursları tələb edə bilər." +QuickTransitions.HotkeyName="Qısa Keçid: %1" +Basic.AddTransition="Konfiqurasiya Oluna Bilən Keçid Əlavə Et" +Basic.RemoveTransition="Konfiqurasiya Oluna Bilən Keçidi Sil" +Basic.TransitionProperties="Keçid Xüsusiyyətləri" Basic.SceneTransitions="Səhnə keçidi" Basic.TransitionDuration="Müddət" Basic.TogglePreviewProgramMode="Studiya Rejimi" +Basic.EnablePreviewProgramMode="Studiya Rejimini Aktivləşdir" +Basic.DisablePreviewProgramMode="Studiya Rejimini Qeyri-Aktiv Et" Undo.Undo="Geri" Undo.Redo="İreli" Undo.Add="%1 əlavə et'" Undo.Delete="Sil" +Undo.Rename="\"%1\" adını dəyişdir" +Undo.SceneCollection.Switch="Bura keç: \"%1\"" +Undo.Item.Undo="Qaytar: %1" +Undo.Item.Redo="Təkrar %1" +Undo.Sources.Multi="%1 Mənbəni Sil" +Undo.Filters="\"%1\" üzərindəki Süzgəc Dəyişiklikləri" +Undo.Filters.Paste.Single="\"%1\" süzgəcini bura əlavə et: \"%2\"" +Undo.Filters.Paste.Multiple="Süzgəcləri, \"%1\" mövqeyindən \"%2\" mövqeyinə köçür" +Undo.Transform="\"%1\" üzərindəki mənbə dəyişimi" Undo.HideSceneItem="%2 içində %1 gizlət" Undo.ReorderSources="%1 içində resursları yenidən sırala" Undo.MoveUp="%2 içindəki %1 yuxarı daşı" diff --git a/UI/data/locale/ba-RU.ini b/UI/data/locale/ba-RU.ini index 7c919e26bffb6a..af8826e66f7cb4 100644 --- a/UI/data/locale/ba-RU.ini +++ b/UI/data/locale/ba-RU.ini @@ -176,7 +176,6 @@ Basic.MainMenu.File.Import="Импорт (&I)" Basic.MainMenu.File.Remux="Яҙмаларҙың ремукслау (&M)" Basic.MainMenu.File.Settings="Көйләүҙәр (&S)" Basic.MainMenu.File.Exit="Сығыу (&X)" -Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Тел" Basic.Settings.General.Projectors="Проекторҙар" Basic.Settings.General.Preview="Алдынан ҡарау" diff --git a/UI/data/locale/be-BY.ini b/UI/data/locale/be-BY.ini index 737199302db5ee..0175b2ba62e20e 100644 --- a/UI/data/locale/be-BY.ini +++ b/UI/data/locale/be-BY.ini @@ -10,7 +10,7 @@ No="Не" Add="Дадаць" Remove="Выдаліць" Rename="Перайменаваць" -Interact="Уставіць" +Interact="Узаемадзейнічаць" Filters="Фільтры" Properties="Уласцівасці" MoveUp="Перамясціць вышэй" @@ -107,10 +107,11 @@ MixerToolbarMenu="Меню аўдыямікшара" SceneFilters="Паказаць фільтры сцэны" List="Спісам" Grid="Сеткай" +Automatic="Аўта" PluginsFailedToLoad.Title="Памылка загрузкі плагіна" -PluginsFailedToLoad.Text="Немагчыма запусціць наступныя плагіны OBS:\n\n%1\nКалі ласка, абнавіце ці выдаліце гэтыя плагіны." +PluginsFailedToLoad.Text="Немагчыма запусціць наступныя плагіны OBS:\n\n%1\nАбнавіце ці выдаліце гэтыя плагіны." AlreadyRunning.Title="OBS ужо працуе" -AlreadyRunning.Text="OBS ужо працуе! Калі вы не збіраліся гэтага зрабіць, выключыце ўсе існуючыя копіі OBS перад запускам новай. Калі вы ўключылі згортванне OBS ў вобласць апавяшчэнняў, праверце, ці ёсць там значок праграмы." +AlreadyRunning.Text="OBS ужо працуе! Калі вам не патрабуецца некалькі адначасова адкрытых вокнаў OBS, скончыце ўсе існуючыя працэсы праграмы перад запускам новага. Калі вы ўключылі згортванне OBS у вобласць апавяшчэнняў, праверце, ці ёсць там значок праграмы." AlreadyRunning.LaunchAnyway="Усё роўна запусціць" AutoSafeMode.Title="Бяспечны рэжым" AutoSafeMode.Text="Падчас апошняга сеансу працы OBS не была завершана належным чынам.\n\n Ці жадаеце Вы запусціць працу ў бяспечным рэжыме (адключаныя іншыя плагіны, скрыпты і WebSockets)?" @@ -122,24 +123,24 @@ ChromeOS.Title="Платформа не падтрымліваецца" ChromeOS.Text="Здаецца, OBS працуе ў кантэйнеры ChromeOS. Гэта платформа не падтрымліваецца." Wine.Title="Выяўлены Wine" Wine.Text="Запуск OBS праз Wine не падтрымліваецца, і многія функцыі, такія як крыніцы захопу або прылад, могуць не працаваць альбо працаваць з абмежаванай магчымасцю.

Рэкамендуецца запускаць версію OBS для вашай платформы, напрыклад нашу версію з Flatpak або са спісу пакетаў вашай сістэмы." -DockCloseWarning.Title="Закрыццё стыкуемага акна" -DockCloseWarning.Text="Вы толькі што зачынілі стыкуемае акно. Калі вы жадаеце зноў яго адкрыць, скарыстайцеся меню «Докі» ў радку меню." +DockCloseWarning.Title="Закрыццё док-акна" +DockCloseWarning.Text="Вы толькі што закрылі док-акно. Калі вы жадаеце зноў яго адкрыць, скарыстайцеся меню «Докі» ў радку меню." ExtraBrowsers="Уласные докі браўзера" ExtraBrowsers.Info="Дадайце докі, даўшы ім імя і URL, потым націсніце «Ужыць» або «Закрыць», каб адкрыць докі. Вы можаце дадаваць ці выдаляць докі ў любы час." -ExtraBrowsers.DockName="Імя дока" +ExtraBrowsers.DockName="Назва дока" Auth.Authing.Title="Уваход..." Auth.Authing.Text="Уваход у акаўнт %1, пачакайце..." Auth.AuthFailure.Title="Памылка аўтэнтыфікацыі" Auth.AuthFailure.Text="Не атрымалася ўвайсці ў %1:\n\n%2: %3" Auth.InvalidScope.Title="Патрабуецца ўваход" -Auth.InvalidScope.Text="Патрабавання для ўваходу ў %1 былі зменены. Некаторыя функцыі могуць быць недаступныя." +Auth.InvalidScope.Text="Патрабаванні для ўваходу ў %1 былі зменены. Некаторыя функцыі могуць быць недаступнымі." Auth.LoadingChannel.Title="Загрузка інфармацыі аб канале..." Auth.LoadingChannel.Text="Загрузка інфармацыі аб канале %1, пачакайце..." Auth.LoadingChannel.Error="Немагчыма атрымаць інфармацыю аб канале." Auth.ChannelFailure.Title="Не атрымалася загрузіць інфармацыю аб канале" Auth.ChannelFailure.Text="Не атрымалася загрузіць інфармацыю аб канале %1\n\n%2: %3" Auth.Chat="Чат" -Auth.StreamInfo="Інфармацыя пра паток" +Auth.StreamInfo="Звесткі пра плынь" TwitchAuth.Stats="Статыстыка Twitch" TwitchAuth.Feed="Стужка актыўнасці Twitch" TwitchAuth.TwoFactorFail.Title="Не атрымалася запытаць ключ трансляцыі." @@ -157,24 +158,24 @@ BandwidthTest.Region.Other="Іншы" Basic.AutoConfig="Майстар аўтаканфігурацыі" Basic.AutoConfig.ApplySettings="Ужыць налады" Basic.AutoConfig.StartPage="Інфармацыя аб выкарыстанні" -Basic.AutoConfig.StartPage.SubTitle="Укажыце, для чаго вы хочаце выкарыстоўваць праграму" -Basic.AutoConfig.StartPage.PrioritizeStreaming="Аптымізаваць для вяшчання, запіс пабочны" +Basic.AutoConfig.StartPage.SubTitle="Выберыце, для чаго вы хочаце выкарыстоўваць праграму" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Аптымізаваць для вяшчання, запіс на другім плане" Basic.AutoConfig.StartPage.PrioritizeRecording="Аптымізаваць толькі для запісу, я не буду весці трансляцыі" Basic.AutoConfig.StartPage.PrioritizeVirtualCam="Я буду толькі выкарыстоўваць віртуальную камеру" Basic.AutoConfig.VideoPage="Налады відэа" -Basic.AutoConfig.VideoPage.SubTitle="Укажыце налады відэа, якія вы хочаце выкарыстоўваць" +Basic.AutoConfig.VideoPage.SubTitle="Выберыце налады відэа, якія вы хочаце выкарыстоўваць" Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Выкарыстоўваць бягучую (%1x%2)" Basic.AutoConfig.VideoPage.BaseResolution.Display="Дысплэй %1 (%2x%3)" Basic.AutoConfig.VideoPage.FPS.UseCurrent="Выкарыстоўваць бягучую (%1)" -Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Альбо 60 альбо 30, але аддаваць перавагу 60 калі магчыма" -Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Альбо 60 альбо 30, але аддаваць перавагу больш высокай раздзяляльнасці" +Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60 або 30, але па магчымасці аддаваць перавагу 60" +Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60 або 30, але аддаваць перавагу больш высокай раздзяляльнасці" Basic.AutoConfig.VideoPage.CanvasExplanation="Заўвага: Раздзяляльнасць палатна (асноўная) неабавязкова супадае з раздзяляльнасцю, з якой вы будзеце весці трансляцыю або запіс. Фактычная раздзяляльнасць стрыма/запісу можа быць зменшана адносна раздзяляльнасці палатна, каб паменшыць выкарыстанне рэсурсаў або патрабаванні да бітрэйту." Basic.AutoConfig.StreamPage="Інфармацыя пра плынь" Basic.AutoConfig.StreamPage.SubTitle="Увядзіце звесткі пра плынь" Basic.AutoConfig.StreamPage.ConnectAccount="Далучыць уліковы запіс (рэкамендуецца)" Basic.AutoConfig.StreamPage.DisconnectAccount="Адлучыць уліковы запіс" Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title="Адлучыць акаўнт?" -Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text="Гэта змена будзе ўжыта неадкладна. Вы ўпэўненыя, што хочаце адлучыць свой акаўнт?" +Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text="Гэта змена будзе ўжыта неадкладна. Ці вы ўпэўненыя, што хочаце адлучыць свой акаўнт?" Basic.AutoConfig.StreamPage.GetStreamKey="Атрымаць ключ стрыма" Basic.AutoConfig.StreamPage.MoreInfo="Дакладней" Basic.AutoConfig.StreamPage.UseStreamKey="Скарыстацца ключом трансляцыі" @@ -193,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Даваць пераваг Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Апаратнае кадаванне менш за ўсё нагружае ЦП, але для дасягнення такога ж узроўню якасці можа патрабаваць большы бітрэйт." Basic.AutoConfig.StreamPage.StreamWarning.Title="Папярэджанне" Basic.AutoConfig.StreamPage.StreamWarning.Text="Тэст паласы прапускання будзе вяшчаць на ваш канал выпадковую відэаінфармацыю без гуку. Калі ёсць магчымасць, рэкамендуецца часова адключыць запіс мінулых трансляцый і пазначыць стрым як прыватны да канца тэсту. Працягнуць?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Тэсціраванне: %1" Basic.AutoConfig.TestPage="Канчатковыя вынікі" Basic.AutoConfig.TestPage.SubTitle.Testing="Зараз праграма выконвае шэраг тэстаў, каб ацаніць найлепшыя налады" Basic.AutoConfig.TestPage.SubTitle.Complete="Тэсціраванне завершана" @@ -211,6 +213,7 @@ Basic.AutoConfig.TestPage.Result.Header="Праграмма вызначыла, Basic.AutoConfig.TestPage.Result.Footer="Каб карыстацца імі, націсніце «Ужыць налады». Каб змяніць канфігурацыю майстра і паўтарыць спробу, націсніце «Назад». Каб задаць налады ўручную, націсніце «Скасаваць» і адкрыйце налады." Basic.AutoConfig.Info="Майстар аўтаканфігурацыі вызначыць найлепшыя налады, зыходзячы з канфігурацыі вашага ПК і хуткасці інтэрнэту." Basic.AutoConfig.RunAnytime="Вы можаце запусціць яго ў любы час праз меню «Інструменты»." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Плынёвая раздзяляльнасць (маштабаваная)" Basic.Stats="Статыстыка" Basic.Stats.CPUUsage="Выкарыстанне працэсара" Basic.Stats.HDDSpaceAvailable="Даступная дыскавая прастора" @@ -231,8 +234,8 @@ Basic.Stats.MegabytesSent="Сума адпраўленых даных" Basic.Stats.Bitrate="Бітрэйт" Basic.Stats.DiskFullIn="Да запаўнення дыска" Basic.Stats.ResetStats="Абнавіць статыстку" -ResetUIWarning.Title="Вы ўпэўненыя, што хочаце скінуць налады інтэрфейса?" -ResetUIWarning.Text="Пасля скіду налад дадатковыя докі будуць схаваны. Каб іх зноў было відаць, вам спатрэбіцца зрабіць іх бачнымі ў меню докаў.\n\nВы ўпэўнены, што хочаце скінуць налады інтэрфейса?" +ResetUIWarning.Title="Ці вы ўпэўненыя, што хочаце скінуць налады інтэрфейса?" +ResetUIWarning.Text="Пасля скіду налад дадатковыя докі будуць схаваны. Каб іх зноў было відаць, вам спатрэбіцца зрабіць іх бачнымі ў меню докаў.\n\nЦі вы ўпэўненыя, што хочаце скінуць налады інтэрфейса?" Updater.Title="Даступна абнаўленне" Updater.Text="Выйшла абнаўленне:" Updater.UpdateNow="Абнавіць зараз" @@ -245,7 +248,7 @@ Updater.BranchNotFound.Text="Выбраны канал абнаўлення бо Updater.RepairButUpdatesAvailable.Title="Праверка цэласнасці недаступная" Updater.RepairButUpdatesAvailable.Text="Правесці праверку цэласнасці файлаў магчыма толькі ў апошняй версіі праграмы. Каб абнавіць OBS, перайдзіце ў меню Даведка → Праверыць абнаўленні." Updater.RepairConfirm.Title="Пацвердзіць праверку цэласнасці" -Updater.RepairConfirm.Text="Пры праверцы цэласнасці ўсе файлы OBS правяраюцца і пашкоджаныя або змененыя файлы спампоўваюцца зноў.\n\nЦі жадаеце працягнуць?" +Updater.RepairConfirm.Text="Пры праверцы цэласнасці правяраюцца ўсе файлы OBS і пашкоджаныя або змененыя файлы спампоўваюцца зноў.\n\nЦі жадаеце працягнуць?" Updater.FailedToLaunch="Не атрымалася запусціць інструмент абнаўлення" QuickTransitions.SwapScenes="Пасля пераходу мяняць месцамі сцэны пердапрагляду і праграмы" QuickTransitions.SwapScenesTT="Мяняе месцамі сцэны перадпрагляду і праграмы пасля пераходу (калі зыходная сцэна праграмы яшчэ існуе).\nЗмяненні, якія былі ўнесены ў зыходную сцэну праграмы, адроблены не будуць." @@ -274,8 +277,8 @@ Undo.Sources.Multi="выдаленне %1 крыніц" Undo.Filters="змяненне фільтраў у «%1»" Undo.Filters.Paste.Single="устаўку фільтра «%1» у «%2»" Undo.Filters.Paste.Multiple="капіяванне фільтраў з «%1» у «%2»" -Undo.Transform="змяненне памеру крыніц(ы) у «%1»" -Undo.Transform.Paste="устаўку налад памеру ў «%1»" +Undo.Transform="змяненне трансфармацыі крыніц(ы) у «%1»" +Undo.Transform.Paste="устаўку налад трансфармацыі ў «%1»" Undo.Transform.Rotate="паварот у «%1»" Undo.Transform.Reset="скід трансфармацыі ў «%1»" Undo.Transform.HFlip="паварот па гарызанталі ў «%1»" @@ -299,16 +302,16 @@ Undo.Scene.Duplicate="дубляванне сцэны «%1»" Undo.ShowTransition="пераход паказу для «%1»" Undo.HideTransition="пераход схавання для «%1»" Undo.ShowSceneItem="паказ «%1» на «%2»" -Undo.HideSceneItem="схоўванне «%1» на «%2»" +Undo.HideSceneItem="хаванне «%1» на «%2»" Undo.ReorderSources="перапарадкаванне крыніц на «%1»" Undo.MoveUp="падняцце «%1» на «%2»" Undo.MoveDown="апусканне «%1» на «%2»" Undo.MoveToTop="падняцце «%1» у самы верх на «%2»" -Undo.MoveToBottom="апусканне «%1 у самы ніз на «%2»" -Undo.PasteSource="устаўку крыніц(ы) ў \"%1\"" +Undo.MoveToBottom="апусканне «%1» у самы ніз на «%2»" +Undo.PasteSource="устаўку крыніц(ы) на «%1»" Undo.PasteSourceRef="уставіць спасылкі(-ак) на крыніцу(-ы) ў «%1»" Undo.GroupItems="групаванне элементаў у «%1»" -TransitionNameDlg.Text="Калі ласка, увядзіце назву перахода" +TransitionNameDlg.Text="Увядзіце назву перахода" TransitionNameDlg.Title="Назва перахода" TitleBar.SafeMode="БЯСПЕЧНЫ РЭЖЫМ" TitleBar.PortableMode="Партатыўны рэжым" @@ -316,34 +319,34 @@ TitleBar.Profile="Профіль" TitleBar.Scenes="Сцэны" NameExists.Title="Назва ўжо існуе" NameExists.Text="Назва ўжо выкарыстоўваецца." -NoNameEntered.Title="Увядзіце правільную назву." +NoNameEntered.Title="Увядзіце правільную назву" NoNameEntered.Text="Вы не можаце выкарыстоўваць пустыя назвы." ConfirmStart.Title="Пачаць трансляцыю?" -ConfirmStart.Text="Вы ўпэўнены, што хочаце пачаць трансляцыю?" +ConfirmStart.Text="Ці вы ўпэўненыя, што хочаце пачаць трансляцыю?" ConfirmStop.Title="Спыніць трансляцыю?" -ConfirmStop.Text="Вы ўпэўнены, што хочаце спыніць трансляцыю?" +ConfirmStop.Text="Ці вы ўпэўненыя, што хочаце спыніць трансляцыю?" ConfirmStopRecord.Title="Спыніць запіс?" -ConfirmStopRecord.Text="Вы ўпэўнены, што хочаце спыніць трансляцыю?" +ConfirmStopRecord.Text="Ці вы ўпэўненыя, што хочаце спыніць запіс?" ConfirmBWTest.Title="Пачаць тэст паласы прапускання?" ConfirmBWTest.Text="OBS наладжана ў рэжыме тэста паласы прапускання. Гэты рэжым дазваляе праверыць сетку без пачынання эфіру. Пасля завяршэння тэста вам спатрэбіцца яго адключыць, каб вашы гледачы маглі бачыць вашу трансляцыю.\n\nХочаце працягнуць?" ConfirmExit.Title="Выйсці з OBS?" -ConfirmExit.Text="OBS зараз актыўны. Усе трансляцыі і (або) запісы будуць адключаныя. Вы ўпэўненыя, што хочаце выйсці?" +ConfirmExit.Text="OBS зараз актыўны. Усе трансляцыі і (або) запісы будуць адключаныя. Ці вы ўпэўненыя, што хочаце выйсці?" ConfirmRemove.Title="Пацвердзіць выдаленне" -ConfirmRemove.Text="Вы ўпэўнены, што хочаце выдаліць \"%1\"?" -ConfirmRemove.TextMultiple="Вы ўпэўнены, што хочаце выдаліць %1 элемент(аў)?" +ConfirmRemove.Text="Ці вы ўпэўненыя, што хочаце выдаліць «%1»?" +ConfirmRemove.TextMultiple="Ці вы ўпэўненыя, што хочаце выдаліць %1 элементы(-аў)?" ConfirmReset.Title="Скінуць налады" -ConfirmReset.Text="Вы ўпэўнены, што хочаце вярнуць усе змены да налад па змаўчанні?" +ConfirmReset.Text="Ці вы ўпэўненыя, што хочаце вярнуць усе змены да налад па змаўчанні?" Output.StartStreamFailed="Не атрымалася пачаць трансляцыю" Output.StartRecordingFailed="Не атрымалася пачаць запіс" Output.StartReplayFailed="Не атрымалася запусціць буфер паўтору" Output.StartVirtualCamFailed="Не атрымалася запусціць віртуальную камеру" Output.StartFailedGeneric="Не атрымалася пачаць вывад. Падрабязнасці праверце ў журнале адладкі.\n\nЗаўвага: Калі вы выкарыстоўваеце кадавальнікі NVENC або AMD, пераканайцеся, што вашы драйверы відэа абноўлены." -Output.ReplayBuffer.PauseWarning.Title="Захоўванне паўтораў падчас паўзы немагчымае" +Output.ReplayBuffer.PauseWarning.Title="Захоўванне паўтораў падчас паўзы не магчымае" Output.ReplayBuffer.PauseWarning.Text="Увага: Паўторы нельга захоўваць, калі запіс прыпынены" Output.ConnectFail.Title="Не атрымалася злучыцца" Output.ConnectFail.BadPath="Няправільны шлях або URL злучэння. Праверце, ці правільна вы наладзілі праграму." Output.ConnectFail.ConnectFailed="Не атрымалася злучыцца з серверам" -Output.ConnectFail.InvalidStream="Не атрымалася атрымаць доступ да вызначанага канала або ключа трансляцыі. Пераканайцеся ў правільнасці ключа трансляцыі. Калі ён правільны, маглі ўзнікнуць праблемы са злучэннем з серверам." +Output.ConnectFail.InvalidStream="Памылка атрымання доступу да вызначанага канала або ключа трансляцыі. Пераканайцеся ў правільнасці ключа трансляцыі. Калі ён правільны, маглі ўзнікнуць праблемы са злучэннем з серверам." Output.ConnectFail.HdrDisabled="Вывад HDR для гэтага вываду зараз адключаны." Output.ConnectFail.Error="Пры злучэнні з серверам узнікла нечаканая памылка. Больш інфармацыі даступна ў файле журнала." Output.ConnectFail.Disconnected="Кліент адлучаны ад сервера." @@ -365,8 +368,8 @@ Output.NoBroadcast.Text="Перад тым, як пачаць стрым, нал Output.BroadcastStartFailed="Не атрымалася пачаць трансляцыю" Output.BroadcastStopFailed="Не атрымалася спыніць трансляцыю" LogReturnDialog="Журнал паспяхова запампаваны" -LogReturnDialog.Description="Ваш файл журнала быў паспяхова запампаваны. Цяпер вы можаце абагуліць спасылку на яго для адладкі або атрымання падтрымкі." -LogReturnDialog.Description.Crash="Ваша справаздача аб збоях была паспяхова запампавана. Цяпер вы можаце абагуліць спасылку на яго для адладкі або атрымання падтрымкі." +LogReturnDialog.Description="Ваш файл журнала быў паспяхова запампаваны. Цяпер вы можаце абагуліць спасылку да яго для адладкі або атрымання падтрымкі." +LogReturnDialog.Description.Crash="Ваша справаздача аб збоях была паспяхова запампавана. Цяпер вы можаце абагуліць спасылку да яе для адладкі або атрымання падтрымкі." LogReturnDialog.CopyURL="Скапіяваць URL" LogReturnDialog.AnalyzeURL="Аналіз" LogReturnDialog.ErrorUploadingLog="Памылка запампоўвання файла журнала" @@ -385,7 +388,7 @@ Remux.SelectTarget="Выберыце канчатковы файл…" Remux.FileExistsTitle="Канчатковыя файлы ўжо існуюць" Remux.FileExists="Гэтыя канчатковыя файлы ўжо існуюць. Ці вы хочаце іх замяніць?" Remux.ExitUnfinishedTitle="Перапакоўванне не завершана" -Remux.ExitUnfinished="Перапакоўванне не завершана. Яго спыненне можа сур'ёзна пашкодзіць канчатковы файл.\nЦі вы ўпэўненыя, што хочаце спыніць перапакоўванне?" +Remux.ExitUnfinished="Перапакоўванне не завершана. Яго спыненне можа фатальна пашкодзіць канчатковы файл.\nЦі вы ўпэўненыя, што хочаце спыніць перапакоўванне?" Remux.HelpText="Перацягніце файлы, якія вы хочаце перапакаваць, у гэтае акно або выберыце пусты пункт у першым слупку для агляду." Remux.NoFilesAddedTitle="Файл не дададзены" Remux.NoFilesAdded="Файлы для перапакавання не былі дададзены. Перацягніце папку, якая змяшчае адно або некалькі відэа." @@ -461,7 +464,7 @@ VolControl.SliderMuted="Рэгулятар гучнасці для «%1»: (за VolControl.Mute="Заглушыць: %1" VolControl.Properties="Уласцівасці: %1" VolControl.UnassignedWarning.Title="Крыніца гуку не прызначана" -VolControl.UnassignedWarning.Text="Крыніца «%1» не прызначана да ніводнай аўдыядарожкі, і на стрымах і запісах гук ад яе чуваць не будзе.\n\nКаб прызначыць крыніцу гуку да дарожкі, адкрыйце пашыраныя ўласцівасці гуку, націснуўшы правую кнопку мышы або на значок у выглядзе шасцерань на доку мікшара." +VolControl.UnassignedWarning.Text="Крыніца «%1» не прызначана да ніводнай аўдыядарожкі, і на стрымах і запісах гук ад яе чуваць не будзе.\n\nКаб прызначыць крыніцу гуку да дарожкі, адкрыйце пашыраныя ўласцівасці гуку, націснуўшы правую кнопку мышы або значок у выглядзе шасцерань на доку мікшара." Basic.Main.AddSceneDlg.Title="Дадаць сцэну" Basic.Main.AddSceneDlg.Text="Увядзіце назву сцэны" Basic.Main.DefaultSceneName.Text="Сцэна %1" @@ -469,11 +472,11 @@ Basic.Main.AddSceneCollection.Title="Дадаць калекцыю сцэн" Basic.Main.AddSceneCollection.Text="Увядзіце назву калекцыі сцэн" Basic.Main.RenameSceneCollection.Title="Перайменаваць калекцыю сцэн" AddProfile.Title="Дадаць профіль" -AddProfile.Text="Калі ласка, увядзіце назву профілю" -AddProfile.WizardCheckbox="Паказваць майстар аўтаканфігурацыі" +AddProfile.Text="Увядзіце назву профілю" +AddProfile.WizardCheckbox="Паказаць майстар аўтаканфігурацыі" RenameProfile.Title="Перайменаваць профіль" Basic.Main.MixerRename.Title="Перайменаваць крыніцу гуку" -Basic.Main.MixerRename.Text="Увялзіце назву крыніцы гуку" +Basic.Main.MixerRename.Text="Увядзіце назву крыніцы гуку" Basic.Main.PreviewDisabled="Перадпрагляд адключаны" Basic.SourceSelect="Стварыць/выбраць крыніцу" Basic.SourceSelect.CreateNew="Стварыць новую" @@ -558,7 +561,7 @@ Basic.TransformWindow.BoundsType.ScaleOuter="Маштабаваць да вон Basic.TransformWindow.BoundsType.ScaleToWidth="Маштабаваць да шырыні межы" Basic.TransformWindow.BoundsType.ScaleToHeight="Маштабаваць да вышыні межы" Basic.TransformWindow.BoundsType.Stretch="Расцягваць да меж" -Basic.TransformWindow.Title="Змяніць памер: %1" +Basic.TransformWindow.Title="Трансфармацыя: %1" Basic.TransformWindow.NoSelectedSource="Крыніца не выбрана" Basic.Main.AddSourceHelp.Title="Не атрымалася дадаць крыніцу" Basic.Main.AddSourceHelp.Text="Каб дадаць крыніцу, неабходна стварыць прынамсі адну сцэну." @@ -566,6 +569,7 @@ Basic.Main.Scenes="Сцэны" Basic.Main.Sources="Крыніцы" Basic.Main.Source="Крыніца" Basic.Main.Controls="Пульт" +Basic.Main.PreparingStream="Падрыхтоўка..." Basic.Main.Connecting="Злучэнне…" Basic.Main.StartRecording="Пачаць запіс" Basic.Main.StartReplayBuffer="Запусціць буфер паўтору" @@ -577,6 +581,7 @@ Basic.Main.StopRecording="Спыніць запіс" Basic.Main.PauseRecording="Прыпыніць запіс" Basic.Main.UnpauseRecording="Працягнуць запіс" Basic.Main.SplitFile="Раздзяліць файл запісу" +Basic.Main.AddChapterMarker="Дадаць метку главы" Basic.Main.StoppingRecording="Спыненне запісу…" Basic.Main.StopReplayBuffer="Спыніць буфер паўтору" Basic.Main.StoppingReplayBuffer="Спыненне буфера паўтору…" @@ -600,17 +605,17 @@ Basic.VCam.OutputType="Тып вываду" Basic.VCam.OutputSelection="Выбар вываду" Basic.VCam.OutputType.Program="Праграмны (прадвызначаны)" Basic.VCam.OutputSelection.NoSelection="Для гэтага тыпу вываду выбар недаступны" -Basic.VCam.RestartWarning="Для ўжывання гэтага змянення віртуальная камера будзе перазапушчана" -Basic.MainMenu.File="&Файл" -Basic.MainMenu.File.Export="&Экспарт" -Basic.MainMenu.File.Import="&Імпарт" -Basic.MainMenu.File.ShowRecordings="Паказаць &запісы" +Basic.VCam.RestartWarning="Для ўжывання гэтай змены віртуальная камера будзе перазапушчана" +Basic.MainMenu.File="Ф&айл" +Basic.MainMenu.File.Export="Экспарт" +Basic.MainMenu.File.Import="Імпарт" +Basic.MainMenu.File.ShowRecordings="Па&казаць запісы" Basic.MainMenu.File.Remux="Перапакавац&ь запісы" -Basic.MainMenu.File.Settings="&Налады" +Basic.MainMenu.File.Settings="Налад&ы" Basic.MainMenu.File.ShowSettingsFolder="Паказаць папку наладаў" Basic.MainMenu.File.ShowProfileFolder="Паказаць папку профілю" Basic.MainMenu.File.ShowMissingFiles="Праверыць адсутнасць файлаў" -Basic.MainMenu.File.Exit="&Выйсці" +Basic.MainMenu.File.Exit="Выйсці" Basic.MainMenu.Edit="Праўка" Basic.MainMenu.Edit.LockPreview="Зафіксаваць пера&дпрагляд" Basic.MainMenu.Edit.Scale="Маштаб перадпрагляду" @@ -682,9 +687,9 @@ Basic.MainMenu.Help.CrashLogs.UploadLastLog="&Запампаваць папяр Basic.MainMenu.Help.About="Пра праграму" Basic.Settings.ProgramRestart="Каб налады ўвайшлі ў сілу, неабходна перазапусціць праграму." Basic.Settings.ConfirmTitle="Пацвярджэнне змяненняў" -Basic.Settings.Confirm="Некаторыя змяненні не захаваны. Захаваць іх?" +Basic.Settings.Confirm="Некаторыя змены не захаваны. Захаваць іх?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 кантралюе некаторыя налады плыні" Basic.Settings.General="Агульныя" -Basic.Settings.General.Theme="Тэма" Basic.Settings.General.Language="Мова" Basic.Settings.General.Updater="Абнаўленні" Basic.Settings.General.UpdateChannel="Канал абнаўленняў" @@ -725,11 +730,11 @@ Basic.Settings.General.AutomaticCollectionSearch="Пры імпарце шука Basic.Settings.General.SwitchOnDoubleClick="Пераход да сцэны пры двайным націсканні" Basic.Settings.General.StudioPortraitLayout="Партрэтнае (вертыкальнае) размяшчэнне" Basic.Settings.General.TogglePreviewProgramLabels="Паказваць тэкставыя абазначэнні перадпрагляду і праграмы" -Basic.Settings.General.Multiview="Шматэкранны прагляд" +Basic.Settings.General.Multiview="Мултыпрагляд" Basic.Settings.General.Multiview.MouseSwitch="Пераключэнне паміж сцэнамі націсканнем мышы" Basic.Settings.General.Multiview.DrawSourceNames="Паказваць назвы сцэн" Basic.Settings.General.Multiview.DrawSafeAreas="Паказваць бяспечныя зоны (EBU R 95)" -Basic.Settings.General.MultiviewLayout="Макет шматэкраннага прагляду" +Basic.Settings.General.MultiviewLayout="Макет мультыпрагляду" Basic.Settings.General.MultiviewLayout.Horizontal.Top="Гарызантальны, уверсе (8 сцэн)" Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Гарызантальны, унізе (8 сцэн)" Basic.Settings.General.MultiviewLayout.Vertical.Left="Вертыкальны, злева (8 сцэн)" @@ -744,7 +749,13 @@ Basic.Settings.General.ChannelName.stable="Стабільны" Basic.Settings.General.ChannelDescription.stable="Апошнія стабільныя рэлізы" Basic.Settings.General.ChannelName.beta="Бэта-версіі / перадрэлізныя версіі" Basic.Settings.General.ChannelDescription.beta="Патэнцыйна нестабільныя" +Basic.Settings.Appearance="Выгляд" +Basic.Settings.Appearance.General="Агульнае" +Basic.Settings.Appearance.General.Theme="Тэма" +Basic.Settings.Appearance.General.Variant="Стыль" +Basic.Settings.Appearance.General.NoVariant="Няма даступных стыляў" Basic.Settings.Stream="Трансляцыя" +Basic.Settings.Stream.Destination="Атрымальнік сігналу" Basic.Settings.Stream.Custom.UseAuthentication="Выкарыстоўваць уваход у акаўнт" Basic.Settings.Stream.Custom.Username="Імя карыстальніка" Basic.Settings.Stream.Custom.Password="Пароль" @@ -766,9 +777,22 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Максімальны біт Basic.Settings.Stream.Recommended.MaxAudioBitrate="Максімальны бітрэйт аўдыя: %1 кбіт/с" Basic.Settings.Stream.Recommended.MaxResolution="Максімальная раздзяляльнасць: %1" Basic.Settings.Stream.Recommended.MaxFPS="Максімальны FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Задаць уласны сервер…" +Basic.Settings.Stream.ServiceCustomServer="Уласны сервер" +Basic.Settings.Stream.EnableMultitrackVideo="Уключыць %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Ліміт паласы прапускання" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Аўта" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Максімум відэадарожак" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Аўта" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Запісваць стрым у FLV (з выкарыстаннем простых налад запісу)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Перазапіс файла канфігурацыі (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Уключыць перазапіс канфігурацыі" +Basic.Settings.Stream.MultitrackVideoLabel="Некалькі дарожак" +Basic.Settings.Stream.AdvancedOptions="Дадатковыя налады" Basic.Settings.Output="Вывад" Basic.Settings.Output.Format="Фармат запісу" Basic.Settings.Output.Format.MKV="Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="Гібрыдны MP4 [БЭТА] (.mp4)" Basic.Settings.Output.Format.fMP4="Фрагментаваны MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Фрагментаваны MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Відэа ў фрагментаваным фармаце MOV запісваецца кавалкамі; фіналізацыя, якую патрабуе традыцыйны фармат MOV, не патрабуецца.\nГэта азначае, што файл можна будзе прайграць, нават калі запіс на дыск быў перарваны, напрыклад, у выніку сіняга экрана смерці або адключэння электрычнасці.\n\nНекаторыя прайгравальнікі і рэдактары відэа такі фармат не падтрымліваюць. Каб адрэндарыць запіс у больш прыдатным фармаце, перайдзіце ў Файл → Перапакаваць запісы." @@ -798,8 +822,8 @@ Basic.Settings.Output.Simple.RecordingQuality.Stream="Як у трансляцы Basic.Settings.Output.Simple.RecordingQuality.Small="Высокая; сярэдні памер файла" Basic.Settings.Output.Simple.RecordingQuality.HQ="Надзвычайная; вялікі памер файла" Basic.Settings.Output.Simple.RecordingQuality.Lossless="Без страт; найвялікшы памер файла" -Basic.Settings.Output.Simple.Warn.VideoBitrate="Увага: бітрэйт відэа для трансляцыі зададзены на %1, што з'яўляецца гранічным бітрэйтам у выбраным стрымінгавым сэрвісе." -Basic.Settings.Output.Simple.Warn.AudioBitrate="Увага: бітрэйт аўдыя для трансляцыі зададзены на %1, што з'яўляецца гранічным бітрэйтам у выбраным стрымінгавым сэрвісе." +Basic.Settings.Output.Simple.Warn.VideoBitrate="Увага: будзе зададзены бітрэйт плыннага відэа, гранічны для выбранага стрымінгавага сэрвісу (%1)." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Увага: будзе зададзены бітрэйт плыннага аўдыя, гранічны для выбранага стрымінгавага сэрвісу (%1)." Basic.Settings.Output.Simple.Warn.CannotPause="Увага: пры зададзенай якасці запісу «Як у трансляцыі» прыпыненне запісу немагчымае." Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Увага: выбраны фармат запісу не сумяшчаецца з выбраным(і) кадавальнікам(і) плыні." Basic.Settings.Output.Simple.Warn.Encoder="Увага: калі вы ведзяце і запіс, і стрым адначасова і ў разных якасцях, прытым запіс вядзецца праз праграмны кадавальнік, для запісу спатрэбяцца дадатковыя рэсурсы працэсара." @@ -830,7 +854,7 @@ Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg="Сэрвіс %1 не Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="Сэрвіс %1 не падтрымлівае кадавальнікі %2 і %3. Замест іх будуць адпаведна прызначаны кадавальнікі %4 і %5.\n\nЦі жадаеце працягнуць?" Basic.Settings.Output.VideoBitrate="Бітрэйт відэа" Basic.Settings.Output.AudioBitrate="Бітрэйт аўдыя" -Basic.Settings.Output.Reconnect="Перазлучацца аўтаматычна" +Basic.Settings.Output.Reconnect="Аўтаматычнае перазлучэнне" Basic.Settings.Output.RetryDelay="Частата паўтору спробы" Basic.Settings.Output.MaxRetries="Макс. паўтораў" Basic.Settings.Output.Advanced="Уласныя налады кадавальніка (дадатковыя)" @@ -886,8 +910,8 @@ Basic.Settings.Output.Adv.FFmpeg.GOPSize="Інтэрвал ключавых ка Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Паказаць усе кодэкі (улічваючы патэнцыйна несумяшчальныя)" Basic.Settings.Output.Adv.FFmpeg.Settings="Налады FFmpeg" Basic.Settings.Output.EnableSplitFile="Аўтаматычнае раздзяленне файла" -Basic.Settings.Output.SplitFile.TypeTime="па даўжыні" -Basic.Settings.Output.SplitFile.TypeSize="па памеры" +Basic.Settings.Output.SplitFile.TypeTime="Па даўжыні" +Basic.Settings.Output.SplitFile.TypeSize="Па памеры" Basic.Settings.Output.SplitFile.TypeManual="Толькі ручое раздзяленне" Basic.Settings.Output.SplitFile.Time="Даўжыня фрагмента" Basic.Settings.Output.SplitFile.Size="Памер фрагмента" @@ -938,7 +962,7 @@ Basic.Settings.Video.Numerator="Лічнік" Basic.Settings.Video.Denominator="Назоўнік" Basic.Settings.Video.Renderer="Рэндарар" Basic.Settings.Video.InvalidResolution="Памылковая раздзяляльнасць. Правільны фармат: [шырыня]x[вышыня] (напр., 1920x1080)" -Basic.Settings.Video.CurrentlyActive="Працуе вывад відэа. Каб мяняць налады відэа, адключыце ўсе выхады відэа." +Basic.Settings.Video.CurrentlyActive="Працуе вывад відэа. Скончыце ўсе вывады, каб мяняць налады відэа." Basic.Settings.Video.DownscaleFilter.Bilinear="Білінейны (найхутчэйшы, але выява размываецца)" Basic.Settings.Video.DownscaleFilter.Bicubic="Бікубічны (рэзкае маштабаванне, 16 сэмплаў)" Basic.Settings.Video.DownscaleFilter.Lanczos="Ланцош (рэзкае маштабаванне, 36 сэмплаў)" @@ -1094,7 +1118,7 @@ SceneItemHide="Схаваць: %1" OutputWarnings.NoTracksSelected="Выберыце прынамсі адну дарожку" OutputWarnings.NoTracksSelectedOnExit.Title="Памылка налад вываду" OutputWarnings.NoTracksSelectedOnExit.Text="Ва ўсіх вывадаў павінна быць выбрана прынамсі адна аўдыядарожка." -OutputWarnings.MP4Recording="Увага: запісы ў фармаце MP4 або MOV немагчыма аднавіць, калі файл не быў фіналізаваны (г. зн. запіс быў завершаны з-за сіняга экрана смерці, адключэння электрычнасці і г. д.). Калі вы хочаце запісаць некалькі аўдыядарожак, скарыстайцеся фарматам MKV, затым, пасля завяршэння запісу, перапакуйце яго ў MP4 або MOV (Файл → Перапакаваць запісы)" +OutputWarnings.MP4Recording="Увага: запісы ў фармаце MP4 або MOV немагчыма аднавіць, калі запіс быў скончаны прымусова (з-за сіняга экрана смерці, адключэння электрычнасці і г. д.). Калі вы хочаце запісаць некалькі аўдыядарожак, скарыстайцеся фарматам MKV, пасля завяршэння запісу перапакуйце яго ў MP4 або MOV (Файл → Перапакаваць запісы)" OutputWarnings.CannotPause="Увага: пры зададзеным кадавальніку запісу «(кадавальнік плыні)» прыпыненне запісу немагчымае." OutputWarnings.CodecIncompatible="Прызначэнне кадавальніка аўдыя або відэа было скінута з-за несумяшчальнасці. Выберыце сумяшчальны кадавальнік са спіса." CodecCompat.Incompatible="(не сумяшчаецца з фарматам «%1»)" @@ -1228,4 +1252,28 @@ YouTube.Errors.liveChatEnded="Стрым скончаны." YouTube.Errors.messageTextInvalid="Памылковы тэкст паведамлення." YouTube.Errors.rateLimitExceeded="Вы адпраўляеце паведамленні занадта хутка." YouTube.DocksRemoval.Title="Ачысціць састарэлыя докі YouTube" -YouTube.DocksRemoval.Text="Гэтыя састарэлыя докі будуць выдалены:\n\n%1\nЗамест іх карыстайцеся «Докамі/Пакоем кіравання трансляцыяй YouTube»." +YouTube.DocksRemoval.Text="Гэтыя састарэлыя докі будуць выдалены:\n\n%1\nЗамест іх карыстайцеся докам «YouTube Live Control Panel»." +ConfigDownload.WarningMessageTitle="Увага" +FailedToStartStream.MissingConfigURL="Для бягучага сэрвісу няма даступных канфігурацыйных URL" +FailedToStartStream.NoCustomRTMPURLInSettings="Уласны RTMP URL не зададзены" +FailedToStartStream.InvalidCustomConfig="Памылковая ўласная канфігурацыя" +FailedToStartStream.FailedToCreateMultitrackVideoService="Не атрымалася стварыць сэрвіс відэа з некалькімі відэадарожкамі" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Не атрымалася стварыць RTMP-вывад відэа з некалькімі відэадарожкамі" +FailedToStartStream.EncoderNotAvailable="NVENC недаступны.\n\nНе атрымалася знайсці кадавальнік тыпу «%1»" +FailedToStartStream.FailedToCreateVideoEncoder="Не атрымалася стварыць кадавальнік відэа «%1» (тып: %2)" +FailedToStartStream.FailedToGetOBSVideoInfo="Падчас стварэння кадавальніка «%1» (тып: %2) узнікла памылка атрымання звестак аб відэа" +FailedToStartStream.FailedToCreateAudioEncoder="Не атрымалася стварыць кадавальнік аўдыя" +FailedToStartStream.NoRTMPURLInConfig="У канфігурацыі адсутнічае мэтавы RTMP(S) URL плыні" +FailedToStartStream.FallbackToDefault="Не атрымалася пачаць стрым праз %1. Ці жадаеце вы выкарыстаць прадвызначаныя налады кадавання?" +FailedToStartStream.ConfigRequestFailed="Канфігурацыя з %1 не атрымана

Памылка HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Атрымана невядомае значэнне стану '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nЦі жадаеце вы працягнуць стрым без %1?" +FailedToStartStream.WarningRetry="\n

\nЦі жадаеце вы працягнуць стрым?" +FailedToStartStream.MissingEncoderConfigs="Канфігурацыя плыні не ўтрымлівае налады кадавальніка" +FailedToStartStream.StatusMissingHTML="Падчас спробы пачатку плыні ўзнікла невядомая памылка" +FailedToStartStream.NoConfigSupplied="Канфігурацыя адсутнічае" +MultitrackVideo.Info="%1 аўтаматычна аптымізуе вашы налады пад кадаванне і адпраўленне відэа ў некалькіх якасцях. Пасля выбару гэтай опцыі сэрвісу %2 будуць адпраўлены звесткі аб вашым камп'ютары і ПЗ." +MultitrackVideo.IncompatibleSettings.Title="Несумяшчальныя налады" +MultitrackVideo.IncompatibleSettings.Text="У дадзены момант %1 не сумяшчаецца з наступнымі пунктамі:\n\n%2\nКаб працягнуць стрым з %1, адключыце несумяшчальныя налады:\n\n%3\nі зноў пачніце стрым." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Адключыць для гэтага стрыму" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Абнавіць налады і пачаць плынь" diff --git a/UI/data/locale/bg-BG.ini b/UI/data/locale/bg-BG.ini index 0c2e9b6cfd704a..8d958a04b92483 100644 --- a/UI/data/locale/bg-BG.ini +++ b/UI/data/locale/bg-BG.ini @@ -79,7 +79,7 @@ None="Без" StudioMode.Preview="Преглед" StudioMode.Program="Програма" StudioMode.PreviewSceneName="Нагледно: %1" -StudioMode.ProgramSceneName="Програма %S" +StudioMode.ProgramSceneName="Програма: %1" ShowInMultiview="Показване в множествен изглед" VerticalLayout="Вертикално разположение" Group="Група" @@ -112,7 +112,8 @@ PluginsFailedToLoad.Text="Неуспешно зареждане на съотв AlreadyRunning.Title="OBS вече се изпълнява" AlreadyRunning.Text="OBS вече е включен! Освен ако не е по желание, моля изключете другите работещи инстанции на OBS преди да включите нова. Ако сте настроили OBS да се минимизира във системната табла, моля проверете дали все още е включен там." AlreadyRunning.LaunchAnyway="Стартирай въпреки това" -AutoSafeMode.Title="Безопасен режим"\nAutoSafeMode.Text="\nOBS не се затвори коректно по време на последната ви сесия.\n\nИскате ли да стартирате в безопасен режим (изключени са сторонни плъгини, скриптове и уебсокети)?" +AutoSafeMode.Title="Безопасен режим" +AutoSafeMode.Text="OBS не се затвори коректно по време на последната ви сесия.\n\nИскате ли да стартирате в безопасен режим (изключени са сторонни плъгини, скриптове и уебсокети)?" AutoSafeMode.LaunchSafe="Пускане в безопасен режим" AutoSafeMode.LaunchNormal="Стартиране нормално" SafeMode.Restart="Искате ли да рестартирате OBS в безопасен режим (приставки на трети страни, скриптове, изключени WebSocket)?" @@ -153,7 +154,7 @@ BandwidthTest.Region.US="САЩ" BandwidthTest.Region.EU="Европа" BandwidthTest.Region.Asia="Азия" BandwidthTest.Region.Other="Друго" -Basic.AutoConfig="Съветник за автоматично конфигуриране" +Basic.AutoConfig="Вълшебник за самонастройване" Basic.AutoConfig.ApplySettings="Прилагане на настройките" Basic.AutoConfig.StartPage="Сведения за употреба" Basic.AutoConfig.StartPage.SubTitle="Посочете за какво искате да използвате програмата" @@ -641,6 +642,7 @@ Basic.MainMenu.View.StatusBar="Статус лента (&S)" Basic.MainMenu.View.Fullscreen.Interface="Интерфейс на цял екран" Basic.MainMenu.View.ResetUI="&Нулиране на потреб. интерфейс" Basic.MainMenu.View.AlwaysOnTop="&Винаги най-отгоре" +Basic.MainMenu.View.SceneListMode="Режим списък със сцени" Basic.MainMenu.Docks="Закачваеми панели (&D)" Basic.MainMenu.Docks.CustomBrowserDocks="Ползвателски браузърни зак. панели(&C)" Basic.MainMenu.SceneCollection="Колекция със сцени (&S)" @@ -674,7 +676,6 @@ Basic.Settings.ProgramRestart="Програмата трябва да бъде Basic.Settings.ConfirmTitle="Потвърди промените" Basic.Settings.Confirm="Имате незаписани промени. Запиши промените?" Basic.Settings.General="Общи" -Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Език" Basic.Settings.General.Updater="Обновявания" Basic.Settings.General.UpdateChannel="Канал за обновяване" @@ -732,6 +733,11 @@ Basic.Settings.General.MultiviewLayout.25Scene="Само сцени (25 сцен Basic.Settings.General.ChannelName.stable="Стабилна" Basic.Settings.General.ChannelDescription.stable="Последна стабилна версия" Basic.Settings.General.ChannelName.beta="Бета версии / Кандидати за издаване" +Basic.Settings.Appearance="Вид" +Basic.Settings.Appearance.General="Общи" +Basic.Settings.Appearance.General.Theme="Тема" +Basic.Settings.Appearance.General.Variant="Стил" +Basic.Settings.Appearance.General.NoVariant="Няма налични стилове" Basic.Settings.Stream="Стрийм" Basic.Settings.Stream.Custom.UseAuthentication="Използване на удостоверяване" Basic.Settings.Stream.Custom.Username="Потребителско име" @@ -755,6 +761,7 @@ Basic.Settings.Stream.Recommended.MaxFPS="Максимални кадри в с Basic.Settings.Output="Изход" Basic.Settings.Output.Format="Формат на записа" Basic.Settings.Output.Format.MKV="Видеоформат Матрьошка (.mkv)" +Basic.Settings.Output.Format.hMP4="Хибрид MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Фрагментиран MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Фрагментиран MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Фрагментирания MOV записва запиза във парчета и не изисква същото финализиране като традиционните MOV файлове.\nТова осигурява че файла остава годен за възпроизвеждане, дори ако записът на диска бъде прекъснат, за пример, в резултат на BSOD или загуба на захранване.\n\nТова може да не е съвместимо с всички плейъри и редактори. Използвайте Файл → Remux Recordings за да конвертирайте файла на по-съвместим формат, ако е необходимо." @@ -824,7 +831,7 @@ Basic.Settings.Output.NoSpaceFileName="Създавай името на запи Basic.Settings.Output.Adv.Rescale="Умащабяване на Изхода" Basic.Settings.Output.Adv.Rescale.Disabled="Изключено" Basic.Settings.Output.Adv.AudioTrack="Звукова писта" -Basic.Settings.Output.Adv.Streaming="Предаване" +Basic.Settings.Output.Adv.Streaming="Поточно предаване" Basic.Settings.Output.Adv.Streaming.Settings="Настройки за поточно предаване" Basic.Settings.Output.Adv.Audio.Track1="Писта 1" Basic.Settings.Output.Adv.Audio.Track2="Писта 2" @@ -949,6 +956,7 @@ Basic.Settings.Audio.UnknownAudioDevice="[Устройството не е св Basic.Settings.Audio.Disabled="Деактивирано" Basic.Settings.Accessibility="Достъпност" Basic.Settings.Accessibility.ColorOverrides="Употреба на различни цветове" +Basic.Settings.Accessibility.ColorOverrides.Preset="Цветови образци" Basic.Settings.Accessibility.ColorOverrides.Preset.Default="По подразбиране" Basic.Settings.Accessibility.ColorOverrides.Preset.Custom="Персонализиран" Basic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1="Цвят за далтонисти" @@ -976,6 +984,7 @@ Basic.Settings.Advanced.Video.ColorRange="Цветови обхват" Basic.Settings.Advanced.Video.ColorRange.Partial="Ограничен" Basic.Settings.Advanced.Video.ColorRange.Full="Пълен" Basic.Settings.Advanced.Video.SdrWhiteLevel="SDR ниво на бялото" +Basic.Settings.Advanced.Video.HdrNominalPeakLevel="Номинално пиково ниво на HDR " Basic.Settings.Advanced.Audio.MonitoringDevice="Устройство за наблюдение" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="По подразбиране" Basic.Settings.Advanced.Audio.DisableAudioDucking="Изключи намалянето на звука при разговори" diff --git a/UI/data/locale/bn-BD.ini b/UI/data/locale/bn-BD.ini index fc3f0527ac7e85..bf4fc4df5b2709 100644 --- a/UI/data/locale/bn-BD.ini +++ b/UI/data/locale/bn-BD.ini @@ -583,7 +583,6 @@ Basic.Settings.ProgramRestart="এই প্রোগ্রামটি এই Basic.Settings.ConfirmTitle="পরিবর্তন করুন" Basic.Settings.Confirm="পরিবর্তন অসংরক্ষিত সংশোধনী রয়েছে। পরিবর্তন সংরক্ষণ করব?" Basic.Settings.General="সাধারণ" -Basic.Settings.General.Theme="থিম" Basic.Settings.General.Language="ভাষা" Basic.Settings.General.Updater="হালনাগাদ" Basic.Settings.General.EnableAutoUpdates="প্রারম্ভকালে আপডেটের জন্য স্বয়ংক্রিয়ভাবে চেক করুন" diff --git a/UI/data/locale/ca-ES.ini b/UI/data/locale/ca-ES.ini index 7eb4369bf83410..ce436ec4883b2b 100644 --- a/UI/data/locale/ca-ES.ini +++ b/UI/data/locale/ca-ES.ini @@ -106,6 +106,7 @@ MixerToolbarMenu="Menú del mesclador d'àudio" SceneFilters="Obre els filtres d'escena" List="Llista" Grid="Graella" +Automatic="Automàtic" PluginsFailedToLoad.Title="Error en iniciar complements" PluginsFailedToLoad.Text="S'ha produït un error en iniciar aquests complements de l'OBS:\n\n%1\nActualitzeu o suprimiu aquests complements." AlreadyRunning.Title="L'OBS ja s'està executant" @@ -192,6 +193,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Prioritza la codificació pe Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="La codificació per maquinari elimina la majoria de l'ús de la CPU, però pot requerir una taxa de bits superior per obtenir el mateix nivell de qualitat." Basic.AutoConfig.StreamPage.StreamWarning.Title="Advertència de la transmissió" Basic.AutoConfig.StreamPage.StreamWarning.Text="La prova d'amplada de banda està a punt de transmetre dades de vídeo aleatòries sense àudio al vostre canal. Es recomana quan sigui possible desactivar temporalment que es desin els vídeos de les transmissions i fer la transmissió privada després que la prova hagi finalitzat. Voleu continuar?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Prova %1" Basic.AutoConfig.TestPage="Resultat final" Basic.AutoConfig.TestPage.SubTitle.Testing="El programa ara està executant un conjunt de proves per estimar la configuració òptima" Basic.AutoConfig.TestPage.SubTitle.Complete="Prova finalitzada" @@ -210,6 +212,7 @@ Basic.AutoConfig.TestPage.Result.Header="El programa ha determinat que aquesta c Basic.AutoConfig.TestPage.Result.Footer="Per a utilitzar aquesta configuració, feu clic a Aplica la configuració. Per a tornar a configurar l'assistent, feu clic a Enrere. Per configurar els paràmetres personalment, feu clic a Cancel·la i obriu la configuració." Basic.AutoConfig.Info="L'assistent de configuració automàtica determinarà la millor configuració segons les especificacions del vostre ordinador i la velocitat d'Internet." Basic.AutoConfig.RunAnytime="Podeu iniciar-ho en qualsevol moment des del menú Eines." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Resolució de transmissió (escalada)" Basic.Stats="Estadístiques" Basic.Stats.CPUUsage="Ús de CPU" Basic.Stats.HDDSpaceAvailable="Espai al disc disponible" @@ -558,6 +561,7 @@ Basic.Main.AddSourceHelp.Text="Cal tenir com a mínim 1 escena per afegir una fo Basic.Main.Scenes="Escenes" Basic.Main.Sources="Orígens" Basic.Main.Source="Origen" +Basic.Main.PreparingStream="S'està preparant..." Basic.Main.Connecting="S'està connectant..." Basic.Main.StartRecording="Inicia l'enregistrament" Basic.Main.StartReplayBuffer="Inicia la reproducció de la memòria intermèdia" @@ -569,6 +573,7 @@ Basic.Main.StopRecording="Atura l'enregistrament" Basic.Main.PauseRecording="Pausa la gravació" Basic.Main.UnpauseRecording="Reprèn la gravació" Basic.Main.SplitFile="Divideix el fitxer enregistrat" +Basic.Main.AddChapterMarker="Afegeix un marcador de capítol" Basic.Main.StoppingRecording="Aturant l'enregistrament..." Basic.Main.StopReplayBuffer="Atura la reproducció de la memòria intermèdia" Basic.Main.StoppingReplayBuffer="S'està aturant la reproducció de la memòria intermèdia..." @@ -677,7 +682,7 @@ Basic.MainMenu.Help.About="Qu&ant a" Basic.Settings.ProgramRestart="El programa ha de ser re-iniciat per tal que aquesta configuració tingui efecte." Basic.Settings.ConfirmTitle="Confirma els canvis" Basic.Settings.Confirm="Hi han canvis no desats. Voleu desar els canvis?" -Basic.Settings.General.Theme="Tema" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 controla algunes de les vostres opcions de reproducció" Basic.Settings.General.Language="Llengua" Basic.Settings.General.Updater="Actualitzacions" Basic.Settings.General.UpdateChannel="Actualitza el canal" @@ -736,7 +741,12 @@ Basic.Settings.General.ChannelName.stable="Estable" Basic.Settings.General.ChannelDescription.stable="Últim llançament estable" Basic.Settings.General.ChannelName.beta="Beta / versio candidata" Basic.Settings.General.ChannelDescription.beta="Versions prèvies potencialment inestables" +Basic.Settings.Appearance="Aparença" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Estil" +Basic.Settings.Appearance.General.NoVariant="No hi ha estils disponibles" Basic.Settings.Stream="Directe" +Basic.Settings.Stream.Destination="Destinació" Basic.Settings.Stream.Custom.UseAuthentication="Utilitza l'autenticació" Basic.Settings.Stream.Custom.Username="Usuari" Basic.Settings.Stream.Custom.Password="Contrasenya" @@ -758,9 +768,20 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Taxa de bits màxima de víde Basic.Settings.Stream.Recommended.MaxAudioBitrate="Taxa de bits màxima d'àudio: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Resolució màxima: %1" Basic.Settings.Stream.Recommended.MaxFPS="FPS màxims: %1" +Basic.Settings.Stream.SpecifyCustomServer="Especifica el servidor personalitzat..." +Basic.Settings.Stream.ServiceCustomServer="Servidor personalitzat" +Basic.Settings.Stream.EnableMultitrackVideo="Activa %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Ample de banda màxim de transmissió" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Màxim de pistes de vídeo" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Activa l'abocament de flux a FLV (utilitza una configuració senzilla del fitxer de gravació)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Anul·la de configuració (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Activa l'anul·lació de la configuració" +Basic.Settings.Stream.MultitrackVideoLabel="Vídeo multipista" +Basic.Settings.Stream.AdvancedOptions="Opcions avançades" Basic.Settings.Output="Sortida" Basic.Settings.Output.Format="Format d'enregistrament" Basic.Settings.Output.Format.MKV="Vídeo Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 híbrid [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 fragmentat (.mp4)" Basic.Settings.Output.Format.fMOV="MOV fragmentat (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="El MOV fragmentat escriu l'enregistrament en trossos i no requereix la mateixa finalització que els fitxers MOV tradicionals.\nAixò garanteix que el fitxer es pugui reproduir fins i tot si s'interromp l'escriptura al disc, per exemple, com a resultat d'un BSOD o una pèrdua d'energia.\n\nÉs possible que això no sigui compatible amb tots els reproductors i editors. Utilitzeu Fitxer → Enregistraments remux per convertir el fitxer a un format més compatible si cal." @@ -1229,3 +1250,25 @@ YouTube.Errors.messageTextInvalid="El text del missatge no és vàlid." YouTube.Errors.rateLimitExceeded="Esteu enviant missatges massa ràpid." YouTube.DocksRemoval.Title="Neteja els acobladors de navegador heretats del YouTube" YouTube.DocksRemoval.Text="Aquests acobladors se suprimiran per ser obsolets:\n\n%1\nUtilitzeu \"Acobladors/Control de directes del YouTube\"." +ConfigDownload.WarningMessageTitle="Avís" +FailedToStartStream.MissingConfigURL="No hi ha cap URL de configuració disponible per al servei actual" +FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP personalitzada no especificada" +FailedToStartStream.InvalidCustomConfig="Configuració personalitzada no vàlida" +FailedToStartStream.FailedToCreateMultitrackVideoService="No s'ha pogut crear el servei de vídeo multipista" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="No s'ha pogut crear la sortida rtmp de vídeo multipista" +FailedToStartStream.EncoderNotAvailable="NVENC no disponible.\n\nNo s'ha pogut trobar el tipus de codificador «%1»" +FailedToStartStream.FailedToCreateVideoEncoder="No s'ha pogut crear el codificador de vídeo «%1» (tipus: «%2»)" +FailedToStartStream.FailedToGetOBSVideoInfo="No s'ha pogut obtenir la informació del vídeo d'obs mentre es crea el codificador «%1» (tipus: «%2»)" +FailedToStartStream.FailedToCreateAudioEncoder="No s'ha pogut crear el codificador d'àudio" +FailedToStartStream.NoRTMPURLInConfig="La configuració no conté l'URL RTMP(S) de destinació del flux" +FailedToStartStream.FallbackToDefault="No s'ha pogut iniciar la transmissió amb %1. Voleu tornar-ho a provar amb la configuració de codificació única?" +FailedToStartStream.ConfigRequestFailed="No s'ha pogut obtenir la configuració de %1

Error HTTP: %2" +FailedToStartStream.WarningUnknownStatus="S'ha rebut el valor d'estat desconegut «%1»" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nVoleu continuar en directe sense %1?" +FailedToStartStream.WarningRetry="\n

\nVoleu continuar en directe?" +FailedToStartStream.NoConfigSupplied="Falta la configuració" +MultitrackVideo.Info="%1 optimitza automàticament la vostra configuració per codificar i enviar múltiples qualitats de vídeo. Si seleccioneu aquesta opció, s'enviarà %2 informació sobre el vostre ordinador i la configuració del programari." +MultitrackVideo.IncompatibleSettings.Title="Configuració incompatible" +MultitrackVideo.IncompatibleSettings.Text="%1 actualment no és compatible amb:\n\n%2\nPer continuar en directe amb %1, desactiveu els paràmetres incompatibles:\n\n%3\ni torneu a començar la transmissió." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Desactiva per aquesta transmissió i comença el directe" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Actualitza la configuració i comença la transmissió" diff --git a/UI/data/locale/cs-CZ.ini b/UI/data/locale/cs-CZ.ini index 5e241dd425f4bc..93b1da3f8c260b 100644 --- a/UI/data/locale/cs-CZ.ini +++ b/UI/data/locale/cs-CZ.ini @@ -569,6 +569,7 @@ Basic.Main.StopRecording="Zastavit nahrávání" Basic.Main.PauseRecording="Pozastavit nahrávání" Basic.Main.UnpauseRecording="Pokračovat v nahrávání" Basic.Main.SplitFile="Rozdělit soubor nahrávky" +Basic.Main.AddChapterMarker="Přidat značku kapitoly" Basic.Main.StoppingRecording="Zastavuji nahrávání..." Basic.Main.StopReplayBuffer="Zastavit záznam do paměti" Basic.Main.StoppingReplayBuffer="Zastavuji záznam do paměti..." @@ -678,7 +679,6 @@ Basic.Settings.ProgramRestart="Pro projevení nastavení je potřeba restartovat Basic.Settings.ConfirmTitle="Potvrzení změn" Basic.Settings.Confirm="Některé změny nejsou uložené. Chcete je uložit nyní ?" Basic.Settings.General="Hlavní" -Basic.Settings.General.Theme="Vzhled" Basic.Settings.General.Language="Jazyk" Basic.Settings.General.Updater="Aktualizace" Basic.Settings.General.UpdateChannel="Kanál aktualizací" @@ -737,6 +737,11 @@ Basic.Settings.General.ChannelName.stable="Stabilní" Basic.Settings.General.ChannelDescription.stable="Nejnovější stabilní vydání" Basic.Settings.General.ChannelName.beta="Beta / kandidáti vydání" Basic.Settings.General.ChannelDescription.beta="Potenciálně nestabilní předběžné verze" +Basic.Settings.Appearance="Vzhled" +Basic.Settings.Appearance.General="Obecné" +Basic.Settings.Appearance.General.Theme="Motiv" +Basic.Settings.Appearance.General.Variant="Styl" +Basic.Settings.Appearance.General.NoVariant="Nejsou dostupné žádné styly" Basic.Settings.Stream="Vysílání" Basic.Settings.Stream.Custom.UseAuthentication="Použít přihlášení" Basic.Settings.Stream.Custom.Username="Uživatelské jméno" @@ -761,6 +766,7 @@ Basic.Settings.Stream.Recommended.MaxResolution="Maximální rozlišení: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximální FPS: %1" Basic.Settings.Output="Výstup" Basic.Settings.Output.Format="Formát nahrávání" +Basic.Settings.Output.Format.hMP4="Hybridní MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmentované MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmentované MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmentované MOV zapisuje záznam po částech a nevyžaduje stejnou finalizaci jako tradiční soubory MOV.\nToto zaručí, že bude možné soubor přehrát i přes přerušení zápisu souboru na disk, třeba z důvodu BSOD nebo ztráty napájení.\n\nToto nemusí být kompatibilní se všemi přehrávači a editory. V případě potřeby použijte Soubor → Převést nahrávky pro převod souboru do kompatibilnějšího formátu." @@ -1178,7 +1184,7 @@ YouTube.Actions.Create_Schedule_Ready="Naplánovat a vybrat vysílání" YouTube.Actions.Dashboard="Otevřít YouTube Studio" YouTube.Actions.Error.Title="Chyba při vytváření živého vysílání" YouTube.Actions.Error.Text="Chyba přístupu k YouTube '%1'.
Detailní popis chyby můžete najít na https://developers.google.com/youtube/v3/live/docs/errors" -YouTube.Actions.Error.General="Chyba přístupu k YouTube. Zkontrolujte prosím své připojení k internetu nebo přístup k \nYouTube serverům." +YouTube.Actions.Error.General="Chyba přístupu k YouTube. Zkontrolujte prosím své připojení k internetu nebo přístup k YouTube serverům." YouTube.Actions.Error.NoBroadcastCreated="Chyba při vytváření vysílání '%1'.
Detailní popis chyby můžete najít na https://developers.google.com/youtube/v3/live/docs/errors" YouTube.Actions.Error.NoStreamCreated="Nebylo vytvořeno žádné vysílání. Připojte účet znovu." YouTube.Actions.Error.YouTubeApi="Chyba YouTube API. Pro více informací otevřete soubor záznamu." diff --git a/UI/data/locale/da-DK.ini b/UI/data/locale/da-DK.ini index e288ffa253363d..9f0dd5f2e62c70 100644 --- a/UI/data/locale/da-DK.ini +++ b/UI/data/locale/da-DK.ini @@ -620,7 +620,6 @@ Basic.Settings.ProgramRestart="Programmet skal genstartes, for at effektuere dis Basic.Settings.ConfirmTitle="Bekræft ændringer" Basic.Settings.Confirm="Du har ugemte ændringer, gem dem nu?" Basic.Settings.General="Generelt" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Sprog" Basic.Settings.General.Updater="Opdateringer" Basic.Settings.General.UpdateChannelDisabled="(Deaktiveret)" diff --git a/UI/data/locale/de-DE.ini b/UI/data/locale/de-DE.ini index 52930877ff3298..c25fed691b3a06 100644 --- a/UI/data/locale/de-DE.ini +++ b/UI/data/locale/de-DE.ini @@ -17,7 +17,7 @@ Properties="Eigenschaften" MoveUp="Hochbewegen" MoveDown="Runterbewegen" Settings="Einstellungen" -Display="Bildschirm" +Display="Monitor" Exit="Beenden" Mixer="Audiomixer" Browse="Durchsuchen" @@ -44,7 +44,7 @@ Duplicate="Duplizieren" Enable="Aktivieren" DisableOSXVSync="macOS-VSync deaktivieren" ResetOSXVSyncOnExit="macOS-VSync beim Beenden zurücksetzen" -HighResourceUsage="Codierung überlastet. Reduzieren Sie die Videoeinstellungen oder verwenden Sie eine schnellere Encodervoreinstellung." +HighResourceUsage="Kodierung überlastet. Reduzieren Sie die Videoeinstellungen oder verwenden Sie eine schnellere Kodierervoreinstellung." Transition="Übergang" QuickTransitions="Schnellübergänge" FadeToBlack="Schwarzüberblende" @@ -105,13 +105,14 @@ MixerToolbarMenu="Audiomixermenü" SceneFilters="Szenenfilter öffnen" List="Liste" Grid="Raster" +Automatic="Automatisch" PluginsFailedToLoad.Title="Fehler beim Laden von Plugins" PluginsFailedToLoad.Text="Folgende OBS-Plugins konnten nicht geladen werden:\n\n%1\nBitte aktualisieren oder entfernen Sie sie." AlreadyRunning.Title="OBS wird bereits ausgeführt" AlreadyRunning.Text="OBS wird bereits ausgeführt und sollte falls gewünscht vor dem Start einer weiteren Instanz (die möglicherweise im Infobereich ist) beendet werden." AlreadyRunning.LaunchAnyway="Trotzdem starten" AutoSafeMode.Title="Abgesicherter Modus" -AutoSafeMode.Text="OBS wurde während Ihrer letzten Sitzung nicht ordnungsgemäß beendet.\n\nMöchten Sie im abgesicherten Modus (mit deaktivierten Drittanbieter-Plugins, Skripte und WebSockets) starten?" +AutoSafeMode.Text="OBS wurde während Ihrer letzten Sitzung nicht ordnungsgemäß beendet.\n\nMöchten Sie im abgesicherten Modus (mit deaktivierten Drittanbieter-Plugins, Skripten und WebSockets) starten?" AutoSafeMode.LaunchSafe="Im abgesicherten Modus starten" AutoSafeMode.LaunchNormal="Normal starten" SafeMode.Restart="Möchten Sie OBS im abgesicherten Modus (mit deaktivierten Drittanbieter-Plugins, Skripte, und WebSockets) neu starten?" @@ -119,7 +120,7 @@ SafeMode.RestartNormal="Möchten Sie OBS normal neu starten?" ChromeOS.Title="Nicht unterstützte Plattform" ChromeOS.Text="OBS scheint in einem ChromeOS-Container zu laufen. Diese Plattform wird nicht unterstützt." Wine.Title="Wine erkannt" -Wine.Text="Das Ausführen von OBS in Wine wird nicht unterstützt und viele Funktionen wie Aufnahme- oder Gerätequellen funktionieren nur teils oder gar nicht.

Es wird empfohlen, stattdessen eine native Version von OBS auszuführen, zum Beispiel unsere Flatpak-Version oder die Pakete Ihres Betriebssystems." +Wine.Text="OBS in Wine auszuführen wird nicht unterstützt und viele Funktionen wie Aufnahme- oder Gerätequellen funktionieren nur teils oder gar nicht.

Es wird empfohlen, stattdessen eine native Version von OBS zu verwenden, zum Beispiel unsere Flatpak-Version oder die Pakete Ihres Betriebssystems." DockCloseWarning.Title="Dockbares Fenster schließen" DockCloseWarning.Text="Sie haben gerade ein dockbares Fenster geschlossen. Wenn Sie es erneut anzeigen möchten, verwenden Sie das Docks-Menü in der Menüleiste." ExtraBrowsers="Benutzerdefinierte Browser-Docks" @@ -160,7 +161,7 @@ Basic.AutoConfig.StartPage.PrioritizeVirtualCam="Ich werde nur die virtuelle Kam Basic.AutoConfig.VideoPage="Videoeinstellungen" Basic.AutoConfig.VideoPage.SubTitle="Geben Sie die gewünschten Videoeinstellungen an." Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Aktuelle verwenden (%1 × %2)" -Basic.AutoConfig.VideoPage.BaseResolution.Display="Bildschirm %1 (%2 × %3)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="Monitor %1 (%2 × %3)" Basic.AutoConfig.VideoPage.FPS.UseCurrent="Aktuelle verwenden (%1)" Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Entweder 60 oder 30, aber wenn möglich 60 bevorzugen" Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Entweder 60 oder 30, aber hohe Auflösung bevorzugen" @@ -180,13 +181,14 @@ Basic.AutoConfig.StreamPage.Service.ShowAll="Alle anzeigen …" Basic.AutoConfig.StreamPage.Service.Custom="Benutzerdefiniert …" Basic.AutoConfig.StreamPage.StreamKey="Streamschlüssel" Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: Geben Sie das Verschlüsselungspasswort ein.\nRTMP: Geben Sie den vom Dienst bereitgestellten Schlüssel ein.\nSRT: Geben Sie die streamid ein, wenn die Plattform eine verwendet." -Basic.AutoConfig.StreamPage.EncoderKey="Encoderschlüssel" +Basic.AutoConfig.StreamPage.EncoderKey="Kodiererschlüssel" Basic.AutoConfig.StreamPage.ConnectedAccount="Verbundenes Konto" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Bitrate mit Bandbreitentest schätzen (kann einige Minuten dauern)" -Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hardwarecodierung bevorzugen" -Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Bei der Hardwareencodierung entfällt die meiste CPU-Nutzung, es kann aber eine höhere Bitrate erforderlich sein, um das gleiche Qualitätsniveau zu erreichen." +Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hardwarekodierung bevorzugen" +Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Bei der Hardwarekodierung entfällt die meiste CPU-Nutzung, es kann aber eine höhere Bitrate erforderlich sein, um das gleiche Qualitätsniveau zu erreichen." Basic.AutoConfig.StreamPage.StreamWarning.Title="Streamwarnung" Basic.AutoConfig.StreamPage.StreamWarning.Text="Der Bandbreitentest streamt zufällige Videodaten ohne Ton zu Ihrem Kanal. Es ist empfohlen, das Speichern von Videos vorrübergehend zu deaktivieren und den Stream privat zu schalten, bis der Test abgeschlossen ist. Möchten Sie fortfahren?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="%1 testen" Basic.AutoConfig.TestPage="Endergebnisse" Basic.AutoConfig.TestPage.SubTitle.Testing="Das Programm führt nun eine Reihe von Tests durch, um optimale Einstellungen zu finden." Basic.AutoConfig.TestPage.SubTitle.Complete="Tests abgeschlossen" @@ -195,22 +197,23 @@ Basic.AutoConfig.TestPage.TestingBandwidth.NoOutput="Keine Ausgabe für das Prot Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Verbindungsaufbau mit: %1 …" Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Keine Verbindung zu Servern möglich, bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut." Basic.AutoConfig.TestPage.TestingBandwidth.Server="Bandbreite wird getestet für: %1" -Basic.AutoConfig.TestPage.TestingStreamEncoder="Streamencoder wird getestet, dies kann einige Minuten dauern …" -Basic.AutoConfig.TestPage.TestingRecordingEncoder="Aufnahmeencoder wird getestet, dies kann einige Minuten dauern …" -Basic.AutoConfig.TestPage.TestingRes.Fail="Starten des Encoders fehlgeschlagen" +Basic.AutoConfig.TestPage.TestingStreamEncoder="Streamkodierer wird getestet, dies kann einige Minuten dauern …" +Basic.AutoConfig.TestPage.TestingRecordingEncoder="Aufnahmekodierer wird getestet, dies kann einige Minuten dauern …" +Basic.AutoConfig.TestPage.TestingRes.Fail="Starten des Kodierers fehlgeschlagen" Basic.AutoConfig.TestPage.TestingRes.Resolution="%1 × %2 mit %3 FPS wird getestet …" -Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streamencoder" -Basic.AutoConfig.TestPage.Result.RecordingEncoder="Aufnahmeencoder" +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Streamkodierer" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Aufnahmekodierer" Basic.AutoConfig.TestPage.Result.Header="Das Programm hat festgestellt, dass diese geschätzten Einstellungen für Sie optimal sind:" Basic.AutoConfig.TestPage.Result.Footer="Um den Assistenten neu zu konfigurieren und es erneut zu versuchen, klicken Sie auf „Zurück“. Um die Einstellungen selbst anzupassen, klicken Sie auf „Abbrechen“ und öffnen Sie die Einstellungen." Basic.AutoConfig.Info="Der Einrichtungsassistent ermittelt die besten Einstellungen basierend auf Ihren Computerspezifikationen und der Internetgeschwindigkeit." Basic.AutoConfig.RunAnytime="Er kann jederzeit über das Werkzeuge-Menü ausgeführt werden." +Basic.AutoConfig.TestPage.Result.StreamingResolution="(Skalierte) Auflösung wird gestreamt" Basic.Stats="Statistiken" Basic.Stats.CPUUsage="CPU-Auslastung" -Basic.Stats.HDDSpaceAvailable="Speicherplatz verfügbar" +Basic.Stats.HDDSpaceAvailable="Verfügbarer Speicherplatz" Basic.Stats.MemoryUsage="RAM-Nutzung" Basic.Stats.AverageTimeToRender="Durchschnittliche Framerenderzeit" -Basic.Stats.SkippedFrames="Übersprungene Frames durch Codierungsverzögerung" +Basic.Stats.SkippedFrames="Übersprungene Frames durch Kodierungsverzögerung" Basic.Stats.MissedFrames="Ausgelassene Frames durch Renderverzögerung" Basic.Stats.Output.Recording="Aufnahme" Basic.Stats.Status.Recording="Aufnehmen" @@ -327,7 +330,7 @@ Output.StartStreamFailed="Starten des Streams fehlgeschlagen" Output.StartRecordingFailed="Starten der Aufnahme fehlgeschlagen" Output.StartReplayFailed="Starten des Wiederholungspuffers fehlgeschlagen" Output.StartVirtualCamFailed="Starten der virtuellen Kamera fehlgeschlagen" -Output.StartFailedGeneric="Starten der Ausgabe fehlgeschlagen. Bitte überprüfen Sie die Logdatei für Details.\n\nHinweis: Wenn Sie die NVENC- oder AMD-Encoder verwenden, stellen Sie sicher, dass Ihre Grafiktreiber aktuell sind." +Output.StartFailedGeneric="Starten der Ausgabe fehlgeschlagen. Bitte überprüfen Sie die Logdatei für Details.\n\nHinweis: Wenn Sie die NVENC- oder AMD-Kodierer verwenden, stellen Sie sicher, dass Ihre Grafiktreiber aktuell sind." Output.ReplayBuffer.PauseWarning.Title="Beim Pausieren können keine Wiederholungen gespeichert werden." Output.ReplayBuffer.PauseWarning.Text="Achtung: Wiederholungen können beim Pausieren der Aufnahme nicht gespeichert werden." Output.ConnectFail.Title="Verbindung fehlgeschlagen" @@ -337,17 +340,17 @@ Output.ConnectFail.InvalidStream="Auf den angegebenen Kanal oder Streamschlüsse Output.ConnectFail.HdrDisabled="HDR Ausgabe ist für diesen Ausgang momentan deaktiviert." Output.ConnectFail.Error="Ein unerwarteter Fehler ist beim Verbindungsversuch zum Server aufgetreten. Mehr Informationen finden Sie in der Logdatei." Output.ConnectFail.Disconnected="Verbindung zum Server getrennt." -Output.StreamEncodeError.Title="Codierungsfehler" -Output.StreamEncodeError.Msg="Ein Encoderfehler ist beim Streamen aufgetreten." -Output.StreamEncodeError.Msg.LastError="Beim Streamen ist ein Encoderfehler aufgetreten:

%1" +Output.StreamEncodeError.Title="Kodierungsfehler" +Output.StreamEncodeError.Msg="Ein Kodiererfehler ist beim Streamen aufgetreten." +Output.StreamEncodeError.Msg.LastError="Beim Streamen ist ein Kodiererfehler aufgetreten:

%1" Output.RecordFail.Title="Starten der Aufnahme fehlgeschlagen" Output.RecordFail.Unsupported="Das Ausgabeformat wird entweder nicht unterstützt oder unterstützt nicht mehrere Audiospuren. Bitte überprüfen Sie Ihre Einstellungen und versuchen Sie es erneut." Output.RecordNoSpace.Title="Nicht genügend Speicherplatz" Output.RecordNoSpace.Msg="Es gibt nicht genügend Speicherplatz, um die Aufnahme fortzusetzen." Output.RecordError.Title="Aufnahmefehler" Output.RecordError.Msg="Während der Aufnahme ist ein unbekannter Fehler aufgetreten." -Output.RecordError.EncodeErrorMsg="Ein Encoderfehler ist beim Aufnehmen aufgetreten." -Output.RecordError.EncodeErrorMsg.LastError="Beim Aufnehmen ist ein Encoderfehler aufgetreten:

%1" +Output.RecordError.EncodeErrorMsg="Ein Kodiererfehler ist beim Aufnehmen aufgetreten." +Output.RecordError.EncodeErrorMsg.LastError="Beim Aufnehmen ist ein Kodiererfehler aufgetreten:

%1" Output.BadPath.Title="Ungültiger Dateipfad" Output.BadPath.Text="Der konfigurierte Aufnahmepfad konnte nicht geöffnet werden. Bitte überprüfen Sie Ihren Aufnahmepfad unter „Einstellungen“ → „Ausgabe“ → „Aufnahme“." Output.NoBroadcast.Title="Keine Übertragung konfiguriert" @@ -396,7 +399,7 @@ MissingFiles.Found="Gefunden" MissingFiles.AutoSearch="Weitere Dateitreffer gefunden" MissingFiles.AutoSearchText="OBS hat weitere Treffer für fehlende Dateien in diesem Ordner gefunden. Möchten Sie diese hinzufügen?" MissingFiles.NoMissing.Title="Auf fehlende Dateien prüfen" -MissingFiles.NoMissing.Text="Es wurden keine fehlenden Dateien gefunden." +MissingFiles.NoMissing.Text="Es scheinen keine Dateien zu fehlen." MacPermissions.MenuAction="App-Berechtigungen überprüfen …" MacPermissions.Title="App-Berechtigungen überprüfen" MacPermissions.Description="OBS Studio benötigt für bestimmte Funktionen zusätzliche Berechtigungen, die zwar empfohlen, aber nicht für die Benutzung des Programms erforderlich sind. Sie können diese jederzeit später aktivieren." @@ -422,7 +425,7 @@ Basic.AuxDevice2="Mikrofon-/AUX-Audio 2" Basic.AuxDevice3="Mikrofon-/AUX-Audio 3" Basic.AuxDevice4="Mikrofon-/AUX-Audio 4" Basic.Scene="Szene" -Basic.DisplayCapture="Bildschirmaufnahme" +Basic.DisplayCapture="Monitoraufnahme" Basic.Main.PreviewConextMenu.Enable="Vorschau aktivieren" Basic.Main.Preview.Disable="Vorschau deaktivieren" ScaleFiltering="Skalierungsfilterung" @@ -547,6 +550,7 @@ Basic.Main.Scenes="Szenen" Basic.Main.Sources="Quellen" Basic.Main.Source="Quelle" Basic.Main.Controls="Steuerung" +Basic.Main.PreparingStream="Wird vorbereitet …" Basic.Main.Connecting="Verbinde …" Basic.Main.StartRecording="Aufnahme starten" Basic.Main.StartReplayBuffer="Wiederholungspuffer starten" @@ -558,6 +562,7 @@ Basic.Main.StopRecording="Aufnahme beenden" Basic.Main.PauseRecording="Aufnahme pausieren" Basic.Main.UnpauseRecording="Aufnahme fortsetzen" Basic.Main.SplitFile="Aufnehmende Datei teilen" +Basic.Main.AddChapterMarker="Kapitelmarkierung hinzufügen" Basic.Main.StoppingRecording="Beende Aufnahme …" Basic.Main.StopReplayBuffer="Wiederholungspuffer beenden" Basic.Main.StoppingReplayBuffer="Beende Wiederholungspuffer …" @@ -664,8 +669,8 @@ Basic.MainMenu.Help.About="Ü&ber" Basic.Settings.ProgramRestart="Das Programm muss neu gestartet werden, damit die Änderungen wirksam werden." Basic.Settings.ConfirmTitle="Änderungen bestätigen" Basic.Settings.Confirm="Sie haben ungespeicherte Änderungen. Änderungen speichern?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 steuert einige Ihrer Streameinstellungen" Basic.Settings.General="Allgemein" -Basic.Settings.General.Theme="Design" Basic.Settings.General.Language="Sprache" Basic.Settings.General.UpdateChannel="Update-Kanal" Basic.Settings.General.UpdateChannelDisabled="(Deaktiviert)" @@ -721,6 +726,12 @@ Basic.Settings.General.MultiviewLayout.16Scene="Nur Szenen (16 Szenen)" Basic.Settings.General.MultiviewLayout.25Scene="Nur Szenen (25 Szenen)" Basic.Settings.General.ChannelDescription.stable="Neuster stabiler Release" Basic.Settings.General.ChannelDescription.beta="Potenziell instabile Pre-Release-Versionen" +Basic.Settings.Appearance="Erscheinungsbild" +Basic.Settings.Appearance.General="Allgemein" +Basic.Settings.Appearance.General.Theme="Design" +Basic.Settings.Appearance.General.Variant="Stil" +Basic.Settings.Appearance.General.NoVariant="Keine Stile verfügbar" +Basic.Settings.Stream.Destination="Ziel" Basic.Settings.Stream.Custom.UseAuthentication="Authentifizierung verwenden" Basic.Settings.Stream.Custom.Username="Benutzername" Basic.Settings.Stream.Custom.Password="Passwort" @@ -742,15 +753,26 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximale Videobitrate: %1 k Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximale Audiobitrate: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximale Auflösung: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximale FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Benutzerdefinierter Server …" +Basic.Settings.Stream.ServiceCustomServer="Benutzerdefinierter Server" +Basic.Settings.Stream.EnableMultitrackVideo="%1 aktivieren" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximale Streamingbandbreite" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatisch" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maximale Videospuren" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatisch" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Konfiguration überschreiben (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Konfiguration überschreiben" +Basic.Settings.Stream.AdvancedOptions="Erweiterte Optionen" Basic.Settings.Output="Ausgabe" Basic.Settings.Output.Format="Aufnahmeformat" Basic.Settings.Output.Format.MKV="Matroska-Video (.mkv)" +Basic.Settings.Output.Format.hMP4="Hybrides MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmentiertes MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmentiertes MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Bei fragmentiertem MOV wird die Aufnahme in Abschnitten geschrieben und es wird nicht dieselbe Fertigstellung wie bei normalen MOV-Dateien benötigt.\nSo bleiben Dateien auch bei Abbrüchen des Schreibvorgangs abspielbar, z. B. durch einen BSOD oder Stromausfall.\n\nDies ist möglicherweise nicht mit allen Medienspielern und Editoren kompatibel. Verwenden Sie „Datei“ → „Aufnahmen remuxen“, um Dateien gegebenenfalls in ein kompatibles Format zu konvertieren." Basic.Settings.Output.Format.TT.fragmented_mp4="Bei fragmentiertem MP4 wird die Aufnahme in Abschnitten geschrieben und es wird nicht dieselbe Fertigstellung wie bei normalen MP4-Dateien benötigt.\nSo bleiben Dateien auch bei Abbrüchen des Schreibvorgangs abspielbar, z. B. durch einen BSOD oder Stromausfall.\n\nDies ist möglicherweise nicht mit allen Medienspielern und Editoren kompatibel. Verwenden Sie „Datei“ → „Aufnahmen remuxen“, um Dateien gegebenenfalls in ein kompatibles Format zu konvertieren." -Basic.Settings.Output.Encoder.Video="Videoencoder" -Basic.Settings.Output.Encoder.Audio="Audioencoder" +Basic.Settings.Output.Encoder.Video="Videokodierer" +Basic.Settings.Output.Encoder.Audio="Audiokodierer" Basic.Settings.Output.SelectDirectory="Wählen Sie den Aufnahmeordner aus" Basic.Settings.Output.DynamicBitrate="Bitrate dynamisch verändern, um Überlastung zu kontrollieren" Basic.Settings.Output.DynamicBitrate.Beta="Bitrate dynamisch verändern, um Überlastung zu kontrollieren (Beta)" @@ -776,8 +798,8 @@ Basic.Settings.Output.Simple.RecordingQuality.Lossless="Verlustfreie Qualität, Basic.Settings.Output.Simple.Warn.VideoBitrate="Achtung: Die Streamvideobitrate wird auf %1 gesetzt, was der oberen Grenze der aktuellen Streamingplattform entspricht." Basic.Settings.Output.Simple.Warn.AudioBitrate="Achtung: Die Streamaudiobitrate wird auf %1 gesetzt, was der oberen Grenze der aktuellen Streamingplattform entspricht." Basic.Settings.Output.Simple.Warn.CannotPause="Achtung: Aufnahmen können nicht pausiert werden, wenn die Aufnahmequalität „Gleich wie Stream“ ist." -Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Achtung: Das Aufnahmeformat ist mit dem/den gewählten Streamencoder(n) nicht kompatibel." -Basic.Settings.Output.Simple.Warn.Encoder="Achtung: Die Aufnahme mit einem Softwareencoder in einer anderen Qualität als der des Streams verursacht zusätzliche CPU-Nutzung, wenn gleichzeitig gestreamt und aufgenommen wird." +Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Achtung: Das Aufnahmeformat ist mit dem/den gewählten Streamkodierer(n) nicht kompatibel." +Basic.Settings.Output.Simple.Warn.Encoder="Achtung: Die Aufnahme mit einem Softwarekodierer in einer anderen Qualität als der des Streams verursacht zusätzliche CPU-Nutzung, wenn gleichzeitig gestreamt und aufgenommen wird." Basic.Settings.Output.Simple.Warn.Lossless="Achtung: Bei verlustfreier Qualität werden enorm große Dateien erzeugt, was bei hohen Auflösungen und Frameraten mehr als 7 GB Speicherplatz pro Minute beanspruchen kann. Der Wiederholungspuffer steht bei verlustfreier Qualität nicht zur Verfügung." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Möchten Sie verlustfreie Qualität wirklich verwenden?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Warnung zur verlustfreien Qualität" @@ -788,20 +810,20 @@ Basic.Settings.Output.Simple.RecAudioTrack="Audiospur" Basic.Settings.Output.Warn.EnforceResolutionFPS.Title="Inkompatible Auflösung/Framerate" Basic.Settings.Output.Warn.EnforceResolutionFPS.Msg="Diese Streamingplattform unterstützt Ihre aktuelle Ausgabeauflösung und/oder Framerate nicht. Die Werte werden auf den nächstmöglichen kompatiblen Wert geändert:\n\n%1\n\nFortfahren?" Basic.Settings.Output.Warn.EnforceResolutionFPS.Resolution="Auflösung: %1" -Basic.Settings.Output.Warn.ServiceCodecCompatibility.Title="Inkompatibler Encoder" -Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg="Ihr Encoder wird zu „%3“ geändert, da „%1“ „%2“ nicht unterstützt.\n\nFortfahren?" -Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="Die Encoder werden zu „%4“ und „%5“ geändert, da die Streamingplattform „%1“ die Encoder „%2“ und „%3“ nicht unterstützt.\n\nFortfahren?" +Basic.Settings.Output.Warn.ServiceCodecCompatibility.Title="Inkompatibler Kodierer" +Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg="Ihr Kodierer wird zu „%3“ geändert, da „%1“ „%2“ nicht unterstützt.\n\nFortfahren?" +Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="Ihre Kodierer werden zu „%4“ und „%5“ geändert, da „%1“ „%2“ und „%3“ nicht unterstützt.\n\nFortfahren?" Basic.Settings.Output.VideoBitrate="Videobitrate" Basic.Settings.Output.AudioBitrate="Audiobitrate" Basic.Settings.Output.Reconnect="Automatisches Wiederverbinden" Basic.Settings.Output.RetryDelay="Wiederholungsverzögerung" Basic.Settings.Output.MaxRetries="Maximale Wiederholungsversuche" -Basic.Settings.Output.Advanced="Benutzerdefinierte Encodereinstellungen aktivieren (Erweitert)" -Basic.Settings.Output.EncoderPreset="Encodervoreinstellung" +Basic.Settings.Output.Advanced="Benutzerdefinierte Kodierereinstellungen aktivieren (Erweitert)" +Basic.Settings.Output.EncoderPreset="Kodierervoreinstellung" Basic.Settings.Output.EncoderPreset.ultrafast="%1 (Niedrige CPU-Nutzung, niedrigste Qualität)" Basic.Settings.Output.EncoderPreset.veryfast="%1 (Standard) (Mittlere CPU-Nutzung, Standardqualität)" Basic.Settings.Output.EncoderPreset.fast="%1 (Hohe CPU-Nutzung und Qualität)" -Basic.Settings.Output.CustomEncoderSettings="Benutzerdefinierte Encodereinstellungen" +Basic.Settings.Output.CustomEncoderSettings="Benutzerdefinierte Kodierereinstellungen" Basic.Settings.Output.CustomMuxerSettings="Benutzerdefinierte Muxereinstellungen" Basic.Settings.Output.NoSpaceFileName="Dateinamen ohne Leerzeichen generieren" Basic.Settings.Output.Adv.Rescale="Ausgabe umskalieren" @@ -816,13 +838,13 @@ Basic.Settings.Output.Adv.Audio.Track4="Spur 4" Basic.Settings.Output.Adv.Audio.Track5="Spur 5" Basic.Settings.Output.Adv.Audio.Track6="Spur 6" Basic.Settings.Output.Adv.TwitchVodTrack="Twitch-VOD-Spur" -Basic.Settings.Output.Adv.Encoder="Encodereinstellungen" +Basic.Settings.Output.Adv.Encoder="Kodierereinstellungen" Basic.Settings.Output.Adv.Recording="Aufnahme" Basic.Settings.Output.Adv.Recording.Settings="Aufnahmeeinstellungen" Basic.Settings.Output.Adv.Recording.RecType="Aufnahmetyp" Basic.Settings.Output.Adv.Recording.Type="Art" Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Benutzerdefinierte Ausgabe (FFmpeg)" -Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Streamencoder verwenden)" +Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Streamkodierer verwenden)" Basic.Settings.Output.Adv.Recording.Filename="Dateinamenformatierung" Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Überschreiben" Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg-Ausgabetyp" @@ -835,12 +857,12 @@ Basic.Settings.Output.Adv.FFmpeg.Format="Container-Format" Basic.Settings.Output.Adv.FFmpeg.FormatDefault="Standardformat" Basic.Settings.Output.Adv.FFmpeg.FormatDesc="Container-Formatbeschreibung" Basic.Settings.Output.Adv.FFmpeg.FormatDescDef="Audio-/Videocodec wird aus Dateipfad oder URL gebildet" -Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Standardencoder" -Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Encoder deaktivieren" -Basic.Settings.Output.Adv.FFmpeg.VEncoder="Videoencoder" -Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Videoencodereinstellungen" -Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audioencoder" -Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audioencodereinstellungen" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault="Standardkodierer" +Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable="Kodierer deaktivieren" +Basic.Settings.Output.Adv.FFmpeg.VEncoder="Videokodierer" +Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Videokodierereinstellungen" +Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audiokodierer" +Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audiokodierereinstellungen" Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxer-Einstellungen" Basic.Settings.Output.Adv.FFmpeg.GOPSize="Keyframeintervall (Frames)" Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Alle Codecs anzeigen (auch wenn möglicherweise inkompatibel)" @@ -897,7 +919,7 @@ Basic.Settings.Video.Numerator="Zähler" Basic.Settings.Video.Denominator="Nenner" Basic.Settings.Video.InvalidResolution="Ungültige Auflösung. Korrekte Formatierung: [Breite]x[Höhe] (z. B. 1920x1080)" Basic.Settings.Video.CurrentlyActive="Videoausgabe ist derzeit aktiv. Bitte schalten Sie alle Ausgaben ab, um die Videoeinstellungen zu ändern." -Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (Am schnellsten, aber unscharf bei Skalierung)" +Basic.Settings.Video.DownscaleFilter.Bilinear="Bilinear (Am schnellsten, bei Skalierung aber unscharf)" Basic.Settings.Video.DownscaleFilter.Bicubic="Bicubic (Geschärfte Skalierung, 16 Stichproben)" Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Geschärfte Skalierung, 36 Stichproben)" Basic.Settings.Video.DownscaleFilter.Area="Area (Gewichtete Summe, 4/6/9 Stichproben)" @@ -909,7 +931,7 @@ Basic.Settings.Audio.MeterDecayRate.Medium="Mittel (Type I PPM)" Basic.Settings.Audio.MeterDecayRate.Slow="Langsam (Type II PPM)" Basic.Settings.Audio.PeakMeterType="Spitzenmessertyp" Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (Höhere CPU-Nutzung)" -Basic.Settings.Audio.MultichannelWarning.Enabled="ACHTUNG: Surround-Sound-Audio ist aktiviert." +Basic.Settings.Audio.MultichannelWarning.Enabled="Achtung: Surround-Sound-Audio ist aktiviert." Basic.Settings.Audio.MultichannelWarning="Überprüfen Sie für das Streamen, ob Ihre Streamingplattform sowohl die Einspeisung als auch die Wiedergabe von Surround-Sound unterstützt. Beispielsweise unterstützt Facebook 360 Live Surround-Sound vollständig. YouTube Live unterstützt 5.1-Audioeinspeisung (und -Wiedergabe auf Fernsehgeräten).\n\nOBS-Audiofilter sind mit Surround-Sound kompatibel, die VST-Pluginunterstützung ist aber nicht garantiert." Basic.Settings.Audio.MultichannelWarning.Title="Surround-Sound-Audio aktivieren?" Basic.Settings.Audio.MultichannelWarning.Confirm="Möchten Sie Surround-Sound-Audio wirklich aktivieren?" @@ -927,7 +949,7 @@ Basic.Settings.Audio.PushToTalkDelay="Push-To-Talk-Verzögerung" Basic.Settings.Audio.UnknownAudioDevice="[Gerät nicht angeschlossen oder verfügbar]" Basic.Settings.Audio.Disabled="Deaktiviert" Basic.Settings.Audio.LowLatencyBufferingMode="Audiopufferung mit niedriger Latenz (für Decklink-/NDI-Ausgaben)" -Basic.Settings.Audio.LowLatencyBufferingWarning.Enabled="ACHTUNG: Audiopufferung mit niedriger Latenz ist aktiviert." +Basic.Settings.Audio.LowLatencyBufferingWarning.Enabled="Achtung: Audiopufferung mit niedriger Latenz ist aktiviert." Basic.Settings.Audio.LowLatencyBufferingWarning="Audiopufferung mit niedriger Latenz kann dazu führen, dass Audio von einigen Quellen glitcht oder stoppt." Basic.Settings.Audio.LowLatencyBufferingWarning.Title="Audiopufferung mit niedriger Latenz aktivieren?" Basic.Settings.Audio.LowLatencyBufferingWarning.Confirm="Möchten Sie Audiopufferung mit niedriger Latenz wirklich aktivieren?" @@ -1056,21 +1078,21 @@ OutputWarnings.NoTracksSelected="Sie müssen mindestens eine Spur auswählen" OutputWarnings.NoTracksSelectedOnExit.Title="Ausgabeeinstellungsfehler" OutputWarnings.NoTracksSelectedOnExit.Text="Für jede Ausgabe muss mindestens eine Audiospur ausgewählt sein." OutputWarnings.MP4Recording="Achtung: Aufnahmen, die in MP4/MOV gespeichert werden, sind nicht wiederherstellbar, wenn die Datei nicht fertiggestellt werden konnte (z. B. als Folge eines BSOD oder Stromausfalls). Wenn Sie mehrere Audiospuren aufnehmen möchten, sollten Sie MKV verwenden und die Aufnahme nach ihrer Fertigstellung zu MP4/MOV remuxen. („Datei“ → „Aufnahmen remuxen“)" -OutputWarnings.CannotPause="Achtung: Aufnahmen können nicht pausiert werden, wenn der Aufnahmeencoder auf „(Streamencoder verwenden)“ gesetzt ist." -OutputWarnings.CodecIncompatible="Der ausgewählte Audio- oder Videoencoder wurde wegen Inkompatibilität zurückgesetzt. Bitte wählen Sie einen passenden Encoder aus der Liste." +OutputWarnings.CannotPause="Achtung: Aufnahmen können nicht pausiert werden, wenn der Aufnahmekodierer auf „(Streamkodierer verwenden)“ gesetzt ist." +OutputWarnings.CodecIncompatible="Der ausgewählte Audio- oder Videokodierer wurde wegen Inkompatibilität zurückgesetzt. Bitte wählen Sie einen passenden Kodierer aus der Liste." CodecCompat.Incompatible="(Nicht mit %1 kompatibel)" -CodecCompat.CodecPlaceholder="Encoder auswählen …" +CodecCompat.CodecPlaceholder="Kodierer auswählen …" CodecCompat.ContainerPlaceholder="Format auswählen …" -CodecCompat.CodecMissingOnExit.Title="Kein Encoder ausgewählt" -CodecCompat.CodecMissingOnExit.Text="Mindestens ein Video- oder Audioencoder wurde nicht gesetzt. Bitte wählen Sie Encoder für das Aufnehmen und Streamen." +CodecCompat.CodecMissingOnExit.Title="Kein Kodierer ausgewählt" +CodecCompat.CodecMissingOnExit.Text="Mindestens ein Video- oder Audiokodierer wurde nicht gesetzt. Bitte wählen Sie Kodierer für das Aufnehmen und Streamen." CodecCompat.ContainerMissingOnExit.Title="Kein Format ausgewählt" -CodecCompat.ContainerMissingOnExit.Text="Kein Aufnahmeformat wurde gesetzt. Bitte wählen Sie ein Aufnahmeformat, welches mit dem gewählten Streamencoder kompatibel ist." +CodecCompat.ContainerMissingOnExit.Text="Kein Aufnahmeformat wurde gesetzt. Bitte wählen Sie ein Aufnahmeformat, welches mit dem gewählten Streamkodierer kompatibel ist." FinalScene.Title="Szene entfernen" FinalScene.Text="Es muss mindestens eine Szene vorhanden sein." NoSources.Title="Keine Quellen" NoSources.Text="Sie haben noch keine Videoquellen hinzugefügt, sodass nur ein leerer Bildschirm ausgegeben wird. Sind Sie sicher, dass Sie das wollen?" NoSources.Text.AddSource="Sie können jederzeit Quellen hinzufügen, indem Sie auf das + unter dem Quellenfeld im Hauptfenster klicken." -NoSources.Label="Sie haben bis jetzt keine Quellen hinzugefügt.\nKlicken Sie auf das + oder\nrechtsklicken Sie hier, um welche hinzuzufügen." +NoSources.Label="Sie haben keine Quellen hinzugefügt.\nKlicken Sie auf das + oder\nrechtsklicken Sie hier, um welche hinzuzufügen." ChangeBG="Farbe auswählen" CustomColor="Benutzerdefinierte Farbe" BrowserSource.EnableHardwareAcceleration="Browserhardwarebeschleunigung aktivieren" @@ -1187,3 +1209,19 @@ YouTube.Errors.messageTextInvalid="Die Nachricht ist ungültig." YouTube.Errors.rateLimitExceeded="Sie senden Nachrichten zu schnell." YouTube.DocksRemoval.Title="Veraltete YouTube-Browser-Docks bereinigen" YouTube.DocksRemoval.Text="Folgende Browser-Docks werden entfernt, da diese veraltet sind:\n\n%1\nVerwenden Sie stattdessen „Docks“ → „YouTube Live Control Room“." +ConfigDownload.WarningMessageTitle="Warnung" +FailedToStartStream.MissingConfigURL="Keine Konfigurations-URL für die aktuelle Plattform verfügbar" +FailedToStartStream.NoCustomRTMPURLInSettings="Benutzerdefinierte RTMP-URL nicht angegeben" +FailedToStartStream.InvalidCustomConfig="Ungültige benutzerdefinierte Konfiguration" +FailedToStartStream.EncoderNotAvailable="NVENC nicht verfügbar.\n\nDer Encoder-Typ '%1' konnte nicht gefunden werden" +FailedToStartStream.FailedToCreateVideoEncoder="Erstellen des Videokodierers „%1“ (Typ: „%2“)" +FailedToStartStream.FailedToCreateAudioEncoder="Erstellen des Audiokodierers fehlgeschlagen" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nMöchten Sie ohne %1 weiter streamen?" +FailedToStartStream.WarningRetry="\n

\nMöchten Sie weiter streamen?" +FailedToStartStream.StatusMissingHTML="Die Anfrage live zu gehen gab einen unbestimmten Fehler zurück" +FailedToStartStream.NoConfigSupplied="Fehlende Konfiguration" +MultitrackVideo.Info="%1 optimiert automatisch Ihre Einstellungen, um mehrere Videoqualitäten zu kodieren und zu senden. %2 werden dann Informationen über die Konfiguration Ihres Computers und Ihrer Software mitgeteilt." +MultitrackVideo.IncompatibleSettings.Title="Inkompatible Einstellungen" +MultitrackVideo.IncompatibleSettings.Text="%1 ist inkompatibel mit:\n\n%2\nUm mit %1 weiter zu streamen, deaktivieren Sie inkompatible Einstellungen:\n\n%3\nund starten Sie den Stream erneut." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Für diesen Stream deaktivieren und Stream starten" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Einstellungen aktualisieren und Stream starten" diff --git a/UI/data/locale/el-GR.ini b/UI/data/locale/el-GR.ini index b7898e78bf9043..24eccc0d1114fd 100644 --- a/UI/data/locale/el-GR.ini +++ b/UI/data/locale/el-GR.ini @@ -678,7 +678,6 @@ Basic.Settings.ProgramRestart="Το πρόγραμμα πρέπει να επα Basic.Settings.ConfirmTitle="Επιβεβαίωση Αλλαγών" Basic.Settings.Confirm="Έχετε μη αποθηκευμένες αλλαγές. Αποθήκευση αλλαγών;" Basic.Settings.General="Γενικά" -Basic.Settings.General.Theme="Θέμα" Basic.Settings.General.Language="Γλώσσα" Basic.Settings.General.Updater="Ενημερώσεις" Basic.Settings.General.UpdateChannel="Ενημέρωση Καναλιού" diff --git a/UI/data/locale/eo-UY.ini b/UI/data/locale/eo-UY.ini index ba79baf289eb41..8ef02bc065602a 100644 --- a/UI/data/locale/eo-UY.ini +++ b/UI/data/locale/eo-UY.ini @@ -326,7 +326,6 @@ Basic.MainMenu.File.Exit="Eliri(&X)" Basic.MainMenu.Edit="&Editi" Basic.MainMenu.Edit.Undo="Malfari(&U)" Basic.MainMenu.Edit.Redo="&Refari" -Basic.Settings.General.Theme="Temo" Basic.Settings.General.Language="Lingvo" Basic.Settings.Stream.Custom.Username="Salutnomo" Basic.Settings.Stream.Custom.Password="Pasvorto" diff --git a/UI/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini index 329dd17f982e2e..419218afd2eaea 100644 --- a/UI/data/locale/es-ES.ini +++ b/UI/data/locale/es-ES.ini @@ -106,6 +106,7 @@ MixerToolbarMenu="Menú del mezclador de audio" SceneFilters="Abrir filtros de la escena" List="Lista" Grid="Cuadrícula" +Automatic="Automático" PluginsFailedToLoad.Title="Error al cargar el plugin" PluginsFailedToLoad.Text="No se han podido cargar los siguientes plugins de OBS:\n\n%1\nPor favor, actualice o elimine estos plugins." AlreadyRunning.Title="OBS ya se está ejecutando" @@ -190,6 +191,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferir codificación por h Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="La codificación por hardware elimina la mayoría del uso de la CPU, pero puede requerir más bitrate para obtener el mismo nivel de calidad." Basic.AutoConfig.StreamPage.StreamWarning.Title="Advertencia de transmisión" Basic.AutoConfig.StreamPage.StreamWarning.Text="La prueba de ancho de banda esta a punto de transmitir datos de vídeo aleatorios sin audio a tu canal. Si puedes, es recomendable desactivar temporalmente el que se guarden los vídeos de las transmisiones y hacer la transmisión privada después de que la prueba haya finalizado. ¿Desea continuar?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Probar %1" Basic.AutoConfig.TestPage="Resultado final" Basic.AutoConfig.TestPage.SubTitle.Testing="El programa ahora está ejecutando un conjunto de pruebas para estimar los ajustes óptimos" Basic.AutoConfig.TestPage.SubTitle.Complete="Prueba completada" @@ -208,6 +210,7 @@ Basic.AutoConfig.TestPage.Result.Header="El programa ha determinado que estos aj Basic.AutoConfig.TestPage.Result.Footer="Para utilizar estos ajustes, haga clic en \"Aplicar Configuración\". Para volver a configurar el asistente, haga clic en Atrás. Para configurar los ajustes por ti mismo, haga clic en Cancelar y abra los ajustes." Basic.AutoConfig.Info="El asistente de configuración automática determinará la mejor configuración basada en las especificaciones de su computadora y la velocidad de Internet." Basic.AutoConfig.RunAnytime="Esto puede ejecutarse en cualquier momento desde el menú Herramientas." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Resolución de la transmisión (escalada) " Basic.Stats="Estadísticas" Basic.Stats.CPUUsage="Uso de CPU" Basic.Stats.HDDSpaceAvailable="Espacio en disco disponible" @@ -556,6 +559,7 @@ Basic.Main.Scenes="Escenas" Basic.Main.Sources="Fuentes" Basic.Main.Source="Fuente" Basic.Main.Controls="Controles" +Basic.Main.PreparingStream="Preparando..." Basic.Main.Connecting="Conectando..." Basic.Main.StartRecording="Iniciar Grabación" Basic.Main.StartReplayBuffer="Iniciar el Búfer de Repetición" @@ -567,6 +571,7 @@ Basic.Main.StopRecording="Detener Grabación" Basic.Main.PauseRecording="Pausar Grabación" Basic.Main.UnpauseRecording="Reanudar Grabación" Basic.Main.SplitFile="Dividir archivo de grabación" +Basic.Main.AddChapterMarker="Agregar marcador de capítulo" Basic.Main.StoppingRecording="Deteniendo Grabación..." Basic.Main.StopReplayBuffer="Detener el Búfer de Repetición" Basic.Main.StoppingReplayBuffer="Deteniendo el Búfer de Repetición..." @@ -675,7 +680,7 @@ Basic.MainMenu.Help.About="&Acerca de" Basic.Settings.ProgramRestart="El programa debe reiniciarse para que esta configuración surta efecto." Basic.Settings.ConfirmTitle="Confirmar cambios" Basic.Settings.Confirm="Hay cambios sin guardar. ¿Guardar los cambios?" -Basic.Settings.General.Theme="Tema" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 está controlando algunos de los ajustes de su transmisión" Basic.Settings.General.Language="Idioma" Basic.Settings.General.Updater="Actualizaciones" Basic.Settings.General.UpdateChannel="Actualizar canal" @@ -735,7 +740,12 @@ Basic.Settings.General.ChannelName.stable="Estable" Basic.Settings.General.ChannelDescription.stable="Última versión estable" Basic.Settings.General.ChannelName.beta="Betas / Candidatos a lanzamiento" Basic.Settings.General.ChannelDescription.beta="Versiones pre-lanzamiento potencialmente inestables" +Basic.Settings.Appearance="Apariencia" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Estilo" +Basic.Settings.Appearance.General.NoVariant="No hay estilos disponibles" Basic.Settings.Stream="Emisión" +Basic.Settings.Stream.Destination="Destino" Basic.Settings.Stream.Custom.UseAuthentication="Usar la autentificación" Basic.Settings.Stream.Custom.Username="Nombre de usuario" Basic.Settings.Stream.Custom.Password="Contraseña" @@ -757,9 +767,20 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Máxima tasa de bits de víde Basic.Settings.Stream.Recommended.MaxAudioBitrate="Máxima tasa de bits de audio: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Resolución máxima: %1" Basic.Settings.Stream.Recommended.MaxFPS="Tasa máxima de fotogramas: %1" +Basic.Settings.Stream.SpecifyCustomServer="Especifica un servidor personalizado..." +Basic.Settings.Stream.ServiceCustomServer="Servidor personalizado" +Basic.Settings.Stream.EnableMultitrackVideo="Habilitar %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Ancho de banda máximo" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Máximas pistas de vídeo" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Habilitar el volcado de la transmisión a FLV (usa la configuración simple de grabación)\n" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Anulación de la configuración (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Activar la anulación de la configuración" +Basic.Settings.Stream.MultitrackVideoLabel="Video multipista" +Basic.Settings.Stream.AdvancedOptions="Opciones avanzadas" Basic.Settings.Output="Salida" Basic.Settings.Output.Format="Formato de grabación" Basic.Settings.Output.Format.MKV="Video Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 híbrido [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 fragmentado (.mp4)" Basic.Settings.Output.Format.fMOV="MOV fragmentado (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="El MOV fragmentado escribe la grabación en fragmentos y no requiere una finalización como los archivos MOV tradicionales.\nEsto garantiza que el archivo se pueda reproducir incluso si se interrumpe la escritura en el disco, por ejemplo, como resultado de un pantallazo azul o una pérdida de energía.\n\nEsto puede no ser compatible con todos los reproductores y editores. Use Archivo → Convertir grabaciones para convertir el archivo a un formato más compatible si es necesario." @@ -1213,3 +1234,27 @@ YouTube.Errors.messageTextInvalid="El texto del mensaje no es válido." YouTube.Errors.rateLimitExceeded="Estás enviando mensajes demasiado rápido." YouTube.DocksRemoval.Title="Borrar los paneles de navegador heredados de YouTube" YouTube.DocksRemoval.Text="Estos paneles de navegador se eliminarán como obsoletos:\n\n%1\nUtiliza \"Paneles/YouTube Live Control Room\" en su lugar." +ConfigDownload.WarningMessageTitle="Advertencia" +FailedToStartStream.MissingConfigURL="No hay una URL de configuración disponible para el servicio actual" +FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP personalizada no especificada" +FailedToStartStream.InvalidCustomConfig="Configuración personalizada no valida" +FailedToStartStream.FailedToCreateMultitrackVideoService="Fallo al crear el servicio de vídeo multipista" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Error al crear la salida rtmp de vídeo multipista" +FailedToStartStream.EncoderNotAvailable="NVENC no disponible.\n\n No se puedo encontrar el tipo de codificador '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="No se pudo crear el codificador de vídeo '%1' (tipo: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="No se puedo obtener la información de vídeo de obs mientras crea el codificador '%1' (tipo: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="No se puedo crear el codificador de audio" +FailedToStartStream.NoRTMPURLInConfig="La configuración no contiene la URL RTMP(S) de destino de la transmisión" +FailedToStartStream.FallbackToDefault="No se puedo iniciar la transmisión usando %1; ¿quieres reintentarlo usando la configuración de codificación simple?" +FailedToStartStream.ConfigRequestFailed="No se pudo obtener la configuración de %1

HTTP error: %2" +FailedToStartStream.WarningUnknownStatus="Recibido valor de estado desconocido '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n¿Quieres continuar con la transmisión sin %1?" +FailedToStartStream.WarningRetry="\n

\n¿Quieres continuar con la transmisión?" +FailedToStartStream.MissingEncoderConfigs="La configuración del directo no incluía las configuraciones del codificador" +FailedToStartStream.StatusMissingHTML="La solicitud del directo devolvió un error no especificado" +FailedToStartStream.NoConfigSupplied="Configuración no encontrada" +MultitrackVideo.Info=" %1 optimiza automáticamente tu configuración para codificar y enviar múltiples calidades de vídeo. Al seleccionar esta opción se enviará a %2 información sobre la configuración de su ordenador y software." +MultitrackVideo.IncompatibleSettings.Title="Ajustes incompatibles" +MultitrackVideo.IncompatibleSettings.Text="%1 no es compatible actualmente con:\n\n%2\nPara continuar la transmisión con %1, desactiva los ajustes incompatibles:\n\n%3\ny empieza a transmitir de nuevo." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Deshabilitar para esta transmisión e iniciar transmisión" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Actualizar ajustes e iniciar transmisión" diff --git a/UI/data/locale/et-EE.ini b/UI/data/locale/et-EE.ini index dda6a395ad5bff..b802200311ac98 100644 --- a/UI/data/locale/et-EE.ini +++ b/UI/data/locale/et-EE.ini @@ -589,7 +589,6 @@ Basic.Settings.ProgramRestart="Nende sätete jõustumiseks tuleb taaskäivitada Basic.Settings.ConfirmTitle="Kinnita muudatused" Basic.Settings.Confirm="Teil on salvestamata muutusi. Salvestame muudatused?" Basic.Settings.General="Üldine" -Basic.Settings.General.Theme="Teema" Basic.Settings.General.Language="Keel" Basic.Settings.General.Updater="Uuendused" Basic.Settings.General.UpdateChannel="Uuenduskanal" diff --git a/UI/data/locale/eu-ES.ini b/UI/data/locale/eu-ES.ini index 6273ee35837d01..6d2fee13220cdb 100644 --- a/UI/data/locale/eu-ES.ini +++ b/UI/data/locale/eu-ES.ini @@ -685,7 +685,6 @@ Basic.Settings.ProgramRestart="Programa berrabiarazi egin behar da ezarpen hauek Basic.Settings.ConfirmTitle="Baieztatu aldaketak" Basic.Settings.Confirm="Gordegabeko aldaketak dituzu. Gorde aldaketak?" Basic.Settings.General="Orokorra" -Basic.Settings.General.Theme="Gaia" Basic.Settings.General.Language="Hizkuntza" Basic.Settings.General.Updater="Eguneraketak" Basic.Settings.General.UpdateChannel="Eguneratu kanala" @@ -745,6 +744,11 @@ Basic.Settings.General.ChannelName.stable="Egonkorra" Basic.Settings.General.ChannelDescription.stable="Azken bertsio egonkorra" Basic.Settings.General.ChannelName.beta="Betak / Argitaratzeko hautagaiak" Basic.Settings.General.ChannelDescription.beta="Ezegonkorrak izan daitezkeen aurreko bertsioak" +Basic.Settings.Appearance="Itxura" +Basic.Settings.Appearance.General="Orokorra" +Basic.Settings.Appearance.General.Theme="Gaia" +Basic.Settings.Appearance.General.Variant="Estiloa" +Basic.Settings.Appearance.General.NoVariant="Ez dago erabilgarri den estilorik." Basic.Settings.Stream="Transmisioa" Basic.Settings.Stream.Custom.UseAuthentication="Erabili autentifikazioa" Basic.Settings.Stream.Custom.Username="Erabiltzaile izena" @@ -1101,7 +1105,7 @@ SceneItemShow="Erakutsi '%1'" SceneItemHide="Ezkutatu '%1'" OutputWarnings.NoTracksSelected="Gutxienez pista bat hautatu behar duzu" OutputWarnings.NoTracksSelectedOnExit.Title="Irteerako ezarpenen errorea" -OutputWarnings.NoTracksSelectedOnExit.Text="Gakoa: OutputWarnings.NoTracksSelectedOnExit.Text\nIrteeraWarnings.NoTracksSelectedOnExit.Text" +OutputWarnings.NoTracksSelectedOnExit.Text="Irteera guztiek audio-pista bat izan behar dute gutxienez hautatuta." OutputWarnings.MP4Recording="Kontuz: MP4 formatuz gordetako grabazioak izan daitezke berreskuraezinak fitxategia ezin bada bukatu (esate baterako BSODs-ren emaitza bat, energia etenak eta abar). Hainbat audio pista grabatu nahi baduzu erabil dezakezu MKV formatua eta mp4 bihurtu grabazioa bukatu ondoren (Fitxategia->Bihurtu grabazioak)" OutputWarnings.CannotPause="Kontuz: Grabazioak ezin dira pausatu grabazioaren kodetzailea \"(Erabili transmisioaren kodetzailea)\" konfigurazioa badu" OutputWarnings.CodecIncompatible="Audio- edo bideo-kodetzaileen aukeraketa berrezarri da bateraezintasunagatik. Hautatu zerrendako kodetzaile bateragarri bat." diff --git a/UI/data/locale/fa-IR.ini b/UI/data/locale/fa-IR.ini index 9f510d9a178211..55fb66244db981 100644 --- a/UI/data/locale/fa-IR.ini +++ b/UI/data/locale/fa-IR.ini @@ -687,7 +687,6 @@ Basic.Settings.ProgramRestart="برای تأثیرگذاری این تنظیما Basic.Settings.ConfirmTitle="تایید تغییرات" Basic.Settings.Confirm="شما تغییرات ذخیره نشده ای دارید. ذخیره تغییرات?" Basic.Settings.General="عمومی" -Basic.Settings.General.Theme="پوسته" Basic.Settings.General.Language="زبان" Basic.Settings.General.Updater="به‌روزرسانی‌ها" Basic.Settings.General.UpdateChannel="کانال بروزرسانی" @@ -747,6 +746,11 @@ Basic.Settings.General.ChannelName.stable="پایدار" Basic.Settings.General.ChannelDescription.stable="آخرین نسخه پایدار" Basic.Settings.General.ChannelName.beta="بتا / انتشار داوطلبان" Basic.Settings.General.ChannelDescription.beta="نسخه های پیش از انتشار بالقوه ناپایدار" +Basic.Settings.Appearance="ظاهر" +Basic.Settings.Appearance.General="کلی" +Basic.Settings.Appearance.General.Theme="پوسته" +Basic.Settings.Appearance.General.Variant="سبک" +Basic.Settings.Appearance.General.NoVariant="سبکی در دسترس نیست" Basic.Settings.Stream="پخش" Basic.Settings.Stream.Custom.UseAuthentication="استفاده از تایید اعتبار" Basic.Settings.Stream.Custom.Username="نام کاربری" diff --git a/UI/data/locale/fi-FI.ini b/UI/data/locale/fi-FI.ini index e0204c5497f2fe..0a482b4a871e8d 100644 --- a/UI/data/locale/fi-FI.ini +++ b/UI/data/locale/fi-FI.ini @@ -568,6 +568,7 @@ Basic.Main.StopRecording="Pysäytä tallennus" Basic.Main.PauseRecording="Keskeytä tallennus" Basic.Main.UnpauseRecording="Jatka tallennusta" Basic.Main.SplitFile="Pilko tallennettu tiedosto" +Basic.Main.AddChapterMarker="Lisää kappale merkintä" Basic.Main.StoppingRecording="Pysäytetään tallennusta..." Basic.Main.StopReplayBuffer="Pysäytä toistopuskuri" Basic.Main.StoppingReplayBuffer="Pysäytetään toistopuskuria..." @@ -677,7 +678,6 @@ Basic.Settings.ProgramRestart="Ohjelma on käynnistettävä uudelleen, jotta ase Basic.Settings.ConfirmTitle="Vahvista muutokset" Basic.Settings.Confirm="Sinulla on tallentamattomia muutoksia. Tallennetaanko?" Basic.Settings.General="Yleiset" -Basic.Settings.General.Theme="Teema" Basic.Settings.General.Language="Kieli" Basic.Settings.General.Updater="Päivitykset" Basic.Settings.General.UpdateChannel="Päivityskanava" @@ -737,6 +737,11 @@ Basic.Settings.General.ChannelName.stable="Vakaa" Basic.Settings.General.ChannelDescription.stable="Viimeisin vakaa julkaisu" Basic.Settings.General.ChannelName.beta="Beetat / julkaisuehdokkaat" Basic.Settings.General.ChannelDescription.beta="Mahdollisesti epävakaat esijulkaisuversiot" +Basic.Settings.Appearance="Ulkoasu" +Basic.Settings.Appearance.General="Yleinen" +Basic.Settings.Appearance.General.Theme="Teema" +Basic.Settings.Appearance.General.Variant="Tyyli" +Basic.Settings.Appearance.General.NoVariant="Tyyliä ei ole saatavilla" Basic.Settings.Stream="Lähetys" Basic.Settings.Stream.Custom.UseAuthentication="Käytä todennusta" Basic.Settings.Stream.Custom.Username="Käyttäjätunnus" diff --git a/UI/data/locale/fil-PH.ini b/UI/data/locale/fil-PH.ini index ce8d3ae4dce096..8532223bb2dc37 100644 --- a/UI/data/locale/fil-PH.ini +++ b/UI/data/locale/fil-PH.ini @@ -642,7 +642,6 @@ Basic.Settings.ProgramRestart="Ang programa ay dapat umummpisa muli upang magbis Basic.Settings.ConfirmTitle="Kumpirmahin ang mga Pagbabago" Basic.Settings.Confirm="May mga hindi na-save na pagbabago. I-save ang mga pagbabago?" Basic.Settings.General="Pangkalahatan" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Wika" Basic.Settings.General.Updater="Mga Update" Basic.Settings.General.UpdateChannel="I-update ang Channel" diff --git a/UI/data/locale/fr-FR.ini b/UI/data/locale/fr-FR.ini index e4e46dac89d829..4c247db192d65c 100644 --- a/UI/data/locale/fr-FR.ini +++ b/UI/data/locale/fr-FR.ini @@ -285,7 +285,7 @@ Undo.Volume.Mute="Couper l'audio de '%1'" Undo.Volume.Unmute="Activer l'audio de '%1'" Undo.Balance.Change="Changer la balance audio sur '%1'" Undo.SyncOffset.Change="Modifier le décalage de la synchronisation audio sur '%1'" -Undo.MonitoringType.Change="Modifier le contrôle audio sur \"%1\"" +Undo.MonitoringType.Change="Modifier le Monitoring audio sur \"%1\"" Undo.Mixers.Change="Modifier les mélangeurs audio sur '%1'" Undo.ForceMono.On="Activer le son mono forcé sur '%1'" Undo.ForceMono.Off="Désactiver le son mono forcé sur '%1'" @@ -422,7 +422,7 @@ SourceLeak.Text="Un problème est survenu lors du changement de collection de sc Basic.DesktopDevice1="Audio du bureau" Basic.DesktopDevice2="Audio du bureau 2" Basic.Scene="Scène" -Basic.DisplayCapture="Capture d'Ecran" +Basic.DisplayCapture="Capture d'écran" Basic.Main.PreviewConextMenu.Enable="Activer l'aperçu" Basic.Main.Preview.Disable="Désactiver l'aperçu" ScaleFiltering="Filtre de mise à l'échelle" @@ -665,7 +665,6 @@ Basic.Settings.ProgramRestart="Le programme doit être redémarré pour que les Basic.Settings.ConfirmTitle="Valider les modifications" Basic.Settings.Confirm="Vous avez des modifications non enregistrées. Voulez-vous les enregistrer ?" Basic.Settings.General="Général" -Basic.Settings.General.Theme="Thème" Basic.Settings.General.Language="Langue" Basic.Settings.General.Updater="Mises à jour" Basic.Settings.General.UpdateChannel="Canal de mise à jour" @@ -723,6 +722,10 @@ Basic.Settings.General.MultiviewLayout.25Scene="Scènes seulement (25 Scènes)" Basic.Settings.General.ChannelDescription.stable="Dernière version stable" Basic.Settings.General.ChannelName.beta="Bêtas / Versions de pré-sortie" Basic.Settings.General.ChannelDescription.beta="Pré-versions potentiellement instables" +Basic.Settings.Appearance="Apparence" +Basic.Settings.Appearance.General="Général" +Basic.Settings.Appearance.General.Theme="Thème" +Basic.Settings.Appearance.General.NoVariant="Aucun style disponible" Basic.Settings.Stream="Stream (flux)" Basic.Settings.Stream.Custom.UseAuthentication="Utiliser l'authentification" Basic.Settings.Stream.Custom.Username="Nom d'utilisateur" @@ -921,13 +924,13 @@ Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (mise à l'échelle avec n Basic.Settings.Video.DownscaleFilter.Area="Zone (somme pondérée, 4/6/9 échantillons)" Basic.Settings.Audio.SampleRate="Fréquence d'échantillonnage" Basic.Settings.Audio.Channels="Canaux" -Basic.Settings.Audio.Meters="Mesures" -Basic.Settings.Audio.MeterDecayRate="Vitesse de relâche" +Basic.Settings.Audio.Meters="VU-mètre" +Basic.Settings.Audio.MeterDecayRate="Taux de décroissance" Basic.Settings.Audio.MeterDecayRate.Fast="Rapide" -Basic.Settings.Audio.MeterDecayRate.Medium="Moyenne (Type I PPM)" -Basic.Settings.Audio.MeterDecayRate.Slow="Lente (Type II PPM)" -Basic.Settings.Audio.PeakMeterType="Type de crête-mètre" -Basic.Settings.Audio.PeakMeterType.SamplePeak="Pic d'échantillon audio" +Basic.Settings.Audio.MeterDecayRate.Medium="Moyen (Type I PPM)" +Basic.Settings.Audio.MeterDecayRate.Slow="Lent (Type II PPM)" +Basic.Settings.Audio.PeakMeterType="Type de crête pour le VU-mètre" +Basic.Settings.Audio.PeakMeterType.SamplePeak="Crête des échantillons audio" Basic.Settings.Audio.PeakMeterType.TruePeak="Crête exacte (plus grande utilisation du CPU)" Basic.Settings.Audio.MultichannelWarning.Enabled="ATTENTION : le son multicanal est activé." Basic.Settings.Audio.MultichannelWarning="Si vous diffusez en direct, vérifiez que votre service de diffusion prend en charge à la fois l'acquisition et la lecture du son surround. Par exemple, Facebook 360 Live prend entièrement en charge le son surround ; YouTube Live prend en charge l'acquisition audio 5.1 (et la lecture sur les téléviseurs).\n\nLes filtres audio OBS sont compatibles avec le son surround, bien que la prise en charge des plugins VST ne soit pas garantie." @@ -992,7 +995,7 @@ Basic.Settings.Advanced.Video.ColorRange.Partial="Limité" Basic.Settings.Advanced.Video.ColorRange.Full="Complète" Basic.Settings.Advanced.Video.SdrWhiteLevel="Niveau de blanc SDR" Basic.Settings.Advanced.Video.HdrNominalPeakLevel="Niveau de crête nominal HDR" -Basic.Settings.Advanced.Audio.MonitoringDevice="Périphérique de contrôle audio" +Basic.Settings.Advanced.Audio.MonitoringDevice="Périphérique de Monitoring audio" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Par défaut" Basic.Settings.Advanced.Audio.DisableAudioDucking="Désactiver l'atténuation audio de Windows (ducking)" Basic.Settings.Advanced.StreamDelay="Retard du stream" @@ -1020,11 +1023,11 @@ Basic.AdvAudio.MonoSource="Conversion en mono pour \"%1\"" Basic.AdvAudio.BalanceSource="Balance audio pour \"%1\"" Basic.AdvAudio.SyncOffset="Décalage de synchronisation" Basic.AdvAudio.SyncOffsetSource="Décalage de synchronisation pour \"%1\"" -Basic.AdvAudio.Monitoring="Contrôle audio" -Basic.AdvAudio.Monitoring.None="Contrôle désactivé" -Basic.AdvAudio.Monitoring.MonitorOnly="Contrôle uniquement (sortie coupée)" -Basic.AdvAudio.Monitoring.Both="Contrôle et sortie" -Basic.AdvAudio.MonitoringSource="Contrôle audio pour \"%1\"" +Basic.AdvAudio.Monitoring="Monitoring audio" +Basic.AdvAudio.Monitoring.None="Monitoring désactivé" +Basic.AdvAudio.Monitoring.MonitorOnly="Monitoring uniquement (sortie coupée)" +Basic.AdvAudio.Monitoring.Both="Monitoring et sortie" +Basic.AdvAudio.MonitoringSource="Monitoring audio pour \"%1\"" Basic.AdvAudio.AudioTracks="Pistes" Basic.Settings.Hotkeys="Raccourcis clavier" Basic.Settings.Hotkeys.Pair="Les combinaisons de touches partagées avec '%1' agissent comme déclencheur" diff --git a/UI/data/locale/gd-GB.ini b/UI/data/locale/gd-GB.ini index 3f3a9f8d28927f..56465aca1d8297 100644 --- a/UI/data/locale/gd-GB.ini +++ b/UI/data/locale/gd-GB.ini @@ -578,7 +578,6 @@ Basic.Settings.ProgramRestart="Feumaidh tu am prògram ath-thòiseachadh gus na Basic.Settings.ConfirmTitle="Dearbh na h-atharraichean" Basic.Settings.Confirm="Tha atharraichean gun sàbhaladh agad. A bheil thu airson an sàbhaladh?" Basic.Settings.General="Coitcheann" -Basic.Settings.General.Theme="Ùrlar" Basic.Settings.General.Language="Cànan" Basic.Settings.General.EnableAutoUpdates="Thoir sùil airson ùrachaidhean gu fèin-obrachail aig an toiseach" Basic.Settings.General.OpenStatsOnStartup="Fosglaidh seo còmhradh na stadastaireachd aig an toiseach" diff --git a/UI/data/locale/gl-ES.ini b/UI/data/locale/gl-ES.ini index 4c9450b7147196..c0745c1daf0ae1 100644 --- a/UI/data/locale/gl-ES.ini +++ b/UI/data/locale/gl-ES.ini @@ -573,7 +573,6 @@ Basic.Settings.ProgramRestart="Debe reiniciarse o programa para que se aplique a Basic.Settings.ConfirmTitle="Confirmar os cambios" Basic.Settings.Confirm="Hai cambios sen gardar. Quere gardalos?" Basic.Settings.General="Xeral" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.EnableAutoUpdates="Comprobar automaticamente no inicio se hai actualizacións" Basic.Settings.General.OpenStatsOnStartup="Abrir o diálogo de estatísticas ao iniciar" diff --git a/UI/data/locale/he-IL.ini b/UI/data/locale/he-IL.ini index 56c4848cd0234e..d829069b958e07 100644 --- a/UI/data/locale/he-IL.ini +++ b/UI/data/locale/he-IL.ini @@ -194,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="העדף קידוד בחומ Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="קידוד בחומרה מבטלת רוב השימוש במעבד, אבל עשוי לדרוש קצב נתונים גבוה יותר כדי להשיג את אותה רמת האיכות." Basic.AutoConfig.StreamPage.StreamWarning.Title="אזהרה לגבי השידור החי" Basic.AutoConfig.StreamPage.StreamWarning.Text="בדיקת רוחב הפס עומדת לעשות שידור חי עם וידאו אקראי ללא שמע בערוץ שלך. אם יש לך אפשרות, מומלץ לכבות זמנית את שמירת הסרטונים של השידורים החיים ולהגדיר את השידור החי כפרטי עד סיום הבדיקה. להמשיך?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="בדיקת %1" Basic.AutoConfig.TestPage="תוצאות סופיות" Basic.AutoConfig.TestPage.SubTitle.Testing="התוכנית מריצה כעת סט של בדיקות להערכת ההגדרות המומלצות ביותר" Basic.AutoConfig.TestPage.SubTitle.Complete="הבדיקה הסתיימה" @@ -579,6 +580,7 @@ Basic.Main.StopRecording="עצירת הקלטה" Basic.Main.PauseRecording="השהיית הקלטה" Basic.Main.UnpauseRecording="ביטול השהיית ההקלטה" Basic.Main.SplitFile="פיצול קובץ הקלטה" +Basic.Main.AddChapterMarker="הוספת סמן פרק" Basic.Main.StoppingRecording="ההקלטה נעצרת…" Basic.Main.StopReplayBuffer="עצור את אוגר ההילוך החוזר" Basic.Main.StoppingReplayBuffer="עצירת אוגר ההילוך החוזר..." @@ -688,7 +690,6 @@ Basic.Settings.ProgramRestart="יש להפעיל את התוכנית מחדש כ Basic.Settings.ConfirmTitle="אישור השינויים" Basic.Settings.Confirm="קיימים שינויים שלא נשמרו. לשמור את השינויים?" Basic.Settings.General="כללי" -Basic.Settings.General.Theme="ערכת עיצוב" Basic.Settings.General.Language="שפה" Basic.Settings.General.Updater="עדכונים" Basic.Settings.General.UpdateChannel="ערוץ עדכונים" @@ -748,7 +749,13 @@ Basic.Settings.General.ChannelName.stable="יציב" Basic.Settings.General.ChannelDescription.stable="המהדורה היציבה העדכנית ביותר" Basic.Settings.General.ChannelName.beta="מהדורות ניסוי (בטא) / מועמדות להפצה" Basic.Settings.General.ChannelDescription.beta="גרסאות מפוקפקות טרום הפצה" +Basic.Settings.Appearance="מראה" +Basic.Settings.Appearance.General="כללי" +Basic.Settings.Appearance.General.Theme="ערכת נושא" +Basic.Settings.Appearance.General.Variant="סגנון" +Basic.Settings.Appearance.General.NoVariant="אין סגנונות זמינים" Basic.Settings.Stream="שידורים חיים" +Basic.Settings.Stream.Destination="יעד" Basic.Settings.Stream.Custom.UseAuthentication="שימוש באימות" Basic.Settings.Stream.Custom.Username="שם משתמש" Basic.Settings.Stream.Custom.Password="סיסמה" @@ -770,9 +777,22 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="קצב נתונים מרבי Basic.Settings.Stream.Recommended.MaxAudioBitrate="קצב נתונים מרבי לשמע: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="רזולוציה מרבית: %1" Basic.Settings.Stream.Recommended.MaxFPS="‏FPS מרבי: %1" +Basic.Settings.Stream.SpecifyCustomServer="בחירת שרת משלך…" +Basic.Settings.Stream.ServiceCustomServer="שרת משלך" +Basic.Settings.Stream.EnableMultitrackVideo="הפעלת %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="רוחב־פס מרבי להזרמה" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="אוטו׳" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="כמות רצועות וידאו מרבית" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="אוטו׳" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="הפעלת הטלת תזרימים ל־FLV (משתמש בהגדרות פשוטות לקובץ הקלטה)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="דריסת הגדרות (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="הפעלת דריסת הגדרות" +Basic.Settings.Stream.MultitrackVideoLabel="סרטון רב־רצועות" +Basic.Settings.Stream.AdvancedOptions="אפשרויות מתקדמות" Basic.Settings.Output="פלט" Basic.Settings.Output.Format="תצורת הקלטה" Basic.Settings.Output.Format.MKV="סרטון Matroska‏ (‎.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 משולב [ניסיוני] (‎‎.mp4)" Basic.Settings.Output.Format.fMP4="MP4 מחולק (‎.mp4)" Basic.Settings.Output.Format.fMOV="MOV מחולק (‎.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="MOV מחולק כותב את ההקלטה במקטעים ואינו דורש את אותו הגימור כמו קובצי MOV מסורתיים.\nאופן השמירה הזה מבטיח שלא תהיה בעיה לנגן את הקובץ אפילו אם הכתיבה לכונן נקטעת, למשל, כתוצאה ממסך כחול או נפילת חשמל.\n\nיש איזשהו חשש לחוסר תאימות מול כל הנגנים והעורכים. אפשר להשתמש בקובץ ← ריבוב הקלטות כדי להמיר את הקובץ לתצורה תואמת יותר במידת הצורך." @@ -1246,3 +1266,8 @@ YouTube.Errors.messageTextInvalid="תוכן ההודעה שגוי." YouTube.Errors.rateLimitExceeded="שלחת הודעות מהר מדי." YouTube.DocksRemoval.Title="מחיקת מעגנים מיושנים של דפדפן YouTube" YouTube.DocksRemoval.Text="מעגני דפדפן אלה יוסרו כיוון שיצאו משימוש.\n\n%1\nאפשר להשתמש ב„מעגנים/חדר בקרה ל־YouTube חי” במקום." +ConfigDownload.WarningMessageTitle="אזהרה" +FailedToStartStream.NoConfigSupplied="הגדרות חסרות" +MultitrackVideo.IncompatibleSettings.Title="הגדרות לא תואמות" +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="להשבית לתזרים הזה ולהתחיל להזרים/לשדר" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="עדכון ההגדרות והתחלת הזרמה/שידור" diff --git a/UI/data/locale/hi-IN.ini b/UI/data/locale/hi-IN.ini index 97fc7d964df57f..c5b81ba6867f8a 100644 --- a/UI/data/locale/hi-IN.ini +++ b/UI/data/locale/hi-IN.ini @@ -682,7 +682,6 @@ Basic.Settings.ProgramRestart="परिवर्तनों को लाग Basic.Settings.ConfirmTitle="परिवर्तनों की पुष्टि करें" Basic.Settings.Confirm="आपने परिवर्तनों को अभी नहीं सहेजा है. क्या आप इन्हें सहेजना चाहते हैं?" Basic.Settings.General="सामान्य" -Basic.Settings.General.Theme="थीम" Basic.Settings.General.Language="भाषा" Basic.Settings.General.Updater="अपडेट" Basic.Settings.General.UpdateChannel="अद्यतन चैनल" @@ -742,6 +741,11 @@ Basic.Settings.General.ChannelName.stable="स्थिर" Basic.Settings.General.ChannelDescription.stable="नवीनतम स्थिर रिलीज" Basic.Settings.General.ChannelName.beta="बीटा / रिलीज़ उम्मीदवार" Basic.Settings.General.ChannelDescription.beta="संभावित रूप से अस्थिर पूर्व-रिलीज़ संस्करण" +Basic.Settings.Appearance="रूप-रंग" +Basic.Settings.Appearance.General="सामान्य" +Basic.Settings.Appearance.General.Theme="प्रदर्शनशैली" +Basic.Settings.Appearance.General.Variant="शैली" +Basic.Settings.Appearance.General.NoVariant="कोई शैलियाँ उपलब्ध नहीं हैं" Basic.Settings.Stream="स्ट्रीम" Basic.Settings.Stream.Custom.UseAuthentication="प्रमाणीकरण का प्रयोग करें" Basic.Settings.Stream.Custom.Username="उपयोगकर्ता का नाम" diff --git a/UI/data/locale/hr-HR.ini b/UI/data/locale/hr-HR.ini index 42283d414d6591..2475a6643a60a6 100644 --- a/UI/data/locale/hr-HR.ini +++ b/UI/data/locale/hr-HR.ini @@ -577,7 +577,6 @@ Basic.Settings.ProgramRestart="Program se mora ponovno pokrenuti da bi ova posta Basic.Settings.ConfirmTitle="Potvrdite promjene" Basic.Settings.Confirm="Niste spremili sve promjene. Spremiti?" Basic.Settings.General="Općenito" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Jezik" Basic.Settings.General.UpdateChannelDisabled="(Deaktivirano)" Basic.Settings.General.UpdateChannelDefault="(Zadano)" diff --git a/UI/data/locale/hu-HU.ini b/UI/data/locale/hu-HU.ini index 5120adf23904da..a5ba3800803eac 100644 --- a/UI/data/locale/hu-HU.ini +++ b/UI/data/locale/hu-HU.ini @@ -482,22 +482,22 @@ Basic.PropertiesWindow.AutoSelectFormat="%1 (autokiválasztás: %2)" Basic.PropertiesWindow.SelectColor="Szín kiválasztása" Basic.PropertiesWindow.SelectFont="Betűtípus kiválasztása" Basic.PropertiesWindow.SelectFont.WindowTitle="Válasszon egy betűkészletet" -Basic.PropertiesWindow.ConfirmTitle="Beállítások módosultak" -Basic.PropertiesWindow.Confirm="Változtatásai nincsenek elmentve. Szeretné megtartani őket?" +Basic.PropertiesWindow.ConfirmTitle="A beállítások megváltoztak" +Basic.PropertiesWindow.Confirm="A változtatások nincsenek elmentve. Megtartja őket?" Basic.PropertiesWindow.NoProperties="Nincsenek elérhető tulajdonságok" Basic.PropertiesWindow.AddFiles="Fájlok hozzáadása" -Basic.PropertiesWindow.AddDir="Mappa hozzáadása" -Basic.PropertiesWindow.AddURL="Elérési út/URL hozzáadása" -Basic.PropertiesWindow.AddEditableListDir="Könyvtár hozzáadása: '%1'" -Basic.PropertiesWindow.AddEditableListFiles="Fájlok hozzáadása: '%1'" -Basic.PropertiesWindow.AddEditableListEntry="Bejegyzés hozzáadása: '%1'" +Basic.PropertiesWindow.AddDir="Könyvtár hozzáadása" +Basic.PropertiesWindow.AddURL="Elérési út/webcím hozzáadása" +Basic.PropertiesWindow.AddEditableListDir="Könyvtár hozzáadása ehhez: „%1”" +Basic.PropertiesWindow.AddEditableListFiles="Fájlok hozzáadása ehhez: „%1”" +Basic.PropertiesWindow.AddEditableListEntry="Bejegyzés hozzáadása ehhez: „%1”" Basic.PropertiesWindow.EditEditableListEntry="Bejegyzés szerkesztése: '%1'" -Basic.PropertiesView.FPS.Simple="Egyszerű FPS érték" -Basic.PropertiesView.FPS.Rational="Racionális FPS érték" +Basic.PropertiesView.FPS.Simple="Egyszerű FPS értékek" +Basic.PropertiesView.FPS.Rational="Racionális FPS értékek" Basic.PropertiesView.FPS.ValidFPSRanges="Érvényes FPS tartományok:" Basic.PropertiesView.UrlButton.Text="Az alapértelmezett böngészőben nyitja meg ezt a hivatkozást?" -Basic.PropertiesView.UrlButton.OpenUrl="URL megnyitása" -Basic.InteractionWindow="Egymásra hatnak \"%1\"-el" +Basic.PropertiesView.UrlButton.OpenUrl="Webcím megnyitása" +Basic.InteractionWindow="Interakció ezzel: „%1”" Basic.StatusBar.Reconnecting="Szétkapcsolt, újrakapcsolódás %2 másodperc múlva (%1. kísérlet)" Basic.StatusBar.AttemptingReconnect="Újrakapcsolódási kísérlet... (%1. próbálkozás)" Basic.StatusBar.ReconnectSuccessful="Sikeres újracsatlakozás" @@ -571,6 +571,7 @@ Basic.Main.StopRecording="Felvétel leállítása" Basic.Main.PauseRecording="Felvétel szüneteltetése" Basic.Main.UnpauseRecording="Felvétel folytatása" Basic.Main.SplitFile="Felvétel fájljának felosztása" +Basic.Main.AddChapterMarker="Fejezetjelölő hozzáadása" Basic.Main.StoppingRecording="Felvétel leállítása..." Basic.Main.StopReplayBuffer="Visszajátszási puffer megállítása" Basic.Main.StoppingReplayBuffer="Visszajátszási puffer megállítása…" @@ -680,7 +681,6 @@ Basic.Settings.ProgramRestart="A beállítások érvénybe lépéséhez a progra Basic.Settings.ConfirmTitle="Változtatások megerősítése" Basic.Settings.Confirm="Nem mentette a módosításokat. Menti a változtatásokat?" Basic.Settings.General="Általános" -Basic.Settings.General.Theme="Téma" Basic.Settings.General.Language="Nyelv" Basic.Settings.General.Updater="Frissítések" Basic.Settings.General.UpdateChannel="Frissítési csatorna" @@ -740,6 +740,11 @@ Basic.Settings.General.ChannelName.stable="Stabil" Basic.Settings.General.ChannelDescription.stable="Legújabb stabil kiadás" Basic.Settings.General.ChannelName.beta="Béták / kiadásra jelölt változatok" Basic.Settings.General.ChannelDescription.beta="Potenciálisan instabil előzetes verziók" +Basic.Settings.Appearance="Megjelenés" +Basic.Settings.Appearance.General="Általános" +Basic.Settings.Appearance.General.Theme="Téma" +Basic.Settings.Appearance.General.Variant="Stílus" +Basic.Settings.Appearance.General.NoVariant="Nincs elérhető stílus" Basic.Settings.Stream="Közvetítés" Basic.Settings.Stream.Custom.UseAuthentication="Hitelesítés használata" Basic.Settings.Stream.Custom.Username="Felhasználónév" @@ -764,6 +769,7 @@ Basic.Settings.Stream.Recommended.MaxResolution="Legnagyobb felbontás: %1" Basic.Settings.Stream.Recommended.MaxFPS="Legnagyobb FPS: %1" Basic.Settings.Output="Kimenet" Basic.Settings.Output.Format="Felvétel formátuma" +Basic.Settings.Output.Format.hMP4="Hibrid MP4 [BÉTA] (.mp4)" Basic.Settings.Output.Format.fMP4="Töredékes MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Töredékes MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="A töredékes MOV darabokban írja ki a felvételt, és nem igényli a hagyományos MOV-fájlok esetén szükséges véglegesítést.\nEz biztosítja azt, hogy a fájl akkor is lejátszható marad, ha a lemezre írás megszakad, például egy kékhalál vagy egy áramszünet miatt.\n\nEz nem biztos, hogy minden lejátszóval és szerkesztővel kompatibilis. Ha szükséges, akkor használja Fájl → Felvételek remuxolása lehetőséget, hogy kompatibilisebb formátumba alakítsa a fájlt." diff --git a/UI/data/locale/hy-AM.ini b/UI/data/locale/hy-AM.ini index dd3a41cdb85517..26924000135ff6 100644 --- a/UI/data/locale/hy-AM.ini +++ b/UI/data/locale/hy-AM.ini @@ -672,7 +672,6 @@ Basic.Settings.ProgramRestart="Այս կարգավորումները փոխել Basic.Settings.ConfirmTitle="Փոփոխությունների հաստատում" Basic.Settings.Confirm="Դուք չպահված փոփոխություններ ունեք: Պահե՞լ դրանք:" Basic.Settings.General="Ընդհանուր" -Basic.Settings.General.Theme="Թեմա" Basic.Settings.General.Language="Լեզու" Basic.Settings.General.Updater="Թարմացրեք" Basic.Settings.General.UpdateChannel="Արդիական Ալիք" diff --git a/UI/data/locale/id-ID.ini b/UI/data/locale/id-ID.ini index e322a4730cb198..78debfcfb1c6c7 100644 --- a/UI/data/locale/id-ID.ini +++ b/UI/data/locale/id-ID.ini @@ -104,6 +104,7 @@ MixerToolbarMenu="Menu Mixer Audio" SceneFilters="Buka Filter Adegan" List="Daftar" Grid="Kisi-kisi" +Automatic="Otomatis" PluginsFailedToLoad.Title="Kesalahan Memuat Plugin" PluginsFailedToLoad.Text="Plugin OBS berikut ini gagal untuk memuat:\n\n%1\nMohon perbarui atau hapus plugin tersebut." AlreadyRunning.Title="OBS sudah berjalan" @@ -187,6 +188,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Lebih memilih pengkodean per Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Pengkodean Perangkat Keras mengurangi sebagian besar penggunaan CPU, namun mungkin memerlukan bitrate lebih besar untuk mendapatkan kualitas yang sama." Basic.AutoConfig.StreamPage.StreamWarning.Title="Peringatan streaming" Basic.AutoConfig.StreamPage.StreamWarning.Text="Pengujian bandwidth akan melakukan streaming data video acak tanpa audio ke saluran Anda. Jika bisa, disarankan untuk menonaktifkan sementara menyimpan video stream dan atur streaming ke privasi pribadi hingga tes selesai. Lanjutkan?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Uji %1" Basic.AutoConfig.TestPage="Hasil Akhir" Basic.AutoConfig.TestPage.SubTitle.Testing="Sekarang program menjalankan serangkaian pengujian untuk memperkirakan pengaturan yang paling ideal" Basic.AutoConfig.TestPage.SubTitle.Complete="Pengujian selesai" @@ -205,6 +207,7 @@ Basic.AutoConfig.TestPage.Result.Header="Program telah menentukan bahwa pengatur Basic.AutoConfig.TestPage.Result.Footer="Untuk menggunakan pengaturan ini, klik Terapkan Pengaturan. Untuk mengonfigurasi ulang wisaya dan mencobanya lagi, klik Kembali. Untuk mengonfigurasi sendiri pengaturan secara manual, klik Batal dan buka Pengaturan." Basic.AutoConfig.Info="Wisaya konfigurasi otomatis akan menentukan pengaturan terbaik berdasarkan spesifikasi komputer dan kecepatan internet Anda." Basic.AutoConfig.RunAnytime="Ini dapat dijalankan kapan saja dengan membuka menu Alat." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Resolusi Streaming (Diskalakan)" Basic.Stats="Statistik" Basic.Stats.CPUUsage="Penggunaan CPU" Basic.Stats.HDDSpaceAvailable="Ketersediaan ruang simpan" @@ -547,6 +550,7 @@ Basic.Main.Scenes="Adegan" Basic.Main.Sources="Sumber" Basic.Main.Source="Sumber" Basic.Main.Controls="Kontrol" +Basic.Main.PreparingStream="Mempersiapkan..." Basic.Main.Connecting="Menghubungkan..." Basic.Main.StartRecording="Mulai Merekam" Basic.Main.StartReplayBuffer="Mulai Buffer Replay" @@ -558,6 +562,7 @@ Basic.Main.StopRecording="Berhenti Merekam" Basic.Main.PauseRecording="Jeda Rekaman" Basic.Main.UnpauseRecording="Lanjut Merekam" Basic.Main.SplitFile="Pisahkan Berkas Rekaman" +Basic.Main.AddChapterMarker="Tambah Penanda Bab" Basic.Main.StoppingRecording="Menghentikan Perekaman..." Basic.Main.StopReplayBuffer="Hentikan Buffer Replay" Basic.Main.StoppingReplayBuffer="Menghentikan Buffer Replay..." @@ -665,8 +670,8 @@ Basic.MainMenu.Help.About="Tent&ang" Basic.Settings.ProgramRestart="Program harus diaktifkan ulang agar pengaturan ini berlaku." Basic.Settings.ConfirmTitle="Konfirmasi Perubahan" Basic.Settings.Confirm="Anda memiliki perubahan yang belum disimpan. Simpan perubahan?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 mengendalikan beberapa setelan streaming Anda" Basic.Settings.General="Umum" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Bahasa" Basic.Settings.General.Updater="Pembaruan" Basic.Settings.General.UpdateChannel="Pembaruan Saluran" @@ -726,7 +731,13 @@ Basic.Settings.General.ChannelName.stable="Stabil" Basic.Settings.General.ChannelDescription.stable="Rilisan stabil mutakhir" Basic.Settings.General.ChannelName.beta="Percobaan / Kandidat Rilisan" Basic.Settings.General.ChannelDescription.beta="Potensi versi pra-rilisan tidak stabil" +Basic.Settings.Appearance="Tampilan" +Basic.Settings.Appearance.General="Umum" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Gaya" +Basic.Settings.Appearance.General.NoVariant="Tidak Ada Gaya yang Tersedia" Basic.Settings.Stream="Streaming" +Basic.Settings.Stream.Destination="Tujuan" Basic.Settings.Stream.Custom.UseAuthentication="Gunakan autentikasi" Basic.Settings.Stream.Custom.Username="Nama Pengguna" Basic.Settings.Stream.Custom.Password="Kata Sandi" @@ -748,8 +759,21 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maksimum Bitrate Video: %1 kb Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maksimum Bitrate Audio: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maksimum Resolusi: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maksimum FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Tentukan Penyesuaian Server..." +Basic.Settings.Stream.ServiceCustomServer="Sesuaikan Server" +Basic.Settings.Stream.EnableMultitrackVideo="Aktifkan %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maksimum Bandwidth Streaming" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Otomatis" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maksimum Trek Video" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Otomatis" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Aktifkan timbunan streaming ke FLV (menggunakn berkas pengaturan rekaman sederhana)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Timpa Konfigurasi (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Aktifkan Penimpa Konfigurasi" +Basic.Settings.Stream.MultitrackVideoLabel="Video Multitrack" +Basic.Settings.Stream.AdvancedOptions="Opsi Lanjutan" Basic.Settings.Output.Format="Format Rekaman" Basic.Settings.Output.Format.MKV="Video Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="Hybrid MP4 [PERCOBAAN] (.mp4)" Basic.Settings.Output.Format.fMP4="Pecahan MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Pecahan MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Pecahan MOV menulis rekaman dalam potongan chunk dan tidak memerlukan finalisasi yang sama seperti berkas MOV tradisional.\nIni memastikan berkas tetap dapat diputar bahkan jika menulis ke penyimpanan nya mengalami interupsi. Contohnya, akibat BSOD atau mati listrik.\n\nIni mungkin tidak kompatibel dengan semua pemutar dan penyunting. Gunakan Berkas → Remux Rekaman untuk mengubah berkas ke dalam format yang kompatibel jika diperlukan." @@ -1189,3 +1213,27 @@ YouTube.Errors.messageTextInvalid="Teks pesan tidak sah." YouTube.Errors.rateLimitExceeded="Anda mengirim pesan terlalu cepat." YouTube.DocksRemoval.Title="Bersihkan Dok Peramban Lawas YouTube" YouTube.DocksRemoval.Text="Dok peramban berikut ini akan dihapus sebagai fitur usang:\n\n%1\nSebagai gantinya, gunakan \"Dok/Ruang Live Control YouTube\"." +ConfigDownload.WarningMessageTitle="Peringatan" +FailedToStartStream.MissingConfigURL="Tidak ada URL konfigurasi yang tersedia untuk layanan saat ini" +FailedToStartStream.NoCustomRTMPURLInSettings="Kustomisasi URL RTMP tidak ditentukan" +FailedToStartStream.InvalidCustomConfig="Penyesuaian konfigurasi tidak sah" +FailedToStartStream.FailedToCreateMultitrackVideoService="Gagal untuk membuat layanan video multitrack" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Gagal untuk membuat video multitrack ber-output rtmp" +FailedToStartStream.EncoderNotAvailable="NVENC tidak tersedia.\n\nGagal untuk mencari tipe pengkodean '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Gagal untuk membuat pengkodean video '%1' (tipe: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Gagal untuk mendapatkan info video obs ketika membuat pengkodean '%1' (tipe: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Gagal untuk membuat pengkodean audio" +FailedToStartStream.NoRTMPURLInConfig="Konfigurasi tidak mengandung target URL streaming RTMP(S) " +FailedToStartStream.FallbackToDefault="Memulai streaming menggunakan %1 gagal; Anda ingin mencoba lagi menggunakan pengaturan mengkodekan tunggal?" +FailedToStartStream.ConfigRequestFailed="Tidak dapat mengambil konfigurasi dari %1

Galat HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Menerima nilai status yang tidak diketahui '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nAnda ingin melanjutkan streaming tanpa %1?" +FailedToStartStream.WarningRetry="\n

\nAnda ingin melanjutkan streaming?" +FailedToStartStream.MissingEncoderConfigs="Konfigurasi mulai siaran tidak termasuk konfigurasi pengkodean" +FailedToStartStream.StatusMissingHTML="Permintaan mulai siaran membalas dengan sebuah galat yang tidak disebutkan" +FailedToStartStream.NoConfigSupplied="Kehilangan Konfigurasi" +MultitrackVideo.Info="%1 otomatis mengoptimisasikan setelan Anda untuk mengkodekan dan mengirimkan beberapa kualitas video. Memilih opsi ini akan mengirimkan informasi %2 tentang komputer dan pengaturan perangkat lunak Anda." +MultitrackVideo.IncompatibleSettings.Title="Pengaturan Tidak Cocok" +MultitrackVideo.IncompatibleSettings.Text="%1 saat ini tidak cocok dengan:\n\n%2\nUntuk melanjutkan streaming dengan %1, nonaktifkan pengaturan yang tidak cocok:\n\n%3\ndan Mulai Streaming lagi." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Nonaktifkan untuk streaming ini dan Mulai Streaming" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Perbarui Pengaturan dan Mulai Streaming" diff --git a/UI/data/locale/it-IT.ini b/UI/data/locale/it-IT.ini index c001535481a8ce..2dfea23a9c1347 100644 --- a/UI/data/locale/it-IT.ini +++ b/UI/data/locale/it-IT.ini @@ -104,6 +104,7 @@ MixerToolbarMenu="Menu mixer audio" SceneFilters="Apri filtri scena" List="Elenco" Grid="Griglia" +Automatic="Automatico" PluginsFailedToLoad.Title="Errore caricamento plugin" PluginsFailedToLoad.Text="Non sono stati caricati i seguenti plugin OBS :\n\n%1\nAggiorna o rimuovi questi plugin." AlreadyRunning.Title="OBS è già in esecuzione" @@ -116,7 +117,7 @@ AutoSafeMode.LaunchNormal="Esegui normalmente" SafeMode.Restart="Vuoi riavviare OBS in modalità provvisoria (verranno disattivati plugin di terze parti, script, e websocket)?" SafeMode.RestartNormal="Vuoi riavviare OBS in modalità normale?" ChromeOS.Title="Piattaforma non supportata" -ChromeOS.Text="OBS sembra essere in esecuzione all'interno di un contenitore ChromeOS. \nQuesta piattaforma non è supportata." +ChromeOS.Text="OBS sembra essere in esecuzione all'interno di un contenitore ChromeOS. Questa piattaforma non è supportata." Wine.Title="Rilevato Wine" Wine.Text="L'esecuzione di OBS in Wine non è supportata e molte funzionalità, come l'acquisizione o alcune sorgenti, non funzioneranno o avranno una capacità limitata.

Ti consigliamo invece di eseguire una versione nativa di OBS, ad esempio la nostra versione Flatpak o i pacchetti per il tuo sistema operativo." DockCloseWarning.Title="Hai chiuso la finestra di un pannello" @@ -187,6 +188,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferisci la codifica hardw Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="La codifica hardware elimina la maggior parte dell'utilizzo della CPU, ma può richiedere più velocità in bit per ottenere lo stesso livello di qualità." Basic.AutoConfig.StreamPage.StreamWarning.Title="Avviso per la diretta" Basic.AutoConfig.StreamPage.StreamWarning.Text="Il test di larghezza di banda consiste nel trasmettere dati video casuali senza audio sul tuo canale. Se possibile, è consigliabile disattivare temporaneamente il salvataggio delle dirette e di impostare le dirette in modalità privata fino a quando non finisce il test. Vuoi continuare?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Prova %1" Basic.AutoConfig.TestPage="Risultato finale" Basic.AutoConfig.TestPage.SubTitle.Testing="Il programma sta ora eseguendo una serie di test per stimare le impostazioni ideali" Basic.AutoConfig.TestPage.SubTitle.Complete="Test completato" @@ -205,6 +207,7 @@ Basic.AutoConfig.TestPage.Result.Header="Il programma ha rilevato che queste imp Basic.AutoConfig.TestPage.Result.Footer="Per utilizzare queste impostazioni, fai clic su «Applica le impostazioni». Per riprovare a configurare la procedura guidata, fai clic su «Indietro». Per configurare manualmente le impostazioni, fai clic su «Annulla» e apri le «Impostazioni»." Basic.AutoConfig.Info="La configurazione guidata automatica determinerà le migliori impostazioni in base alle specifiche del computer e alla velocità di Internet." Basic.AutoConfig.RunAnytime="Questo può essere eseguito in qualsiasi momento andando nel menu Strumenti." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Risoluzione (scalata) della trasmissione " Basic.Stats="Statistiche" Basic.Stats.CPUUsage="Utilizzo della CPU" Basic.Stats.HDDSpaceAvailable="Spazio su disco disponibile" @@ -281,7 +284,7 @@ Undo.Transform.VCenter="Centro verticale allo schermo in '%1'" Undo.Transform.HCenter="Centro orizzontale allo schermo in '%1'" Undo.Volume.Change="Cambio volume su '%1'" Undo.Volume.Mute="Silenzia '%1'" -Undo.Volume.Unmute="Riattiva '%1'" +Undo.Volume.Unmute="Riattiva audio '%1'" Undo.Balance.Change="Bilanciamento audio su '%1'" Undo.SyncOffset.Change="Cambio sincronizzazione audio su '%1'" Undo.MonitoringType.Change="Cambia monitoraggio audio su '%1'" @@ -418,7 +421,7 @@ MacPermissions.Item.Accessibility="Accessibilità" MacPermissions.Item.Accessibility.Details="Abilita questa autorizzazione per far funzionare le scorciatoie da tastiera (tasti di scelta rapida) mentre altre applicazioni sono attivate." MacPermissions.Continue="Continua" SourceLeak.Title="Errore pulizia sorgente" -SourceLeak.Text="Si è verificato un problema durante la modifica delle raccolte di scene e non è stato possibile scaricare alcune sorgenti.\n\nQuesto problema è in genere causato da plug-in che non rilasciano correttamente le risorse.\nAssicurati che tutti i plugin che stai usando siano aggiornati.\n\nOBS Studio ora verrà chiuso per prevenire qualsiasi potenziale danneggiamento dei dati." +SourceLeak.Text="Si è verificato un problema durante la modifica delle raccolte di scene e non è stato possibile scaricare alcune sorgenti.\nQuesto problema è in genere causato da plug-in che non rilasciano correttamente le risorse.\nAssicurati che tutti i plugin che stai usando siano aggiornati.\n\nOBS Studio ora verrà chiuso per prevenire qualsiasi potenziale danneggiamento dei dati." Basic.DesktopDevice1="Audio del desktop" Basic.DesktopDevice2="Audio del desktop 2" Basic.AuxDevice1="Microfono/disp. ausiliario" @@ -559,6 +562,7 @@ Basic.Main.Scenes="Scene" Basic.Main.Sources="Fonti" Basic.Main.Source="Fonte" Basic.Main.Controls="Controlli" +Basic.Main.PreparingStream="Preparazione..." Basic.Main.Connecting="Connessione in corso..." Basic.Main.StartRecording="Avvia la registrazione" Basic.Main.StartReplayBuffer="Avvia il buffer di replay" @@ -570,6 +574,7 @@ Basic.Main.StopRecording="Termina la registrazione" Basic.Main.PauseRecording="Sospendi la registrazione" Basic.Main.UnpauseRecording="Riprendi la registrazione" Basic.Main.SplitFile="Dividi file registrazione" +Basic.Main.AddChapterMarker="Aggiungi marcatore del capitolo" Basic.Main.StoppingRecording="Terminazione della registrazione in corso..." Basic.Main.StopReplayBuffer="Termina il buffer di replay" Basic.Main.StoppingReplayBuffer="Terminazione del buffer di replay in corso..." @@ -676,8 +681,8 @@ Basic.MainMenu.Help.About="Inform&azioni" Basic.Settings.ProgramRestart="Il programma deve essere riavviato perché questi cambiamenti abbiano effetto." Basic.Settings.ConfirmTitle="Conferma i cambiamenti" Basic.Settings.Confirm="Hai dei cambiamenti non salvati. Vuoi salvarli?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 sta controllando alcune delle impostazioni della tua trasmissione" Basic.Settings.General="Generale" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Lingua" Basic.Settings.General.Updater="Aggiornamenti" Basic.Settings.General.UpdateChannel="Canale aggiornamento" @@ -737,8 +742,15 @@ Basic.Settings.General.ChannelName.stable="Stabile" Basic.Settings.General.ChannelDescription.stable="Ultimo rilascio stabile" Basic.Settings.General.ChannelName.beta="Beta / versioni Candidate" Basic.Settings.General.ChannelDescription.beta="Versioni pre-rilascio potenzialmente instabili" +Basic.Settings.Appearance="Aspetto" +Basic.Settings.Appearance.General="Generale" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Stile" +Basic.Settings.Appearance.General.NoVariant="Nessuno stile disponibile" Basic.Settings.Stream="Dirette" +Basic.Settings.Stream.Destination="Destinazione" Basic.Settings.Stream.Custom.UseAuthentication="Usa autenticazione" +Basic.Settings.Stream.Custom.Username="Nome utente" Basic.Settings.Stream.Custom.Username.ToolTip="RIST: inserisci srp_username.\nRTMP: inserisci il nome utente.\nSRT: non utilizzato." Basic.Settings.Stream.Custom.Password.ToolTip="RIST: inserisci srp_password.\nRTMP: inserisci la password.\nSRT: inserisci la passphrase." Basic.Settings.Stream.BandwidthTestMode="Attiva la modalità test della larghezza di banda" @@ -757,9 +769,17 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Bitrate video massimo: %1 kbp Basic.Settings.Stream.Recommended.MaxAudioBitrate="Bitrate audio massimo: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Risoluzione massima: %1" Basic.Settings.Stream.Recommended.MaxFPS="FPS Massimi: %1" +Basic.Settings.Stream.SpecifyCustomServer="Specifica un Server personalizzato..." +Basic.Settings.Stream.ServiceCustomServer="Server personalizzato" +Basic.Settings.Stream.EnableMultitrackVideo="Abilita %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Larghezza di banda della trasmissione massima" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatica" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Numero massimo di traccie video" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatico" Basic.Settings.Output="Uscita" Basic.Settings.Output.Format="Formato di registrazione" Basic.Settings.Output.Format.MKV="Video Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 ibrido [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 frammentato (.mp4)" Basic.Settings.Output.Format.fMOV="MOV frammentato (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Un file MOV frammentato scrive la registrazione in pezzi e non richiede la stessa finalizzazione dei file MOV tradizionali.\nQuesto assicura che il file rimanga riproducibile anche se la scrittura su disco viene interrotta, ad esempio, a causa di un BSOD o di una mancanza alimentazione.\n\nIl file frammentato porebbe non essere compatibile con tutti i programmi di riproduzione/modifica.\nSe necessario usa 'File' → 'Remux registrazioni' per convertire il file in un formato più compatibile." @@ -794,7 +814,7 @@ Basic.Settings.Output.Simple.Warn.AudioBitrate="Attenzione: il bitrate audio in Basic.Settings.Output.Simple.Warn.CannotPause="Attenzione: Le registrazioni non possono essere sospese se la qualità di registrazione è impostata su \"La stessa della diretta\"." Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Attenzione: il formato di registrazione attualmente selezionato non è compatibile con l'encoder per lo streaming selezionati." Basic.Settings.Output.Simple.Warn.Encoder="Attenzione: registrare con una codifica software a una qualità diversa dalla diretta richiederà un utilizzo aggiuntivo della CPU se esegui dirette e registri allo stesso tempo." -Basic.Settings.Output.Simple.Warn.Lossless="Avviso: la qualità senza perdita di dati genera file di dimensioni tremendamente grandi! La qualità senza perdite può usare fino a 7 gigabyte di spazio su disco al minuto a risoluzioni e frame rate elevati. La modalità senza perdita dati non è consigliata per registrazioni lunghe, a meno che non si disponga di una grande quantità di spazio su disco. Il buffer di riproduzione non è disponibile quando si utilizza la qualità senza perdita dati." +Basic.Settings.Output.Simple.Warn.Lossless="Attenzione: la qualità senza perdita di dati genera file di dimensioni enormi! La qualità senza perdite può occupare fino a 7 gigabyte di spazio sul disco al minuto a risoluzioni e framerate elevati. La modalità senza perdita dati non è consigliata per registrazioni lunghe, a meno che non si disponga di una quantità di spazio su disco molto grande. Il buffer di riproduzione non è disponibile quando si utilizza la qualità senza perdita dati." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sei sicuro di volere utilizzare la qualità lossless?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Avviso sulla qualità lossless!" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264, preset con basso utilizzo della CPU, aumenta le dimensioni dei file)" @@ -929,7 +949,7 @@ Basic.Settings.Audio.PeakMeterType="Tipo di misuratore di picco" Basic.Settings.Audio.PeakMeterType.SamplePeak="Picco a campionamento" Basic.Settings.Audio.PeakMeterType.TruePeak="Picco esatto (alto utilizzo della CPU)" Basic.Settings.Audio.MultichannelWarning.Enabled="ATTENZIONE: L'audio surround è attivato." -Basic.Settings.Audio.MultichannelWarning="In caso di streaming, controlla se il servizio di streaming supporta sia l'acquisizione del suono surround che la riproduzione del suono surround. \nAd esempio, Facebook 360 Live supporta completamente il suono surround e YouTube Live supporta l'importazione audio 5.1 (e la riproduzione su TV).\n\nI filtri audio OBS sono compatibili con l'audio surround, anche se il supporto del plug-in VST non è garantito." +Basic.Settings.Audio.MultichannelWarning="In caso di streaming, controlla se il servizio di streaming supporta sia l'acquisizione del suono surround che la riproduzione del suono surround. Ad esempio, Facebook 360 Live supporta completamente il suono surround e YouTube Live supporta l'importazione audio 5.1 (e la riproduzione su TV).\n\nI filtri audio OBS sono compatibili con l'audio surround, anche se il supporto del plug-in VST non è garantito." Basic.Settings.Audio.MultichannelWarning.Title="Vuoi attivare l'audio surround?" Basic.Settings.Audio.MultichannelWarning.Confirm="Sei sicuro di voler attivare l'audio surround?" Basic.Settings.Audio.Devices="Dispositivi audio globali" @@ -973,7 +993,7 @@ Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Superiore al normal Basic.Settings.Advanced.General.ProcessPriority.Normal="Normale" Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Inferiore al normale" Basic.Settings.Advanced.General.ProcessPriority.Idle="Bassa" -Basic.Settings.Advanced.FormatWarning="Attenzione: formati di colore diversi da NV12/P010 sono principalmente destinati alla registrazione, e non sono raccomandati durante lo streaming. \nA causa della conversione del formato colore lo streaming potrebbe comportare un maggiore uso della CPU." +Basic.Settings.Advanced.FormatWarning="Attenzione: formati di colore diversi da NV12/P010 sono principalmente destinati alla registrazione, e non sono raccomandati durante lo streaming. A causa della conversione del formato colore lo streaming potrebbe comportare un maggiore uso della CPU." Basic.Settings.Advanced.FormatWarningPreciseSdr="Avviso: i formati ad alta precisione sono più comunemente usati con gli spazi colore HDR." Basic.Settings.Advanced.FormatWarning2100="Attenzione: Rec. 2100 dovrebbe usare un formato con più precisione." Basic.Settings.Advanced.Video.ColorFormat="Formato colore" @@ -1024,9 +1044,9 @@ Basic.AdvAudio.SyncOffset="Ritardo di sincronizzazione" Basic.AdvAudio.SyncOffsetSource="Scostamento di sincronizzazione per «%1»" Basic.AdvAudio.Monitoring="Monitoraggio audio" Basic.AdvAudio.Monitoring.None="Disattivato" -Basic.AdvAudio.Monitoring.MonitorOnly="Solo monitor (uscita silenziata)" +Basic.AdvAudio.Monitoring.MonitorOnly="Monitora solamente (uscita audio disattivata)" Basic.AdvAudio.Monitoring.Both="Monitora l'audio e invia all'uscita" -Basic.AdvAudio.MonitoringSource="Monitoraggio audio per «%1»" +Basic.AdvAudio.MonitoringSource="Monitoraggio audio per '%1'" Basic.AdvAudio.AudioTracks="Tracce" Basic.Settings.Hotkeys="Scorciatoie" Basic.Settings.Hotkeys.Pair="Consigliamo di utilizzare la stessa combinazione di tasti insieme a «%1»" diff --git a/UI/data/locale/ja-JP.ini b/UI/data/locale/ja-JP.ini index e960d83b968e81..b6d06811753382 100644 --- a/UI/data/locale/ja-JP.ini +++ b/UI/data/locale/ja-JP.ini @@ -107,6 +107,7 @@ MixerToolbarMenu="音声ミキサーメニュー" SceneFilters="シーンのフィルタを開く" List="リスト" Grid="グリッド" +Automatic="自動" PluginsFailedToLoad.Title="プラグイン読み込みエラー" PluginsFailedToLoad.Text="以下のOBSプラグイン読み込みに失敗しました:\n\n%1\nこれらのプラグインを更新または削除してください。" AlreadyRunning.Title="OBSは既に実行中です" @@ -163,7 +164,7 @@ Basic.AutoConfig.StartPage.PrioritizeVirtualCam="仮想カメラのみ使用す Basic.AutoConfig.VideoPage="映像設定" Basic.AutoConfig.VideoPage.SubTitle="使用する映像設定を指定" Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="現在の値を使用 (%1x%2)" -Basic.AutoConfig.VideoPage.BaseResolution.Display="画面 %1 (%2x%3)" +Basic.AutoConfig.VideoPage.BaseResolution.Display="ディスプレイ %1 (%2x%3)" Basic.AutoConfig.VideoPage.FPS.UseCurrent="現在の値を使用 (%1)" Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="60または30のいずれか、可能なら60を優先" Basic.AutoConfig.VideoPage.FPS.PreferHighRes="60または30のいずれか、高解像度を優先" @@ -192,6 +193,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="ハードウェアエンコ Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="ハードウェアエンコードはCPU使用率がほとんどなくなりますが、同レベルの品質を得るためにはより多くのビットレートが必要になります。" Basic.AutoConfig.StreamPage.StreamWarning.Title="配信の警告" Basic.AutoConfig.StreamPage.StreamWarning.Text="帯域幅のテストはランダム化された音声なしの映像データを自分のチャンネルに配信しようとしています。可能ならば、一時的に配信の映像保存をオフにしてテストが完了するまで配信を非公開に設定することをおすすめします。 続行しますか?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="テスト %1" Basic.AutoConfig.TestPage="最終結果" Basic.AutoConfig.TestPage.SubTitle.Testing="プログラムは最も理想的な設定を推定するための一連のテストを現在実行中です" Basic.AutoConfig.TestPage.SubTitle.Complete="テスト完了" @@ -210,6 +212,7 @@ Basic.AutoConfig.TestPage.Result.Header="プログラムはこれらの推定設 Basic.AutoConfig.TestPage.Result.Footer="これらの設定を使用するには、設定を適用をクリックします。 ウィザードを再構成して再試行するには、戻るをクリックします。 手動で設定をするには、キャンセルをクリックして設定を開きます。" Basic.AutoConfig.Info="自動設定ウィザードはお使いのコンピュータのスペックとインターネットの速度に基づいて最適な設定を決定します。" Basic.AutoConfig.RunAnytime="これはツールメニューからいつでも実行することができます。" +Basic.AutoConfig.TestPage.Result.StreamingResolution="配信 (スケーリング) 解像度" Basic.Stats="統計" Basic.Stats.CPUUsage="CPU使用率" Basic.Stats.HDDSpaceAvailable="ディスク空き容量" @@ -253,8 +256,8 @@ QuickTransitions.DuplicateSceneTT="同じシーンを編集する場合、プロ QuickTransitions.EditProperties="ソースを複製" QuickTransitions.EditPropertiesTT="同じシーンを編集する場合、プログラム出力を変更することなくソースの変換/表示設定を編集することができます。\nこの機能は 'シーンを複製' が有効になっている場合にのみ使用可能です。\n一部のソース (キャプチャやメディアソース) はこの機能をサポートしておらず個別に編集することはできません。\nこの値の変更は (もしまだ存在する場合) 現在のプログラムシーンをリセットします。\n\n警告: ソースが複製されるため、余分なシステムまたはビデオリソースを必要になる場合があります。" QuickTransitions.HotkeyName="クイックトランジション: %1" -Basic.AddTransition="構成可能なトランジションを追加" -Basic.RemoveTransition="構成可能なトランジションを削除" +Basic.AddTransition="設定可能なトランジションを追加" +Basic.RemoveTransition="設定可能なトランジションを削除" Basic.TransitionProperties="トランジションのプロパティ" Basic.SceneTransitions="シーントランジション" Basic.TransitionDuration="期間" @@ -565,6 +568,7 @@ Basic.Main.Scenes="シーン" Basic.Main.Sources="ソース" Basic.Main.Source="ソース" Basic.Main.Controls="コントロール" +Basic.Main.PreparingStream="準備中..." Basic.Main.Connecting="接続中..." Basic.Main.StartRecording="録画開始" Basic.Main.StartReplayBuffer="リプレイバッファ開始" @@ -576,6 +580,7 @@ Basic.Main.StopRecording="録画終了" Basic.Main.PauseRecording="録画一時停止" Basic.Main.UnpauseRecording="録画再開" Basic.Main.SplitFile="録画ファイルの分割" +Basic.Main.AddChapterMarker="チャプターマーカーを追加" Basic.Main.StoppingRecording="録画停止処理中..." Basic.Main.StopReplayBuffer="リプレイバッファ停止" Basic.Main.StoppingReplayBuffer="リプレイバッファ停止処理中..." @@ -684,8 +689,8 @@ Basic.MainMenu.Help.About="OBS Studioについて(&A)" Basic.Settings.ProgramRestart="これらの設定を有効にするためにはプログラムの再起動が必要です。" Basic.Settings.ConfirmTitle="変更確認" Basic.Settings.Confirm="保存していない変更があります。変更を保存しますか?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 は配信設定の一部を制御しています" Basic.Settings.General="一般" -Basic.Settings.General.Theme="テーマ" Basic.Settings.General.Language="言語" Basic.Settings.General.Updater="更新" Basic.Settings.General.UpdateChannel="更新チャンネル" @@ -745,7 +750,13 @@ Basic.Settings.General.ChannelName.stable="安定" Basic.Settings.General.ChannelDescription.stable="最新の安定版リリース" Basic.Settings.General.ChannelName.beta="ベータ / リリース候補" Basic.Settings.General.ChannelDescription.beta="潜在的に不安定なプレリリースバージョン" +Basic.Settings.Appearance="外観" +Basic.Settings.Appearance.General="一般" +Basic.Settings.Appearance.General.Theme="テーマ" +Basic.Settings.Appearance.General.Variant="スタイル" +Basic.Settings.Appearance.General.NoVariant="利用可能なスタイルはありません" Basic.Settings.Stream="配信" +Basic.Settings.Stream.Destination="伝送先" Basic.Settings.Stream.Custom.UseAuthentication="認証を使用する" Basic.Settings.Stream.Custom.Username="ユーザー名" Basic.Settings.Stream.Custom.Password="パスワード" @@ -767,8 +778,20 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="最大映像ビットレー Basic.Settings.Stream.Recommended.MaxAudioBitrate="最大音声ビットレート: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="最大解像度: %1" Basic.Settings.Stream.Recommended.MaxFPS="最大FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="カスタムサーバーを指定..." +Basic.Settings.Stream.ServiceCustomServer="カスタムサーバー" +Basic.Settings.Stream.EnableMultitrackVideo="%1 を有効にする" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="最大配信帯域幅" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="自動" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="最大ビデオトラック数" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="自動" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="FLVへのストリームダンプを有効にする (基本録画ファイル設定を使用)"\nBasic.Settings.Stream.MultitrackVideoConfigOverride="\n設定を上書き (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="設定の上書きを有効にする" +Basic.Settings.Stream.MultitrackVideoLabel="マルチトラックビデオ" +Basic.Settings.Stream.AdvancedOptions="詳細オプション" Basic.Settings.Output="出力" Basic.Settings.Output.Format="録画フォーマット" +Basic.Settings.Output.Format.hMP4="Hybrid MP4 [ベータ版] (.mp4)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmented MOVは録画をチャンクで書き込み従来のMOVファイルのようなファイナライズ(完成処理)を必要としません。\nこれにより、例えばブルースクリーンや電源断の結果としてディスクへの書き込みが中断された場合でも、ファイルの再生が可能な状態を維持します。\n\nこれはすべてのプレーヤーやエディターと互換性がない可能性があります。 ファイル → 録画の再多重化 を使用してファイルをより互換性のある形式に変換してください。" Basic.Settings.Output.Format.TT.fragmented_mp4="Fragmented MP4は録画をチャンクで書き込み従来のMP4ファイルのようなファイナライズ(完成処理)を必要としません。\nこれにより、例えばブルースクリーンや電源断の結果としてディスクへの書き込みが中断された場合でも、ファイルの再生が可能な状態を維持します。\n\nこれはすべてのプレーヤーやエディターと互換性がない可能性があります。 ファイル → 録画の再多重化 を使用してファイルをより互換性のある形式に変換してください。" Basic.Settings.Output.Encoder.Video="映像エンコーダ" @@ -1233,3 +1256,27 @@ YouTube.Errors.messageTextInvalid="メッセージテキストが無効です。 YouTube.Errors.rateLimitExceeded="メッセージの送信が速すぎます。" YouTube.DocksRemoval.Title="従来のYouTubeブラウザドックをクリア" YouTube.DocksRemoval.Text="これらのブラウザドックは非推奨として削除されます。\n\n%1\n代わりに「ドック/YouTube Live Control Room」を使用してください。" +ConfigDownload.WarningMessageTitle="警告" +FailedToStartStream.MissingConfigURL="現在のサービスで利用可能な設定URLはありません" +FailedToStartStream.NoCustomRTMPURLInSettings="カスタムRTMP URLが指定されていません" +FailedToStartStream.InvalidCustomConfig="無効なカスタム設定" +FailedToStartStream.FailedToCreateMultitrackVideoService="マルチトラックビデオサービスの作成に失敗しました" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="マルチトラックビデオrtmp出力の作成に失敗しました" +FailedToStartStream.EncoderNotAvailable="NVENCが利用できません。\n\nエンコーダタイプ '%1' が見つかりません" +FailedToStartStream.FailedToCreateVideoEncoder="映像エンコーダ '%1' (タイプ: '%2') の作成に失敗しました" +FailedToStartStream.FailedToGetOBSVideoInfo="エンコーダ '%1' (タイプ: '%2') の作成中にOBSの映像情報を取得できませんでした" +FailedToStartStream.FailedToCreateAudioEncoder="音声エンコーダの作成に失敗しました" +FailedToStartStream.NoRTMPURLInConfig="設定に配信ターゲットRTMP(S)のURLが含まれていません" +FailedToStartStream.FallbackToDefault="%1 を使用した配信の開始に失敗しました。シングルエンコード設定を使用して再試行しますか?" +FailedToStartStream.ConfigRequestFailed="%1 から設定を取得できませんでした

HTTP エラー: %2" +FailedToStartStream.WarningUnknownStatus="不明なステータス値 '%1' を受信しました" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n%1 を使用せずに配信を続行しますか?" +FailedToStartStream.WarningRetry="\n

\n配信を続行しますか?" +FailedToStartStream.MissingEncoderConfigs="配信設定にエンコーダ設定が含まれていませんでした" +FailedToStartStream.StatusMissingHTML="配信開始リクエストで不明なエラーが返されました" +FailedToStartStream.NoConfigSupplied="設定が見つかりません" +MultitrackVideo.Info="%1 は設定を自動的に最適化し複数のビデオ品質をエンコードして送信します。 このオプションを選択するとコンピュータとソフトウェアのセットアップに関する情報が %2 に送信されます。" +MultitrackVideo.IncompatibleSettings.Title="互換性のない設定" +MultitrackVideo.IncompatibleSettings.Text="%1 は現在以下と互換性がありません:\n\n%2\n%1 で配信を続行するには、互換性のない設定を無効にして:\n\n%3\n再度配信を開始してください。" +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="この配信では無効にして配信を開始する" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="設定を更新して配信を開始する" diff --git a/UI/data/locale/ka-GE.ini b/UI/data/locale/ka-GE.ini index 7bdfd19a1c344d..c789a2c9bc1ee2 100644 --- a/UI/data/locale/ka-GE.ini +++ b/UI/data/locale/ka-GE.ini @@ -683,7 +683,6 @@ Basic.Settings.ProgramRestart="ამ ცვლილებების ას Basic.Settings.ConfirmTitle="ცვლილებების დადასტურება" Basic.Settings.Confirm="ცვლილებები არაა დამახსოვრებული. გსურთ მათი შენახვა?" Basic.Settings.General="მთავარი" -Basic.Settings.General.Theme="იერსახე" Basic.Settings.General.Language="ენა" Basic.Settings.General.Updater="განახლებები" Basic.Settings.General.UpdateChannel="განახლების არხი" diff --git a/UI/data/locale/kab-KAB.ini b/UI/data/locale/kab-KAB.ini index 23a02ef7e063aa..7aa607ae84bea7 100644 --- a/UI/data/locale/kab-KAB.ini +++ b/UI/data/locale/kab-KAB.ini @@ -436,7 +436,6 @@ Basic.Settings.ProgramRestart="Ahil isefk ad yales tanekkra akken ad ddun yiɣew Basic.Settings.ConfirmTitle="Sentem ibeddilen" Basic.Settings.Confirm="Tesɛiḍ ibeddilen ur nettwasekles ara. Ad teskelseḍ ibeddilen?" Basic.Settings.General="Amatu" -Basic.Settings.General.Theme="Asentel" Basic.Settings.General.Language="Tutlayt" Basic.Settings.General.EnableAutoUpdates="Nadi ileqman s wudem awurman di tnekra" Basic.Settings.General.OpenStatsOnStartup="Ldi adiwenni n tddadanin deg usekker" diff --git a/UI/data/locale/kmr-TR.ini b/UI/data/locale/kmr-TR.ini index 4d590f71ada303..ebfcd0c6681f9e 100644 --- a/UI/data/locale/kmr-TR.ini +++ b/UI/data/locale/kmr-TR.ini @@ -643,7 +643,6 @@ Basic.Settings.ProgramRestart="Ji bo ku ev sazkarî bi bandor be divê bername j Basic.Settings.ConfirmTitle="Guhertinan piştrast bike" Basic.Settings.Confirm="Guhertinên nehatine tomarkirî hene. Guhertinan tomar bike?" Basic.Settings.General="Giştî" -Basic.Settings.General.Theme="Rûkar" Basic.Settings.General.Language="Ziman" Basic.Settings.General.Updater="Rojanekirin" Basic.Settings.General.UpdateChannel="Kanalê rojane bike" diff --git a/UI/data/locale/ko-KR.ini b/UI/data/locale/ko-KR.ini index 6993eaf1390cbb..a5d344e8adf6d6 100644 --- a/UI/data/locale/ko-KR.ini +++ b/UI/data/locale/ko-KR.ini @@ -576,6 +576,7 @@ Basic.Main.StopRecording="녹화 중단" Basic.Main.PauseRecording="녹화 일시정지" Basic.Main.UnpauseRecording="녹화 재개" Basic.Main.SplitFile="녹화 파일 분할" +Basic.Main.AddChapterMarker="챕터 마커 추가" Basic.Main.StoppingRecording="녹화를 중단합니다...." Basic.Main.StopReplayBuffer="리플레이 버퍼 중단" Basic.Main.StoppingReplayBuffer="리플레이 버퍼 중단중..." @@ -685,7 +686,6 @@ Basic.Settings.ProgramRestart="설정을 적용하려면 프로그램을 다시 Basic.Settings.ConfirmTitle="변경사항 확인" Basic.Settings.Confirm="저장하지 않은 설정이 있습니다. 저장하시겠습니까?" Basic.Settings.General="일반" -Basic.Settings.General.Theme="테마" Basic.Settings.General.Language="언어" Basic.Settings.General.Updater="업데이트" Basic.Settings.General.UpdateChannel="업데이트 채널" @@ -745,6 +745,11 @@ Basic.Settings.General.ChannelName.stable="안정 버전" Basic.Settings.General.ChannelDescription.stable="최신 안정 버전" Basic.Settings.General.ChannelName.beta="베타 / 릴리스 후보" Basic.Settings.General.ChannelDescription.beta="불안정할 수 있는 사전 릴리스 버전" +Basic.Settings.Appearance="표시" +Basic.Settings.Appearance.General="일반" +Basic.Settings.Appearance.General.Theme="테마" +Basic.Settings.Appearance.General.Variant="스타일" +Basic.Settings.Appearance.General.NoVariant="사용 가능한 스타일 없음" Basic.Settings.Stream="방송" Basic.Settings.Stream.Custom.UseAuthentication="인증 기능 사용" Basic.Settings.Stream.Custom.Username="사용자 이름" @@ -770,6 +775,7 @@ Basic.Settings.Stream.Recommended.MaxFPS="최대 FPS: %1" Basic.Settings.Output="출력" Basic.Settings.Output.Format="녹화 형식" Basic.Settings.Output.Format.MKV="Matroska 비디오 (.mkv)" +Basic.Settings.Output.Format.hMP4="하이브리드 MP4 [베타] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmented MP4 비디오 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmented MOV 비디오 (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmented MOV는 녹화본을 청크 단위로 기록하며 기존의 MOV 파일에서 필요하던 마무리 작업이 필요하지 않습니다.\n따라서, 블루 스크린이 발생하거나 전원이 끊겨 파일 기록이 중단되더라도 녹화본을 이용할 수 있습니다.\n\n이는 모든 영상 플레이어 및 에디터와는 호환되지 않을 수 있습니다. 필요하다면 파일 → 녹화본 Remux를 이용하여, 보다 호환성 있는 형식으로 변환하세요." diff --git a/UI/data/locale/lt-LT.ini b/UI/data/locale/lt-LT.ini index a8bbc1fa2a29b2..96b540e1720bff 100644 --- a/UI/data/locale/lt-LT.ini +++ b/UI/data/locale/lt-LT.ini @@ -112,7 +112,7 @@ AlreadyRunning.Title="OBS jau yra ijungtas" AlreadyRunning.Text="OBS jau paleistas! Nebent jūs norėjote taip padaryti, prašome išjungti esančius OBS procesus prieš paleidžiant naują. Jeigu turite nusistatę OBS susimažinti į paslėptas piktogramas, prašome pažiūrėti ar programa vis dar veikia ten." AlreadyRunning.LaunchAnyway="Vistiek paleisti" AutoSafeMode.Title="Saugus režimas" -AutoSafeMode.Text="OBS tinkamai neužsidarė per jūsų praitą sesija.\n\nAr norėtumete pradėti saugujį režimą\n(Trečios partijos įskiepiai, senarijai ir \"WebSockets\" išjungti)?" +AutoSafeMode.Text="OBS tinkamai neužsidarė per jūsų praitą sesija.\n\nAr norėtumete pradėti saugujį režimą (Trečios partijos įskiepiai, senarijai ir \"WebSockets\" išjungti)?" AutoSafeMode.LaunchSafe="Pradėti su Sauguoju režimu" AutoSafeMode.LaunchNormal="Pradėti Normaliai" SafeMode.Restart="Ar norite perkrauti OBS Saugiame Režime (Trečios partijos įskiepiai, scenarijai ir \"WebSockets\" isjungti)?" diff --git a/UI/data/locale/lv-LV.ini b/UI/data/locale/lv-LV.ini index 561b7f0e87dee9..3e67926dff7ea5 100644 --- a/UI/data/locale/lv-LV.ini +++ b/UI/data/locale/lv-LV.ini @@ -111,9 +111,15 @@ AlreadyRunning.Title="OBS jau darbojas (ir palaists)" AlreadyRunning.Text="OBS jau darbojas! Ja nevēlaties vairākas paralēli darbojošās OBS kopijas, lūdzu, aiztaisiet/izslēdziet pašreiz darbojošās. Ja jums iestatīts, ka OBS tiek minimizēts sistēmas teknē, lūdzu, pārbaudiet, vai tas tur joprojām darbojas." AlreadyRunning.LaunchAnyway="Palaist vēl vienu" AutoSafeMode.Title="Drošais režīms" +AutoSafeMode.Text="OBS neizslēdzās pareizi pagājušās sesijas laikā.\n\nVai vēlies sākt Drošajā Režīmā (trešo pušu spraudņi, scripti, un WebSockets atspējoti)?" +AutoSafeMode.LaunchSafe="Palaist Drošajā Režīmā" +AutoSafeMode.LaunchNormal="Palaist Normāli" +SafeMode.Restart="Vai vēlies restartēt OBS Drošajā Režīmā (trešo pušu spraudņi, skripti un WebSockets atspējoti)?" +SafeMode.RestartNormal="Vai vēlies restartēt OBS Normālajā Režīmā?" ChromeOS.Title="Neatbalstīta operētājsistēma" ChromeOS.Text="Šķiet, ka OBS darbojas ChromeOS konteinerā. Šī platforma netiek atbalstīta." Wine.Title="Ir atklāts Wine" +Wine.Text="OBS darbināšana Wine nav atbalstīta, un daudzas iespējas, piemēram, ierakstīšana vai ierīču avoti nestrādās vai strādās ierobežotā apjomā.

Ieteicams palaist OBS dzimto versiju, piemēram mūsu Flatpak versiju vai tavas operētājsistēmas pakotnes." DockCloseWarning.Title="Dokojamā loga aizvēršana" DockCloseWarning.Text="Jūs tikko aizvērāt dokojamu logu. Ja vēlaties to darīt redzamu, izmantojiet izvēlnes joslas izvēlni Skats → Doki." ExtraBrowsers="Pielāgoti pārlūka doki" @@ -178,6 +184,7 @@ Basic.AutoConfig.StreamPage.Server="Serveris" Basic.AutoConfig.StreamPage.StreamKey="Straumes atslēga" Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: ievadiet šifrēšanas paroli.\nRTMP: ievadiet atslēgu, ko norādījis pakalpojums.\nSRT: ievadiet streamid, ja pakalpojums tādu lieto." Basic.AutoConfig.StreamPage.EncoderKey="Enkodera atslēga" +Basic.AutoConfig.StreamPage.BearerToken="Nesēja Žetons" Basic.AutoConfig.StreamPage.ConnectedAccount="Piesaistītais konts" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Notestēt bitrate (bitu pārraides ātrumu) ar interneta ātruma testu (var aizņemt dažas minūtes)" Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Prioritāte aparatūras enkodēšanai" @@ -188,6 +195,7 @@ Basic.AutoConfig.TestPage="Rezultāti" Basic.AutoConfig.TestPage.SubTitle.Testing="Programma pašreiz veic testu kopumu, lai piemeklētu ideālākos iestatījumus" Basic.AutoConfig.TestPage.SubTitle.Complete="Tests pabeigts" Basic.AutoConfig.TestPage.TestingBandwidth="Tiek veikta interneta pieslēguma ātruma pārbaude, tas var aizņemt dažas minūtes ..." +Basic.AutoConfig.TestPage.TestingBandwidth.NoOutput="Šī pakalpojuma protokolam netika atrasta izvade." Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Savienojums ar: %1..." Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Neizdevās izveidot savienojumu ar serveriem. Lūdzu, pārbaudiet interneta pieslēgumu un mēģiniet vēlreiz." Basic.AutoConfig.TestPage.TestingBandwidth.Server="Ātruma tests savienojumam ar: %1" @@ -230,6 +238,12 @@ Updater.RemindMeLater="Atgādināt vēlāk" Updater.Skip="Izlaist šo versiju" Updater.NoUpdatesAvailable.Title="Aktualizācijas (updates) nav pieejamas" Updater.NoUpdatesAvailable.Text="Aktualizācijas (updates) pašreiz nav pieejamas" +Updater.BranchNotFound.Title="Atjauninājumu Kanāls Noņemts" +Updater.BranchNotFound.Text="Tevis izvēlētais atjauninājumu kanāls vairs nav pieejams, OBS ir atiestatīts uz noklusējumu." +Updater.RepairButUpdatesAvailable.Title="Integritātes pārbaude nav pieejama" +Updater.RepairButUpdatesAvailable.Text="Datņu integritātes pārbaude ir pieejama tikai jaunākajai versijai. Ej uz Palīdzība → Meklēt atjauninājumus, lai verificētu un atjauninātu tavu OBS instalāciju." +Updater.RepairConfirm.Title="Apstiprināt integritātes pārbaudi" +Updater.RepairConfirm.Text="Integritātes pārbaude skenēs tavu OBS instalāciju, lai atrastu bojājumus un atkārtoti lejuplādētu salauztas/mainītas datnes. Šis process var aizņemt kādu laiku.\n\nVai vēlies turpināt?" Updater.FailedToLaunch="Neizdevās palaist aktualizatoru" QuickTransitions.SwapScenes="Samainiet vietām Priekšskata/Izvada Ainas pēc Pārejas" QuickTransitions.SwapScenesTT="Pēc pārejas maina vietām priekšskata un izvadu ainas (ja izvada sākotnējā aina joprojām pastāv).\nTas neatgriezīs visas izmaiņas, kas varētu būt veiktas izvada sākotnējā ainā." @@ -244,6 +258,8 @@ Basic.TransitionProperties="Pārejas rekvizīti" Basic.SceneTransitions="Ainas pārejas" Basic.TransitionDuration="Ilgums" Basic.TogglePreviewProgramMode="Studio režīms" +Basic.EnablePreviewProgramMode="Iespējot studijas režīmu" +Basic.DisablePreviewProgramMode="Atspējot studijas režīmu" Undo.Undo="Atgriezt" Undo.Redo="Atatgriezt (redo)" Undo.Add="Pielikt '%1'" @@ -270,8 +286,18 @@ Undo.Transform.HCenter="Vertikāli centrēt ekrānā ainā '%1'" Undo.Volume.Change="Skaļuma maiņa '%1'" Undo.Volume.Mute="Noklusināt '%1'" Undo.Volume.Unmute="Atgriezt skaņu '%1'" +Undo.Balance.Change="Audio stabilitātes maiņa '%1'" +Undo.SyncOffset.Change="Audio sinhronizācijas novirzes maiņa '%1'" +Undo.MonitoringType.Change="Audio monitoringa maiņa '%1'" +Undo.Mixers.Change="Audio mikseru maiņa '%1'" +Undo.ForceMono.On="Piepiedu mono iespējošana '%1'" +Undo.ForceMono.Off="Piespiedu mono atspējošana '%1'" +Undo.Scene.Duplicate="Dubultot ainu '%1'" +Undo.ShowTransition="Rādīt pāreju '%1'" TransitionNameDlg.Text="Lūdzu ievadiet pārejas nosaukumu" TransitionNameDlg.Title="Pārejas nosaukums" +TitleBar.SafeMode="DROŠAIS REŽĪMS" +TitleBar.PortableMode="Portatīvais režīms" TitleBar.Profile="Profils" TitleBar.Scenes="Ainas" NameExists.Title="Nosaukums jau pastāv" @@ -289,7 +315,10 @@ ConfirmBWTest.Text="OBS ir konfigurēts interneta ātruma pārbaudes režīmam. ConfirmExit.Title="Aizvērt OBS?" ConfirmExit.Text="OBS pašlaik ir aktīvs. Visas straumes/ieraksti tiks beigti. Tiešām vēlaties OBS aizvērt?" ConfirmRemove.Title="Dzēšanas apstiprinājums" +ConfirmRemove.Text="Vai tiešām vēlies noņemt '%1'?" ConfirmRemove.TextMultiple="Tiešām vēlaties noņemt %1 gabalus?" +ConfirmReset.Title="Atiestatīt rekvizītus" +ConfirmReset.Text="Vai tiešām vēlies atiestatīt pašreizējās rekvizītu vērtības uz to noklusējuma vērtībām?" Output.StartStreamFailed="Neizdevās uzsākt straumēšanu" Output.StartRecordingFailed="Neizdevās uzsākt ierakstīšanu" Output.StartReplayFailed="Neizdevās sākt replay bufera ierakstu" @@ -301,10 +330,12 @@ Output.ConnectFail.Title="Neizdevās savienoties" Output.ConnectFail.BadPath="Nederīgs ceļš vai savienojuma URL. Lūdzu, pārbaudiet vai iestatījumi ir korekti." Output.ConnectFail.ConnectFailed="Neizdevās savienoties ar serveri" Output.ConnectFail.InvalidStream="Nevarēja piekļūt norādītajam kanālam vai straumēšanas atslēgai. Lūdzu, pārbaudiet straumēšanas atslēgu. Ja tā derīga, pie vainas var būt problēma pievienoties serverim." +Output.ConnectFail.HdrDisabled="HDR izvade pašreiz ir atspējota šai izvadei." Output.ConnectFail.Error="Mēģinot izveidot savienojumu ar serveri, radās negaidīta kļūme. Vairāk informācijas žurnālfailā." Output.ConnectFail.Disconnected="Zuda savienojums ar serveri." Output.StreamEncodeError.Title="Enkodēšanas kļūme" Output.StreamEncodeError.Msg="Straumēšanas laikā atgadījās enkodera kļūme." +Output.StreamEncodeError.Msg.LastError="Straumēšanas laikā radās kodētāja kļūda:

%1" Output.RecordFail.Title="Neizdevās sākt ierakstu" Output.RecordFail.Unsupported="Izvada formāts ir vai nu neatbalstīts vai arī tas neatbalsta vairāk nekā vienu audio celiņu. Lūdzu, pārbaudiet iestatījumus un mēģiniet vēlreiz." Output.RecordNoSpace.Title="Nepietiek diska vietas (noliktavas)" @@ -312,6 +343,7 @@ Output.RecordNoSpace.Msg="Ieraksta turpināšanai nepietiek diska vietas (failu Output.RecordError.Title="Ierakstīšanas kļūme" Output.RecordError.Msg="Ieraksta laikā radās nenoteikta kļūme." Output.RecordError.EncodeErrorMsg="Ieraksta laikā radās enkodera kļūda." +Output.RecordError.EncodeErrorMsg.LastError="Ierakstīšanas laikā radās kodētāja kļūda:

%1" Output.BadPath.Title="Nederīgs faila ceļš" LogReturnDialog="Žurnāla augšuplāde veiksmīga" LogReturnDialog.Description="Jūsu žurnālfails ir augšuplādēts. Tagad jūs varat koplietot tā URL atkļūdošanas vai atbalsta nolūkiem." @@ -562,7 +594,6 @@ Basic.MainMenu.Help.Logs.UploadCurrentLog="Augšuplādēt tekošo žurnālfailu" Basic.Settings.ConfirmTitle="Apstiprināt izmaiņas" Basic.Settings.Confirm="Jums ir nesaglabātās izmaiņas. Saglabās tos?" Basic.Settings.General="Vispārīgi" -Basic.Settings.General.Theme="Motīvs" Basic.Settings.General.Language="Valoda" Basic.Settings.General.Updater="Atjauninājumi" Basic.Settings.General.UpdateChannel="Atjauninājuma kanāls" diff --git a/UI/data/locale/mn-MN.ini b/UI/data/locale/mn-MN.ini index 9d35d1b922a982..a46a2c1829f1ff 100644 --- a/UI/data/locale/mn-MN.ini +++ b/UI/data/locale/mn-MN.ini @@ -317,7 +317,6 @@ Basic.MainMenu.Help.CrashLogs.ShowLogs="Эвдрэлийн мэдээлэлүү Basic.MainMenu.Help.About="Тухай (&A)" Basic.Settings.ConfirmTitle="Өөрчлөлтийг батлах" Basic.Settings.General="Ерөнхий" -Basic.Settings.General.Theme="Загвар" Basic.Settings.General.Language="Хэл" Basic.Settings.General.EnableAutoUpdates="Эхлэх үед шинэчлэлийг автоматаар шалгах" Basic.Settings.General.Snapping="Эх сурвалжийг Буулган Тэгшлэх" diff --git a/UI/data/locale/ms-MY.ini b/UI/data/locale/ms-MY.ini index 63189d2e0dfe97..95886ac1b784f5 100644 --- a/UI/data/locale/ms-MY.ini +++ b/UI/data/locale/ms-MY.ini @@ -409,6 +409,7 @@ MacPermissions.Item.ScreenRecording.Details="OBS memerlukan keizinan ini supaya MacPermissions.Item.Camera="Kamera" MacPermissions.Item.Camera.Details="Keizinan ini diperlukan supaya dapat menangkap kandungan daripada kamera sesawang atau kad tangkap." MacPermissions.Item.Microphone="Mikrofon" +MacPermissions.Item.Microphone.Details="OBS memerlukan keizinan ini jika anda mahu menggunakan mikrofon anda atau sumber audio luaran." MacPermissions.Item.Accessibility="Kebolehcapaian" MacPermissions.Item.Accessibility.Details="Bagi memastikan pintasan papan kekunci (kekunci panas) berfungsi ketika apl lain difokus, sila benarkan keizinan ini." MacPermissions.Continue="Teruskan" @@ -519,6 +520,7 @@ Basic.TransformWindow.BoundsAlignment="Jajaran dalam Kotak Pembatas" Basic.TransformWindow.Bounds="Saiz Kotak Pembatas" Basic.TransformWindow.BoundsWidth="Lebar Kotak Pembatas" Basic.TransformWindow.BoundsHeight="Tinggi Kotak Pembatas" +Basic.TransformWindow.CropToBounds="Pangkas ke Kotak Sempadan" Basic.TransformWindow.Crop="Kerat" Basic.TransformWindow.CropLeft="Kerat Kiri" Basic.TransformWindow.CropRight="Kerat Kanan" @@ -652,7 +654,7 @@ Basic.MainMenu.Help.Logs.ShowLogs="&Tunjuk Fail Log" Basic.MainMenu.Help.Logs.UploadCurrentLog="Muat &Naik Fail Log Semasa" Basic.MainMenu.Help.Logs.UploadLastLog="Muat-naik &Log File Terdahulu" Basic.MainMenu.Help.Logs.ViewCurrentLog="&Lihat Log Semasa" -Basic.MainMenu.Help.ReleaseNotes="Key: Basic.MainMenu.Help.ReleaseNotes\nAdded in https://github.com/obsproject/obs-studio/pull/2987" +Basic.MainMenu.Help.ReleaseNotes="Nota Keluaran" Basic.MainMenu.Help.CheckForUpdates="Periksa Kemas Kini" Basic.MainMenu.Help.Repair="Semak Integriti Fail" Basic.MainMenu.Help.RestartSafeMode="Mula Semula dalam Mod Selamat" @@ -665,7 +667,6 @@ Basic.Settings.ProgramRestart="Program mesti dimulakan semula supaya tetapan ini Basic.Settings.ConfirmTitle="Sahkan Perubahan" Basic.Settings.Confirm="Anda mempunyai perubahan yang tidak disimpan. Simpan perubahan?" Basic.Settings.General="Am" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Bahasa" Basic.Settings.General.Updater="Kemaskini" Basic.Settings.General.UpdateChannel="Kemas Kini Saluran" @@ -725,6 +726,11 @@ Basic.Settings.General.ChannelName.stable="Stabil" Basic.Settings.General.ChannelDescription.stable="Keluaran terbaru yang stabil" Basic.Settings.General.ChannelName.beta="Cubaan / Release Candidates" Basic.Settings.General.ChannelDescription.beta="Berkemungkinan siri pre-lancar tidak stabil" +Basic.Settings.Appearance="Penampilan" +Basic.Settings.Appearance.General="Umum" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Gaya" +Basic.Settings.Appearance.General.NoVariant="Tiada Gaya Tersedia" Basic.Settings.Stream="Strim" Basic.Settings.Stream.Custom.UseAuthentication="Guna pengesahihan" Basic.Settings.Stream.Custom.Username="Nama Pengguna" @@ -765,6 +771,7 @@ Basic.Settings.Output.Mode.Adv="Mahir" Basic.Settings.Output.Mode.FFmpeg="Output FFmpeg" Basic.Settings.Output.UseReplayBuffer="Benarkan Penimbal Main Semula" Basic.Settings.Output.ReplayBuffer.SecondsMax="Masa Main Semula Maksimum" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="Memori Maksima" Basic.Settings.Output.ReplayBuffer.Estimate="Penggunaan ingatan anggaran: %1 MB" Basic.Settings.Output.ReplayBuffer.EstimateTooLarge="Amaran: Anggaran penggunaan memori %1 MiB adalah terlebih besar dari saiz maksimum yang disyorkan iaitu %2 MiB" Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Tidak dapat menganggar penggunaan ingatan. Sila tetapkan had ingatan maksimum." @@ -782,6 +789,7 @@ Basic.Settings.Output.Simple.Warn.AudioBitrate="Amaran: Kadar bit audio penstrim Basic.Settings.Output.Simple.Warn.CannotPause="Amaran: Rakaman tidak dapat dijeda jika kualiti rakaman ditetapkan pada \"Sama dengan strim\"." Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Amaran: Format rakaman yang dipilih pada masa ini tidak serasi dengan pengekod strim yang dipilih." Basic.Settings.Output.Simple.Warn.Encoder="Amaran: Rakaman dengan satu pengekod perisian pada kualiti berbeza berbanding strim akan memerlukan lebih penggunaan CPU jika strim dan rakam pada masa yang sama." +Basic.Settings.Output.Simple.Warn.Lossless="Amaran: Kualiti tak-hilang akan menjana saiz fail yang amat besar! Kualiti tak-hilang menggunakan sehingga 7 gigabait ruang cakera per minit pada resolusi dan kadar bingkai tinggi. Tak-hilang tidak disarankan untuk rakaman panjang melainkan anda mempunyai ruang cakera yang sangat besar. Penampan main semula tidak tersedia apabila menggunakan kualiti tak-hilang." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Anda pasti mahu guna kualiti tak hilang?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Amaran kualiti tak hilang!" Basic.Settings.Output.Simple.Encoder.Software="Perisian (x264)" @@ -819,6 +827,7 @@ Basic.Settings.Output.CustomEncoderSettings="Tetapan Pengekod Suai" Basic.Settings.Output.CustomMuxerSettings="Tetapan Muxer Suai" Basic.Settings.Output.NoSpaceFileName="Jana Nama Fail tanpa Jarak" Basic.Settings.Output.Adv.Rescale="Skala Semula Output" +Basic.Settings.Output.Adv.Rescale.Disabled="Dinyah-aktifkan" Basic.Settings.Output.Adv.AudioTrack="Trek Audio" Basic.Settings.Output.Adv.Streaming="Penstriman" Basic.Settings.Output.Adv.Streaming.Settings="Tetapan Penstriman" diff --git a/UI/data/locale/nb-NO.ini b/UI/data/locale/nb-NO.ini index 0866bfa4e98744..c4c0468aa4277a 100644 --- a/UI/data/locale/nb-NO.ini +++ b/UI/data/locale/nb-NO.ini @@ -105,13 +105,18 @@ SceneFilters="Åpne scenefiltre" List="Liste" Grid="Rutenett" PluginsFailedToLoad.Title="Utvidelsesinnlastingsfeil" +PluginsFailedToLoad.Text="Følgende utvidelser kunne ikke lastes: \n\n%1\n Vennligst oppdater eller fjern disse utvidelsene." AlreadyRunning.Title="OBS kjører allerede" AlreadyRunning.Text="OBS kjører allerede! Hvis dette ikke var bevisst, vennligst lukk alle eksisterende kjørende tilfeller av OBS før du kjører noen nye. Hvis du har satt OBS til å minimere til oppgavelinjen, vennligst sjekk om den fortsatt kjører der." AlreadyRunning.LaunchAnyway="Start uansett" AutoSafeMode.Title="Sikkerhetmodus" +AutoSafeMode.Text="OBS ble ikke avsluttet riktig forrige gang.\n\nØnsker du å starte i sikker-modus (tredjeparts utvidelser, skripting og WebSockets deaktivert)?" AutoSafeMode.LaunchSafe="Kjør i Sikkerhetsmodus" AutoSafeMode.LaunchNormal="Kjør normalt" +SafeMode.Restart="Ønsker du å starte OBS i sikker-modus (tredjeparts utvidelser, skripting og WebSockets deaktivert)?" +SafeMode.RestartNormal="Ønsker du å starte OBS på nytt som normalt?" ChromeOS.Title="Usupportert platform" +ChromeOS.Text="OBS ser ut til å kjøre i en Chrome OS-kontainer. Denne plattformen støttes ikke." Wine.Title="Wine oppdaget" Wine.Text="Å kjøre OBS i Wine er ikke støttet, og mange funksjoner som opptak eller enhetskilder vil ikke fungere eller bare i begrenset kapasitet.

Det anbefales å kjøre en opprinnelig versjon av OBS i stedet. for eksempel vår Flatpak versjon eller operativsystemets pakker." DockCloseWarning.Title="Lukker festbart vindu" @@ -174,6 +179,7 @@ Basic.AutoConfig.StreamPage.Service.ShowAll="Vis alle..." Basic.AutoConfig.StreamPage.Service.Custom="Egendefinert …" Basic.AutoConfig.StreamPage.Server="Tjener" Basic.AutoConfig.StreamPage.StreamKey="Strømmenøkkel" +Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: skriv inn krypterings-passordet.\nRTMP: skriv in nøkkel levert av tjenesten.\nSRT: skriv inn strømmings-ID om tjenesten bruker dette." Basic.AutoConfig.StreamPage.EncoderKey="Enkoder nøkkel" Basic.AutoConfig.StreamPage.ConnectedAccount="Tilkobla konto" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Beregn bitfrekvensen med en båndbreddetest (Kan ta noen minutter)" @@ -185,6 +191,7 @@ Basic.AutoConfig.TestPage="Sluttresultater" Basic.AutoConfig.TestPage.SubTitle.Testing="Programmet kjører en rekke tester for å beregne de mest ideelle innstillingene" Basic.AutoConfig.TestPage.SubTitle.Complete="Testingen er fullført" Basic.AutoConfig.TestPage.TestingBandwidth="Utfører båndbreddetest. Dette kan ta noen minutter..." +Basic.AutoConfig.TestPage.TestingBandwidth.NoOutput="Ingen utgang for protokollen av denne tjenesten var funnet" Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Kobler til: %1..." Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Kan ikke koble til noen servere, kontroller Internett-tilkoblingen og prøv på nytt." Basic.AutoConfig.TestPage.TestingBandwidth.Server="Tester båndbredde til: %1" @@ -218,6 +225,7 @@ Basic.Stats.Bitrate="Bitfrekvens" Basic.Stats.DiskFullIn="Lagringsplass fullt om (ca.)" Basic.Stats.ResetStats="Tilbakestill statistikk" ResetUIWarning.Title="Er du sikker på at du vil tilbakestille grensesnittet?" +ResetUIWarning.Text="Tilbakestilling av grensesnittet vil skjule andre kildevisninger. Du må velge å vise disse fra Docks-menyen hvis du ønsker at disse skal være synlige.\n\nEr du sikker på at du vil tilbakestille grensesnittet?" Updater.Title="Ny oppdatering tilgjengelig" Updater.Text="Det finnes en ny oppdatering:" Updater.UpdateNow="Oppdater nå" @@ -226,8 +234,11 @@ Updater.Skip="Hopp over versjonen" Updater.NoUpdatesAvailable.Title="Ingen oppdateringer er tilgjengelig" Updater.NoUpdatesAvailable.Text="Ingen oppdateringer er tilgjengelig" Updater.BranchNotFound.Title="Oppdateringskanalen ble fjernet" +Updater.BranchNotFound.Text="Din valgte oppdateringskanal er ikke lengre tilgjengelig og OBS har blitt tilbakestilt til standard." Updater.RepairButUpdatesAvailable.Title="Integritetssjekken er utilgjengelig" +Updater.RepairButUpdatesAvailable.Text="Integritetssjekk er bare tilgjengelig for den nyeste versjonen av OBS. Bruk Hjelp → Se etter oppdateringer for å verifisere og oppdatere OBS." Updater.RepairConfirm.Title="Bekreft integritetssjekk" +Updater.RepairConfirm.Text="Integritetssjekken vil skanne OBS-installasjonen for korrupte filer og erstatte ødelagte/modifiserte filer. Dette kan ta noen minutter.\n\nØnsker du å fortsette?" Updater.FailedToLaunch="Kunne ikke starte oppdaterer" QuickTransitions.SwapScenes="Bytt forhåndsvisning/programscener etter overgang" QuickTransitions.SwapScenesTT="Bytter forhåndsvisnings- og programscenen etter overgang (hvis programmets opprinnelige scene fortsatt eksisterer).\nDette vil ikke annullere endringer som kan ha blitt gjort på programmets opprinnelige scene." @@ -313,6 +324,7 @@ ConfirmRemove.Title="Bekreft Fjerning" ConfirmRemove.Text="Er du sikker på at du vil fjerne '%1'?" ConfirmRemove.TextMultiple="Er du sikker du ønsker å fjerne %1 filer?" ConfirmReset.Title="Tilbakestill egenskaper" +ConfirmReset.Text="Er du sikker du ønsker å tilbakestille gjeldende innstillinger?" Output.StartStreamFailed="Kan ikke starte streaming" Output.StartRecordingFailed="Kan ikke starte innspillingen" Output.StartReplayFailed="Kunne ikke å starte opp omspillingsbufferen" @@ -324,10 +336,12 @@ Output.ConnectFail.Title="Tilkobling mislyktes" Output.ConnectFail.BadPath="Ugyldig filbane eller tilkoblings-URL. Vennligst sjekk at innstillingene dine er riktige." Output.ConnectFail.ConnectFailed="Klarte ikke å koble til tjeneren" Output.ConnectFail.InvalidStream="Kunne ikke få adgang til den angitte kanalen eller strømmenøkkelen. Strømmenøkkelen kan være feil eller det kan være et problem med tilkoblingen til tjeneren." +Output.ConnectFail.HdrDisabled="HDR er deaktivert for denne utgangen." Output.ConnectFail.Error="En uventet feil oppstod ved tilkobling til serveren. Detaljert informasjon kan du finne i loggfila." Output.ConnectFail.Disconnected="Koblet fra tjeneren." Output.StreamEncodeError.Title="Kodingsfeil" Output.StreamEncodeError.Msg="En kodingsfeil skjedde under strømmingen." +Output.StreamEncodeError.Msg.LastError="En kodingsfeil oppstod under strømming:

%1" Output.RecordFail.Title="Kunne ikke starte opptak" Output.RecordFail.Unsupported="Utgangsformatet støttes enten ikke eller støtter ikke mer enn ett lydspor. Kontroller innstillingene og prøv igjen." Output.RecordNoSpace.Title="Ikke nok diskplass" @@ -335,8 +349,11 @@ Output.RecordNoSpace.Msg="Det er ikke nok diskplass til å fortsette opptaket." Output.RecordError.Title="Innspillingsfeil" Output.RecordError.Msg="Det oppstod en uspesifisert feil under opptaket." Output.RecordError.EncodeErrorMsg="En kodingsfeil skjedde under opptaket." +Output.RecordError.EncodeErrorMsg.LastError="En kodingsfeil oppstod under opptak:

%1" Output.BadPath.Title="Ugyldig Filbane" +Output.BadPath.Text="Konfigurert opptaksbane kunne ikke bli åpnet. Vennligst sjekk opptaksbane-feltet i Innstillinger → Utgang → Opptak." Output.NoBroadcast.Title="Ingen Sending Konfigurert" +Output.NoBroadcast.Text="Du må sette opp strømming før du kan strømme." Output.BroadcastStartFailed="Kunne ikke starte kringkastingen" Output.BroadcastStopFailed="Kunne ikke stoppe kringkastingen" LogReturnDialog="Vellykket Loggopplasting" @@ -348,7 +365,7 @@ LogReturnDialog.ErrorUploadingLog="Feil ved opplasting av loggfil." Remux.SourceFile="OBS-opptak" Remux.TargetFile="Målfil" Remux.Remux="Remuks" -Remux.Stop="Stopp remuxing" +Remux.Stop="Stopp remuksing" Remux.ClearFinished="Tøm fullførte elementer" Remux.ClearAll="Tøm alle elementer" Remux.OBSRecording="OBS-opptak" @@ -362,6 +379,8 @@ Remux.FileExists="De følgende målfilene finnes allerede. Vil du erstatte dem?" Remux.ExitUnfinishedTitle="Remuksing pågår" Remux.ExitUnfinished="Remuks er ikke ferdig. Om du avbryter kan målfilen bli ubrukelig.\nEr du sikker på at du vil avbryte remuksingen?" Remux.HelpText="Slipp filer ned i dette vinduet for å remukse, eller velg en tom «OBS-opptak»-celle for å lete etter en fil." +Remux.NoFilesAddedTitle="Ingen remuks-fil er lagt til" +Remux.NoFilesAdded="Ingen fil er lagt til remuks. Legg til en mappe med en eller flere videofiler." MissingFiles="Manglende Filer" MissingFiles.MissingFile="Manglende Fil" MissingFiles.NewFile="Ny fil" @@ -382,13 +401,19 @@ MissingFiles.NoMissing.Title="Sjekk for manglende filer" MissingFiles.NoMissing.Text="Ingen filer ser ut til å mangle." MacPermissions.MenuAction="Gå gjennom apptillatelser …" MacPermissions.Title="Gå gjennom apptillatelser" +MacPermissions.Description="OBS Studio trenger din tillatelse for å tilby noen funksjoner. Det er anbefalt å tillate disse, men det er ikke nødvendig for å bruke programmet. Du kan gi tillatelse senere." +MacPermissions.Description.OpenDialog="Du kan åpne denne dialogen på nytt i OBS Studio-menyen." MacPermissions.AccessGranted="Tilgang innvilget" MacPermissions.RequestAccess="Be om tilgang" MacPermissions.OpenPreferences="Åpne %1-innstillinger" MacPermissions.Item.ScreenRecording="Skjermopptak" +MacPermissions.Item.ScreenRecording.Details="OBS krever denne tillatelseen for å kunne ta opptak av skjermen din." MacPermissions.Item.Camera="Kamera" +MacPermissions.Item.Camera.Details="Denne tillatelsen er nødvendig for å ta opptak fra webkamera eller opptakskort." MacPermissions.Item.Microphone="Mikrofon" +MacPermissions.Item.Microphone.Details="OBS trenger denne tillatelsen hvis du ønsker å ta opptak av mikrofonen din eller eksternt lydkort." MacPermissions.Item.Accessibility="Tilgjengelighet" +MacPermissions.Item.Accessibility.Details="OBS krever denne tillatelsen for at tastatursnarveier (hotkeys) skal fungere når andre apper er i fokus." MacPermissions.Continue="Fortsett" SourceLeak.Title="Kildeopprydningsfeil" Basic.DesktopDevice1="Skrivebordlyd" @@ -636,7 +661,6 @@ Basic.Settings.ProgramRestart="Programmet må startes på nytt for at disse inns Basic.Settings.ConfirmTitle="Bekreft endringer" Basic.Settings.Confirm="Du har endringer som ikke er lagret. Vil du lagre?" Basic.Settings.General="Generelt" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Språk" Basic.Settings.General.Updater="Oppdateringer" Basic.Settings.General.UpdateChannel="Oppdateringskanal" diff --git a/UI/data/locale/nl-NL.ini b/UI/data/locale/nl-NL.ini index a478410d603874..d4a1ba6f784d2f 100644 --- a/UI/data/locale/nl-NL.ini +++ b/UI/data/locale/nl-NL.ini @@ -101,6 +101,7 @@ SourceFilters="Open bron filters" MixerToolbarMenu="Audio mixer menu" List="Lijst" Grid="Raster" +Automatic="Automatisch" PluginsFailedToLoad.Title="Plugin laad fout" PluginsFailedToLoad.Text="De volgende OBS plugins konden niet laden:\n\n%1\nGelieve deze plugins bij te werken of te verwijderen." AlreadyRunning.Title="OBS is al actief" @@ -202,6 +203,7 @@ Basic.AutoConfig.TestPage.Result.Header="Het programma heeft vastgesteld dat dez Basic.AutoConfig.TestPage.Result.Footer="Om deze instellingen te gebruiken, klik op Instellingen Toepassen. Om de configuratieassistent aan te passen, klik op Terug. Om de instellingen handmatig te bepalen, klik op Annuleren en open de instellingen." Basic.AutoConfig.Info="De automatische configuratiewizard bepaalt de beste instellingen op basis van de computerspecificaties en de snelheid van de internetverbinding." Basic.AutoConfig.RunAnytime="Dit kan op elk moment uitgevoerd worden door naar het Tools-menu te gaan." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Streaming (geschaalde) resolutie" Basic.Stats="Statistieken" Basic.Stats.CPUUsage="Processorgebruik" Basic.Stats.HDDSpaceAvailable="Harde-schijfruimte beschikbaar" @@ -539,6 +541,7 @@ Basic.Main.Scenes="Scènes" Basic.Main.Sources="Bronnen" Basic.Main.Source="Bron" Basic.Main.Controls="Bedieningselementen" +Basic.Main.PreparingStream="Voorbereiden..." Basic.Main.Connecting="Verbinden..." Basic.Main.StartRecording="Opname starten" Basic.Main.StartReplayBuffer="Replaybuffer starten" @@ -550,6 +553,7 @@ Basic.Main.StopRecording="Opname stoppen" Basic.Main.PauseRecording="Pauzeer de opname" Basic.Main.UnpauseRecording="Vervolg de opname" Basic.Main.SplitFile="Opnamebestand splitsen" +Basic.Main.AddChapterMarker="Hoofdstuk markering toevoegen" Basic.Main.StoppingRecording="De opname stoppen..." Basic.Main.StopReplayBuffer="Replaybuffer stoppen" Basic.Main.StoppingReplayBuffer="De replaybuffer aan het stoppen..." @@ -653,8 +657,8 @@ Basic.MainMenu.Help.About="Over (&A)" Basic.Settings.ProgramRestart="Het programma moet opnieuw worden opgestart om deze instellingen effectief te maken." Basic.Settings.ConfirmTitle="Bevestig de wijzigingen" Basic.Settings.Confirm="Er zijn niet-opgeslagen wijzigingen; wijzigingen opslaan?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 beheert enkele van uw streaminstellingen " Basic.Settings.General="Algemeen" -Basic.Settings.General.Theme="Thema" Basic.Settings.General.Language="Taal" Basic.Settings.General.UpdateChannel="Update kanaal" Basic.Settings.General.UpdateChannelDisabled="(Uitgeschakeld)" @@ -711,6 +715,11 @@ Basic.Settings.General.ChannelName.stable="Stabiel" Basic.Settings.General.ChannelDescription.stable="Laatste stabiele versie" Basic.Settings.General.ChannelName.beta="Beta's / Release Kandidaten" Basic.Settings.General.ChannelDescription.beta="Mogelijk onstabiele pre-release versies" +Basic.Settings.Appearance="Uiterlijk" +Basic.Settings.Appearance.General="Algemeen" +Basic.Settings.Appearance.General.Theme="Thema" +Basic.Settings.Appearance.General.Variant="Stijl" +Basic.Settings.Stream.Destination="Bestemming" Basic.Settings.Stream.Custom.UseAuthentication="Gebruik authenticatie" Basic.Settings.Stream.Custom.Username="Gebruikersnaam" Basic.Settings.Stream.Custom.Password="Wachtwoord" @@ -732,8 +741,19 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximale Video Bitrate: %1 kb Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximale Audio Bitrate: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximale Resolutie: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximale FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Aangepaste server opgeven..." +Basic.Settings.Stream.ServiceCustomServer="Aangepaste server" +Basic.Settings.Stream.EnableMultitrackVideo="%1 inschakelen" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximale streaming bandbreedte" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maximale videotracks" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Schakel streamdump naar FLV in (gebruikt eenvoudige instellingen voor opnamebestanden)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Configuratie overschrijven (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Configuratie overschrijven inschakelen" +Basic.Settings.Stream.MultitrackVideoLabel="Multitrack video" +Basic.Settings.Stream.AdvancedOptions="Geavanceerde opties" Basic.Settings.Output="Uitvoer" Basic.Settings.Output.Format="Opnameformaat" +Basic.Settings.Output.Format.hMP4="Hybride MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Gefragmenteerde MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Gefragmenteerde MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Gefragmenteerde MOV schrijft de opname in delen en vereist niet dezelfde afsluiting als de traditionele MOV bestanden.\nDit zorgt ervoor dat het bestand speelbaar blijft, zelfs als het schrijven naar schijf wordt onderbroken, bijvoorbeeld als gevolg van een BSOD of stroomverlies.\n\nDit is mogelijk niet compatibel met alle spelers en editors. Gebruik Bestand → Remux opnamen om het bestand indien nodig in een meer compatibele indeling om te zetten." @@ -1134,3 +1154,11 @@ YouTube.Errors.messageTextInvalid="De berichttekst is niet geldig." YouTube.Errors.rateLimitExceeded="Je verstuurt te snel berichten." YouTube.DocksRemoval.Title="Wis legacy YouTube browser docks" YouTube.DocksRemoval.Text="Deze browser docks worden als verouderd verwijderd:\n\n%1\nGebruik in plaats daarvan \"Docks/YouTube Live Control Room\"." +ConfigDownload.WarningMessageTitle="Waarschuwing" +FailedToStartStream.MissingConfigURL="Geen configuratie url bescchikbaar voor de huidige service" +FailedToStartStream.NoCustomRTMPURLInSettings="Aangepaste RTMP url niet gespecificeerd" +FailedToStartStream.InvalidCustomConfig="Incorrecte aangepaste configuratie" +FailedToStartStream.NoConfigSupplied="Ontbrekende configuratie" +MultitrackVideo.IncompatibleSettings.Title="Incompatible instellingen" +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Uitschakelen voor deze stream en start streamen" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Update instellingen en start streamen" diff --git a/UI/data/locale/nn-NO.ini b/UI/data/locale/nn-NO.ini index c54b8075606a75..85467afed8bc70 100644 --- a/UI/data/locale/nn-NO.ini +++ b/UI/data/locale/nn-NO.ini @@ -183,7 +183,6 @@ Basic.MainMenu.Tools="Verk&tøy" Basic.MainMenu.Help="&Hjelp" Basic.MainMenu.Help.WhatsNew="Kva er nytt" Basic.Settings.General="Generelt" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Språk" Basic.Settings.General.Updater="Oppdateringar" Basic.Settings.General.UpdateChannel="Oppdateringskanal" diff --git a/UI/data/locale/oc-FR.ini b/UI/data/locale/oc-FR.ini index 2bb10e5fa4a5e3..fad38e2d1f1204 100644 --- a/UI/data/locale/oc-FR.ini +++ b/UI/data/locale/oc-FR.ini @@ -129,7 +129,6 @@ Basic.MainMenu.View.ContextBar="Barra d’aisina font" Basic.MainMenu.Profile="&Perfil" Basic.MainMenu.Help="&Ajuda" Basic.MainMenu.Help.About="&A prepaus" -Basic.Settings.General.Theme="Tèma" Basic.Settings.General.Language="Lenga" Basic.Settings.General.Preview="Apercebut" Basic.Settings.Stream.Custom.Username="Nom d’utilizaire" diff --git a/UI/data/locale/pl-PL.ini b/UI/data/locale/pl-PL.ini index 30dfa74e175d4d..896b13403d9aa8 100644 --- a/UI/data/locale/pl-PL.ini +++ b/UI/data/locale/pl-PL.ini @@ -104,6 +104,7 @@ MixerToolbarMenu="Menu miksera audio" SceneFilters="Otwórz filtry scen" List="Lista" Grid="Siatka" +Automatic="Automatycznie" PluginsFailedToLoad.Title="Błąd ładowania wtyczki" PluginsFailedToLoad.Text="Poniższe wtyczki OBS nie załadowały się:\n\n%1\n Proszę zaktualizuj lub usuń te wtyczki." AlreadyRunning.Title="OBS jest już uruchomiony" @@ -207,6 +208,7 @@ Basic.AutoConfig.TestPage.Result.Header="Aplikacja ustaliła poniższe ustawieni Basic.AutoConfig.TestPage.Result.Footer="Aby użyć tych ustawień, kliknij przycisk Zastosuj ustawienia. Aby ponownie skonfigurować kreatora i spróbować ponownie, kliknij przycisk Wstecz. Aby ręcznie skonfigurować ustawienia, kliknij przycisk Anuluj i otwórz Ustawienia." Basic.AutoConfig.Info="Kreator automatycznej konfiguracji określi najlepsze ustawienia w oparciu o specyfikację komputera i szybkość połączenia." Basic.AutoConfig.RunAnytime="Możesz to uruchomić w dowolnym momencie, przechodząc do menu Narzędzia." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Rozdzielczość streamu (skalowana)" Basic.Stats="Statystyki" Basic.Stats.CPUUsage="Użycie procesora" Basic.Stats.HDDSpaceAvailable="Dostępne miejsce na dysku" @@ -255,7 +257,8 @@ Basic.TransitionProperties="Właściwości przejścia" Basic.SceneTransitions="Efekty przejścia scen" Basic.TransitionDuration="Czas trwania" Basic.TogglePreviewProgramMode="Tryb studia" -Basic.EnablePreviewProgramMode="Włącz Tryb Studia"\nBasic.DisablePreviewProgramMode="\nWyłącz Tryb Studia" +Basic.EnablePreviewProgramMode="Włącz Tryb Studia" +Basic.DisablePreviewProgramMode="Wyłącz Tryb Studia" Undo.Undo="Cofnij" Undo.Redo="Ponów" Undo.Add="Dodaj '%1'" @@ -557,6 +560,7 @@ Basic.Main.Scenes="Sceny" Basic.Main.Sources="Źródła obrazu" Basic.Main.Source="Źródło" Basic.Main.Controls="Panel sterujący" +Basic.Main.PreparingStream="Przygotowywanie..." Basic.Main.Connecting="Łączenie..." Basic.Main.StartRecording="Rozpocznij nagrywanie" Basic.Main.StartReplayBuffer="Rozpocznij nagrywanie powtórek" @@ -568,6 +572,7 @@ Basic.Main.StopRecording="Zatrzymaj nagrywanie" Basic.Main.PauseRecording="Pauzuj nagrywanie" Basic.Main.UnpauseRecording="Wznów nagrywanie" Basic.Main.SplitFile="Podziel plik nagrywania" +Basic.Main.AddChapterMarker="Dodaj Marker Rozdziału" Basic.Main.StoppingRecording="Zatrzymywanie nagrywania..." Basic.Main.StopReplayBuffer="Zatrzymaj nagrywanie powtórek" Basic.Main.StoppingReplayBuffer="Zatrzymywanie nagrywania powtórek..." @@ -674,8 +679,8 @@ Basic.MainMenu.Help.About="O progr&amie" Basic.Settings.ProgramRestart="Aby te ustawienia zaczęły obowiązywać, należy ponownie uruchomić program." Basic.Settings.ConfirmTitle="Potwierdź zmiany" Basic.Settings.Confirm="Zmiany nie zostały zapisane. Czy zapisać zmiany?" +Basic.Settings.MultitrackVideoDisabledSettings="Niektóre ustawienia strumieniowania kontrolowane są przez %1 %2" Basic.Settings.General="Główne" -Basic.Settings.General.Theme="Motyw" Basic.Settings.General.Language="Język" Basic.Settings.General.Updater="Aktualizacje" Basic.Settings.General.UpdateChannel="Kanał aktualizacji" @@ -734,7 +739,13 @@ Basic.Settings.General.ChannelName.stable="Stabilna" Basic.Settings.General.ChannelDescription.stable="Najnowsze stabilne wydanie" Basic.Settings.General.ChannelName.beta="Beta / RC" Basic.Settings.General.ChannelDescription.beta="Potencjalnie niestabilne wersje testowe" +Basic.Settings.Appearance="Wygląd" +Basic.Settings.Appearance.General="Ogólny" +Basic.Settings.Appearance.General.Theme="Motyw" +Basic.Settings.Appearance.General.Variant="Styl" +Basic.Settings.Appearance.General.NoVariant="Brak dostępnych stylów" Basic.Settings.Stream="Transmisja" +Basic.Settings.Stream.Destination="Cel" Basic.Settings.Stream.Custom.UseAuthentication="Użyj uwierzytelniania" Basic.Settings.Stream.Custom.Username="Nazwa użytkownika" Basic.Settings.Stream.Custom.Password="Hasło" @@ -756,8 +767,21 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maksymalny bitrate: %1 kbps" Basic.Settings.Stream.Recommended.MaxAudioBitrate="Minimalny bitrate: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maksymalna rozdzielczość: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maksymalna liczba FPS (kl./s): %1" +Basic.Settings.Stream.SpecifyCustomServer="Zdefiniuj własny serwer..." +Basic.Settings.Stream.ServiceCustomServer="Własny serwer" +Basic.Settings.Stream.EnableMultitrackVideo="Aktywuj %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maksymalny bitrate" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatycznie" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maksymalna liczba ścieżek video" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatycznie" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Zapis streamu do pliku FLV (korzysta z podstawowych ustawień nagrywania do pliku)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Nadpisanie konfiguracji (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Włącz nadpisywanie konfiguracji" +Basic.Settings.Stream.MultitrackVideoLabel="Wideo z wieloma ścieżkami" +Basic.Settings.Stream.AdvancedOptions="Opcje zaawansowane" Basic.Settings.Output="Wyjście" Basic.Settings.Output.Format="Format nagrywania" +Basic.Settings.Output.Format.hMP4="Hybrydowy MP4 [Beta] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmentowane MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmentowane MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmentaryczny MOV zapisuje nagranie w kawałkach i nie wymaga takiej samej finalizacji jak tradycyjne pliki MOV.\nDzięki temu plik pozostaje odtwarzalny nawet wtedy, gdy zapis na dysku zostanie przerwany, na przykład w wyniku wystąpienia \"niebieskiego ekranu\" lub utraty zasilania.\n\nMoże nie być kompatybilny ze wszystkimi odtwarzaczami i edytorami. Użyj opcji Plik → Przepakuj nagrania, aby w razie potrzeby przekonwertować plik na bardziej kompatybilny format." @@ -945,7 +969,7 @@ Basic.Settings.Audio.PeakMeterType="Typ szczytowego poziomu paska audio" Basic.Settings.Audio.PeakMeterType.SamplePeak="Poziom szczytowy próbkowany" Basic.Settings.Audio.PeakMeterType.TruePeak="Rzeczywisty szczytowy poziom (większe użycie procesora)" Basic.Settings.Audio.MultichannelWarning.Enabled="Uwaga: Dźwięk przestrzenny jest włączony." -Basic.Settings.Audio.MultichannelWarning="W przypadku korzystania z transmisji strumieniowej należy sprawdzić, czy usługa ta obsługuje zarówno przesyłanie dźwięku przestrzennego surround, jak i jego odtwarzanie. Na przykład, Facebook 360 Live w pełni obsługuje dźwięk przestrzenny. YouTube Live obsługuje przesyłanie dźwięku 5.1 (i odtwarzanie na telewizorach). \nFiltry audio OBS są kompatybilne z dźwiękiem przestrzennym, choć obsługa wtyczek VST nie jest gwarantowana." +Basic.Settings.Audio.MultichannelWarning="W przypadku korzystania z transmisji strumieniowej należy sprawdzić, czy usługa ta obsługuje zarówno przesyłanie dźwięku przestrzennego surround, jak i jego odtwarzanie. Na przykład, Facebook 360 Live w pełni obsługuje dźwięk przestrzenny. YouTube Live obsługuje przesyłanie dźwięku 5.1 (i odtwarzanie na telewizorach). Filtry audio OBS są kompatybilne z dźwiękiem przestrzennym, choć obsługa wtyczek VST nie jest gwarantowana." Basic.Settings.Audio.MultichannelWarning.Title="Włączyć dźwięk przestrzenny?" Basic.Settings.Audio.MultichannelWarning.Confirm="Czy na pewno chcesz włączyć dźwięk przestrzenny?" Basic.Settings.Audio.Devices="Globalne urządzenia audio" @@ -1208,3 +1232,27 @@ YouTube.Errors.messageTextInvalid="Treść wiadomości jest nieprawidłowa." YouTube.Errors.rateLimitExceeded="Wysyłasz wiadomości zbyt szybko." YouTube.DocksRemoval.Title="Wyczyść przestarzałe panele przeglądarkowe YouTube " YouTube.DocksRemoval.Text="Poniższe przestarzałe panele przeglądarkowe zostaną usunięte.\n\n%1\nZamiast tego użyj \"Panele/YouTube Live Control Room\"." +ConfigDownload.WarningMessageTitle="Uwaga" +FailedToStartStream.MissingConfigURL="Brak adresu URL z konfiguracją dla wybranego serwisu" +FailedToStartStream.NoCustomRTMPURLInSettings="Nie określono własnego adresu URL RTMP" +FailedToStartStream.InvalidCustomConfig="Nieprawidłowa własna konfiguracja" +FailedToStartStream.FailedToCreateMultitrackVideoService="Nie udało się stworzyć usługi wideo z wieloma ścieżkami" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Nie udało się stworzyć wyjścia RTMP z wideo z wieloma ścieżkami" +FailedToStartStream.EncoderNotAvailable="Brak NVENC.\n\nNie znaleziono enkodera typu '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Nie udało się stworzyć enkodera wideo '%1' (typ: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Nie udało się pobrać informacji o wideo OBS przy tworzeniu enkodera '%1' (typ: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Nie udało się stworzyć enkodera audio" +FailedToStartStream.NoRTMPURLInConfig="Konfiguracja nie zawiera adresu URL z serwerem RTMP(S)." +FailedToStartStream.FallbackToDefault="Nie udało się uruchomić strumieniowania korzystając z %1. Czy chcesz spróbować korzystając z ustawień dla pojedynczego enkodowania?" +FailedToStartStream.ConfigRequestFailed="Nie udało się pobrać konfiguracji z %1

Błąd HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Otrzymano nieznany status: '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nCzy chcesz kontynuować bez %1?" +FailedToStartStream.WarningRetry="\n

\nCzy chesz kontynuować?" +FailedToStartStream.MissingEncoderConfigs="Konfiguracja nie zawiera opcji dla enkodera" +FailedToStartStream.StatusMissingHTML="Request zwrócił nieokreślony błąd" +FailedToStartStream.NoConfigSupplied="Brak konfiguracji" +MultitrackVideo.Info="%1 automatycznie optymalizuje ustawienia by enkodować i wysyłać wideo w różnych poziomach jakości. Wybranie tej opcji wyśle do %2 informacje o konfiguracji Twojego komputera i oprogramowania." +MultitrackVideo.IncompatibleSettings.Title="Niezgodne ustawienia" +MultitrackVideo.IncompatibleSettings.Text="%1 nie jest kompatybilny z:\n\n%2\nWyłącz nieprawidłowe opcje w ustawieniach, aby kontynuować korzystanie z %1:\n\n%3\ni uruchom transmisję jeszcze raz." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Wyłącz dla tego streamu i uruchom transmisję" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Aktualizuj ustawienia i uruchom transmisję" diff --git a/UI/data/locale/pt-BR.ini b/UI/data/locale/pt-BR.ini index ad1ea9c484a136..7ccd5394baa8be 100644 --- a/UI/data/locale/pt-BR.ini +++ b/UI/data/locale/pt-BR.ini @@ -98,16 +98,16 @@ RemoveScene="Excluir cena selecionada" RemoveSource="Remover fonte(s) selecionada(s)" MoveSceneUp="Mover cena para cima" MoveSceneDown="Mover cena para baixo" -MoveSourceUp="Mover Fonte(s) para cima" -MoveSourceDown="Mover Fonte(s) para Baixo" +MoveSourceUp="Mover fonte(s) para cima" +MoveSourceDown="Mover fonte(s) para baixo" SourceProperties="Abrir propriedades da fonte" SourceFilters="Abrir propriedades do filtro" -MixerToolbarMenu="Menu de Mixagem de Áudio" +MixerToolbarMenu="Menu de mixagem" SceneFilters="Abrir filtros de cena" List="Lista" Grid="Grade" PluginsFailedToLoad.Title="Erro ao carregar o plugin" -PluginsFailedToLoad.Text="Os seguintes plugins do OBS falharam ao carregar:\n\n%1\nPor favor, atualize ou remova estes plugins." +PluginsFailedToLoad.Text="Não foi possível carregar os seguintes plugins do OBS:\n\n%1\nPor favor, atualize ou remova estes plugins." AlreadyRunning.Title="OBS já está em execução" AlreadyRunning.Text="OBS já está em execução! A menos que você tenha a intenção de fazer isso, por favor, feche todas as instâncias existentes do OBS antes de tentar executar uma nova. Se você tiver definido para minimizar o OBS na bandeja do sistema, verifique se ainda está lá em execução." AlreadyRunning.LaunchAnyway="Executar mesmo assim" @@ -240,8 +240,8 @@ Updater.NoUpdatesAvailable.Text="Nenhuma atualização disponível no momento" Updater.BranchNotFound.Title="Canal de atualização removido" Updater.BranchNotFound.Text="Seu canal de atualização selecionado não está mais disponível e o OBS foi redefinido para o padrão." Updater.RepairButUpdatesAvailable.Title="Verificação de integridade indisponível" -Updater.RepairButUpdatesAvailable.Text="Verificar a integridade do arquivo só é possível para a versão mais recente disponível. Use Ajuda → Verificar Atualizações para verificar e atualizar sua instalação do OBS." -Updater.RepairConfirm.Title="Confirmar Verificação de Integridade" +Updater.RepairButUpdatesAvailable.Text="Verificar a integridade do arquivo só é possível para a versão mais recente disponível. Vá até Ajuda → Verificar atualizações para verificar e atualizar sua instalação do OBS." +Updater.RepairConfirm.Title="Confirmar verificação de integridade" Updater.RepairConfirm.Text="Iniciar a verificação de integridade determinará se a instalação do OBS está corrompida e baixará novamente os arquivos corrompidos/modificados. Isso pode demorar um pouco.\n\nDeseja continuar?" Updater.FailedToLaunch="Falha ao iniciar o atualizador" QuickTransitions.SwapScenes="Alternar cenas de pré-visualização/programa após uma transição" @@ -288,7 +288,7 @@ Undo.Volume.Unmute="desativar mudo de \"%1\"" Undo.Balance.Change="alteração no balanço de áudio em '%1'" Undo.SyncOffset.Change="alteração no atraso na sincronização do áudio em \"%1\"" Undo.MonitoringType.Change="alteração do monitoramento de áudio em \"%1\"" -Undo.Mixers.Change="alteração dos mixers de áudio em \"%1\"" +Undo.Mixers.Change="alteração dos mixers em \"%1\"" Undo.ForceMono.On="ativação do mono forçado em \"%1\"" Undo.ForceMono.Off="desativação do mono forçado em \"%1\"" Undo.Properties="mudança de propriedade em \"%1\"" @@ -326,10 +326,10 @@ ConfirmBWTest.Text="Você está com o OBS configurado no modo de teste de largur ConfirmExit.Title="Encerrar OBS?" ConfirmExit.Text="OBS está ativo no momento. Todas as transmissões e/ou gravações serão interrompidas. Deseja mesmo encerrar o programa?" ConfirmRemove.Title="Confirmar a remoção" -ConfirmRemove.Text="Tem certeza que deseja remover '%1'?" +ConfirmRemove.Text="Deseja mesmo remover '%1'?" ConfirmRemove.TextMultiple="Deseja mesmo remover %1 itens?" -ConfirmReset.Title="Redefinir Propriedades" -ConfirmReset.Text="Tem certeza que deseja redefinir as propriedades atuais para os padrões?" +ConfirmReset.Title="Redefinir propriedades" +ConfirmReset.Text="Deseja mesmo redefinir as propriedades atuais para os padrões?" Output.StartStreamFailed="Falha ao iniciar a transmissão" Output.StartRecordingFailed="Falha ao iniciar a gravação" Output.StartReplayFailed="Falha ao iniciar o buffer de repetição" @@ -405,7 +405,7 @@ MissingFiles.AutoSearchText="O OBS encontrou outras correspondências para arqui MissingFiles.NoMissing.Title="Verificação de arquivos em falta" MissingFiles.NoMissing.Text="Nenhum arquivo parece estar em falta." MacPermissions.MenuAction="Revisar as permissões do aplicativo..." -MacPermissions.Title="Revisar Permissões do Aplicativo" +MacPermissions.Title="Revisar permissões do aplicativo" MacPermissions.Description="O OBS Studio requer sua permissão para poder fornecer certos recursos. É recomendável habilitar estas permissões, mas não é necessário para usar o aplicativo. Você pode sempre habilitá-los mais tarde." MacPermissions.Description.OpenDialog="Você pode reabrir este caixa de diálogo através do menu OBS Studio." MacPermissions.AccessGranted="Acesso Concedido" @@ -420,7 +420,7 @@ MacPermissions.Item.Microphone.Details="OBS requer essa permissão se você dese MacPermissions.Item.Accessibility="Acessibilidade" MacPermissions.Item.Accessibility.Details="Para atalhos de teclado (teclas de atalho) funcionarem enquanto outros aplicativos estiverem em foco, ative esta permissão." MacPermissions.Continue="Continuar" -SourceLeak.Title="Erro ao Limpar Fonte" +SourceLeak.Title="Erro de limpeza de fonte(s)" SourceLeak.Text="Ocorreu um problema ao alterar as coleções de cenas e algumas fontes não puderam ser descarregadas. Esse problema geralmente é causado por plug-ins que não estão liberando os recursos corretamente. Por favor, certifique-se de que todos os plug-ins que você está usando estão atualizados.\n\nOBS Studio agora será encerrado para evitar qualquer possível corrupção de dados." Basic.DesktopDevice1="Áudio do desktop" Basic.DesktopDevice2="Áudio do desktop 2" @@ -450,7 +450,7 @@ VolControl.SliderUnmuted="Barra de volume de '%1':" VolControl.SliderMuted="Barra de volume de '%1': (atualmente sem áudio)" VolControl.Mute="Ativar mudo de '%1'" VolControl.Properties="Propriedades de '%1'" -VolControl.UnassignedWarning.Title="Fonte de Áudio não Atribuída" +VolControl.UnassignedWarning.Title="Fonte de áudio não atribuída" VolControl.UnassignedWarning.Text="\"%1\" não está atribuído à faixa alguma e não estará audível em transmissões ou gravações.\n\nPara atribuir uma fote de áudio à uma faixa, abra Propriedades de Áudio Avançadas clicando com o botão direito do mouse ou na engrenagem do painel do mixer." Basic.Main.AddSceneDlg.Title="Adicionar cena" Basic.Main.AddSceneDlg.Text="Por favor, digite o nome da cena" @@ -548,7 +548,7 @@ Basic.TransformWindow.BoundsType.ScaleOuter="Redimensionar até as bordas extern Basic.TransformWindow.BoundsType.ScaleToWidth="Redimensionar até a largura das bordas" Basic.TransformWindow.BoundsType.ScaleToHeight="Redimensionar até a altura das bordas" Basic.TransformWindow.BoundsType.Stretch="Esticar até as bordas" -Basic.TransformWindow.Title="Editar Transformação para '%1'" +Basic.TransformWindow.Title="Editar transformação de '%1'" Basic.TransformWindow.NoSelectedSource="Nenhuma fonte selecionada" Basic.Main.AddSourceHelp.Title="Não foi possível adicionar a fonte" Basic.Main.AddSourceHelp.Text="É necessário pelo menos uma cena para adicionar uma fonte." @@ -567,10 +567,11 @@ Basic.Main.StopRecording="Interromper gravação" Basic.Main.PauseRecording="Pausar gravação" Basic.Main.UnpauseRecording="Continuar gravação" Basic.Main.SplitFile="Cortar Arquivo de Gravação" +Basic.Main.AddChapterMarker="Adicionar marcador de capítulo" Basic.Main.StoppingRecording="Interrompendo gravação..." Basic.Main.StopReplayBuffer="Interromper buffer de repetição" Basic.Main.StoppingReplayBuffer="Interrompendo buffer de repetição..." -Basic.Main.SetupBroadcast="Gerenciar Transmissão" +Basic.Main.SetupBroadcast="Gerenciar transmissão" Basic.Main.StopStreaming="Interromper transmissão" Basic.Main.StopBroadcast="Encerrar transmissão" Basic.Main.AutoStopEnabled="(Parada Automática)" @@ -584,7 +585,7 @@ Basic.Main.GroupItems="Agrupar itens selecionados" Basic.Main.Ungroup="Desagrupar" Basic.Main.GridMode="Modo grade" Basic.Main.ListMode="Modo lista" -Basic.Main.VirtualCamConfig="Configurar Câmera Virtual" +Basic.Main.VirtualCamConfig="Configurar câmera virtual" Basic.VCam.VirtualCamera="Câmera Virtual" Basic.VCam.OutputType="Tipo de saída" Basic.VCam.OutputSelection="Seleção de saída" @@ -676,7 +677,6 @@ Basic.Settings.ProgramRestart="Reinicie o programa para aplicar as configuraçõ Basic.Settings.ConfirmTitle="Confirmar alterações" Basic.Settings.Confirm="Há alterações não salvas. Deseja salvar agora?" Basic.Settings.General="Geral" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.Updater="Atualizações" Basic.Settings.General.UpdateChannel="Canal de atualização" @@ -736,6 +736,11 @@ Basic.Settings.General.ChannelName.stable="Estável" Basic.Settings.General.ChannelDescription.stable="Último lançamento estável" Basic.Settings.General.ChannelName.beta="Betas / Candidatas a lançamento" Basic.Settings.General.ChannelDescription.beta="Versões prévias potencialmente instáveis" +Basic.Settings.Appearance="Aparência" +Basic.Settings.Appearance.General="Geral" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Estilo" +Basic.Settings.Appearance.General.NoVariant="Nenhum estilo disponível" Basic.Settings.Stream="Transmissão" Basic.Settings.Stream.Custom.UseAuthentication="Utilizar autenticação" Basic.Settings.Stream.Custom.Username="Nome de usuário" @@ -760,6 +765,7 @@ Basic.Settings.Stream.Recommended.MaxResolution="Resolução máxima: %1" Basic.Settings.Stream.Recommended.MaxFPS="Taxa de quadros máxima: %1" Basic.Settings.Output="Saída" Basic.Settings.Output.Format="Formato de gravação" +Basic.Settings.Output.Format.hMP4="MP4 híbrido [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 fragmentado (.mp4)" Basic.Settings.Output.Format.fMOV="MOV fragmentado (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="O MOV fragmentado efetua a gravação em partes e não requer a mesma finalização que os arquivos MOV tradicionais.\nIsso garante que o arquivo permaneça reproduzível mesmo que a gravação no disco seja interrompida, por exemplo, como resultado de uma BSOD ou perda de energia.\n\nIsso pode não ser compatível com todos os reprodutores e editores. Use Arquivo → Remux Recordings para converter o arquivo em um formato mais compatível, se necessário." @@ -776,25 +782,25 @@ Basic.Settings.Output.Mode.Adv="Avançado" Basic.Settings.Output.Mode.FFmpeg="Saída de FFmpeg" Basic.Settings.Output.UseReplayBuffer="Ativar buffer de repetição" Basic.Settings.Output.ReplayBuffer.SecondsMax="Tempo máximo do replay" -Basic.Settings.Output.ReplayBuffer.MegabytesMax="Memória Máxima" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="Máximo de memória" Basic.Settings.Output.ReplayBuffer.Estimate="Uso de memória estimado: %1 MB" -Basic.Settings.Output.ReplayBuffer.EstimateTooLarge="Aviso: O uso de memória estimado de %1 MiB é maior do que o máximo recomendado de %2 MiB" +Basic.Settings.Output.ReplayBuffer.EstimateTooLarge="Aviso: o uso de memória estimado de %1 MiB é maior que o máximo recomendado de %2 MiB" Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Não foi possível estimar o uso de memória. Por favor, defina o limite máximo de memória." Basic.Settings.Output.ReplayBuffer.Prefix="Prefixo do buffer de repetição" Basic.Settings.Output.ReplayBuffer.Suffix="Sufixo" -Basic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg="Buffer de Replay não pode ser utilizado quando tipo de gravação estiver como Saída Personalizada (FFmpeg)" +Basic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg="O buffer de repetição não pode ser utilizado quando o tipo de gravação estiver como saída personalizada (FFmpeg)" Basic.Settings.Output.Simple.SavePath="Caminho de gravação" Basic.Settings.Output.Simple.RecordingQuality="Qualidade da gravação" Basic.Settings.Output.Simple.RecordingQuality.Stream="A mesma da transmissão" Basic.Settings.Output.Simple.RecordingQuality.Small="Qualidade alta, arquivo normal" -Basic.Settings.Output.Simple.RecordingQuality.HQ="Qualidade muito boa, arquivo pesado" +Basic.Settings.Output.Simple.RecordingQuality.HQ="Qualidade indistinguível, arquivo pesado" Basic.Settings.Output.Simple.RecordingQuality.Lossless="Sem perda de qualidade, arquivo muito pesado" Basic.Settings.Output.Simple.Warn.VideoBitrate="Aviso: a taxa de bits do vídeo transmitido será definida como %1, que é o limite máximo para o serviço de transmissão atual." Basic.Settings.Output.Simple.Warn.AudioBitrate="Aviso: a taxa de bits do áudio transmitido será definida como %1, que é o limite máximo para o serviço de transmissão atual." Basic.Settings.Output.Simple.Warn.CannotPause="Aviso: gravações não podem ser pausadas se a qualidade da gravação estiver definida como \"A mesma da transmissão\"." Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Alerta: A gravação atualmente selecionada é incompatível com o(s) encoder(s) de stream selecionado." Basic.Settings.Output.Simple.Warn.Encoder="Aviso: gravar com um codificador de software em uma qualidade diferente da transmissão vai exigir mais da CPU se você transmitir e gravar ao mesmo tempo." -Basic.Settings.Output.Simple.Warn.Lossless="Aviso: O modo sem perda de qualidade gera arquivos extremamente grandes! Este modo pode usar até 7 gigabytes de espaço por minuto em altas resoluções e taxas de quadro. Não é recomendado para gravações longas a não ser que você tenha uma abundante quantidade de espaço no disco disponível. Buffer de repetição fica indisponível enquanto usa sem perda de qualidade." +Basic.Settings.Output.Simple.Warn.Lossless="Aviso: o modo sem perda de qualidade gera arquivos extremamente grandes! Este modo pode usar até 7 gigabytes de espaço por minuto em altas resoluções e taxas de quadro. Não é recomendado para gravações longas a não ser que você tenha muito espaço disponível no disco. O buffer de repetição fica indisponível quando o modo sem perda de qualidade está ativo." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Deseja mesmo usar a qualidade sem perdas?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Aviso de qualidade muito alta!" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (preset x264 de baixa utilização de CPU, aumenta o tamanho do arquivo)" diff --git a/UI/data/locale/pt-PT.ini b/UI/data/locale/pt-PT.ini index f602f5b8ef6828..129875184794de 100644 --- a/UI/data/locale/pt-PT.ini +++ b/UI/data/locale/pt-PT.ini @@ -73,8 +73,8 @@ Defaults="Predefinições" RestoreDefaults="Predefinições" HideMixer="Ocultar no misturador" TransitionOverride="Sobrepor transição" -ShowTransition="Mostrar transição" -HideTransition="Ocultar transição" +ShowTransition="Transição de mostrar" +HideTransition="Transição de ocultar" None="Nenhuma" StudioMode.Preview="Antevisão" StudioMode.Program="Programa" @@ -107,6 +107,7 @@ MixerToolbarMenu="Menu do misturador de áudio" SceneFilters="Abrir filtros de cena" List="Lista" Grid="Grelha" +Automatic="Automático" PluginsFailedToLoad.Title="Erro ao carregar o plugin" PluginsFailedToLoad.Text="Falha ao carregar os seguintes plugins do OBS:\n\n%1\nPor favor, atualize ou remova estes plugins." AlreadyRunning.Title="O OBS já está em execução" @@ -192,6 +193,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferir codificação de eq Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="A codificação por equipamento elimina a maior parte da utilização da CPU, mas pode necessitar de uma taxa de bits maior para obter o mesmo nível de qualidade." Basic.AutoConfig.StreamPage.StreamWarning.Title="Aviso de transmissão" Basic.AutoConfig.StreamPage.StreamWarning.Text="O teste de largura de banda vai transmitir dados de vídeo aleatórios sem áudio para o seu canal. É recomendado desligar temporariamente gravações de transmissões e definir a transmissão como privada até o teste estar completo. Continuar?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Testar %1" Basic.AutoConfig.TestPage="Resultado final" Basic.AutoConfig.TestPage.SubTitle.Testing="O programa está agora a executar testes para estimar as definições ideais" Basic.AutoConfig.TestPage.SubTitle.Complete="Teste completo" @@ -202,7 +204,7 @@ Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Falha ao ligar a qualq Basic.AutoConfig.TestPage.TestingBandwidth.Server="A testar largura de banda para: %1" Basic.AutoConfig.TestPage.TestingStreamEncoder="A testar codificador de transmissão, poderá demorar algum tempo..." Basic.AutoConfig.TestPage.TestingRecordingEncoder="A testar codificador de gravação, poderá demorar algum tempo..." -Basic.AutoConfig.TestPage.TestingRes.Fail="Erro ao iniciar o codificador" +Basic.AutoConfig.TestPage.TestingRes.Fail="Falha ao iniciar o codificador" Basic.AutoConfig.TestPage.TestingRes.Resolution="A testar %1x%2 %3 FPS..." Basic.AutoConfig.TestPage.Result.StreamingEncoder="Codificador de transmissão" Basic.AutoConfig.TestPage.Result.RecordingEncoder="Codificador de gravação" @@ -295,8 +297,8 @@ Undo.ForceMono.On="Ativar Mono forçado em '%1'" Undo.ForceMono.Off="Desativar Mono forçado em '%1'" Undo.Properties="Alteração de propriedade em '%1'" Undo.Scene.Duplicate="Duplicar cena '%1'" -Undo.ShowTransition="Mostrar transição em '%1'" -Undo.HideTransition="Ocultar transição em '%1'" +Undo.ShowTransition="Transição de mostrar em '%1'" +Undo.HideTransition="Transição de ocultar em '%1'" Undo.ShowSceneItem="Mostrar '%1' em '%2'" Undo.HideSceneItem="Ocultar '%1' em '%2'" Undo.ReorderSources="Reordenar fontes em '%1'" @@ -324,7 +326,7 @@ ConfirmStop.Text="Tem a certeza de que quer parar a transmissão?" ConfirmStopRecord.Title="Parar a gravação?" ConfirmStopRecord.Text="Tem a certeza de que deseja parar a gravação?" ConfirmBWTest.Title="Iniciar teste de largura de banda?" -ConfirmBWTest.Text="Tem o OBS configurado no modo de teste de largura de banda. Este modo permite o teste de rede sem que o seu canal seja emitido. Uma vez terminado o teste terá de o desativar para que os espectadores possam ver a transmissão.\n\nDeseja continuar?" +ConfirmBWTest.Text="O OBS está configurado em modo de teste de largura de banda. Este modo permite o teste de rede sem que o seu canal seja emitido. Uma vez terminado o teste terá de o desativar para que os espetadores possam ver a transmissão.\n\nDeseja continuar?" ConfirmExit.Title="Sair do OBS?" ConfirmExit.Text="O OBS está ativo. Todas as transmissões e gravações serão terminadas. Tem a certeza de que pretende sair?" ConfirmRemove.Title="Confirmar remoção" @@ -335,7 +337,7 @@ ConfirmReset.Text="Tem certeza que deseja redefinir as propriedades atuais para Output.StartStreamFailed="Falha ao iniciar a transmissão" Output.StartRecordingFailed="Falha ao iniciar a gravação" Output.StartReplayFailed="Falha ao iniciar a memória de repetição" -Output.StartVirtualCamFailed="Falha ao iniciar câmera virtual" +Output.StartVirtualCamFailed="Falha ao iniciar a câmera virtual" Output.StartFailedGeneric="Falha ao iniciar a saída. Verifique o ficheiro de diário para mais detalhes.\n\nNota: se está a usar codificadores NVENC ou AMD, certifique-se de que os controladores da placa gráfica estão atualizados." Output.ReplayBuffer.PauseWarning.Title="Não é possível guardar repetições quando pausado" Output.ReplayBuffer.PauseWarning.Text="Aviso: as repetições não podem ser guardadas quando a gravação está em pausa." @@ -504,9 +506,9 @@ Basic.InteractionWindow="A interagir com '%1'" Basic.StatusBar.Reconnecting="Desligado. A ligar em %2 segundo(s) (tentativa %1)" Basic.StatusBar.AttemptingReconnect="A tentar nova ligação... (tentativa %1)" Basic.StatusBar.ReconnectSuccessful="Nova ligação bem sucedida" -Basic.StatusBar.Delay="Atraso (%1 seg)" -Basic.StatusBar.DelayStartingIn="Atraso (a iniciar em %1 seg)" -Basic.StatusBar.DelayStoppingIn="Atraso (a parar em %1 seg)" +Basic.StatusBar.Delay="Desfasamento (%1 seg)" +Basic.StatusBar.DelayStartingIn="Desfasamento (a iniciar em %1 seg)" +Basic.StatusBar.DelayStoppingIn="Desfasamento (a parar em %1 seg)" Basic.StatusBar.DelayStartingStoppingIn="Atraso (a parar em %1 seg, a iniciar em %2 seg)" Basic.StatusBar.RecordingSavedTo="Gravação guardada em '%1'" Basic.StatusBar.ReplayBufferSavedTo="Memória de repetição guardada em '%1'" @@ -563,6 +565,7 @@ Basic.Main.Scenes="Cenas" Basic.Main.Sources="Fontes" Basic.Main.Source="Fonte" Basic.Main.Controls="Controlos" +Basic.Main.PreparingStream="A preparar..." Basic.Main.Connecting="A ligar..." Basic.Main.StartRecording="Iniciar gravação" Basic.Main.StartReplayBuffer="Iniciar memória de repetição" @@ -574,6 +577,7 @@ Basic.Main.StopRecording="Parar gravação" Basic.Main.PauseRecording="Pausar gravação" Basic.Main.UnpauseRecording="Retomar gravação" Basic.Main.SplitFile="Dividir arquivo de gravação" +Basic.Main.AddChapterMarker="Adicionar marcador de capítulo" Basic.Main.StoppingRecording="A parar gravação..." Basic.Main.StopReplayBuffer="Parar memória de repetição" Basic.Main.StoppingReplayBuffer="A parar memória de repetição..." @@ -582,7 +586,7 @@ Basic.Main.StopStreaming="Parar transmissão" Basic.Main.StopBroadcast="Terminar emissão" Basic.Main.AutoStopEnabled="(paragem automática)" Basic.Main.StoppingStreaming="A parar transmissão..." -Basic.Main.ForceStopStreaming="Parar transmissão (ignorar atraso)" +Basic.Main.ForceStopStreaming="Parar transmissão (ignorar desfasamento)" Basic.Main.ShowContextBar="Mostrar barra de ferramentas da fonte" Basic.Main.HideContextBar="Ocultar barra de ferramentas da fonte" Basic.Main.StopVirtualCam="Parar câmera virtual" @@ -683,7 +687,6 @@ Basic.Settings.ProgramRestart="O programa necessita de ser reiniciado para estas Basic.Settings.ConfirmTitle="Confirmar alterações" Basic.Settings.Confirm="Tem alterações não guardadas. Deseja guardá-las?" Basic.Settings.General="Geral" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.Updater="Atualizações" Basic.Settings.General.UpdateChannel="Canal de atualização" @@ -743,9 +746,15 @@ Basic.Settings.General.ChannelName.stable="Estável" Basic.Settings.General.ChannelDescription.stable="Última versão estável" Basic.Settings.General.ChannelName.beta="Betas / Candidatos a lançamento" Basic.Settings.General.ChannelDescription.beta="Versões de pré-lançamento potencialmente instáveis" +Basic.Settings.Appearance="Visual" +Basic.Settings.Appearance.General="Geral" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Estilo" +Basic.Settings.Appearance.General.NoVariant="Nenhum estilo disponível" Basic.Settings.Stream="Transmissão" -Basic.Settings.Stream.Custom.UseAuthentication="Usar autenticação" -Basic.Settings.Stream.Custom.Username="Utilizador" +Basic.Settings.Stream.Destination="Destino" +Basic.Settings.Stream.Custom.UseAuthentication="Utilizar autenticação" +Basic.Settings.Stream.Custom.Username="Nome de utilizador" Basic.Settings.Stream.Custom.Password="Senha" Basic.Settings.Stream.Custom.Username.ToolTip="RIST: digite o srp_username.\nRTMP: digite o nome de utilizador.\nSRT: não usado." Basic.Settings.Stream.Custom.Password.ToolTip="RIST: digite o srp_password.\nRTMP: digite a senha.\nSRT: digite a senha de criptografia." @@ -765,9 +774,16 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Taxa de bits de vídeo máxim Basic.Settings.Stream.Recommended.MaxAudioBitrate="Taxa de bits de áudio máxima: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Resolução máxima: %1" Basic.Settings.Stream.Recommended.MaxFPS="Máximo FPS:%1" +Basic.Settings.Stream.SpecifyCustomServer="Especificar o servidor personalizado" +Basic.Settings.Stream.ServiceCustomServer="Servidor personalizado" +Basic.Settings.Stream.EnableMultitrackVideo="Ativar %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automático" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automático" +Basic.Settings.Stream.AdvancedOptions="Opções avançadas" Basic.Settings.Output="Saída" Basic.Settings.Output.Format="Formato de gravação" Basic.Settings.Output.Format.MKV="Vídeo Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 híbrido [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 fragmentado (.mp4)" Basic.Settings.Output.Format.fMOV="MOV fragmentado (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="MOV fragmentado escreve a gravação em partes e não requer a mesma finalização que os ficheiros MOV tradicionais.\nIsto garante que o ficheiro poderá ser executado mesmo se a gravação no disco for interrompida, por exemplo, como resultado de um BSOD ou perda de energia.\n\nPode não ser compatível com todas as aplicações de leitura e edição de vídeo. Utilize Ficheiro → Converter gravações, para converter o ficheiro para um formato compatível, se necessário." @@ -777,7 +793,7 @@ Basic.Settings.Output.Encoder.Audio="Codificador de áudio" Basic.Settings.Output.SelectDirectory="Selecionar pasta de gravação" Basic.Settings.Output.DynamicBitrate="Alterar dinamicamente a taxa de bits para gerir congestionamento" Basic.Settings.Output.DynamicBitrate.Beta="Alterar dinamicamente a taxa de bits para gerir congestionamento (beta)" -Basic.Settings.Output.DynamicBitrate.TT="Em vez de perder fotogramas para reduzir o congestionamento, muda dinamicamente a taxa de bits.\n\nNote que isto pode aumentar o atraso para os espectadores, se houver um congestionamento súbito significativo.\nQuando a taxa de bits cai pode levar alguns minutos a restaurar.\n\nAtualmente só é suportado em RTMP." +Basic.Settings.Output.DynamicBitrate.TT="Em vez de perder fotogramas para reduzir o congestionamento, muda dinamicamente a taxa de bits.\n\nNote que isto pode aumentar o desfasamento para os espetadores se houver um congestionamento súbito significativo.\nQuando a taxa de bits cai pode levar alguns minutos a restaurar.\n\nAtualmente só é suportado em RTMP." Basic.Settings.Output.Mode="Modo de saída" Basic.Settings.Output.Mode.Simple="Simples" Basic.Settings.Output.Mode.Adv="Avançado" @@ -819,7 +835,7 @@ Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="O serviço de transmi Basic.Settings.Output.VideoBitrate="Taxa de bits do vídeo" Basic.Settings.Output.AudioBitrate="Taxa de bits do áudio" Basic.Settings.Output.Reconnect="Religar automaticamente" -Basic.Settings.Output.RetryDelay="Atraso de repetição" +Basic.Settings.Output.RetryDelay="Desfasamento antes de nova tentativa" Basic.Settings.Output.MaxRetries="Máximo de tentativas" Basic.Settings.Output.Advanced="Ativar definições personalizadas do codificador (Avançado)" Basic.Settings.Output.EncoderPreset="Predefinição do codificador" @@ -954,9 +970,9 @@ Basic.Settings.Audio.AuxDevice2="Áudio Mic./Auxiliar 2" Basic.Settings.Audio.AuxDevice3="Áudio Mic./Auxiliar 3" Basic.Settings.Audio.AuxDevice4="Áudio Mic./Auxiliar 4" Basic.Settings.Audio.EnablePushToMute="Ativar Premir-para-silenciar" -Basic.Settings.Audio.PushToMuteDelay="Atraso do Premir-para-silenciar" +Basic.Settings.Audio.PushToMuteDelay="Desfasamento do Premir-para-silenciar" Basic.Settings.Audio.EnablePushToTalk="Ativar Premir-para-falar" -Basic.Settings.Audio.PushToTalkDelay="Atraso do Premir-para-falar" +Basic.Settings.Audio.PushToTalkDelay="Desfasamento do Premir-para-falar" Basic.Settings.Audio.UnknownAudioDevice="[dispositivo não ligado ou indisponível]" Basic.Settings.Audio.Disabled="Desativado" Basic.Settings.Audio.LowLatencyBufferingMode="Modo de memória temporária de áudio de baixa latência (para saídas Decklink/NDI)" @@ -1006,9 +1022,9 @@ Basic.Settings.Advanced.Video.HdrNominalPeakLevel="Nível de pico nominal do HDR Basic.Settings.Advanced.Audio.MonitoringDevice="Dispositivo de monitorização" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Predefinição" Basic.Settings.Advanced.Audio.DisableAudioDucking="Desativar redução de áudio do Windows" -Basic.Settings.Advanced.StreamDelay="Atraso da transmissão" +Basic.Settings.Advanced.StreamDelay="Desfasamento da transmissão" Basic.Settings.Advanced.StreamDelay.Duration="Duração" -Basic.Settings.Advanced.StreamDelay.Preserve="Preservar o ponto de corte (aumenta atraso) quando religar" +Basic.Settings.Advanced.StreamDelay.Preserve="Preservar o ponto de corte (aumenta desfasamento) quando religar" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilização estimada de memória: %1 MB" Basic.Settings.Advanced.Network="Rede" Basic.Settings.Advanced.Network.Disabled="O protocolo de transmissão atualmente selecionado não suporta a alteração das configurações de rede." @@ -1193,7 +1209,7 @@ YouTube.Actions.Stream.Resume="Retomar transmissão interrompida" YouTube.Actions.Stream.YTStudio="Criado automaticamente pelo YouTube Studio" YouTube.Actions.Notify.CreatingBroadcast="A criar emissão ao vivo, por favor aguarde..." YouTube.Actions.AutoStartStreamingWarning.Title="Início manual necessário" -YouTube.Actions.AutoStartStreamingWarning="O início automático está desativado para este evento, clique em \"Emitir em direto\" para iniciar a transmissão." +YouTube.Actions.AutoStartStreamingWarning="O início automático está desativado para este evento, clique em \"Emitir em direto\" para iniciar a emissão." YouTube.Actions.AutoStopStreamingWarning="Não poderá voltar a ligar.
A sua transmissão parará e deixará de estar em direto." YouTube.Chat.Input.Send="Enviar" YouTube.Chat.Input.Placeholder="Escreva aqui a sua mensagem..." @@ -1211,3 +1227,5 @@ YouTube.Errors.messageTextInvalid="O texto da mensagem não é valido." YouTube.Errors.rateLimitExceeded="Está a enviar mensagens rápido demais!" YouTube.DocksRemoval.Title="Limpar docas de navegador legadas do YouTube" YouTube.DocksRemoval.Text="Estas docas de navegador serão removidas por serem obsoletas:\n\n%1\nEm vez disto, use \"Docas/Sala de controle ao vivo do YouTube\"." +ConfigDownload.WarningMessageTitle="Aviso" +FailedToStartStream.FailedToCreateAudioEncoder="Falha ao criar o codificador áudio" diff --git a/UI/data/locale/ro-RO.ini b/UI/data/locale/ro-RO.ini index 0f32aa696c03d9..3e228e62288458 100644 --- a/UI/data/locale/ro-RO.ini +++ b/UI/data/locale/ro-RO.ini @@ -349,6 +349,7 @@ Output.RecordNoSpace.Msg="Nu există spațiu suficient pe disc pentru a continua Output.RecordError.Title="Eroare privind înregistrarea" Output.RecordError.Msg="A apărut o eroare nespecificată în timpul înregistrării." Output.RecordError.EncodeErrorMsg="A apărut o eroare de codificare în timpul înregistrării." +Output.RecordError.EncodeErrorMsg.LastError="Codificatorul a eșuat in timpul înregistrării:

%1" Output.BadPath.Title="Calea fișierului greșită" Output.BadPath.Text="Calea configurată de înregistrare nu a putut fi deschisă. Verificați calea de înregistrare din meniul Setări → Ieșire → Înregistrare." Output.NoBroadcast.Title="Nicio transmisiune configurată" @@ -473,6 +474,7 @@ Basic.PropertiesWindow="Proprietăți pentru „%1”" Basic.PropertiesWindow.AutoSelectFormat="%1 (selectare automată: %2)" Basic.PropertiesWindow.SelectColor="Selectează culoarea" Basic.PropertiesWindow.SelectFont="Selectează fontul" +Basic.PropertiesWindow.SelectFont.WindowTitle="Alege un Font" Basic.PropertiesWindow.ConfirmTitle="Setări schimbate" Basic.PropertiesWindow.Confirm="Există modificări nesalvate. Vrei să le păstrezi?" Basic.PropertiesWindow.NoProperties="Nicio proprietate disponibilă" @@ -582,6 +584,8 @@ Basic.Main.VirtualCamConfig="Configurare cameră virtuală" Basic.VCam.VirtualCamera="Cameră virtuală" Basic.VCam.OutputType="Tipul outputului" Basic.VCam.OutputSelection="Selectarea outputului" +Basic.VCam.OutputType.Program="Program (Implicit)" +Basic.VCam.RestartWarning="Camera virtuală va fi repornită pentru a aplica această modificare" Basic.MainMenu.File="&Fișier" Basic.MainMenu.File.Export="&Exportă" Basic.MainMenu.File.Import="&Importă" @@ -651,7 +655,7 @@ Basic.MainMenu.Help.WhatsNew="Ce este nou" Basic.MainMenu.Help.Logs="Fișiere jurna&l" Basic.MainMenu.Help.Logs.ShowLogs="Afișează fișierele jurnal (&S)" Basic.MainMenu.Help.Logs.UploadCurrentLog="Încarcă a&ctualul fișier jurnal" -Basic.MainMenu.Help.Logs.UploadLastLog="Încărcați fișierul jurnal anterior" +Basic.MainMenu.Help.Logs.UploadLastLog="Încarcă fișierul jurnal anterior (&P)" Basic.MainMenu.Help.Logs.ViewCurrentLog="&Vezi fișierul log actual" Basic.MainMenu.Help.ReleaseNotes="Note privind versiunea" Basic.MainMenu.Help.CheckForUpdates="Caută actualizări" @@ -660,13 +664,12 @@ Basic.MainMenu.Help.RestartSafeMode="Repornește în modul sigur" Basic.MainMenu.Help.RestartNormal="Repornește în modul normal" Basic.MainMenu.Help.CrashLogs="&Rapoarte de defecțiuni" Basic.MainMenu.Help.CrashLogs.ShowLogs="Afișează rapoartele de defecțiuni (&S)" -Basic.MainMenu.Help.CrashLogs.UploadLastLog="Încărcați raportul de defecțiune anterior" +Basic.MainMenu.Help.CrashLogs.UploadLastLog="Încarcă raportul de defecțiune anterior (&P)" Basic.MainMenu.Help.About="Despre (&A)" Basic.Settings.ProgramRestart="Programul trebuie repornit pentru ca aceste setări să aibă efect." Basic.Settings.ConfirmTitle="Confirmă modificările" Basic.Settings.Confirm="Ai modificări nesalvate. Salvezi modificările?" Basic.Settings.General="Generale" -Basic.Settings.General.Theme=" Temă" Basic.Settings.General.Language="Limbă" Basic.Settings.General.Updater="Actualizări" Basic.Settings.General.UpdateChannel="Canal de actualizare" @@ -765,6 +768,7 @@ Basic.Settings.Output.Mode.Adv="Avansat" Basic.Settings.Output.Mode.FFmpeg="Output FFmpeg" Basic.Settings.Output.UseReplayBuffer="Activează bufferul de reluări" Basic.Settings.Output.ReplayBuffer.SecondsMax="Timp maxim pentru reluare" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="Memorie Maximă" Basic.Settings.Output.ReplayBuffer.Estimate="Utilizare estimată a memoriei: %1 MB" Basic.Settings.Output.ReplayBuffer.EstimateTooLarge="Avertisment: Utilizarea estimată a memoriei de %1 MiB este mai mare decât maximul recomandat de %2 MiB" Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Nu se poate estima utilizarea memoriei. Te rugăm să setezi limita maximă de memorie." @@ -782,6 +786,7 @@ Basic.Settings.Output.Simple.Warn.AudioBitrate="Avertisment: Rata de biți pentr Basic.Settings.Output.Simple.Warn.CannotPause="Avertisment: Înregistrările nu pot fi puse pe pauză dacă calitatea înregistrării este setată pe „La fel cu cea a transmisiunii”." Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Avertisment: formatul de înregistrare selectat în prezent este incompatibil cu encoderul de streaming selectat." Basic.Settings.Output.Simple.Warn.Encoder="Avertisment: Înregistrarea cu un codificator software la o calitate diferită de cea a transmisiunii va necesita o utilizare CPU crescută dacă transmiți şi înregistrezi în același timp." +Basic.Settings.Output.Simple.Warn.Lossless="Avertisment: Calitatea fără pierderi generează dimensiuni extrem de mari de fișiere! Calitatea fără pierderi poate folosi până la 7GB spațiu de disc per minut la frecvențe de cadre și rezoluții ridicate. Această calitate nu este recomandată pentru înregistrări lungi decât dacă ai o cantitate foarte mare de spațiu disponibil pe disc. Bufferul de reluări nu este disponibil atunci când se utilizează calitatea fără pierderi." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sigur vrei să folosești calitatea fără pierderi?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Avertizare privind calitatea fără pierderi!" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (presetare x264 cu utilizare CPU scăzută, crește dimensiunea fișierelor)" @@ -808,6 +813,7 @@ Basic.Settings.Output.CustomEncoderSettings="Setări personalizate pentru codifi Basic.Settings.Output.CustomMuxerSettings="Setări personalizate pentru muxer" Basic.Settings.Output.NoSpaceFileName="Generează nume de fișiere fără spațiu" Basic.Settings.Output.Adv.Rescale="Rescalează outputul" +Basic.Settings.Output.Adv.Rescale.Disabled="Dezactivat" Basic.Settings.Output.Adv.AudioTrack="Pistă audio" Basic.Settings.Output.Adv.Streaming="Transmisiune" Basic.Settings.Output.Adv.Streaming.Settings="Setări de transmisie în flux" diff --git a/UI/data/locale/ru-RU.ini b/UI/data/locale/ru-RU.ini index acc135389c0924..2c91002c62f49f 100644 --- a/UI/data/locale/ru-RU.ini +++ b/UI/data/locale/ru-RU.ini @@ -1,5 +1,4 @@ Language="Русский" -OK="Хорошо" Apply="Применить" Cancel="Отмена" Close="Закрыть" @@ -108,6 +107,7 @@ MixerToolbarMenu="Меню микшера звука" SceneFilters="Открыть фильтры сцены" List="Список" Grid="Сетка" +Automatic="Автоматически" PluginsFailedToLoad.Title="Ошибка загрузки плагина" PluginsFailedToLoad.Text="Следующие плагины OBS не удалось загрузить:\n\n%1\nПожалуйста, обновите или удалите эти плагины." AlreadyRunning.Title="OBS уже запущен" @@ -194,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Предпочитать а Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Аппаратное кодирование снижает использование процессора, но может потребовать больший битрейт, для достижения того же уровня качества." Basic.AutoConfig.StreamPage.StreamWarning.Title="Предупреждение трансляции" Basic.AutoConfig.StreamPage.StreamWarning.Text="Проверка пропускной способности трансляции случайных видеоданных без звука на вашем канале. Рекомендуется временно отключить запись трансляции в файл и сделать трансляцию закрытой, пока проверка не завершится. Продолжить?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Тест %1" Basic.AutoConfig.TestPage="Конечные результаты" Basic.AutoConfig.TestPage.SubTitle.Testing="Сейчас программа выполняет набор тестов для оценки лучших настроек" Basic.AutoConfig.TestPage.SubTitle.Complete="Проверка завершена" @@ -373,7 +374,7 @@ LogReturnDialog.AnalyzeURL="Анализ" LogReturnDialog.ErrorUploadingLog="Ошибка отгрузки файла журнала" Remux.SourceFile="Файл записи OBS" Remux.TargetFile="Конечный файл" -Remux.Remux="Перепаковка" +Remux.Remux="Перепаковать" Remux.Stop="Остановить перепаковку" Remux.ClearFinished="Убрать завершённые из списка" Remux.ClearAll="Очистить список" @@ -562,7 +563,7 @@ Basic.TransformWindow.BoundsType.ScaleToHeight="Масштабирование Basic.TransformWindow.BoundsType.Stretch="Растянуть до границы" Basic.TransformWindow.Title="Изменить преобразование для «%1»" Basic.TransformWindow.NoSelectedSource="Источник не выбран" -Basic.Main.AddSourceHelp.Title="невозможно добавить источник" +Basic.Main.AddSourceHelp.Title="Невозможно добавить источник" Basic.Main.AddSourceHelp.Text="Вы должны создать по крайней мере одну сцену, чтобы добавить источник." Basic.Main.Scenes="Сцены" Basic.Main.Sources="Источники" @@ -574,11 +575,12 @@ Basic.Main.StartReplayBuffer="Запустить повтор" Basic.Main.SaveReplay="Сохранить повтор" Basic.Main.StartStreaming="Начать трансляцию" Basic.Main.StartBroadcast="Выйти в эфир" -Basic.Main.StartVirtualCam="Запуск вирт. камеры" +Basic.Main.StartVirtualCam="Запуск виртуальной камеры" Basic.Main.StopRecording="Остановить запись" Basic.Main.PauseRecording="Приостановить запись" Basic.Main.UnpauseRecording="Возобновить запись" Basic.Main.SplitFile="Разделить файл записи" +Basic.Main.AddChapterMarker="Добавить метку главы" Basic.Main.StoppingRecording="Остановка записи..." Basic.Main.StopReplayBuffer="Остановить повтор" Basic.Main.StoppingReplayBuffer="Остановка повтора..." @@ -629,7 +631,7 @@ Basic.MainMenu.Edit.Transform.ResetTransform="Сбросить преобраз Basic.MainMenu.Edit.Transform.Rotate90CW="Повернуть на 90 градусов по часовой" Basic.MainMenu.Edit.Transform.Rotate90CCW="Повернуть на 90 градусов против часовой" Basic.MainMenu.Edit.Transform.Rotate180="Повернуть на 180 градусов" -Basic.MainMenu.Edit.Transform.FlipHorizontal="Отразить по горизонтали" +Basic.MainMenu.Edit.Transform.FlipHorizontal="Отразить горизонтально (&H)" Basic.MainMenu.Edit.Transform.FlipVertical="Отразить вертикально (&V)" Basic.MainMenu.Edit.Transform.FitToScreen="Подогнать по размеру экрана (&F)" Basic.MainMenu.Edit.Transform.StretchToScreen="Растянуть на весь экран (&S)" @@ -669,7 +671,7 @@ Basic.MainMenu.Help="Справка (&H)" Basic.MainMenu.Help.HelpPortal="Справочный портал (&P)" Basic.MainMenu.Help.Website="Посетить сайт (&W)" Basic.MainMenu.Help.Discord="Посетить Дискорд-сервер (&D)" -Basic.MainMenu.Help.WhatsNew="Что нового" +Basic.MainMenu.Help.WhatsNew="Новости" Basic.MainMenu.Help.Logs="Файлы журнала (&L)" Basic.MainMenu.Help.Logs.ShowLogs="Показать файлы журнала (&S)" Basic.MainMenu.Help.Logs.UploadCurrentLog="Отправить текущий файл журнала (&C)" @@ -688,7 +690,6 @@ Basic.Settings.ProgramRestart="Чтобы эти настройки вступи Basic.Settings.ConfirmTitle="Подтверждение изменений" Basic.Settings.Confirm="У вас есть несохранённые изменения. Сохранить их?" Basic.Settings.General="Общие" -Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Язык" Basic.Settings.General.Updater="Обновления" Basic.Settings.General.UpdateChannel="Канал обновления" @@ -748,7 +749,13 @@ Basic.Settings.General.ChannelName.stable="Стабильный" Basic.Settings.General.ChannelDescription.stable="Последний стабильный выпуск" Basic.Settings.General.ChannelName.beta="Бета-версии / предвыпускные" Basic.Settings.General.ChannelDescription.beta="Предварительные нестабильные версии" +Basic.Settings.Appearance="Внешний вид" +Basic.Settings.Appearance.General="Общие" +Basic.Settings.Appearance.General.Theme="Тема" +Basic.Settings.Appearance.General.Variant="Стиль" +Basic.Settings.Appearance.General.NoVariant="Нет доступных стилей" Basic.Settings.Stream="Трансляция" +Basic.Settings.Stream.Destination="Назначение" Basic.Settings.Stream.Custom.UseAuthentication="Использовать вход в аккаунт" Basic.Settings.Stream.Custom.Username="Имя пользователя" Basic.Settings.Stream.Custom.Password="Пароль" @@ -770,9 +777,11 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Максимальный би Basic.Settings.Stream.Recommended.MaxAudioBitrate="Максимальный битрейт аудио: %1 кбит/с" Basic.Settings.Stream.Recommended.MaxResolution="Максимальное разрешение: %1" Basic.Settings.Stream.Recommended.MaxFPS="Максимум частоты кадров: %1" +Basic.Settings.Stream.EnableMultitrackVideo="Включить %1" Basic.Settings.Output="Вывод" Basic.Settings.Output.Format="Формат записи" Basic.Settings.Output.Format.MKV="Видеоформат «Матрёшка» (.mkv)" +Basic.Settings.Output.Format.hMP4="Гибридный MP4 [БЕТА] (.mp4)" Basic.Settings.Output.Format.fMP4="Фрагментированный MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Фрагментированный MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Фрагментированные MOV производят запись кусочно и не требуют такой же финализации как традиционные файлы MOV.\nЭто гарантирует, что файл будет проигрываться, даже если запись на диск будет прервана, например, в результате «синего экрана смерти» или потери питания.\n\nМожет быть несовместимо со всеми плеерами и редакторами. При необходимости используйте «Файл→ Перепаковать записи» для преобразования файла в более совместимый формат." @@ -843,10 +852,10 @@ Basic.Settings.Output.EncoderPreset.ultrafast="%1 (низкое использо Basic.Settings.Output.EncoderPreset.veryfast="%1 (по умолчанию) (среднее использование ЦП, стандартное качество)" Basic.Settings.Output.EncoderPreset.fast="%1 (высокое использование ЦП, высокое качество)" Basic.Settings.Output.CustomEncoderSettings="Дополнительные настройки кодировщика" -Basic.Settings.Output.CustomMuxerSettings="Собственные настройки паковщика" +Basic.Settings.Output.CustomMuxerSettings="Свои настройки паковщика" Basic.Settings.Output.NoSpaceFileName="Генерировать имя файла без пробела" Basic.Settings.Output.Adv.Rescale="Масштабировать вывод" -Basic.Settings.Output.Adv.Rescale.Disabled="Выключено" +Basic.Settings.Output.Adv.Rescale.Disabled="Отключено" Basic.Settings.Output.Adv.AudioTrack="Звуковая дорожка" Basic.Settings.Output.Adv.Streaming="Трансляция" Basic.Settings.Output.Adv.Streaming.Settings="Настройки трансляции" @@ -1034,8 +1043,8 @@ Basic.Settings.Advanced.Network.Disabled="Текущий выбранный пр Basic.Settings.Advanced.Network.BindToIP="Привязать к IP" Basic.Settings.Advanced.Network.IPFamily="Семейство IP" Basic.Settings.Advanced.Network.EnableNewSocketLoop="Включить сетевую оптимизацию" -Basic.Settings.Advanced.Network.EnableLowLatencyMode="Включить прореживание данных (TCP pacing)" -Basic.Settings.Advanced.Network.TCPPacing.Tooltip="Пытается уменьшить негативный эффект на другие приложения при использовании вывода RTMP путём регулировки скорости передачи.\nМожет увеличить риск потери кадров при нестабильном соединении." +Basic.Settings.Advanced.Network.EnableLowLatencyMode="Включить прореживание пакетов (TCP pacing)" +Basic.Settings.Advanced.Network.TCPPacing.Tooltip="Снижает негативное воздействие вывода RTMP на другие сетевые приложения, чувствительные к задержкам, путём регулировки частоты передачи.\nМожет увеличить риск потери кадров при нестабильном соединении." Basic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior="Поведение фокуса горячих клавиш" Basic.Settings.Advanced.Hotkeys.NeverDisableHotkeys="Никогда не отключать горячие клавиши" Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Отключать горячие клавиши, когда главное окно в фокусе" diff --git a/UI/data/locale/si-LK.ini b/UI/data/locale/si-LK.ini index 95af3055bda97e..10fd25dcd5fad7 100644 --- a/UI/data/locale/si-LK.ini +++ b/UI/data/locale/si-LK.ini @@ -587,7 +587,6 @@ Basic.Settings.ProgramRestart="මෙම සැකසුම් යෙදීම Basic.Settings.ConfirmTitle="වෙනස්කම් තහවුරුව" Basic.Settings.Confirm="නොසුරැකි වෙනස්කම් තිබේ. සුරකින්න ද?" Basic.Settings.General="පොදු" -Basic.Settings.General.Theme="තේමාව" Basic.Settings.General.Language="භාෂාව" Basic.Settings.General.Updater="යාවත්කාල" Basic.Settings.General.UpdateChannelDisabled="(අබලයි)" diff --git a/UI/data/locale/sk-SK.ini b/UI/data/locale/sk-SK.ini index a13e366fa7c5f9..b6a61dd8ec3e84 100644 --- a/UI/data/locale/sk-SK.ini +++ b/UI/data/locale/sk-SK.ini @@ -677,7 +677,6 @@ Basic.Settings.ProgramRestart="Tieto nastavenia sa prejavia až po reštarte pro Basic.Settings.ConfirmTitle="Potvrdenie zmien" Basic.Settings.Confirm="Máte neuložené zmeny. Chcete uložiť zmeny?" Basic.Settings.General="Všeobecné" -Basic.Settings.General.Theme="Vzhľad" Basic.Settings.General.Language="Jazyk" Basic.Settings.General.Updater="Aktualizácie" Basic.Settings.General.UpdateChannel="Kanál aktualizácií" @@ -736,6 +735,11 @@ Basic.Settings.General.ChannelName.stable="Stabilný" Basic.Settings.General.ChannelDescription.stable="Najnovšia stabilná verzia" Basic.Settings.General.ChannelName.beta="Beta verzie / Výdajní kandidáti" Basic.Settings.General.ChannelDescription.beta="Potenciálne nestabilné pred-výdajné verzie" +Basic.Settings.Appearance="Vzhľad" +Basic.Settings.Appearance.General="Všeobecné" +Basic.Settings.Appearance.General.Theme="Téma" +Basic.Settings.Appearance.General.Variant="Štýl" +Basic.Settings.Appearance.General.NoVariant="Žiadne dostupné štýly" Basic.Settings.Stream.Custom.UseAuthentication="Použiť overenie" Basic.Settings.Stream.Custom.Username="Užívateľské meno" Basic.Settings.Stream.Custom.Password="Heslo" diff --git a/UI/data/locale/sl-SI.ini b/UI/data/locale/sl-SI.ini index fa267bcd6723ec..84194ccb18aaf5 100644 --- a/UI/data/locale/sl-SI.ini +++ b/UI/data/locale/sl-SI.ini @@ -682,7 +682,6 @@ Basic.Settings.ProgramRestart="Program je treba ponovno zagnati, da se te nastav Basic.Settings.ConfirmTitle="Potrdi spremembe" Basic.Settings.Confirm="Imate neshranjene spremembe. Shrani spremembe?" Basic.Settings.General="Splošno" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Jezik" Basic.Settings.General.Updater="Posodobitve" Basic.Settings.General.UpdateChannel="Kanal za posodobitev" @@ -742,6 +741,11 @@ Basic.Settings.General.ChannelName.stable="Stabilno" Basic.Settings.General.ChannelDescription.stable="Zadnja stabilna izdaja" Basic.Settings.General.ChannelName.beta="Beta / kandidat za izdajo" Basic.Settings.General.ChannelDescription.beta="Morebiti nestabilna različica pred izdajo" +Basic.Settings.Appearance="Videz" +Basic.Settings.Appearance.General="Splošno" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Slog" +Basic.Settings.Appearance.General.NoVariant="Slogi niso na voljo" Basic.Settings.Stream="Pretok" Basic.Settings.Stream.Custom.UseAuthentication="Uporabi overitev" Basic.Settings.Stream.Custom.Username="Uporabniško ime" diff --git a/UI/data/locale/sr-CS.ini b/UI/data/locale/sr-CS.ini index 71e8fcb3e3e7be..f754b03d7696af 100644 --- a/UI/data/locale/sr-CS.ini +++ b/UI/data/locale/sr-CS.ini @@ -465,7 +465,6 @@ Basic.Settings.ProgramRestart="Program mora biti ponovo pokrenut da bi ova pode Basic.Settings.ConfirmTitle="Potvrdite promene" Basic.Settings.Confirm="Postoje promene koje nisu sačuvane. Sačuvati?" Basic.Settings.General="Opšte" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Jezik" Basic.Settings.General.EnableAutoUpdates="Automatski potraži ažuriranja pri pokretanju" Basic.Settings.General.OpenStatsOnStartup="Prikaži statistiku pri pokretanju" diff --git a/UI/data/locale/sr-SP.ini b/UI/data/locale/sr-SP.ini index 61a81f64cbdbcd..9f27982642ec1c 100644 --- a/UI/data/locale/sr-SP.ini +++ b/UI/data/locale/sr-SP.ini @@ -651,7 +651,6 @@ Basic.Settings.ProgramRestart="Програм мора бити поново п Basic.Settings.ConfirmTitle="Потврдите промене" Basic.Settings.Confirm="Постоје промене које нису сачуване. Сачувати?" Basic.Settings.General="Опште" -Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Језик" Basic.Settings.General.Updater="Ажурирања" Basic.Settings.General.UpdateChannel="Канал ажурирања" diff --git a/UI/data/locale/sv-SE.ini b/UI/data/locale/sv-SE.ini index 5ac1a75c309a84..ef59b8ae08cbc8 100644 --- a/UI/data/locale/sv-SE.ini +++ b/UI/data/locale/sv-SE.ini @@ -23,11 +23,11 @@ Mixer="Ljudmixer" Browse="Bläddra" DroppedFrames="Förorade bildrutor %1 (%2 %)" StudioProgramProjector="Helskärmsprojektor (program)" -PreviewProjector="Fullskärmsprojektor (Förhandsvisning)" -SceneProjector="Fullskärmsprojektor (Scen)" -SourceProjector="Fullskärmsprojektor (Källa)" -StudioProgramWindow="Fönsterprojektor (Program)" -PreviewWindow="Fönsterprojektor (Förhandsgranskning)" +PreviewProjector="Fullskärmsprojektor (förhandsvisning)" +SceneProjector="Fullskärmsprojektor (scen)" +SourceProjector="Fullskärmsprojektor (källa)" +StudioProgramWindow="Fönsterprojektor (program)" +PreviewWindow="Fönsterprojektor (förhandsvisning)" SceneWindow="Fönsterprojektor (Scen)" SourceWindow="Fönsterprojektor (Källa)" MultiviewProjector="Multivy (Fullskärm)" @@ -75,7 +75,7 @@ ShowTransition="Visa övergång" HideTransition="Dölj övergång" None="Ingen" StudioMode.Preview="Förhandsvisning" -StudioMode.PreviewSceneName="Förhandsgranskning: %1" +StudioMode.PreviewSceneName="Förhandsvisning: %1" ShowInMultiview="Visa i multivy" VerticalLayout="Vertikal layout" Group="Grupp" @@ -103,8 +103,9 @@ MixerToolbarMenu="Ljudmixermeny" SceneFilters="Öppna scenfilter" List="Lista" Grid="Rutnät" -PluginsFailedToLoad.Title="Fel vid inläsning av insticksmodul" -PluginsFailedToLoad.Text="Följande OBS-insticksmoduler kunde inte läsas in:\n\n%1\nVar god uppdatera eller ta bort dessa insticksmoduler." +Automatic="Automatisk" +PluginsFailedToLoad.Title="Fel vid inläsning av insticksprogram" +PluginsFailedToLoad.Text="Följande OBS-insticksprogram kunde inte läsas in:\n\n%1\nVar god uppdatera eller ta bort dessa insticksprogram." AlreadyRunning.Title="OBS körs redan" AlreadyRunning.Text="OBS körs redan! Såvida du gjorde detta med flit, stäng ned alla befintliga instanser av OBS innan du försöker köra en ny instans. Om du har minimerat OBS till systemfältet, kontroller om det fortfarande körs där." AlreadyRunning.LaunchAnyway="Kör ändå" @@ -205,6 +206,7 @@ Basic.AutoConfig.TestPage.Result.Header="Programmet har uppskattat att de här i Basic.AutoConfig.TestPage.Result.Footer="För att använda dessa inställningar, klicka på Verkställ inställningar. För att köra guiden igen, klicka på Tillbaka. För att konfigurera inställningarna manuellt, klicka på Avbryt och öppna Inställningar." Basic.AutoConfig.Info="Autokonfigurationsguiden kommer att avgöra de bästa inställningarna baserat på din dators specifikation och din internethastighet." Basic.AutoConfig.RunAnytime="Detta kan köras när som helst via verktygsmenyn." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Strömningsupplösning (skalad)" Basic.Stats="Statistik" Basic.Stats.CPUUsage="Processoranvändning" Basic.Stats.HDDSpaceAvailable="Ledigt hårddiskutrymme" @@ -308,7 +310,7 @@ TitleBar.PortableMode="Portabelt läge" TitleBar.Profile="Profil" TitleBar.Scenes="Scener" NameExists.Title="Namnet används redan" -NameExists.Text="Det namnet används redan." +NameExists.Text="Detta namn används redan." NoNameEntered.Title="Var god ange ett giltigt namn" NoNameEntered.Text="Du måste ange ett namn." ConfirmStart.Title="Börja strömma?" @@ -368,7 +370,7 @@ Remux.TargetFile="Målfil" Remux.Stop="Stoppa remuxing" Remux.ClearFinished="Rensa slutförda objekt" Remux.ClearAll="Rensa alla objekt" -Remux.OBSRecording="OBS Inspelning" +Remux.OBSRecording="OBS-inspelning" Remux.FinishedTitle="Remuxing färdig" Remux.Finished="Inspelning remuxed" Remux.FinishedError="Inspelning remuxed, men filen kan vara ofullständig" @@ -416,7 +418,7 @@ MacPermissions.Item.Accessibility="Tillgänglighet" MacPermissions.Item.Accessibility.Details="För att kortkommandon ska fungera medan andra appar har fokus, var god aktivera denna behörighet." MacPermissions.Continue="Fortsätt" SourceLeak.Title="Fel vid städning av källor" -SourceLeak.Text="Ett problem uppstod när scensamlingar skulle ändras och några källor kunde inte avlastas. Detta problem orsakas vanligtvis av insticksmoduler som inte frigör resurser ordentligt. Se till att alla insticksmoduler du använder är uppdaterade.\n\nOBS Studio kommer nu avslutas för att förhindra data från att potentiellt skadas." +SourceLeak.Text="Ett problem uppstod när scensamlingar skulle ändras och några källor inte kunde avlastas. Detta problem orsakas vanligtvis av insticksprogram som inte frigör resurser ordentligt. Se till att alla insticksprogram du använder är uppdaterade.\n\nOBS Studio kommer nu avslutas för att förhindra data från att potentiellt skadas." Basic.DesktopDevice1="Skrivbordsljud" Basic.DesktopDevice2="Skrivbordsljud 2" Basic.Scene="Scen" @@ -448,7 +450,7 @@ VolControl.SliderMuted="Volymreglage för \"%1\": (tyst för tillfället)" VolControl.Mute="Tysta \"%1\"" VolControl.Properties="Egenskaper för \"%1\"" VolControl.UnassignedWarning.Title="Ej tilldelad ljudkälla" -VolControl.UnassignedWarning.Text="\"%1\" är inte tilldelad något ljudspår och kommer inte höras i stream eller inspelning.\n\nFör att tilldela en ljudkälla till ett ljudspår, öppna ljudinställningarna via högerklicksmenyn eller kugghjulet i ljudmixern.\nis not assigned to any audio tracks and it will not be audible in streams or recordings.\n\nTo assign an audio source to a track, open the Advanced Audio Properties via the right-click menu or the cog button in the mixer dock toolbar." +VolControl.UnassignedWarning.Text="\"%1\" är inte tilldelad något ljudspår och kommer inte höras i stream eller inspelning.\n\nFör att tilldela en ljudkälla till ett ljudspår, öppna ljudinställningarna via högerklicksmenyn eller kugghjulet i ljudmixern." Basic.Main.AddSceneDlg.Title="Lägg till scen" Basic.Main.AddSceneDlg.Text="Var god ange scenens namn" Basic.Main.DefaultSceneName.Text="Scen %1" @@ -461,7 +463,7 @@ AddProfile.WizardCheckbox="Visa automatisk konfigurationsguide" RenameProfile.Title="Byt namn på profilen" Basic.Main.MixerRename.Title="Byt namn på ljudkälla" Basic.Main.MixerRename.Text="Var god ange ljudkällans namn" -Basic.Main.PreviewDisabled="Förhandsvisningen är inaktiverad" +Basic.Main.PreviewDisabled="Förhandsvisning har för tillfället inaktiverats" Basic.SourceSelect="Skapa/välj källa" Basic.SourceSelect.CreateNew="Skapa ny" Basic.SourceSelect.AddExisting="Lägg till befintlig" @@ -480,7 +482,7 @@ Basic.PropertiesWindow.Confirm="Det finns osparade ändringar. Vill du behålla Basic.PropertiesWindow.NoProperties="Inga inställningar tillgängliga" Basic.PropertiesWindow.AddFiles="Lägg till filer" Basic.PropertiesWindow.AddDir="Lägg till mapp" -Basic.PropertiesWindow.AddURL="Lägg till Sökväg/URL" +Basic.PropertiesWindow.AddURL="Lägg till sökväg/URL" Basic.PropertiesWindow.AddEditableListDir="Lägg till mapp i \"%1\"" Basic.PropertiesWindow.AddEditableListFiles="Lägg till filer i \"%1\"" Basic.PropertiesWindow.AddEditableListEntry="Lägg till post i \"%1\"" @@ -552,6 +554,7 @@ Basic.Main.Scenes="Scener" Basic.Main.Sources="Källor" Basic.Main.Source="Källa" Basic.Main.Controls="Kontroller" +Basic.Main.PreparingStream="Förbereder ..." Basic.Main.Connecting="Ansluter..." Basic.Main.StartRecording="Börja spela in" Basic.Main.StartReplayBuffer="Starta reprisbuffert" @@ -563,6 +566,7 @@ Basic.Main.StopRecording="Sluta spela in" Basic.Main.PauseRecording="Pausa inspelning" Basic.Main.UnpauseRecording="Återuppta inspelning" Basic.Main.SplitFile="Dela upp inspelningsfil" +Basic.Main.AddChapterMarker="Lägg till kapitelmarkör" Basic.Main.StoppingRecording="Slutar spela in..." Basic.Main.StopReplayBuffer="Stoppa reprisbuffert" Basic.Main.StoppingReplayBuffer="Stoppar reprisbuffert..." @@ -584,7 +588,7 @@ Basic.Main.VirtualCamConfig="Konfigurera virtuell kamera" Basic.VCam.VirtualCamera="Virtuell kamera" Basic.VCam.OutputType="Utmatningstyp" Basic.VCam.OutputSelection="Utmatningsmarkering" -Basic.VCam.OutputType.Program="Program (Standard)" +Basic.VCam.OutputType.Program="Program (standard)" Basic.VCam.OutputSelection.NoSelection="Inget val för den här typen av utmatning" Basic.VCam.RestartWarning="Den virtuella kameran kommer att startas om för att verkställa den här ändringen" Basic.MainMenu.File="Arkiv (&F)" @@ -661,7 +665,7 @@ Basic.MainMenu.Help.Logs.UploadLastLog="Ladda upp &föregående loggfil" Basic.MainMenu.Help.Logs.ViewCurrentLog="&Visa aktuell logg" Basic.MainMenu.Help.ReleaseNotes="Versionsfakta" Basic.MainMenu.Help.CheckForUpdates="Sök efter uppdateringar" -Basic.MainMenu.Help.Repair="Kontrollera Filintegritet" +Basic.MainMenu.Help.Repair="Kontrollera filintegritet" Basic.MainMenu.Help.RestartSafeMode="Starta om i felsäkert läge" Basic.MainMenu.Help.RestartNormal="Starta om i normalt läge" Basic.MainMenu.Help.CrashLogs="K&raschrapporter" @@ -671,8 +675,8 @@ Basic.MainMenu.Help.About="Om (&A)" Basic.Settings.ProgramRestart="Du måste starta om programmet för att ändringarna ska träda i kraft." Basic.Settings.ConfirmTitle="Bekräfta ändringar" Basic.Settings.Confirm="Du har osparade ändringar. Vill du spara ändringarna?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 kontrollerar några av dina strömningsinställningar" Basic.Settings.General="Allmänt" -Basic.Settings.General.Theme=" Tema" Basic.Settings.General.Language="Språk" Basic.Settings.General.Updater="Uppdateringar" Basic.Settings.General.UpdateChannel="Uppdateringskanal" @@ -732,6 +736,11 @@ Basic.Settings.General.ChannelName.stable="Stabil" Basic.Settings.General.ChannelDescription.stable="Senaste stabila utgåvan" Basic.Settings.General.ChannelName.beta="Beta/frisläppningskandidater" Basic.Settings.General.ChannelDescription.beta="Potentiellt instabila förhandsversioner" +Basic.Settings.Appearance="Utseende" +Basic.Settings.Appearance.General="Allmänt" +Basic.Settings.Appearance.General.Theme=" Tema" +Basic.Settings.Appearance.General.Variant="Stil" +Basic.Settings.Appearance.General.NoVariant="Inga stilar är tillgängliga" Basic.Settings.Stream="Ström" Basic.Settings.Stream.Custom.UseAuthentication="Använd autentisering" Basic.Settings.Stream.Custom.Username="Användarnamn" @@ -754,6 +763,15 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximal videobithastighet: %1 Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximal ljudbithastighet: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximal upplösning: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximal bildfrekvens: %1" +Basic.Settings.Stream.SpecifyCustomServer="Specificera anpassad server..." +Basic.Settings.Stream.ServiceCustomServer="Anpassad server" +Basic.Settings.Stream.EnableMultitrackVideo="Aktivera %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximal strömningsbandbredd" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maximala videospår" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Aktivera strömdumpning till FLV (använder enkla inställningar för inspelningsfil)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Överskrid konfiguration (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Aktivera överskridande av konfiguration" +Basic.Settings.Stream.MultitrackVideoLabel="Flerspårsvideo" Basic.Settings.Output="Utmatning" Basic.Settings.Output.Format="Inspelningsformat" Basic.Settings.Output.Format.MKV="Matroska-video (.mkv)" @@ -763,7 +781,7 @@ Basic.Settings.Output.Format.TT.fragmented_mov="Fragmenterad MOV skriver inspeln Basic.Settings.Output.Format.TT.fragmented_mp4="Fragmenterad MP4 skriver inspelningen i segment och kräver inte samma slut som traditionella MP4-filer.\nDetta säkerställer att filen förblir spelbar även om skrivning till disk skulle avbrytas, t.ex. p.g.a. en BSOD eller strömförlust.\n\nDetta kanske inte är kompatibelt med alla spelare och redigerare. Använd Arkiv → Remuxa inspelningar för att konvertera filen till ett mer kompatibelt format om det behövs." Basic.Settings.Output.Encoder.Video="Videokodare" Basic.Settings.Output.Encoder.Audio="Ljudkodare" -Basic.Settings.Output.SelectDirectory="Välj inspelningsplats" +Basic.Settings.Output.SelectDirectory="Välj inspelningskatalog" Basic.Settings.Output.DynamicBitrate="Ändra bithastigheten dynamiskt för att hantera överbelastning" Basic.Settings.Output.DynamicBitrate.Beta="Ändra bithastigheten dynamiskt för att hantera överbelastning (beta)" Basic.Settings.Output.DynamicBitrate.TT="Istället för att kasta bildrutor för att reducera överbelastning kommer bithastigheten ändras dynamiskt.\n\nObservera att detta kan öka fördröjningen för tittare om en avsevärd belastning uppstår.\nNär bithastigheten sjunker kan det ta ett par minuter att återställas.\n\nStöds för närvarande endast för RTMP." @@ -939,7 +957,7 @@ Basic.Settings.Audio.PeakMeterType="Typ av maxpunktsmätare" Basic.Settings.Audio.PeakMeterType.SamplePeak="Samplingsmaxpunkt" Basic.Settings.Audio.PeakMeterType.TruePeak="Sann maxpunkt (högre CPU-användning)" Basic.Settings.Audio.MultichannelWarning.Enabled="VARNING: Surroundljud är aktiverat." -Basic.Settings.Audio.MultichannelWarning="Om du strömmar, se till att kolla om din strömtjänst stöder både inmatning och uppspelning av surroundljud. Till exempel har Facebook 360 Live fullt stöd för surround och YouTube Live har stöd för 5.1-ljud (samt uppspelning på TV).\n\nLjudfiltren i OBS är kompatibla med surroundljud, fast stöd för VST-insticksmodulen garanteras inte." +Basic.Settings.Audio.MultichannelWarning="Om du strömmar, se till att kolla om din strömtjänst stöder både inmatning och uppspelning av surroundljud. Till exempel har Facebook 360 Live fullt stöd för surround och YouTube Live har stöd för 5.1-ljud (samt uppspelning på TV).\n\nLjudfiltren i OBS är kompatibla med surroundljud, fast stöd för VST-insticksprogram garanteras inte." Basic.Settings.Audio.MultichannelWarning.Title="Aktivera surroundljud?" Basic.Settings.Audio.MultichannelWarning.Confirm="Är du säker på att du vill aktivera surroundljud?" Basic.Settings.Audio.Devices="Globala ljudenheter" diff --git a/UI/data/locale/szl-PL.ini b/UI/data/locale/szl-PL.ini index de1f54220d2ddd..078fc773d7b4bb 100644 --- a/UI/data/locale/szl-PL.ini +++ b/UI/data/locale/szl-PL.ini @@ -502,7 +502,6 @@ Basic.Settings.ProgramRestart="Coby te ustawiynia zaczły ôbowiōnzować, trza Basic.Settings.ConfirmTitle="Potwiyrdź zmiany" Basic.Settings.Confirm="Mosz niyzapisane zmiany. Chcesz jy zapisać?" Basic.Settings.General="Bazowe" -Basic.Settings.General.Theme="Motyw" Basic.Settings.General.Language="Godka" Basic.Settings.General.EnableAutoUpdates="Autōmatycznie wybaduj dostympność aktualizacyji" Basic.Settings.General.OpenStatsOnStartup="Ôtwōrz sztatystyki przi sztarcie aplikacyje" diff --git a/UI/data/locale/ta-IN.ini b/UI/data/locale/ta-IN.ini index 5e869ecdafd527..7e76e636263486 100644 --- a/UI/data/locale/ta-IN.ini +++ b/UI/data/locale/ta-IN.ini @@ -502,7 +502,6 @@ Basic.Settings.ProgramRestart="இந்த அமைப்புகள் ந Basic.Settings.ConfirmTitle="மாற்றம் உறுதி செய்தல்" Basic.Settings.Confirm="சேமிக்கப்படாத மாற்றங்கள் உள்ளன. மாற்றங்களை சேமிக்கவா?" Basic.Settings.General="பொதுவானவை" -Basic.Settings.General.Theme="வண்ண அமைப்பு" Basic.Settings.General.Language="மொழி" Basic.Settings.General.EnableAutoUpdates="தொடக்கத்தில் மேம்படுத்தல்களை தானியங்கு முறையில் சோதிக்க" Basic.Settings.General.OpenStatsOnStartup="தொடக்கத்தில் விபரம் திறந்த உரையாடல்" diff --git a/UI/data/locale/th-TH.ini b/UI/data/locale/th-TH.ini index e8df1003a243df..3746b53516da9e 100644 --- a/UI/data/locale/th-TH.ini +++ b/UI/data/locale/th-TH.ini @@ -11,16 +11,16 @@ No="ไม่" Add="เพิ่ม" Remove="ลบ" Rename="เปลี่ยนชื่อ" -Interact="การโต้ตอบ" +Interact="โต้ตอบ" Filters="ฟิลเตอร์" Properties="คุณสมบัติ" MoveUp="เลื่อนขึ้น" MoveDown="เลื่อนลง" Settings="ตั้งค่า" -Display="แสดงผล" +Display="หน้าจอ" Name="ชื่อ" Exit="ออก" -Mixer="ตัวแต่งเสียง" +Mixer="มิกเซอร์เสียง" Browse="เรียกดู" Mono="โมโน" Stereo="สเตอริโอ" @@ -43,7 +43,7 @@ Hide="ซ่อน" UnhideAll="ยกเลิกการซ่อนทั้งหมด" Untitled="ไม่มีชื่อ" New="ใหม่" -Duplicate="ทำสำเนา" +Duplicate="ทำซ้ำ" Enable="เปิดใช้งาน" DisableOSXVSync="ปิดใช้งาน V-Sync ใน macOS" ResetOSXVSyncOnExit="คืนค่าเดิมของ V-Sync ใน macOS เมื่อออก" @@ -420,6 +420,7 @@ MacPermissions.Item.ScreenRecording.Details="OBS ต้องการ การ MacPermissions.Item.Camera="กล้อง" MacPermissions.Item.Camera.Details="การอนุญาตนี้จำเป็นสำหรับการบันทึกเนื้อหาจากเว็บแคมหรือการ์ดจับภาพ" MacPermissions.Item.Microphone="ไมโครโฟน" +MacPermissions.Item.Microphone.Details="OBS ต้องการสิทธิ์นี้หากคุณต้องการบันทึกเสียงไมโครโฟนหรืออุปกรณ์เสียงภายนอกของคุณ" MacPermissions.Item.Accessibility="การช่วยการเข้าถึง" MacPermissions.Item.Accessibility.Details="เพื่อให้แป้นพิมพ์ลัด (ปุ่มลัด) ทำงานขณะที่แอปอื่นโฟกัสอยู่ โปรดเปิดใช้การอนุญาตนี้" MacPermissions.Continue="ดำเนินการต่อ" @@ -525,6 +526,7 @@ Basic.TransformWindow.BoundsAlignment="การจัดแนวในกล Basic.TransformWindow.Bounds="ขนาดกล่องขอบเขต" Basic.TransformWindow.BoundsWidth="ความกว้างกล่องขอบเขต" Basic.TransformWindow.BoundsHeight="ความสูงกล่องขอบเขต" +Basic.TransformWindow.CropToBounds="ตัดภาพตามกรอบ" Basic.TransformWindow.Crop="ครอบตัด" Basic.TransformWindow.CropLeft="ครอบตัดซ้าย" Basic.TransformWindow.CropRight="ครอบตัดขวา" @@ -673,7 +675,6 @@ Basic.Settings.ProgramRestart="ต้องรีสตาร์ทโปรแ Basic.Settings.ConfirmTitle="ยืนยันการเปลี่ยนแปลง" Basic.Settings.Confirm="คุณมีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก บันทึกการเปลี่ยนแปลง?" Basic.Settings.General="ทั่วไป" -Basic.Settings.General.Theme="ธีม" Basic.Settings.General.Language="ภาษา" Basic.Settings.General.Updater="อัปเดต" Basic.Settings.General.UpdateChannel="ช่องทางอัปเดต" @@ -733,6 +734,11 @@ Basic.Settings.General.ChannelName.stable="เสถียร" Basic.Settings.General.ChannelDescription.stable="รุ่นเสถียรล่าสุด" Basic.Settings.General.ChannelName.beta="รุ่นเบต้า / Release Candidate" Basic.Settings.General.ChannelDescription.beta="รุ่นก่อนปล่อยที่อาจไม่เสถียร" +Basic.Settings.Appearance="รูปร่าง" +Basic.Settings.Appearance.General="ทั่วไป" +Basic.Settings.Appearance.General.Theme="ธีม" +Basic.Settings.Appearance.General.Variant="สไตล์" +Basic.Settings.Appearance.General.NoVariant="ไม่มีสไตล์ที่พร้อมใช้งาน" Basic.Settings.Stream="สตรีม" Basic.Settings.Stream.Custom.UseAuthentication="ใช้การยืนยันตัวตน" Basic.Settings.Stream.Custom.Username="ชื่อผู้ใช้" @@ -771,6 +777,7 @@ Basic.Settings.Output.Mode.Adv="ขั้นสูง" Basic.Settings.Output.Mode.FFmpeg="FFmpeg เอาต์พุต" Basic.Settings.Output.UseReplayBuffer="เปิดใช้บัฟเฟอร์เล่นซ้ำ" Basic.Settings.Output.ReplayBuffer.SecondsMax="เวลาเล่นซ้ำสูงสุด" +Basic.Settings.Output.ReplayBuffer.MegabytesMax="หน่วยความจำสูงสุด" Basic.Settings.Output.ReplayBuffer.Estimate="การใช้หน่วยความจำโดยประมาณ: %1 MB" Basic.Settings.Output.ReplayBuffer.EstimateTooLarge="คำเตือน: การใช้หน่วยความจำโดยประมาณที่ %1 MiB มากกว่าขีดสูงสุดที่แนะนำซึ่งอยู่ที่ %2 MiB" Basic.Settings.Output.ReplayBuffer.EstimateUnknown="ไม่สามารถคำนวณการใช้หน่วยความจำโดยประมาณได้ โปรดกำหนดขีดจำกัดหน่วยความจำสูงสุด" @@ -788,6 +795,7 @@ Basic.Settings.Output.Simple.Warn.AudioBitrate="คำเตือน: อัต Basic.Settings.Output.Simple.Warn.CannotPause="คำเตือน: ไม่สามารถหยุดการบันทึกต่างๆ ชั่วคราวได้หากคุณภาพการบันทึกถูกตั้งเป็น \"เหมือนกับสตรีม\"" Basic.Settings.Output.Simple.Warn.IncompatibleContainer="คำเตือน: รูปแบบการบันทึกที่เลือกปัจจุบันเข้ากันไม่ได้กับตัวเข้ารหัสสตรีมที่เลือก" Basic.Settings.Output.Simple.Warn.Encoder="คำเตือน: การบันทึกด้วยตัวเข้ารหัสแบบซอฟต์แวร์ที่คุณภาพต่างจากสตรีมจะต้องใช้ CPU เพิ่มเติมหากคุณทำการสตรีมและบันทึกไปพร้อมกัน" +Basic.Settings.Output.Simple.Warn.Lossless="คำเตือน: คุณภาพแบบ Lossless สร้างไฟล์ขนาดใหญ่โตมโหฬาร! คุณภาพแบบ Lossless ใช้พื้นที่ดิสก์สูงถึง 7 กิกะไบต์ต่อนาทีที่ความละเอียดและเฟรมเรทสูง การใช้ Lossless ไม่แนะนำสำหรับการบันทึกยาว ๆ เว้นแต่คุณจะมีพื้นที่ดิสก์เหลือเฟือ บัฟเฟอร์สำหรับการย้อนกลับ (Replay buffer) จะไม่สามารถใช้งานได้เมื่อใช้คุณภาพแบบ Lossless" Basic.Settings.Output.Simple.Warn.Lossless.Msg="คุณแน่ใจหรือไม่ว่าต้องการใช้คุณภาพแบบไม่สูญเสีย?" Basic.Settings.Output.Simple.Warn.Lossless.Title="คำเตือนคุณภาพแบบไม่สูญเสีย!" Basic.Settings.Output.Simple.Encoder.Software="ซอฟต์แวร์ (x264)" @@ -825,6 +833,7 @@ Basic.Settings.Output.CustomEncoderSettings="การตั้งค่าต Basic.Settings.Output.CustomMuxerSettings="การตั้งค่า Muxer แบบกำหนดเอง" Basic.Settings.Output.NoSpaceFileName="สร้างชื่อไฟล์โดยไม่ต้องเว้นวรรค" Basic.Settings.Output.Adv.Rescale="รีสเกลเอาต์พุต" +Basic.Settings.Output.Adv.Rescale.Disabled="ปิดใช้งาน" Basic.Settings.Output.Adv.AudioTrack="แทร็กเสียง" Basic.Settings.Output.Adv.Streaming="การสตรีม" Basic.Settings.Output.Adv.Streaming.Settings="ตั้งค่าการสตรีม" diff --git a/UI/data/locale/tl-PH.ini b/UI/data/locale/tl-PH.ini index bd6f883b4bc047..d05696efd12798 100644 --- a/UI/data/locale/tl-PH.ini +++ b/UI/data/locale/tl-PH.ini @@ -390,7 +390,6 @@ Basic.Settings.ProgramRestart="Ang programa ay kailangan i-start muli para guman Basic.Settings.ConfirmTitle="Kumpirmahin ang mga Pagbabago" Basic.Settings.Confirm="Mayroon kang mga binago na hindi pa na-save. I-save ang mga pagbabago?" Basic.Settings.General="Pangkalahatan" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Lenggwahe" Basic.Settings.General.EnableAutoUpdates="Awtomatikong maghanap ng mga update sa pag-start up" Basic.Settings.General.OpenStatsOnStartup="Buksan ang dialogong panstatistiko sa pag-startup" diff --git a/UI/data/locale/tr-TR.ini b/UI/data/locale/tr-TR.ini index fc00b418656d14..871ee9463627fa 100644 --- a/UI/data/locale/tr-TR.ini +++ b/UI/data/locale/tr-TR.ini @@ -28,7 +28,7 @@ StudioProgramProjector="Tam Ekran Yansıtması (Program)" PreviewProjector="Tam Ekran Yansıtması (Önizleme)" SceneProjector="Tam Ekran Yansıtması (Sahne)" SourceProjector="Tam Ekran Yansıtması (Kaynak)" -StudioProgramWindow="Pencereli Projektör (Program)" +StudioProgramWindow="Pencereli Yansıtma (Program)" PreviewWindow="Pencereli Projektör (Önizleme)" SceneWindow="Pencereli Projektör (Sahne)" SourceWindow="Pencereli Projektör (Kaynak)" @@ -108,20 +108,20 @@ Grid="Izgara" PluginsFailedToLoad.Title="Eklenti Yükleme Hatası" PluginsFailedToLoad.Text="Aşağıdaki OBS eklentileri yüklenemedi:\n\n%1\nLütfen bu eklentileri güncelleyin veya silin." AlreadyRunning.Title="OBS zaten çalışıyor" -AlreadyRunning.Text="OBS zaten çalışıyor! Bunu yapmak istemediyseniz, lütfen yeni bir örneği çalıştırmayı denemeden önce varolan tüm OBS örneklerini kapatın. OBS'yi sistem tablasına küçülmesi için ayarladıysanız, lütfen hala çalışıp çalışmadığını görmek için orayı kontrol edin." +AlreadyRunning.Text="OBS zaten çalışıyor! Eğer bunu yapmak istemediyseniz, lütfen yeni bir objeyi çalıştırmayı denemeden önce varolan tüm OBS objelerini kapatın. OBS'yi sistem tepsisine küçülmesi için ayarladıysanız, lütfen hala çalışıp çalışmadığını görmek için orayı kontrol edin." AlreadyRunning.LaunchAnyway="Yine de Başlat" AutoSafeMode.Title="Güvenli Mod" -AutoSafeMode.Text="Son oturumunuzda OBS kendini düzgün kapatamadı.\n\nOBS'i güvenli modda başlatmak ister misin? (3. parti eklentiler, betikler ve WebSocketler kapalı şekilde)" +AutoSafeMode.Text="Son oturumunuzda OBS kendini düzgün kapatamadı.\n\nOBS'i güvenli modda başlatmak ister misin? (3. parti eklentiler, betikler ve WebSocketler kapatılacak)" AutoSafeMode.LaunchSafe="Güvenli Mod'da başlat" AutoSafeMode.LaunchNormal="Normal Çalıştır" -SafeMode.Restart="OBS'i Güvenli Mod'da yeniden başlatmak ister misin? (3. parti eklentiler, betikler ve WebSocketler kapalı şekilde)" +SafeMode.Restart="OBS'i Güvenli Mod'da yeniden başlatmak ister misin? (3. parti eklentiler, betikler ve WebSocketler kapatılacak)" SafeMode.RestartNormal="OBS'i Normal Mod'da yeniden başlatmak ister misin?" ChromeOS.Title="Desteklenmeyen Platform" ChromeOS.Text="OBS bir ChromeOS kapsayıcısının içinde çalışıyor gibi görünüyor. Bu platform desteklenmiyor." Wine.Title="Wine algılandı" Wine.Text="OBS, Wine'da çalıştırılmayı desteklemiyor ve yakalama veya aygıt kaynakları gibi birçok özellik çalışmayacak veya yalnızca sınırlı kapasitede çalışacak.

Bunun yerine OBS'nin yerel bir sürümünü çalıştırmanız önerilir, örneğin Flatpak sürümümüz veya işletim sisteminizin paketleri." -DockCloseWarning.Title="Yuvalanabilir Pencere Kapatma" -DockCloseWarning.Text="Az önce yuvalanabilir bir pencere kapattınız. Tekrar göstermek istiyorsanız, menü çubuğundan Paneller menüsünü kullanın." +DockCloseWarning.Title="Yerleştirilebilir Pencere Kapatma" +DockCloseWarning.Text="Az önce yerleştirilebilir bir pencere kapattınız. Tekrar göstermek istiyorsanız, menü çubuğundan Paneller menüsünü kullanın." ExtraBrowsers="Özel Tarayıcı Yuvaları" ExtraBrowsers.Info="Bir isim veya bağlantı adresi belirterek yuva ekleyin, ardından yuvaları açmak için Uygula veya Kapat'a tıklayın. İstediğiniz zaman yuva ekleyebilir ya da kaldırabilirsiniz." ExtraBrowsers.DockName="Yuva Adı" @@ -594,16 +594,16 @@ Basic.MainMenu.File.Export="Dışa Aktar (&E)" Basic.MainMenu.File.Import="İçe Aktar (&I)" Basic.MainMenu.File.ShowRecordings="Kayıtla&rı Göster" Basic.MainMenu.File.Remux="Kayıtları Re&mux Et" -Basic.MainMenu.File.Settings="Ayarlar (&S)" +Basic.MainMenu.File.Settings="&Ayarlar" Basic.MainMenu.File.ShowSettingsFolder="Ayarlar Dosyasını Göster" Basic.MainMenu.File.ShowProfileFolder="Profil Dosyasını Göster" Basic.MainMenu.File.ShowMissingFiles="Kayıp dosyaları kontrol et" -Basic.MainMenu.File.Exit="Çıkış (&X)" +Basic.MainMenu.File.Exit="Çı&kış" Basic.MainMenu.Edit="Düz&enle" Basic.MainMenu.Edit.Undo="&Geri al" Basic.MainMenu.Edit.Redo="Tek&rar Yap" Basic.MainMenu.Edit.LockPreview="Öniz&lemeyi Kilitle" -Basic.MainMenu.Edit.Scale="Boyutlandırmayı Önizle (&S)" +Basic.MainMenu.Edit.Scale="Boyutlandırmayı Ö&nizle" Basic.MainMenu.Edit.Scale.Window="Pencereye Boyutlandır" Basic.MainMenu.Edit.Scale.Canvas="Tuval (%1x%2)" Basic.MainMenu.Edit.Scale.Output="Çıktı (%1x%2)" @@ -615,25 +615,25 @@ Basic.MainMenu.Edit.Transform.ResetTransform="Dönüştü&rmeyi Sıfırla" Basic.MainMenu.Edit.Transform.Rotate90CW="90 derece saat yönüne döndür" Basic.MainMenu.Edit.Transform.Rotate90CCW="90 derece saatin tersi yönüne döndür" Basic.MainMenu.Edit.Transform.Rotate180="180 derece döndür" -Basic.MainMenu.Edit.Transform.FlipHorizontal="Yatay Döndür (&H)" +Basic.MainMenu.Edit.Transform.FlipHorizontal="&Yatay Döndür" Basic.MainMenu.Edit.Transform.FlipVertical="Dikey Çe&vir" -Basic.MainMenu.Edit.Transform.FitToScreen="Ekrana Sığdır (&F)" -Basic.MainMenu.Edit.Transform.StretchToScreen="Ekrana genişlet (&S)" -Basic.MainMenu.Edit.Transform.CenterToScreen="Ekrana Ortala (&C)" +Basic.MainMenu.Edit.Transform.FitToScreen="Ekrana &Sığdır" +Basic.MainMenu.Edit.Transform.StretchToScreen="Ekrana &genişlet" +Basic.MainMenu.Edit.Transform.CenterToScreen="Ekrana &Ortala" Basic.MainMenu.Edit.Transform.VerticalCenter="Dikeye Hizala" Basic.MainMenu.Edit.Transform.HorizontalCenter="Yataya Hizala" -Basic.MainMenu.Edit.Order="Sırala (&O)" +Basic.MainMenu.Edit.Order="&Sırala" Basic.MainMenu.Edit.Order.MoveUp="Y&ukarı Taşı" -Basic.MainMenu.Edit.Order.MoveDown="Aşağı Taşı (&D)" +Basic.MainMenu.Edit.Order.MoveDown="&Aşağı Taşı" Basic.MainMenu.Edit.Order.MoveToTop="En Üs&te Taşı" -Basic.MainMenu.Edit.Order.MoveToBottom="En Alta Taşı (&B)" -Basic.MainMenu.Edit.AdvAudio="Gelişmiş Ses Özellikleri (&A)" -Basic.MainMenu.View="Görünüm (&V)" +Basic.MainMenu.Edit.Order.MoveToBottom="&En Alta Taşı" +Basic.MainMenu.Edit.AdvAudio="&Gelişmiş Ses Özellikleri" +Basic.MainMenu.View="&Görünüm" Basic.MainMenu.View.Toolbars="Araç Çubukları (&T)" Basic.MainMenu.View.ListboxToolbars="Yuva Araç Çubukları" Basic.MainMenu.View.ContextBar="Kaynak Menüsü" Basic.MainMenu.View.SourceIcons="Kaynak S&imgeleri" -Basic.MainMenu.View.StatusBar="Durum Çubuğu (&S)" +Basic.MainMenu.View.StatusBar="&Durum Çubuğu" Basic.MainMenu.View.Fullscreen.Interface="Tam Ekran Arayüz" Basic.MainMenu.View.ResetUI="&Arayüzü Sıfırla" Basic.MainMenu.View.AlwaysOnTop="&Her Zaman Üstte" @@ -650,17 +650,17 @@ Basic.MainMenu.Profile.Export="Profili Dışa Aktar" Basic.MainMenu.SceneCollection.Import="Sahne Koleksiyonunu İçe Aktar" Basic.MainMenu.SceneCollection.Export="Sahne Koleksiyonunu Dışa Aktar" Basic.MainMenu.Profile.Exists="Profil zaten var" -Basic.MainMenu.Tools="Araçlar (&T)" -Basic.MainMenu.Help="Yardım (&H)" +Basic.MainMenu.Tools="&Araçlar" +Basic.MainMenu.Help="&Yardım" Basic.MainMenu.Help.HelpPortal="Yardım &Portalı" -Basic.MainMenu.Help.Website="Siteyi Ziyaret Et (&W)" +Basic.MainMenu.Help.Website="&Siteyi Ziyaret Et" Basic.MainMenu.Help.Discord="&Discord Sunucusuna Katıl" Basic.MainMenu.Help.WhatsNew="Yenilikler" Basic.MainMenu.Help.Logs="Gün&lük Dosyaları" Basic.MainMenu.Help.Logs.ShowLogs="Günlük Do&syalarını Göster" Basic.MainMenu.Help.Logs.UploadCurrentLog="Mev&cut Günlük Dosyasını Karşıya Yükle" Basic.MainMenu.Help.Logs.UploadLastLog="&Son Kayıt Dosyasını Yükle" -Basic.MainMenu.Help.Logs.ViewCurrentLog="Şimdiki Günlüğü Göster (&V)" +Basic.MainMenu.Help.Logs.ViewCurrentLog="Şimdiki &Günlüğü Göster" Basic.MainMenu.Help.ReleaseNotes="Sürüm Notları" Basic.MainMenu.Help.CheckForUpdates="Güncellemeleri Denetle" Basic.MainMenu.Help.Repair="Dosya Bütünlüğünü Denetle" @@ -674,7 +674,6 @@ Basic.Settings.ProgramRestart="Programın, bu ayarların etkinleşmesi için yen Basic.Settings.ConfirmTitle="Değişiklikleri Onayla" Basic.Settings.Confirm="Kayıt edilmemiş değişiklikleriniz var. Değişiklikler kayıt edilsin mi?" Basic.Settings.General="Genel" -Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Dil" Basic.Settings.General.Updater="Güncellemeler" Basic.Settings.General.UpdateChannel="Güncelleme kanalı" @@ -734,6 +733,11 @@ Basic.Settings.General.ChannelName.stable="Stabil" Basic.Settings.General.ChannelDescription.stable="En yeni stabil sürüm" Basic.Settings.General.ChannelName.beta="Betalar / Sürüm Adayı" Basic.Settings.General.ChannelDescription.beta="Potansiyel olarak stabil olmayan yayım-öncesi versiyonlar" +Basic.Settings.Appearance="Görünüm" +Basic.Settings.Appearance.General="Genel" +Basic.Settings.Appearance.General.Theme="Tema" +Basic.Settings.Appearance.General.Variant="Stil" +Basic.Settings.Appearance.General.NoVariant="Hiç stil mevcut değil." Basic.Settings.Stream="Yayın" Basic.Settings.Stream.Custom.UseAuthentication="Kimlik doğrulaması kullan" Basic.Settings.Stream.Custom.Username="Kullanıcı adı" @@ -955,7 +959,7 @@ Basic.Settings.Audio.EnablePushToMute="Bas sustur'u etkinleştir" Basic.Settings.Audio.PushToMuteDelay="Bas sustur gecikmesi" Basic.Settings.Audio.EnablePushToTalk="Bas-Konuş'u Etkinleştir" Basic.Settings.Audio.PushToTalkDelay="Bas-Konuş gecikmesi" -Basic.Settings.Audio.UnknownAudioDevice="[Cihaz bağlı yada kullanılabilir değil]" +Basic.Settings.Audio.UnknownAudioDevice="[Cihaz bağlı ya da kullanılabilir değil]" Basic.Settings.Audio.Disabled="Devre Dışı" Basic.Settings.Audio.LowLatencyBufferingMode="Düşük Gecikmeli Ses Arabelleğe Alma Modu (Decklink/NDI çıkışları için)" Basic.Settings.Audio.LowLatencyBufferingWarning.Enabled="UYARI: Düşük gecikmeli ses arabelleğe alma etkinleştirildi." @@ -1096,7 +1100,7 @@ FinalScene.Text="En az bir sahne olması gerekiyor." NoSources.Title="Kaynak Yok" NoSources.Text="Henüz hiç video kaynağı eklemediniz gibi görünüyor, bu yüzden sadece boş bir ekran çıktısı alacaksınız. Bunu yapmak istediğinize emin misiniz?" NoSources.Text.AddSource="Ana pencerede Kaynaklar kutusundaki + simgesine tıklayarak istediğiniz zaman kaynak ekleyebilirsiniz." -NoSources.Label="Herhangi bir kaynağınız yok.\nBir kaynak eklemek için aşağıdaki tuşlara basın yada sağ tıklayın." +NoSources.Label="Herhangi bir kaynağınız yok.\nBir kaynak eklemek için aşağıdaki tuşlara basın ya da sağ tıklayın." ChangeBG="Renk Ayarla" CustomColor="Özel Renk" BrowserSource.EnableHardwareAcceleration="Tarayıcı Kaynak Donanım Hızlandırmasını Etkinleştir" diff --git a/UI/data/locale/tt-RU.ini b/UI/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..f9ca84df58af2f --- /dev/null +++ b/UI/data/locale/tt-RU.ini @@ -0,0 +1,343 @@ +Language="Татарча" +OK="Ярый" +Apply="Куллану" +Cancel="Баш тарту" +Close="Ябу" +Save="Саклау" +Discard="Кире кагу" +Disable="Сүндерү" +Yes="Әйе" +No="Юк" +Add="Өстәү" +Remove="Бетерү" +Rename="Яңадан исемләү" +Properties="Үзләкләр" +Settings="Көйләүләр" +Name="Исем" +Exit="Чыгу" +Browse="Күзәтү" +Mono="Моно" +Stereo="Стерео" +Clear="Чистарту" +Revert="Кайтару" +Show="Күрсәтү" +Hide="Яшерү" +Untitled="Исемсез" +New="Яңа" +Enable="Кушу" +Left="Сул" +Right="Уң" +Reset="Ташлату" +Hours="сәгатьтән" +Minutes="минуттан" +Seconds="секундтан" +Deprecated="Искергән" +Import="Импортлау" +Export="Экспортлау" +Copy="Күчермә алу" +Next="Алга" +Back="Артка" +None="Юк" +Group="Төркем" +DoNotShowAgain="Бүтән күрсәтмәскә" +Calculating="Исәпләнү..." +Fullscreen="Тулы экран шарты" +RefreshBrowser="Яңарту" +LockVolume="Катылыкны бикләү" +LogViewer="Журналны карау" +OpenFile="Файлны ачу" +AddSource="Чыганакны өстәү" +RemoveScene="Сайланган сәхнәне бетерү" +RemoveSource="Сайланган чыганак(лар)ны бетерү" +List="Исемлек" +Grid="Ятьмә" +Automatic="Автоматик" +AlreadyRunning.Title="OBS кабызылды инде" +AlreadyRunning.LaunchAnyway="Барыбер кабызу" +AutoSafeMode.Title="Сак шарты" +AutoSafeMode.LaunchSafe="Сак шартында кабызу" +AutoSafeMode.LaunchNormal="Гади кабызу" +ChromeOS.Title="Хупланмаган платформа" +Wine.Title="Wine табылды" +Auth.LoadingChannel.Title="Канал турында мәгълүматны йөкләү..." +Auth.LoadingChannel.Text="%1 өчен канал турында мәгълүматны йөкләү, көтегезче..." +Auth.LoadingChannel.Error="Канал турында мәгълүматны алып булмады." +Auth.ChannelFailure.Title="Каналны йөкләп булмады" +Auth.Chat="Чат" +Auth.StreamInfo="Агым турында мәгълүмат" +RestreamAuth.Channels="Restream каналлары" +BrowserPanelInit.Title="Браузер әзерләнү..." +BrowserPanelInit.Text="Браузер әзерләнү, көтегезче..." +BandwidthTest.Region="Регион" +BandwidthTest.Region.US="АКШ" +BandwidthTest.Region.EU="Аурупа" +BandwidthTest.Region.Asia="Азия" +BandwidthTest.Region.Other="Башка" +Basic.AutoConfig.ApplySettings="Көйләүләрне куллану" +Basic.AutoConfig.StartPage="Кулланылу турында мәгълүмат" +Basic.AutoConfig.StartPage.PrioritizeVirtualCam="Мин виртуаль камераны гына кулланачакмын" +Basic.AutoConfig.VideoPage="Видео көйләүләре" +Basic.AutoConfig.StreamPage="Агым турында мәгълүмат" +Basic.AutoConfig.StreamPage.DisconnectAccount="Аккаунттан чыгу" +Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title="Аккаунттан чыгарга?" +Basic.AutoConfig.StreamPage.GetStreamKey="Агым ачкычын алу" +Basic.AutoConfig.StreamPage.MoreInfo="Күбрәк мәгълүмат" +Basic.AutoConfig.StreamPage.UseStreamKey="Агым ачкычын куллану" +Basic.AutoConfig.StreamPage.Service="Хезмәт" +Basic.AutoConfig.StreamPage.Service.ShowAll="Барлыкны күрсәтү..." +Basic.AutoConfig.StreamPage.Service.Custom="Көйләнелүчән..." +Basic.AutoConfig.StreamPage.Server="Сервер" +Basic.AutoConfig.StreamPage.StreamKey="Агым ачкычы" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="%1 тикшерү" +Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="%1 урынына тоташылу..." +Basic.Stats="Статистика" +Basic.Stats.MemoryUsage="Хәтерне куллану" +Basic.Stats.Output.Stream="Агым" +Basic.Stats.Output.Recording="Яздыру" +Basic.Stats.Status="Халәт" +Basic.Stats.Status.Recording="Яздырылу" +Basic.Stats.Status.Live="АГЫМДА" +Basic.Stats.Status.Reconnecting="Яңадан тоташылу" +Basic.Stats.Status.Inactive="Активсыз" +Basic.Stats.Status.Active="Актив" +Basic.Stats.Bitrate="Битрейт" +Updater.Title="Яңа яңарту бар" +Updater.Text="Яңа яңарту бар:" +Updater.UpdateNow="Хәзер яңарту" +Updater.Skip="Версияне калдыру" +Updater.NoUpdatesAvailable.Title="Яңарту юк" +Updater.FailedToLaunch="Яңарткычны кабызып булмады" +Basic.TransitionDuration="Озынлык" +Undo.Undo="Кире кагу" +Undo.Redo="Кабатлау" +Undo.Add="«%1» өстәүне" +Undo.Delete="«%1» бетерүне" +Undo.Rename="«%1» яңадан исемләүне" +Undo.SceneCollection.Switch="«%1» күчерүне" +Undo.Item.Undo="%1 баш тарту" +Undo.Sources.Multi="%1 чыганакны бетерү" +TitleBar.SafeMode="САК ШАРТЫ" +TitleBar.Profile="Профиль" +TitleBar.Scenes="Сәхнәләр" +NameExists.Title="Иесм бар инде" +NameExists.Text="Исем кулланыла инде." +ConfirmStart.Title="Агымны башларгамы?" +ConfirmStop.Title="Агымны туктатыргамы?" +ConfirmStopRecord.Title="Яздыруны туктатыргамы?" +ConfirmReset.Title="Үзләкләрне ташлату" +Output.StartRecordingFailed="Яздыруны башлап булмады" +Output.StartVirtualCamFailed="Виртуаль камераны башлап булмады" +Output.ConnectFail.Title="Тоташылып булмады" +Output.ConnectFail.ConnectFailed="Серверга тоташылып булмады" +Output.ConnectFail.Disconnected="Сервердан өзелде." +Output.RecordFail.Title="Яздыруны башлап булмады" +Output.RecordNoSpace.Title="Дисктагы урын җитми" +Output.RecordError.Title="Яздыру хатасы" +Output.BadPath.Title="Хаталы файл юлы" +Remux.SourceFile="OBS яздыруы" +Remux.TargetFile="Чыгыш файл" +Remux.OBSRecording="OBS яздыруы" +Remux.SelectRecording="OBS яздыруын сайлагыз..." +Remux.SelectTarget="Чыгыш файл сайлагыз..." +Remux.FileExistsTitle="Чыгыш файллар бар" +MissingFiles.NewFile="Яңа файл" +MissingFiles.Clear="<чистартылган>" +MissingFiles.NumFound="%2 нәтиҗәдән %1 табылды" +MissingFiles.SelectFile="Файлны сайлау..." +MissingFiles.State="Халәт" +MissingFiles.Found="Табылды" +MacPermissions.OpenPreferences="%1 көйләүләрен ачу" +MacPermissions.Item.ScreenRecording="Экранны яздыру" +MacPermissions.Item.Camera="Камера" +MacPermissions.Item.Microphone="Микрофон" +MacPermissions.Item.Accessibility="Махсус мөмкинлекләр" +MacPermissions.Continue="Дәвам итү" +Basic.Scene="Сәхнә" +VolControl.Properties="«%1» үзлекләре" +Basic.Main.AddSceneDlg.Title="Сәхнәне өстәргә" +Basic.Main.DefaultSceneName.Text="Сәхнә %1" +Basic.Main.AddSceneCollection.Title="Сәхнә коллекциясен өстәү" +Basic.Main.RenameSceneCollection.Title="Сәхнә коллекциясен яңадан исемләү" +AddProfile.Title="Профильне өстәү" +RenameProfile.Title="Профильне яңадан исемләү" +Basic.SourceSelect="Чыганакны ясау/сайлау" +Basic.SourceSelect.CreateNew="Яңаны ясау" +Basic.Main.Sources.Lock="Бикләү" +Basic.PropertiesWindow="«%1» үзлекләре" +Basic.PropertiesWindow.SelectColor="Төсне сайлау" +Basic.PropertiesWindow.SelectFont="Хәрефләрне сайлау" +Basic.PropertiesWindow.SelectFont.WindowTitle="Хәрефне сайлау" +Basic.PropertiesWindow.ConfirmTitle="Көйләүләр үзгәртелде" +Basic.PropertiesWindow.NoProperties="Үзлекләр юк" +Basic.PropertiesWindow.AddFiles="Файлларны өстәү" +Basic.TransformWindow.Size="Зурлык" +Basic.TransformWindow.Width="Киңлек" +Basic.TransformWindow.Height="Биеклек" +Basic.TransformWindow.NoSelectedSource="Чыганак сайланмады" +Basic.Main.AddSourceHelp.Title="Чыганакны өстәп булмый" +Basic.Main.Scenes="Сәхнәләр" +Basic.Main.Sources="Чыганаклар" +Basic.Main.Source="Чыганак" +Basic.Main.Controls="Идарә" +Basic.Main.PreparingStream="Әзерләү..." +Basic.Main.Connecting="Тоташылу..." +Basic.Main.StartRecording="Яздыруны башлау" +Basic.Main.StartStreaming="Агымны башлау" +Basic.Main.StartBroadcast="Агымда тапшыру" +Basic.Main.StartVirtualCam="Виртуаль камераны башлау" +Basic.Main.StopRecording="Яздыруны туктату" +Basic.Main.PauseRecording="Яздыруны туктатып тору" +Basic.Main.UnpauseRecording="Яздыруны дәвам итү" +Basic.Main.StoppingRecording="Яздыру туктатып торылу..." +Basic.Main.StopStreaming="Агымны туктату" +Basic.Main.StopBroadcast="Агымда тапшыруны тәмамлау" +Basic.Main.StoppingStreaming="Агым туктатып торылу..." +Basic.Main.Group="Төркем %1" +Basic.Main.GridMode="Ятьмә шарты" +Basic.Main.ListMode="Исемлек шарты" +Basic.VCam.VirtualCamera="Виртуаль камера" +Basic.VCam.OutputType="Чыгыш төре" +Basic.MainMenu.File="Файл (&F)" +Basic.MainMenu.File.Export="Экспортлау (&E)" +Basic.MainMenu.File.Import="Импортлау (&I)" +Basic.MainMenu.File.ShowRecordings="Яздыруларны күрсәтү (&R)" +Basic.MainMenu.File.Settings="Көйләүләр (&S)" +Basic.MainMenu.File.Exit="Чыгу (&X)" +Basic.MainMenu.Edit="Үзгәртү (&E)" +Basic.MainMenu.Edit.Order="Тәртип (&O)" +Basic.MainMenu.Profile="Профиль (&P)" +Basic.MainMenu.Profile.Import="Профильне импортлау" +Basic.MainMenu.Profile.Export="Профильне эскпортлау" +Basic.MainMenu.SceneCollection.Import="Сәхнә коллекциясен импортлау" +Basic.MainMenu.SceneCollection.Export="Сәхнә коллекциясен экспортлау" +Basic.MainMenu.Profile.Exists="Профиль бар инде" +Basic.MainMenu.Tools="Кораллар (&T)" +Basic.MainMenu.Help="Ярдәм (&H)" +Basic.Settings.General="Төп" +Basic.Settings.General.Language="Тел" +Basic.Settings.General.Updater="Яңартулар" +Basic.Settings.General.UpdateChannelDisabled="(Сүнек)" +Basic.Settings.General.SysTray="Хәбәрләр өлкәсе" +Basic.Settings.General.ChannelName.stable="Тотраклы версия" +Basic.Settings.General.ChannelDescription.stable="Соңгы тотраклы версия" +Basic.Settings.General.ChannelName.beta="Ярдәмче версияләр" +Basic.Settings.Appearance="Тышкы күренеш" +Basic.Settings.Appearance.General="Төп" +Basic.Settings.Appearance.General.Theme="Тема" +Basic.Settings.Appearance.General.Variant="Стиль" +Basic.Settings.Appearance.General.NoVariant="Стиль юк" +Basic.Settings.Stream="Агым" +Basic.Settings.Stream.Custom.Username="Кулланучы исеме" +Basic.Settings.Stream.Custom.Password="Серсүз" +Basic.Settings.Stream.TTVAddon="Twitch чаты өстәмәләре" +Basic.Settings.Stream.TTVAddon.None="Юк" +Basic.Settings.Stream.StreamSettingsWarning="Көйләүләрне ачу" +Basic.Settings.Stream.EnableMultitrackVideo="%1 кушу" +Basic.Settings.Output="Чыгыш" +Basic.Settings.Output.Format="Яздыру форматы" +Basic.Settings.Output.Mode="Чыгыш шарты" +Basic.Settings.Output.Mode.Simple="Гади" +Basic.Settings.Output.Mode.Adv="Киңәйтелгән" +Basic.Settings.Output.Mode.FFmpeg="FFmpeg чыгышы" +Basic.Settings.Output.ReplayBuffer.Suffix="Суффикс" +Basic.Settings.Output.Simple.SavePath="Яздыру юлы" +Basic.Settings.Output.Simple.RecordingQuality="Яздыру сыйфаты" +Basic.Settings.Output.Warn.EnforceResolutionFPS.Resolution="Ачыклык: %1" +Basic.Settings.Output.Warn.EnforceResolutionFPS.FPS="Кадр ешлыгы: %1" +Basic.Settings.Output.VideoBitrate="Видео битрейты" +Basic.Settings.Output.AudioBitrate="Аудио битрейты" +Basic.Settings.Output.Adv.Rescale.Disabled="Сүнек" +Basic.Settings.Output.Adv.Streaming="Агым" +Basic.Settings.Output.Adv.Streaming.Settings="Агым көйләүләре" +Basic.Settings.Output.Adv.Recording="Яздыру" +Basic.Settings.Output.Adv.Recording.Settings="Яздыру көйләүләре" +Basic.Settings.Output.Adv.Recording.RecType="Яздыру төре" +Basic.Settings.Output.Adv.Recording.Type="Төр" +Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg чыгыш төре" +Basic.Settings.Output.Adv.FFmpeg.SaveFilter.All="Барлык файллар" +Basic.Settings.Output.Adv.FFmpeg.SavePathURL="Файл юлы яки сылтама" +Basic.Settings.Output.Adv.FFmpeg.FormatAudio="Аудио" +Basic.Settings.Output.Adv.FFmpeg.FormatVideo="Видео" +Basic.Settings.Output.Adv.FFmpeg.Settings="FFmpeg көйләүләре" +FilenameFormatting.TT.CCYY="Ел, дүрт сан" +FilenameFormatting.TT.mm="Минут (00-59)" +FilenameFormatting.TT.ss="Секунд (00-59)" +FilenameFormatting.TT.Percent="% символы" +FilenameFormatting.TT.B="Айның туры исеме" +FilenameFormatting.TT.M="Минут (00-59)" +FilenameFormatting.TT.S="Секунд (00-59)" +FilenameFormatting.TT.Y="Ел" +FilenameFormatting.TT.FPS="Кадр ешлыгы" +FilenameFormatting.TT.VF="Видео форматы" +Basic.Settings.Video="Видео" +Basic.Settings.Video.FPS="Кадр ешлыгы" +Basic.Settings.Video.Renderer="Күрсәткеч" +Basic.Settings.Audio="Аудио" +Basic.Settings.Audio.Channels="Каналлар" +Basic.Settings.Audio.MeterDecayRate.Fast="Тиз" +Basic.Settings.Audio.Devices="Төп аудио җайланмалары" +Basic.Settings.Audio.UnknownAudioDevice="[Җайланма тоташылмады яки ирешерлек түгел]" +Basic.Settings.Audio.Disabled="Сүнек" +Basic.Settings.Accessibility="Махсус мөмкинлекләр" +Basic.Settings.Advanced="Киңәйтелгән" +Basic.Settings.Advanced.General.ProcessPriority.High="Югары" +Basic.Settings.Advanced.Video.ColorFormat="Төс форматы" +Basic.Settings.Advanced.Video.ColorRange="Төс арасы" +Basic.Settings.Advanced.Video.ColorRange.Full="Тулы" +Basic.Settings.Advanced.StreamDelay.Duration="Озынлык" +Basic.Settings.Advanced.Network="Челтәр" +Basic.Settings.Advanced.AutoRemux.MP4="(mkv дип яздыру)" +Basic.AdvAudio="Киңәйтелгән аудио үзлекләре" +Basic.AdvAudio.ActiveOnly="Актив чыганаклар гына" +Basic.AdvAudio.Name="Исем" +Basic.AdvAudio.Volume="Катылык" +Basic.AdvAudio.VolumeSource="«%1» өчен катылык" +Basic.AdvAudio.Mono="Моно" +Basic.SystemTray.Show="Күрсәтү" +Basic.SystemTray.Hide="Яшерү" +Basic.SystemTray.Message.Reconnecting="Өзелде. Яңадан тоташылу..." +Hotkeys.Insert="Insert (INS)" +Hotkeys.Delete="Delete (DEL)" +Hotkeys.PageUp="Page Up (PGUP)" +Hotkeys.PageDown="Page Down (PGDN)" +Hotkeys.ScrollLock="Scroll Lock (SCRLK)" +Hotkeys.Backspace="Backspace (←)" +Hotkeys.Tab="Tab (↹)" +Hotkeys.Print="Print Screen (PRTSC)" +Hotkeys.Windows="⊞ Win" +Hotkeys.Escape="Escape" +SceneItemShow="«%1» күрсәтү" +SceneItemHide="«%1» яшерү" +CodecCompat.Incompatible="(%1 белән ярашлы түгел)" +CodecCompat.ContainerPlaceholder="Форматны сайлау..." +CodecCompat.ContainerMissingOnExit.Title="Формат сайланмады" +FinalScene.Title="Сәхнәне бетерү" +NoSources.Title="Чыганак юк" +About="Програм турында" +About.Authors="Авторлар" +About.License="Лицензия" +ResizeOutputSizeOfSource.Continue="Дәвам итәргә телисезме?" +Importer.SelectCollection="Сәхнә коллекциясен сайлагыз" +Importer.Collection="Сәхнә коллекциясе" +Importer.Path="Коллекция юлы" +Restart="Яңадан башлау" +ContextBar.NoSelectedSource="Чыганак сайланмады" +YouTube.Actions.Title="Исем*" +YouTube.Actions.MyBroadcast="Минем агымым" +YouTube.Actions.Description="Тасвирлама" +YouTube.Actions.Privacy="Хосусыйлык*" +YouTube.Actions.Privacy.Private="Ябык" +YouTube.Actions.Privacy.Public="Ачык" +YouTube.Actions.Category="Категория" +YouTube.Actions.Thumbnail.SelectFile="Файлны сайлау..." +YouTube.Actions.Thumbnail.NoFileSelected="Файл сайланмады" +YouTube.Actions.Thumbnail.ClearFile="Чистарту" +YouTube.Actions.AdditionalSettings="Өстәмә көйләүләр" +YouTube.Actions.Error.FileMissing="Сайланган файл юк." +YouTube.Actions.Error.FileOpeningFailed="Сайлаган файлны ачып булмады." +YouTube.Actions.EventCreated.Title="Вакыйга ясалды" +YouTube.Actions.Stream="Агым" +YouTube.Chat.Input.Send="Җибәрү" +YouTube.Chat.Input.Placeholder="Хәбәрне монда языгыз..." +YouTube.Chat.Input.Sending="Җибәрелү..." +ConfigDownload.WarningMessageTitle="Кисәтү" diff --git a/UI/data/locale/ug-CN.ini b/UI/data/locale/ug-CN.ini index 55e0c516e8cf58..7092a02befc3f9 100644 --- a/UI/data/locale/ug-CN.ini +++ b/UI/data/locale/ug-CN.ini @@ -96,10 +96,23 @@ LogViewer="خاتىرە كۆرگۈچ" ShowOnStartup="قوزغالغاندا كۆرسەت" OpenFile="ھۆججەت ئاچ" AddSource="مەنبە قوش" +RemoveScene="تاللانغان كۆرۈنۈشنى چىقىرىۋەت" +RemoveSource="تاللانغان مەنبەنى چىقىرىۋەت" +MoveSceneUp="كۆرۈنۈشنى يۇقىرىغا يۆتكە" +MoveSceneDown="كۆرۈنۈشنى تۆۋەنگە يۆتكە" +MoveSourceUp="مەنبەنى يۇقىرىغا يۆتكە" +MoveSourceDown="مەنبەنى تۆۋەنگە يۆتكە" +SourceProperties="مەنبە خاسلىقىنى ئاچ" +SourceFilters="مەنبە سۈزگۈچنى ئاچ" +MixerToolbarMenu="ئۈن بىرىكتۈرگۈچ تىزىملىكى" +SceneFilters="كۆرۈنۈش سۈزگۈچنى ئاچ" List="تىزىم" Grid="سېتكا" +PluginsFailedToLoad.Title="قىستۇرما يۈكلەش خاتالىقى" +PluginsFailedToLoad.Text="تۆۋەندىكى OBS قىستۇرمىنى يۈكلىيەلمىدى: \n\n%1\nبۇ قىستۇرمىنى يېڭىلاڭ ياكى چىقىرىۋېتىڭ." AlreadyRunning.Title="OBS ئىجرا قىلىنىۋاتىدۇ!" AlreadyRunning.LaunchAnyway="قوزغىتىۋەر" +AutoSafeMode.Title="بىخەتەر ھالىتى" ChromeOS.Title="قوللىمايدىغان سۇپا" Wine.Title="Wine بايقالدى" ExtraBrowsers="ئىختىيارى توركۆرگۈچ سۇپىسى" @@ -261,6 +274,8 @@ Undo.PasteSourceRef="«%1» دە مەنبە نەقىلىنى چاپلا" Undo.GroupItems="«%1» گۇرۇپپا تۈرلىرى" TransitionNameDlg.Text="ئالمىشىش ئىسمىنى كىرگۈزۈڭ" TransitionNameDlg.Title="ئالمىشىش ئىسمى" +TitleBar.SafeMode="بىخەتەر ھالىتى" +TitleBar.PortableMode="ئەپچىل ھالىتى" TitleBar.Profile="تەرجىمىھال" TitleBar.Scenes="كۆرۈنۈش" NameExists.Title="ئىسىم مەۋجۇت" @@ -278,9 +293,12 @@ ConfirmExit.Title="OBS تىن چېكىنەمدۇ؟" ConfirmExit.Text="OBS ھازىر ئاكتىپ ھالەتتە. بارلىق ئېقىم/خاتىرىلەر تاقىلىدۇ. راستىنلا چېكىنىشنى خالامسىز؟" ConfirmRemove.Title="چىقىرىۋېتىشنى جەزملە" ConfirmRemove.TextMultiple="%1 تۈرنى چىقىرىۋېتىشنى خالامسىز؟" +ConfirmReset.Title="خاسلىقنى ئەسلىگە قايتۇر" +ConfirmReset.Text="نۆۋەتتىكى خاسلىقنى راستتىنلا كۆڭۈلدىكى قىممىتىگە ئەسلىگە قايتۇرامسىز؟" Output.StartStreamFailed="ئېقىمنى باشلىيالمىدى" Output.StartRecordingFailed="خاتىرىلەشنى باشلىيالمىدى" Output.StartReplayFailed="غەملەكنى قايتا قوزغىتالمىدى" +Output.StartVirtualCamFailed="مەۋھۇم كامېرانى باشلىيالمىدى" Output.ReplayBuffer.PauseWarning.Title="ۋاقىتلىق توختىتىلغاندا قايتا ساقلىغىلى بولمايدۇ" Output.ReplayBuffer.PauseWarning.Text="ئاگاھلاندۇرۇش: خاتىرىلەش ۋاقىتلىق توختىتىلغاندا قايتا ساقلىغىلى بولمايدۇ." Output.ConnectFail.Title="باغلىنالمىدى" @@ -327,6 +345,7 @@ Remux.FileExists="تۆۋەندىكى نىشان ھۆججەتلەر مەۋجۇت. Remux.ExitUnfinishedTitle="قايتا كودلىنىۋاتىدۇ" Remux.ExitUnfinished="قايتا كودلاش تاماملانمىدى، ھازىر توختىتىش نىشان ھۆججەتنى ئىشلەتكىلى بولمايدىغان قىلىپ قويۇشى مۇمكىن.\nقايتا كودلاشنى راستىنلا توختىتامسىز؟" Remux.HelpText="ھۆججەت بۇ كۆزنەككە تاشلانسا قايتا كودلايدۇ ياكى بوش «OBS خاتىرىلەش»." +Remux.NoFilesAddedTitle="ھېچقانداق قايتا كودلانغان ھۆججەت قوشۇلمىدى" MissingFiles="ھۆججەت كەم" MissingFiles.MissingFile="ھۆججەت كەم" MissingFiles.NewFile="يېڭى ھۆججەت" @@ -343,8 +362,15 @@ MissingFiles.AutoSearch="قوشۇمچە ھۆججەت ماسلىقى تېپىلد MissingFiles.AutoSearchText="OBS بۇ مۇندەرىجىدە يوقاپ كەتكەن ھۆججەتلەرگە قوشۇمچە ماس كېلىدىغان نەرسىلەرنى تاپتى. ئۇلارنى قوشامسىز؟" MissingFiles.NoMissing.Title="يوقاپ كەتكەن ھۆججەتلەرنى تەكشۈرۈش" MissingFiles.NoMissing.Text="يوقالغان ھۆججەت كۆرۈنمىدى." +MacPermissions.AccessGranted="زىيارەتكە يول قويۇلدى" +MacPermissions.RequestAccess="زىيارەت ئىلتىماسى" +MacPermissions.OpenPreferences="%1 مايىللىقىنى ئاچ" +MacPermissions.Item.ScreenRecording="ئېكران خاتىرىلەش" +MacPermissions.Item.ScreenRecording.Details="OBS بۇ ئىجازەتكە ئېرىشسە ئاندىن ئېكراندىن سۈرەت تۇتالايدۇ." MacPermissions.Item.Camera="كامېرا" +MacPermissions.Item.Camera.Details="بۇ ئىجازەت بولغاندا ئاندىن تور كامېراسى ياكى سۈرەت تۇتۇش كارتىسىدىن مەزمۇن تۇتالايدۇ." MacPermissions.Item.Microphone="مىكروفون" +MacPermissions.Item.Microphone.Details="ئەگەر مىكروفون ياكى سىرتقى ئاۋاز ئۈسكۈنىسىدىن ئاۋاز ئالماقچى بولسىڭىز OBS نىڭ بۇ ئىجازىتى بولۇشى كېرەك." MacPermissions.Item.Accessibility="قوشۇمچە ئىقتىدار" MacPermissions.Continue="داۋاملاشتۇر" Basic.DesktopDevice1="ئۈستەلئۈستى ئاۋاز" @@ -560,7 +586,6 @@ Basic.Settings.ProgramRestart="بۇ تەڭشەكلەرنىڭ كۈچكە ئىگە Basic.Settings.ConfirmTitle="ئۆزگەرتىشنى جەزملە" Basic.Settings.Confirm="ساقلىمىغان ئۆزگەرتىشلىرىڭىز بار. ئۆزگەرتكەننى ساقلامدۇ؟" Basic.Settings.General="ئادەتتىكى" -Basic.Settings.General.Theme="ئۆرنەك" Basic.Settings.General.Language="تىل" Basic.Settings.General.Updater="يېڭىلانمىلار" Basic.Settings.General.UpdateChannelDefault="(كۆڭۈلدىكى)" diff --git a/UI/data/locale/uk-UA.ini b/UI/data/locale/uk-UA.ini index cf3d1617efcde1..a949b1936e8b2b 100644 --- a/UI/data/locale/uk-UA.ini +++ b/UI/data/locale/uk-UA.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="Меню аудіомікшера" SceneFilters="Відкрити фільтри сцени" List="Список" Grid="Сітка" +Automatic="Автоматично" PluginsFailedToLoad.Title="Помилка завантаження плагіна" PluginsFailedToLoad.Text="Не вдалося завантажити такі плагіни OBS:\n\n%1\nОновіть або вилучіть ці плагіни." AlreadyRunning.Title="OBS уже запущено" @@ -577,6 +578,7 @@ Basic.Main.StopRecording="Зупинити записування" Basic.Main.PauseRecording="Призупинити запис" Basic.Main.UnpauseRecording="Відновити запис" Basic.Main.SplitFile="Розділити файл Запису" +Basic.Main.AddChapterMarker="Додати позначку розділу" Basic.Main.StoppingRecording="Припинення запису…" Basic.Main.StopReplayBuffer="Зупинити буфер повторів" Basic.Main.StoppingReplayBuffer="Зупинення буферу повторів…" @@ -686,7 +688,6 @@ Basic.Settings.ProgramRestart="Щоб ці параметри набули чи Basic.Settings.ConfirmTitle="Підтвердження змін" Basic.Settings.Confirm="У вас є незбережені зміни. Зберегти їх?" Basic.Settings.General="Загальні" -Basic.Settings.General.Theme="Тема" Basic.Settings.General.Language="Мова" Basic.Settings.General.Updater="Оновлення" Basic.Settings.General.UpdateChannel="Канал оновлення" @@ -746,6 +747,11 @@ Basic.Settings.General.ChannelName.stable="Стабільний" Basic.Settings.General.ChannelDescription.stable="Останній стабільний випуск" Basic.Settings.General.ChannelName.beta="Бета / кандидати на випуск" Basic.Settings.General.ChannelDescription.beta="Потенційно нестабільні передрелізні версії" +Basic.Settings.Appearance="Вигляд" +Basic.Settings.Appearance.General="Основне" +Basic.Settings.Appearance.General.Theme="Тема" +Basic.Settings.Appearance.General.Variant="Стиль" +Basic.Settings.Appearance.General.NoVariant="Стилів немає в наявності" Basic.Settings.Stream="Трансляція" Basic.Settings.Stream.Custom.UseAuthentication="Використовувати автентифікацію" Basic.Settings.Stream.Custom.Username="Ім’я користувача" @@ -771,6 +777,7 @@ Basic.Settings.Stream.Recommended.MaxFPS="Найбільша частота ка Basic.Settings.Output="Вивід" Basic.Settings.Output.Format="Формат запису" Basic.Settings.Output.Format.MKV="Відео Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="Гібридний MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Фрагментований MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Фрагментований MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Фрагментований MOV записує запис фрагментами та не потребує такої ж обробки, як звичайні файли MOV.\nЦе гарантує, що файл можна буде відтворити, навіть якщо запис на диск буде перервано, наприклад, унаслідок аварійного завершення роботи пристрою або втрати живлення.\n\nЦе може бути несумісним з деякими програвачами та редакторами. За потреби скористайтеся командою Файл → Ремультиплексувати записи, щоб перетворити файл у більш сумісний формат." diff --git a/UI/data/locale/vi-VN.ini b/UI/data/locale/vi-VN.ini index 921d98909e7ddc..5e7a7b9fea2e5e 100644 --- a/UI/data/locale/vi-VN.ini +++ b/UI/data/locale/vi-VN.ini @@ -107,6 +107,7 @@ MixerToolbarMenu="Bảng chọn bộ trộn âm thanh" SceneFilters="Mở bộ lọc phân cảnh" List="Danh sách" Grid="Lưới" +Automatic="Tự động" PluginsFailedToLoad.Title="Lỗi khởi động tiện ích" PluginsFailedToLoad.Text="Những tiện ích sau đây đang bị lỗi:\n\n%1\nVui lòng cập nhật hoặc xóa nó đi" AlreadyRunning.Title="OBS hiện đang chạy" @@ -193,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Ưu tiên mã hóa bằng ph Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Mã hóa bằng phần cứng giúp loại bỏ hầu hết lượng tải CPU, đồng thời có thể yêu cầu thêm tốc độ bit để đạt được chất lượng tương đối." Basic.AutoConfig.StreamPage.StreamWarning.Title="Cảnh báo luồng" Basic.AutoConfig.StreamPage.StreamWarning.Text="Bài kiểm tra băng thông sẽ phát luồng dữ liêu video ngẫu nhiên mà không có âm thanh đến kênh của bạn. Nếu được, hãy tạm thời tắt các video luồng đang lưu và đặt luồng thành riêng tư cho đến khi kiểm tra xong. Tiếp tục?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Kiểm tra %1" Basic.AutoConfig.TestPage="Kết quả cuối cùng" Basic.AutoConfig.TestPage.SubTitle.Testing="Chương trình hiện đang thực thi một số bài kiểm tra để ước lượng các thiết đặt lí tưởng nhất" Basic.AutoConfig.TestPage.SubTitle.Complete="Thử nghiệm hoàn tất" @@ -211,6 +213,7 @@ Basic.AutoConfig.TestPage.Result.Header="Chương trình đã xác định đư Basic.AutoConfig.TestPage.Result.Footer="Để sử dụng các thiết đặt này, nhấp vào Áp dụng thiết đặt. Để cấu hình lại và thử lại, bấm Lùi lại. Để cấu hình thiết đặt bằng tay, bấm Hủy bỏ và mở Thiết đặt." Basic.AutoConfig.Info="Thuật sĩ tự cấu hình sẽ xác định các thiết đặt tốt nhất dựa trên đặc tả máy tính và tốc độ internet của bạn." Basic.AutoConfig.RunAnytime="Có thể chạy bất kì lúc nào bằng cách vào bảng chọn Công cụ." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Độ phân giải phát trực tuyến (được phóng to)" Basic.Stats="Thống kê" Basic.Stats.CPUUsage="Lượng tải CPU" Basic.Stats.HDDSpaceAvailable="Không gian đĩa còn trống" @@ -337,7 +340,7 @@ Output.StartStreamFailed="Không thể phát luồng" Output.StartRecordingFailed="Không thể bắt đầu quay video" Output.StartReplayFailed="Không thể khởi động replay buffer" Output.StartVirtualCamFailed="Không khởi động được máy ảnh ảo" -Output.StartFailedGeneric="Không thể ghi đầu ra. Vui lòng kiểm tra các tệp nhật kí để biết thêm chi tiết.\n\nGhi chú: nếu bạn đang sử dụng bộ biên mã NVENC hoặc AMD, bạn phải cập nhật GPU lên phiên bản mới nhất." +Output.StartFailedGeneric="Không thể ghi đầu ra. Vui lòng kiểm tra các tệp nhật ký để biết thêm chi tiết.\n\nGhi chú: nếu bạn đang sử dụng bộ biên mã NVENC hoặc AMD, bạn phải cập nhật GPU lên phiên bản mới nhất." Output.ReplayBuffer.PauseWarning.Title="Không thể lưu bản phát lại trong khi tạm dừng" Output.ReplayBuffer.PauseWarning.Text="Cảnh báo: Bản phát lại sẽ không được lưu trong khi tạm dừng ghi hình." Output.ConnectFail.Title="Không thể kết nối" @@ -345,7 +348,7 @@ Output.ConnectFail.BadPath="URL không hợp lệ của đường dẫn hoặc k Output.ConnectFail.ConnectFailed="Không thể kết nối tới máy chủ" Output.ConnectFail.InvalidStream="Không thể truy cập kênh chỉ định hoặc khóa luồng phát. Vui lòng kiểm tra khóa luồng của bạn. Nếu bạn không nhập sai khóa, có thể luồng kết nối máy chủ có vấn đề." Output.ConnectFail.HdrDisabled="Đầu ra HDR hiện bị tắt cho đầu ra này." -Output.ConnectFail.Error="1 lỗi bất ngờ xảy ra khi thử kết nối tới máy chủ. Thông tin thêm nằm trong log file." +Output.ConnectFail.Error="1 lỗi bất ngờ xảy ra khi thử kết nối tới máy chủ. Thông tin thêm nằm trong tệp nhật ký." Output.ConnectFail.Disconnected="Ngắt kết nối từ máy chủ." Output.StreamEncodeError.Title="Lỗi biên mã" Output.StreamEncodeError.Msg="Xảy ra lỗi biên mã trong khi phát luồng." @@ -364,12 +367,12 @@ Output.NoBroadcast.Title="Không có phát sóng nào được cấu hình" Output.NoBroadcast.Text="Bạn cần thiết lập một phát sóng trước khi bạn có thể bắt đầu phát luồng." Output.BroadcastStartFailed="Không thể bắt đầu phát sóng" Output.BroadcastStopFailed="Không thể dừng phát sóng" -LogReturnDialog="Đăng tải lên thành công" -LogReturnDialog.Description="File log của bạn đã được đăng tải. Bạn có thể chia sẻ đường dẫn URL để được gỡ rối hoặc với mục đích hỗ trợ." +LogReturnDialog="Tải lên thành công" +LogReturnDialog.Description="File nhật ký của bạn đã được tải lên. Bạn có thể chia sẻ đường dẫn URL để được gỡ rối hoặc với mục đích hỗ trợ." LogReturnDialog.Description.Crash="Tệp báo cáo crash của bạn đã được đăng tải. Bạn có thể chia sẻ đường dẫn URL cho mục đích gỡ lỗi." LogReturnDialog.CopyURL="Chép địa chỉ" LogReturnDialog.AnalyzeURL="Phân tích" -LogReturnDialog.ErrorUploadingLog="Lỗi tải lên tập tin đăng nhập" +LogReturnDialog.ErrorUploadingLog="Lỗi gửi tệp nhật ký" Remux.TargetFile="Tệp đích" Remux.Remux="Ghép lại" Remux.Stop="Dừng ghép lại" @@ -551,6 +554,7 @@ Basic.Main.Scenes="Phân cảnh" Basic.Main.Sources="Nguồn" Basic.Main.Source="Nguồn" Basic.Main.Controls="Điều khiển" +Basic.Main.PreparingStream="Đang chuẩn bị..." Basic.Main.Connecting="Đang kết nối..." Basic.Main.StartRecording="Bắt đầu ghi" Basic.Main.StartReplayBuffer="Bắt đầu Replay Buffer" @@ -562,6 +566,7 @@ Basic.Main.StopRecording="Dừng ghi" Basic.Main.PauseRecording="Tạm dừng ghi hình" Basic.Main.UnpauseRecording="Tiếp tục ghi hình" Basic.Main.SplitFile="Tách tệp ghi hình" +Basic.Main.AddChapterMarker="Thêm Đánh dấu Chương" Basic.Main.StoppingRecording="Dừng ghi video..." Basic.Main.StopReplayBuffer="Dừng Replay Buffer" Basic.Main.StoppingReplayBuffer="Đang dừng Replay Buffer..." @@ -653,11 +658,11 @@ Basic.MainMenu.Help.HelpPortal="Cổng thông tin trợ giúp (&P)" Basic.MainMenu.Help.Website="Ghé thăm &Website" Basic.MainMenu.Help.Discord="Tham gia &Discord Server" Basic.MainMenu.Help.WhatsNew="Có gì mới" -Basic.MainMenu.Help.Logs="Tập tin đăng nhập (&L)" -Basic.MainMenu.Help.Logs.ShowLogs="Hiển thị tập tin đăng nhập (&S)" -Basic.MainMenu.Help.Logs.UploadCurrentLog="Tải lên tệp sổ ghi hiện tại (&C)" +Basic.MainMenu.Help.Logs="Tập tin nhật ký (&L)" +Basic.MainMenu.Help.Logs.ShowLogs="Hiển thị thư mục nhật ký (&S)" +Basic.MainMenu.Help.Logs.UploadCurrentLog="Tải lên tệp nhật ký hiện tại (&C)" Basic.MainMenu.Help.Logs.UploadLastLog="Tải lên tệp nhật ký trước đó" -Basic.MainMenu.Help.Logs.ViewCurrentLog="Xem sổ ghi hiện tại (&V)" +Basic.MainMenu.Help.Logs.ViewCurrentLog="Xem nhật ký hiện tại (&V)" Basic.MainMenu.Help.ReleaseNotes="Ghi chú" Basic.MainMenu.Help.CheckForUpdates="Kiểm tra cập nhật mới" Basic.MainMenu.Help.Repair="Kiểm tra tính toàn vẹn tệp" @@ -670,8 +675,8 @@ Basic.MainMenu.Help.About="Giới thiệu (&A)" Basic.Settings.ProgramRestart="Chương trình phải được khởi động lại để những thiết đặt có hiệu lực." Basic.Settings.ConfirmTitle="Xác nhận thay đổi" Basic.Settings.Confirm="Bạn đã lưu thay đổi. Lưu thay đổi?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 đang kiểm soát một số cài đặt phát trực tiếp của bạn" Basic.Settings.General="Chung" -Basic.Settings.General.Theme="Chủ đề" Basic.Settings.General.Language="Ngôn ngữ" Basic.Settings.General.Updater="Cập nhật" Basic.Settings.General.UpdateChannel="Kênh cập nhật" @@ -731,7 +736,13 @@ Basic.Settings.General.ChannelName.stable="Ổn định" Basic.Settings.General.ChannelDescription.stable="Phiên bản ổn định mới nhất" Basic.Settings.General.ChannelName.beta="Bản beta / Bản phát hành ứng cử" Basic.Settings.General.ChannelDescription.beta="Phiên bản tiền phát hành có thể không ổn định" +Basic.Settings.Appearance="Diện mạo" +Basic.Settings.Appearance.General="Tổng quan" +Basic.Settings.Appearance.General.Theme="Chủ đề" +Basic.Settings.Appearance.General.Variant="Kiểu cách" +Basic.Settings.Appearance.General.NoVariant="Không có sẵn kiểu nào" Basic.Settings.Stream="Luồng" +Basic.Settings.Stream.Destination="Điểm đến" Basic.Settings.Stream.Custom.UseAuthentication="Sử dụng xác thực" Basic.Settings.Stream.Custom.Username="Tên đăng nhập" Basic.Settings.Stream.Custom.Password="Mật khẩu" @@ -753,8 +764,21 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Tốc độ bit tối đa c Basic.Settings.Stream.Recommended.MaxAudioBitrate="Tốc độ bit âm thanh tối đa: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Độ phân giải tối đa: %1" Basic.Settings.Stream.Recommended.MaxFPS="FPS tối đa: %1" +Basic.Settings.Stream.SpecifyCustomServer="Chỉ định Máy chủ tùy chỉnh..." +Basic.Settings.Stream.ServiceCustomServer="Máy chủ tùy chỉnh" +Basic.Settings.Stream.EnableMultitrackVideo="Bật %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Băng thông phát trực tuyến tối đa" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Tự động" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Số lượng kênh video tối đa" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Tự động" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Cho phép phát trực tiếp sang FLV (sử dụng cài đặt tệp ghi đơn giản)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Ghi đè cấu hình (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Bật ghi đè cấu hình" +Basic.Settings.Stream.MultitrackVideoLabel="Video đa kênh" +Basic.Settings.Stream.AdvancedOptions="Tùy chọn Nâng cao" Basic.Settings.Output="Đầu ra" Basic.Settings.Output.Format="Định dạng ghi hình" +Basic.Settings.Output.Format.hMP4="MP4 lai [THỬ NGHIỆM] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 bị phân mảnh (.mp4)" Basic.Settings.Output.Format.fMOV="MOV bị phân mảnh (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="MOV phân mảnh ghi các bản ghi theo từng đoạn và không yêu cầu quá trình hoàn thiện giống như các tệp MOV truyền thống.\nĐiều này đảm bảo tệp vẫn có thể phát được ngay cả khi quá trình ghi vào đĩa bị gián đoạn, chẳng hạn như do lỗi màn hình xanh hoặc mất điện.\n\nĐiều này có thể không tương thích với tất cả trình phát và trình chỉnh sửa. Sử dụng Tập tin → Ghép lại bản ghi để chuyển đổi tệp thành định dạng tương thích hơn nếu cần." @@ -1161,7 +1185,7 @@ YouTube.Actions.Thumbnail.ClearFile="Xóa" YouTube.Actions.MadeForKids="Video này có dành cho trẻ em không?*" YouTube.Actions.MadeForKids.Yes="Có, video này dành cho trẻ em" YouTube.Actions.MadeForKids.No="Không, video này không dành cho trẻ em" -YouTube.Actions.AdditionalSettings="Thiết lập bổ sung" +YouTube.Actions.AdditionalSettings="Thiết đặt bổ sung" YouTube.Actions.Latency="Độ trễ" YouTube.Actions.Latency.Normal="Trung bình" YouTube.Actions.Latency.Low="Thấp" @@ -1219,3 +1243,27 @@ YouTube.Errors.messageTextInvalid="Tin nhắn không hợp lệ" YouTube.Errors.rateLimitExceeded="Bạn gửi tin nhắn quá nhanh!" YouTube.DocksRemoval.Title="Xóa định cố trình duyệt YouTube cũ" YouTube.DocksRemoval.Text="Định cố trình duyệt này sẽ bị xóa vì hiện đã bất cập:\n\n%1\nThay vào đó, hãy sử dụng \"định cố/ điều khiển luồng phát YouTube\"." +ConfigDownload.WarningMessageTitle="Cảnh báo" +FailedToStartStream.MissingConfigURL="Không có URL cấu hình nào khả dụng cho dịch vụ hiện tại" +FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP tùy chỉnh không được chỉ định" +FailedToStartStream.InvalidCustomConfig="Cấu hình tùy chỉnh không hợp lệ" +FailedToStartStream.FailedToCreateMultitrackVideoService="Không tạo được dịch vụ video đa luồng" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Không tạo được đầu ra video rtmp đa luồng" +FailedToStartStream.EncoderNotAvailable="NVENC không khả dụng.\n\nKhông tìm thấy bộ biên mã '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Không tạo được bộ biên mã video '%1' (loại: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Không lấy được thông tin video obs khi tạo bộ biên mã '%1' (loại: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Không tạo được bộ biên mã âm thanh" +FailedToStartStream.NoRTMPURLInConfig="Cấu hình không chứa URL RTMP(S) của luồng mục tiêu" +FailedToStartStream.FallbackToDefault="Không thể bắt đầu phát trực tiếp bằng %1; bạn có muốn thử lại bằng cách sử dụng cài đặt mã hóa đơn không?" +FailedToStartStream.ConfigRequestFailed="Không thể lấy cấu hình từ %1

Lỗi HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Đã nhận được giá trị trạng thái không xác định '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nBạn có muốn tiếp tục phát trực tuyến mà không có %1 không?" +FailedToStartStream.WarningRetry="\n

\nBạn có muốn tiếp tục phát trực tuyến không?" +FailedToStartStream.MissingEncoderConfigs="Cấu hình phát trực tiếp không bao gồm cấu hình bộ biên mã" +FailedToStartStream.StatusMissingHTML="Yêu cầu phát trực tiếp trả về lỗi không xác định" +FailedToStartStream.NoConfigSupplied="Thiếu cấu hình" +MultitrackVideo.Info="%1 tự động tối ưu hóa cài đặt của bạn để mã hóa và gửi nhiều chất lượng video tới. Việc chọn tùy chọn này sẽ gửi %2 thông tin về máy tính và cài đặt phần mềm của bạn." +MultitrackVideo.IncompatibleSettings.Title="Cài đặt không tương thích" +MultitrackVideo.IncompatibleSettings.Text="%1 hiện không tương thích với:\n\n%2\nĐể tiếp tục phát trực tuyến với %1, hãy tắt các cài đặt không tương thích:\n\n%3\nvà Bắt đầu phát trực tuyến lại." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Tắt cho lượt phát trực tiếp này và Bắt đầu phát trực tuyến" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Cập nhật Cài đặt và Bắt đầu Phát trực tuyến" diff --git a/UI/data/locale/zh-CN.ini b/UI/data/locale/zh-CN.ini index cca5d343e76e9e..d8eae8c4b417f3 100644 --- a/UI/data/locale/zh-CN.ini +++ b/UI/data/locale/zh-CN.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="混音器菜单" SceneFilters="打开场景滤镜" List="列表" Grid="网格" +Automatic="自动" PluginsFailedToLoad.Title="插件载入出错" PluginsFailedToLoad.Text="以下 OBS 插件加载失败:\n\n%1\n请更新或移除这些插件。" AlreadyRunning.Title="OBS 已在运行" @@ -194,6 +195,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="优先选择硬件编码" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="硬件编码可大量降低 CPU 的使用率,但要获得与软件编码相同的质量,可能需要更多的码率。" Basic.AutoConfig.StreamPage.StreamWarning.Title="直播警告" Basic.AutoConfig.StreamPage.StreamWarning.Text="带宽测试将开始串流无音频的随机视频数据。如果可以,建议你暂时关闭视频保存功能并把串流设置成私人直到测试结束。要继续吗?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="测试 %1" Basic.AutoConfig.TestPage="最终结果" Basic.AutoConfig.TestPage.SubTitle.Testing="该程序目前正在执行一系列的测试来估计最理想的设置" Basic.AutoConfig.TestPage.SubTitle.Complete="测试完成" @@ -212,6 +214,7 @@ Basic.AutoConfig.TestPage.Result.Header="程序已确定这些估计设置是最 Basic.AutoConfig.TestPage.Result.Footer="要使用这些设置,单击“应用设置”。要重新配置向导并再试一次,单击“后退”。要自己手动配置设置,单击“取消”,然后打开设置。" Basic.AutoConfig.Info="自动配置向导将根据您的计算机配置和互联网速度来决定最佳设置。" Basic.AutoConfig.RunAnytime="也可以随时通过工具菜单去运行。" +Basic.AutoConfig.TestPage.Result.StreamingResolution="串流 (缩放) 分辨率" Basic.Stats="统计" Basic.Stats.CPUUsage="CPU 使用率" Basic.Stats.HDDSpaceAvailable="可用磁盘空间" @@ -567,6 +570,7 @@ Basic.Main.Scenes="场景" Basic.Main.Sources="来源" Basic.Main.Source="来源" Basic.Main.Controls="控制按钮" +Basic.Main.PreparingStream="准备中…" Basic.Main.Connecting="连接中……" Basic.Main.StartRecording="开始录制" Basic.Main.StartReplayBuffer="启动回放缓存" @@ -578,6 +582,7 @@ Basic.Main.StopRecording="停止录制" Basic.Main.PauseRecording="暂停录制" Basic.Main.UnpauseRecording="恢复录制" Basic.Main.SplitFile="分割录制文件" +Basic.Main.AddChapterMarker="添加章节标记" Basic.Main.StoppingRecording="正在停止录制..." Basic.Main.StopReplayBuffer="关闭回放缓存" Basic.Main.StoppingReplayBuffer="正在关闭回放缓存..." @@ -686,8 +691,8 @@ Basic.MainMenu.Help.About="关于(&A)" Basic.Settings.ProgramRestart="要使这些设置生效,必须重新启动该程序。" Basic.Settings.ConfirmTitle="确认更改" Basic.Settings.Confirm="你有未保存的更改。保存更改吗?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 正在控制某些串流设置" Basic.Settings.General="常规" -Basic.Settings.General.Theme="主题" Basic.Settings.General.Language="语言" Basic.Settings.General.Updater="更新" Basic.Settings.General.UpdateChannel="更新通道" @@ -747,7 +752,13 @@ Basic.Settings.General.ChannelName.stable="稳定版" Basic.Settings.General.ChannelDescription.stable="最新的稳定版本" Basic.Settings.General.ChannelName.beta="测试版 / RC 版" Basic.Settings.General.ChannelDescription.beta="可能不稳定的先行版本" +Basic.Settings.Appearance="外观" +Basic.Settings.Appearance.General="常规" +Basic.Settings.Appearance.General.Theme="主题" +Basic.Settings.Appearance.General.Variant="样式" +Basic.Settings.Appearance.General.NoVariant="没有可用的样式" Basic.Settings.Stream="直播" +Basic.Settings.Stream.Destination="目的地" Basic.Settings.Stream.Custom.UseAuthentication="使用身份认证" Basic.Settings.Stream.Custom.Username="用户名" Basic.Settings.Stream.Custom.Password="密码" @@ -769,9 +780,22 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="最大视频码率: %1 kbps" Basic.Settings.Stream.Recommended.MaxAudioBitrate="最大音频码率: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="最大分辨率: %1" Basic.Settings.Stream.Recommended.MaxFPS="最高帧率: %1" +Basic.Settings.Stream.SpecifyCustomServer="指定自定义服务器…" +Basic.Settings.Stream.ServiceCustomServer="自定义服务器" +Basic.Settings.Stream.EnableMultitrackVideo="启用 %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="最大串流带宽" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="自动" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="最大视频轨道数" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="自动" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="启用串流转储到 FLV(使用简单录制文件设置)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="配置覆盖(JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="启用配置覆盖" +Basic.Settings.Stream.MultitrackVideoLabel="多轨视频" +Basic.Settings.Stream.AdvancedOptions="高级选项" Basic.Settings.Output="输出" Basic.Settings.Output.Format="录像格式" Basic.Settings.Output.Format.MKV="Matroska 视频 (.mkv)" +Basic.Settings.Output.Format.hMP4="混合 MP4 [测试版] (.mp4)" Basic.Settings.Output.Format.fMP4="分片 MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="分片 MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="分片 MOV 以块的形式录制,不需要像传统 MOV 文件一样做最后处理。\n这确保即使磁盘写入中断(例如,由于蓝屏或断电),文件仍可播放。\n\n可能不是所有播放器和编辑软件都能兼容。必要的话,请使用 “文件 → 录像转封装” 将文件转换为更兼容的格式。" @@ -1232,3 +1256,27 @@ YouTube.Errors.messageTextInvalid="消息文本无效。" YouTube.Errors.rateLimitExceeded="您发送消息的频率太快。" YouTube.DocksRemoval.Title="清除旧版 YouTube 浏览器停靠栏" YouTube.DocksRemoval.Text="这些浏览器停靠栏因弃用而将被移除:\n\n%1\n请改用“停靠栏/YouTube 直播控制室”。" +ConfigDownload.WarningMessageTitle="警告" +FailedToStartStream.MissingConfigURL="没有可用于当前服务的配置 URL" +FailedToStartStream.NoCustomRTMPURLInSettings="自定义 RTMP URL 未指定" +FailedToStartStream.InvalidCustomConfig="自定义配置无效" +FailedToStartStream.FailedToCreateMultitrackVideoService="无法创建多轨视频服务" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="无法创建多轨视频 RTMP 输出" +FailedToStartStream.EncoderNotAvailable="NVENC 不可用。\n\n找不到编码器类型“%1”" +FailedToStartStream.FailedToCreateVideoEncoder="无法创建视频编码器“%1”(类型:“%2”)" +FailedToStartStream.FailedToGetOBSVideoInfo="创建编码器“%1”(类型:“%2”)时无法获取 OBS 视频信息" +FailedToStartStream.FailedToCreateAudioEncoder="无法创建音频编码器" +FailedToStartStream.NoRTMPURLInConfig="配置不包含串流目标 RTMP(S) URL" +FailedToStartStream.FallbackToDefault="使用 %1 启动串流失败;是否要使用单一编码设置重试?" +FailedToStartStream.ConfigRequestFailed="无法从 %1 获取配置

HTTP 错误:%2 " +FailedToStartStream.WarningUnknownStatus="接收到未知状态值“%1”" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n是否要在没有 %1 的情况下继续串流?" +FailedToStartStream.WarningRetry="\n

\n是否继续串流?" +FailedToStartStream.MissingEncoderConfigs="直播配置不包含编码器配置" +FailedToStartStream.StatusMissingHTML="直播请求返回了未指定的错误" +FailedToStartStream.NoConfigSupplied="缺少配置" +MultitrackVideo.Info="%1 会自动优化您的设置来编码并发送多种视频质量。选择此选项将发送有关您的计算机和软件设置的 %2 信息。" +MultitrackVideo.IncompatibleSettings.Title="设置不兼容" +MultitrackVideo.IncompatibleSettings.Text="%1 当前与以下设置不兼容:\n\n%2\n若要继续使用 %1 进行串流,请禁用不兼容的设置:\n\n%3\n然后重新开始串流。" +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="禁用此流并开始串流" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="更新设置并开始串流" diff --git a/UI/data/locale/zh-TW.ini b/UI/data/locale/zh-TW.ini index 332f6dc64e7c7e..769217749405f3 100644 --- a/UI/data/locale/zh-TW.ini +++ b/UI/data/locale/zh-TW.ini @@ -578,6 +578,7 @@ Basic.Main.StopRecording="停止錄製" Basic.Main.PauseRecording="暫停錄影" Basic.Main.UnpauseRecording="繼續錄製" Basic.Main.SplitFile="分割錄影檔案" +Basic.Main.AddChapterMarker="增加章節標記" Basic.Main.StoppingRecording="停止錄製..." Basic.Main.StopReplayBuffer="停止重播緩衝" Basic.Main.StoppingReplayBuffer="正在停止重播緩衝..." @@ -687,7 +688,6 @@ Basic.Settings.ProgramRestart="為了套用新設定,請關閉後重啟。" Basic.Settings.ConfirmTitle="確認修改" Basic.Settings.Confirm="您有未儲存的修改。 是否儲存?" Basic.Settings.General="一般" -Basic.Settings.General.Theme="佈景主題" Basic.Settings.General.Language="語言" Basic.Settings.General.Updater="更新" Basic.Settings.General.UpdateChannel="更新頻道" @@ -747,6 +747,11 @@ Basic.Settings.General.ChannelName.stable="穩定版" Basic.Settings.General.ChannelDescription.stable="最新穩定發行版" Basic.Settings.General.ChannelName.beta="測試版/候選發行版" Basic.Settings.General.ChannelDescription.beta="可能不穩定的預先發佈版本" +Basic.Settings.Appearance="外觀" +Basic.Settings.Appearance.General="一般" +Basic.Settings.Appearance.General.Theme="佈景主題" +Basic.Settings.Appearance.General.Variant="樣式" +Basic.Settings.Appearance.General.NoVariant="沒有樣式" Basic.Settings.Stream="串流" Basic.Settings.Stream.Custom.UseAuthentication="使用身份驗證" Basic.Settings.Stream.Custom.Username="使用者名稱" @@ -772,6 +777,7 @@ Basic.Settings.Stream.Recommended.MaxFPS="最高 FPS:%1" Basic.Settings.Output="輸出" Basic.Settings.Output.Format="錄影格式" Basic.Settings.Output.Format.MKV="Matroska 視訊檔案 (.mkv)" +Basic.Settings.Output.Format.hMP4="混合 MP4 [測試版] (.mp4)" Basic.Settings.Output.Format.fMP4="分段 MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="分段 MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="分段的 MOV 會分塊寫入錄影內容,並且和傳統 MOV 檔案不同,不需要寫入檔案結尾。\n這樣「寫入磁碟」程序即便被打斷(舉例來說,因為 BSOD 或斷電),檔案依然是可以播放的。\n\n這可能不相容某些播放器和編輯器,如果需要相容的話,可以使用 [檔案] → [重新封裝錄影] 將檔案轉換為更能廣泛相容的格式。" diff --git a/UI/frontend-plugins/aja-output-ui/data/locale/be-BY.ini b/UI/frontend-plugins/aja-output-ui/data/locale/be-BY.ini index 69015eadde8f64..659c432cb3637b 100644 --- a/UI/frontend-plugins/aja-output-ui/data/locale/be-BY.ini +++ b/UI/frontend-plugins/aja-output-ui/data/locale/be-BY.ini @@ -1,4 +1,4 @@ -AJAOutput.Device="Захват прылады AJA I/O" +AJAOutput.Device="Захоп прылады AJA I/O" AJAOutput.ProgramOutput="Праграмны вывад" AJAOutput.PreviewOutput="Прадпрагляд вываду" AJAOutput.MiscOutput="Дадатковыя налады" diff --git a/UI/frontend-plugins/aja-output-ui/data/locale/bg-BG.ini b/UI/frontend-plugins/aja-output-ui/data/locale/bg-BG.ini new file mode 100644 index 00000000000000..38035f228ae471 --- /dev/null +++ b/UI/frontend-plugins/aja-output-ui/data/locale/bg-BG.ini @@ -0,0 +1,6 @@ +AJAOutput.Device="Изход на устройството AJA I/O" +AJAOutput.ProgramOutput="Програмен изход" +AJAOutput.PreviewOutput="Нагледно изхода" +AJAOutput.MiscOutput="Допълнителни настройки" +AJAOutput.MultiViewEnable="Включване на множко изгледи" +AJAOutput.MultiViewAudioSource="Източник на звука за множко изгледи" diff --git a/UI/frontend-plugins/aja-output-ui/data/locale/hr-HR.ini b/UI/frontend-plugins/aja-output-ui/data/locale/hr-HR.ini index c5772706dc9900..ce0663bf98bcfc 100644 --- a/UI/frontend-plugins/aja-output-ui/data/locale/hr-HR.ini +++ b/UI/frontend-plugins/aja-output-ui/data/locale/hr-HR.ini @@ -1 +1,6 @@ +AJAOutput.Device="Pretpregled AJA I/O uređaja " +AJAOutput.ProgramOutput="Pretpregled programa" +AJAOutput.PreviewOutput="Pretpregled izlaznog rezultata" AJAOutput.MiscOutput="Dodatne postavke" +AJAOutput.MultiViewEnable="Omogući višestruki prikaz" +AJAOutput.MultiViewAudioSource="Višestruki prikaz zvučnog izvora " diff --git a/UI/frontend-plugins/aja-output-ui/data/locale/tt-RU.ini b/UI/frontend-plugins/aja-output-ui/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..73fd470d262d98 --- /dev/null +++ b/UI/frontend-plugins/aja-output-ui/data/locale/tt-RU.ini @@ -0,0 +1 @@ +AJAOutput.MiscOutput="Өстәмә көйләүләр" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini b/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini index f8780850bd14a2..61bfb5d0087237 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini @@ -37,7 +37,7 @@ PythonSettings.BrowsePythonPath="Sfoglia Percorso Python" PythonSettings.PythonVersion="Versione Python caricata: %1" PythonSettings.PythonNotLoaded="Python non attualmente caricato" PythonSettings.AlreadyLoaded.Title="Python già caricato" -PythonSettings.AlreadyLoaded.Message="Una copia di Python %1 è già stata caricata. \nPer caricare la nuova versione Python selezionata, riavvia OBS." +PythonSettings.AlreadyLoaded.Message="Una copia di Python %1 è già stata caricata. Per caricare la nuova versione Python selezionata, riavvia OBS." ScriptLogWindow="Log degli script" Description="Descrizione" ScriptDescriptionLink.Text="Vuoi aprire questo link con il tuo browser predefinito?" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini b/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini index 3a9483e7683776..d82d44ebabdde1 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/th-TH.ini @@ -15,7 +15,33 @@ Captions.AudioSource="แหล่งที่มาเสียง" Captions.CurrentSystemLanguage="ภาษาระบบปัจจุบัน (%1)" Captions.Provider="ระบบบริการ" Captions.Error.GenericFail="การเริ่มคำบรรยายล้มเหลว" +OutputTimer="ตัวตั้งเวลาเอาต์พุต" +OutputTimer.Stream="หยุดสตรีมมิ่งหลังจาก:" +OutputTimer.Record="หยุดบันทึกหลังจาก:" +OutputTimer.Stream.StoppingIn="สตรีมมิ่งหยุดใน:" +OutputTimer.Record.StoppingIn="การบันทึกหยุดใน:" +OutputTimer.Stream.EnableEverytime="เปิดใช้งานตัวจับเวลาสตรีมมิ่งทุกครั้ง" +OutputTimer.Record.EnableEverytime="เปิดใช้งานตัวจับเวลาบันทึกทุกครั้ง" +OutputTimer.Record.PauseTimer="หยุดจับเวลาชั่วคราวเมื่อหยุดการบันทึกชั่วคราว" Scripts="สคริปต์" +LoadedScripts="สคริปต์ที่โหลด" AddScripts="เพิ่มสคริปต์" +RemoveScripts="ลบสคริปต์" +ReloadScripts="โหลดสคริปต์ใหม่" +EditScript="แก้ไขสคริปต์" +Reload="โหลดใหม่" +OpenFileLocation="เปิดตำแหน่งไฟล์" +PythonSettings="การตั้งค่าไพธอน" +PythonSettings.PythonInstallPath32bit="ตำแหน่งติดตั้งไพธอน (32 บิต)" +PythonSettings.PythonInstallPath64bit="ตำแหน่งติดตั้งไพธอน (64 บิต)" +PythonSettings.BrowsePythonPath="ค้นหาตำแหน่งไพธอน" +PythonSettings.PythonVersion="เวอร์ชั่นของไพธอนที่โหลด: %1" +PythonSettings.PythonNotLoaded="ไพธอนยังไม่ได้โหลดในขณะนี้" +PythonSettings.AlreadyLoaded.Title="ไพธอนโหลดมาแล้ว" +PythonSettings.AlreadyLoaded.Message="โปรแกรมไพธอน %1 ที่คัดลอกมาได้โหลดเรียบร้อยแล้ว หากต้องการโหลดไพธอนเวอร์ชั่นที่เลือกมาใหม่ กรุณารีสตาร์ท OBS" +ScriptLogWindow="ข้อมูลสคริปต์" Description="คำอธิบาย" +ScriptDescriptionLink.Text="เปิดลิงก์นี้ในเบราว์เซอร์เริ่มต้นของคุณ?" +ScriptDescriptionLink.OpenURL="เปิด URL" +FileFilter.ScriptFiles="ไฟล์สคริปต์" FileFilter.AllFiles="ไฟล์ทั้งหมด" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/tt-RU.ini b/UI/frontend-plugins/frontend-tools/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..a39cd599f390f1 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/tt-RU.ini @@ -0,0 +1,11 @@ +Active="Актив" +Inactive="Активсыз" +Start="Башлау" +Stop="туктату" +Captions.AudioSource="Аудио чыганагы" +Captions.CurrentSystemLanguage="Систем хәзерге тел (%1)" +Reload="Яңадан йөкләү" +OpenFileLocation="Файл урынын ачу" +PythonSettings="Python көйләүләре" +Description="Тасвирлама" +FileFilter.AllFiles="Барлык файллар" diff --git a/plugins/aja/data/locale/tt-RU.ini b/plugins/aja/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..fa7dfae51ba725 --- /dev/null +++ b/plugins/aja/data/locale/tt-RU.ini @@ -0,0 +1,8 @@ +Device="Җайланма" +Output="Чыгыш" +Input="Кертү" +Mode="Шарт" +VideoFormat="Видео форматы" +IOSelect="Сайлау..." +ChannelFormat="Канал" +ChannelFormat.None="Юк" diff --git a/plugins/coreaudio-encoder/data/locale/tt-RU.ini b/plugins/coreaudio-encoder/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..5b99874a73fb6d --- /dev/null +++ b/plugins/coreaudio-encoder/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Bitrate="Битрейт" diff --git a/plugins/decklink/data/locale/tt-RU.ini b/plugins/decklink/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..efcef90ea5b73a --- /dev/null +++ b/plugins/decklink/data/locale/tt-RU.ini @@ -0,0 +1,7 @@ +Device="Җайланма" +ColorRange="Төс арасы" +ColorRange.Full="Тулы" +ChannelFormat="Канал" +ChannelFormat.None="Юк" +VideoConnection="Видео тоташу" +AudioConnection="Аудио тоташу" diff --git a/plugins/image-source/data/locale/it-IT.ini b/plugins/image-source/data/locale/it-IT.ini index 1ed072e4811b46..794fe7d4b9537f 100644 --- a/plugins/image-source/data/locale/it-IT.ini +++ b/plugins/image-source/data/locale/it-IT.ini @@ -9,6 +9,7 @@ SlideShow.Files="File di immagini" SlideShow.CustomSize="Dimensioni/proporzioni" SlideShow.CustomSize.Auto="Automatiche" SlideShow.Randomize="Riproduzione casuale" +SlideShow.Loop="Ripetizione" SlideShow.Transition="Transizione" SlideShow.Transition.Cut="Taglio" SlideShow.Transition.Fade="Dissolvenza" @@ -18,13 +19,13 @@ SlideShow.PlaybackBehavior="Comportamento visibilità" SlideShow.PlaybackBehavior.StopRestart="Interrompi quando non visibile, riavvia quando visibile" SlideShow.PlaybackBehavior.PauseUnpause="Pausa quando non visibile, riprendi quando visibile" SlideShow.PlaybackBehavior.AlwaysPlay="Continua sempre anche quando non visibile" -SlideShow.SlideMode="Modalità Slide" -SlideShow.SlideMode.Auto="Automatico" +SlideShow.SlideMode="Modalità diapositiva" +SlideShow.SlideMode.Auto="Automatica" SlideShow.SlideMode.Manual="Manuale (usa i tasti di scelta rapida per controllare la presentazione)" SlideShow.PlayPause="Play/Pausa" SlideShow.Restart="Riavvia" -SlideShow.NextSlide="Prossima Slide" -SlideShow.PreviousSlide="Slide Precedente" +SlideShow.NextSlide="Diapositiva successiva" +SlideShow.PreviousSlide="Diapositiva precedente" SlideShow.HideWhenDone="Nascondi quando la presentazione è terminata" ColorSource="Fonte di colore" ColorSource.Color="Colore" diff --git a/plugins/image-source/data/locale/ru-RU.ini b/plugins/image-source/data/locale/ru-RU.ini index ddc61d9cc9457a..f5a3d34ed74b4d 100644 --- a/plugins/image-source/data/locale/ru-RU.ini +++ b/plugins/image-source/data/locale/ru-RU.ini @@ -22,7 +22,7 @@ SlideShow.PlaybackBehavior.AlwaysPlay="Всегда проигрывать, да SlideShow.SlideMode="Режим перелистывания" SlideShow.SlideMode.Auto="Автоматический" SlideShow.SlideMode.Manual="Ручной (Используйте горячие клавиши для управления слайдшоу)" -SlideShow.PlayPause="Воспроизвести/Пауза" +SlideShow.PlayPause="Воспроизвести/пауза" SlideShow.Restart="Перезапустить" SlideShow.Stop="Остановить" SlideShow.NextSlide="Следующий слайд" diff --git a/plugins/image-source/data/locale/tt-RU.ini b/plugins/image-source/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..f8d8c4a09a81e5 --- /dev/null +++ b/plugins/image-source/data/locale/tt-RU.ini @@ -0,0 +1,9 @@ +ImageInput="Сурәт" +File="Сурәт файлы" +SlideShow.Files="Сурәт файллары" +SlideShow.PlayPause="Уйнату/туктатып тору" +SlideShow.Restart="Яңадан башлау" +SlideShow.Stop="туктату" +ColorSource.Color="Төс" +ColorSource.Width="Киңлек" +ColorSource.Height="Биеклек" diff --git a/plugins/linux-alsa/data/locale/th-TH.ini b/plugins/linux-alsa/data/locale/th-TH.ini index 2bd66313cc0396..94740697dc639f 100644 --- a/plugins/linux-alsa/data/locale/th-TH.ini +++ b/plugins/linux-alsa/data/locale/th-TH.ini @@ -1,2 +1,4 @@ AlsaInput="อุปกรณ์บันทึกเสียง (ALSA)" +Default="ค่าเริ่มต้น" +Custom="กำหนดเอง" Device="อุปกรณ์" diff --git a/plugins/linux-alsa/data/locale/tt-RU.ini b/plugins/linux-alsa/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..3fc7b5f4ddf757 --- /dev/null +++ b/plugins/linux-alsa/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Device="Җайланма" diff --git a/plugins/linux-capture/data/locale/af-ZA.ini b/plugins/linux-capture/data/locale/af-ZA.ini index 909d8cab43cb75..43329f0fced7db 100644 --- a/plugins/linux-capture/data/locale/af-ZA.ini +++ b/plugins/linux-capture/data/locale/af-ZA.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Skermopname (XSHM)" -Screen="Skerm" CaptureCursor="Neem muiswyser op" AdvancedSettings="Gevorderde instellings" XServer="X-bediener" diff --git a/plugins/linux-capture/data/locale/ar-SA.ini b/plugins/linux-capture/data/locale/ar-SA.ini index 78f6852ce91014..e39946cf624867 100644 --- a/plugins/linux-capture/data/locale/ar-SA.ini +++ b/plugins/linux-capture/data/locale/ar-SA.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="التقاط الشاشة (XSHM)" -Screen="الشاشة" CaptureCursor="التقاط المؤشر" AdvancedSettings="إعدادات متقدمة" XCCapture="التقاط النافذة (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/az-AZ.ini b/plugins/linux-capture/data/locale/az-AZ.ini index 06a6b37224bd14..6e6bc3746ea25a 100644 --- a/plugins/linux-capture/data/locale/az-AZ.ini +++ b/plugins/linux-capture/data/locale/az-AZ.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Ekran Yaxalama (XSHM)" -Screen="Ekran" CaptureCursor="Kursoru Yaxala" AdvancedSettings="Qabaqcıl Tənzimləmələr" XCCapture="Pəncərə Yaxalama (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/ba-RU.ini b/plugins/linux-capture/data/locale/ba-RU.ini index 8e83abfd60cd9f..770c71e1bdce1a 100644 --- a/plugins/linux-capture/data/locale/ba-RU.ini +++ b/plugins/linux-capture/data/locale/ba-RU.ini @@ -1,2 +1 @@ -Screen="Экран" XServer="X-Сервер" diff --git a/plugins/linux-capture/data/locale/be-BY.ini b/plugins/linux-capture/data/locale/be-BY.ini index f0c160226f76ca..eca52bfff52917 100644 --- a/plugins/linux-capture/data/locale/be-BY.ini +++ b/plugins/linux-capture/data/locale/be-BY.ini @@ -1,9 +1,9 @@ -X11SharedMemoryScreenInput="Захоп экрану (XSHM)" -Screen="Экран" -CaptureCursor="Захват курсору" +X11SharedMemoryDisplayInput="Захоп экрана (XSHM)" +Display="Экран" +CaptureCursor="Запісваць курсор" AdvancedSettings="Дадатковыя налады" XServer="Х Сервер" -XCCapture="Захват акна (Xcomposite)" +XCCapture="Захоп акна (Xcomposite)" Window="Акно" CropTop="Абрэзаць уверсе" CropLeft="Абрэзаць злева" diff --git a/plugins/linux-capture/data/locale/bg-BG.ini b/plugins/linux-capture/data/locale/bg-BG.ini index 5f43f4443d153c..dc97f9c4ace347 100644 --- a/plugins/linux-capture/data/locale/bg-BG.ini +++ b/plugins/linux-capture/data/locale/bg-BG.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Заснемане на екрана (XSHM)" -Screen="Екран" CaptureCursor="Заснемане на курсора" AdvancedSettings="Разширени настройки" XServer="X сървър" diff --git a/plugins/linux-capture/data/locale/bn-BD.ini b/plugins/linux-capture/data/locale/bn-BD.ini index fe7235b3cf2026..fed191844a026a 100644 --- a/plugins/linux-capture/data/locale/bn-BD.ini +++ b/plugins/linux-capture/data/locale/bn-BD.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="স্ক্রিন ক্যাপচার (XSHM)" -Screen="পর্দা" CaptureCursor="কার্সার ক্যাপচার করুন" AdvancedSettings="উন্নত সেটিংস" XServer="X সার্ভার" diff --git a/plugins/linux-capture/data/locale/ca-ES.ini b/plugins/linux-capture/data/locale/ca-ES.ini index 80b53b0834408c..539d79db00a5ef 100644 --- a/plugins/linux-capture/data/locale/ca-ES.ini +++ b/plugins/linux-capture/data/locale/ca-ES.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Captura la pantalla (XSHM)" -Screen="Pantalla" +X11SharedMemoryDisplayInput="Captura de pantalla (XSHM)" +Display="Pantalla" CaptureCursor="Captura el cursor" AdvancedSettings="Configuració avançada" XServer="Servidor X" diff --git a/plugins/linux-capture/data/locale/cs-CZ.ini b/plugins/linux-capture/data/locale/cs-CZ.ini index 50c6edcb414cd2..8ceecf063c7a93 100644 --- a/plugins/linux-capture/data/locale/cs-CZ.ini +++ b/plugins/linux-capture/data/locale/cs-CZ.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Záznam obrazovky (XSHM)" -Screen="Obrazovka" +X11SharedMemoryDisplayInput="Záznam obrazovky (XSHM)" +Display="Obrazovka" CaptureCursor="Zaznamenávat kurzor" AdvancedSettings="Rozšířená nastavení" XServer="Server X" diff --git a/plugins/linux-capture/data/locale/da-DK.ini b/plugins/linux-capture/data/locale/da-DK.ini index 169028cda1d58e..fb7d3f2fec5da8 100644 --- a/plugins/linux-capture/data/locale/da-DK.ini +++ b/plugins/linux-capture/data/locale/da-DK.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Skærmoptagelse (XSHM)" -Screen="Skærm" CaptureCursor="Optag markør" AdvancedSettings="Avancerede Indstillinger" XCCapture="Vinduesoptagelse (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/de-DE.ini b/plugins/linux-capture/data/locale/de-DE.ini index 84476fa1a8edfd..d39368951297fd 100644 --- a/plugins/linux-capture/data/locale/de-DE.ini +++ b/plugins/linux-capture/data/locale/de-DE.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Bildschirmaufnahme (XSHM)" -Screen="Bildschirm" +X11SharedMemoryDisplayInput="Monitoraufnahme (XSHM)" +Display="Monitor" CaptureCursor="Mauszeiger erfassen" AdvancedSettings="Erweiterte Einstellungen" XServer="X-Server" diff --git a/plugins/linux-capture/data/locale/el-GR.ini b/plugins/linux-capture/data/locale/el-GR.ini index 38064310a40098..43b272f482c98a 100644 --- a/plugins/linux-capture/data/locale/el-GR.ini +++ b/plugins/linux-capture/data/locale/el-GR.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Σύλληψη Οθόνης (XSHM)" -Screen="Οθόνη" CaptureCursor="Σύλληψη δρομέα" AdvancedSettings="Ρυθμίσεις για Προχωρημένους" XCCapture="Σύλληψη Παραθύρου (XComposite)" diff --git a/plugins/linux-capture/data/locale/es-ES.ini b/plugins/linux-capture/data/locale/es-ES.ini index eb561b053da318..2db362c95a090e 100644 --- a/plugins/linux-capture/data/locale/es-ES.ini +++ b/plugins/linux-capture/data/locale/es-ES.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Captura de pantalla (XSHM)" -Screen="Pantalla" +X11SharedMemoryDisplayInput="Captura de pantalla (XSHM)" +Display="Pantalla" CaptureCursor="Captura de Cursor" AdvancedSettings="Ajustes Avanzados" XCCapture="Captura de ventana (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/et-EE.ini b/plugins/linux-capture/data/locale/et-EE.ini index d974e585fa4530..54ff773c1d13aa 100644 --- a/plugins/linux-capture/data/locale/et-EE.ini +++ b/plugins/linux-capture/data/locale/et-EE.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Ekraanijäädvustus (XSHM)" -Screen="Ekraan" +X11SharedMemoryDisplayInput="Kuva salvestus (XSHM)" +Display="Kuva" CaptureCursor="Jäädvusta kursor" AdvancedSettings="Täpsemad sätted" XCCapture="Akna hõive (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/eu-ES.ini b/plugins/linux-capture/data/locale/eu-ES.ini index 3335e6fd9a456e..97129d62a52228 100644 --- a/plugins/linux-capture/data/locale/eu-ES.ini +++ b/plugins/linux-capture/data/locale/eu-ES.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Pantaila-kaptura (XSHM)" -Screen="Pantaila" CaptureCursor="Kapturatu kurtsorea" AdvancedSettings="Ezarpen aurreratuak" XServer="X Zerbitzaria" diff --git a/plugins/linux-capture/data/locale/fa-IR.ini b/plugins/linux-capture/data/locale/fa-IR.ini index bcb0ce737675f1..8822a273842b6a 100644 --- a/plugins/linux-capture/data/locale/fa-IR.ini +++ b/plugins/linux-capture/data/locale/fa-IR.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="ضبط صفحه نمایش(XSHM)" -Screen="صفحه نمایش" +X11SharedMemoryDisplayInput="ضبط صفحه نمایش (XSHM)" +Display="صفحه نمایش" CaptureCursor="ضبط مکان نمای ماوس" AdvancedSettings="تنظیمات پیشرفته" XServer="سرویس دهنده" diff --git a/plugins/linux-capture/data/locale/fi-FI.ini b/plugins/linux-capture/data/locale/fi-FI.ini index 3e7863c50c6ffb..24a0e8e1060a97 100644 --- a/plugins/linux-capture/data/locale/fi-FI.ini +++ b/plugins/linux-capture/data/locale/fi-FI.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Ruudunkaappaus (XSHM)" -Screen="Kuvaruutu" +X11SharedMemoryDisplayInput="Näytön kaappaus (XSHM)" +Display="Näyttö" CaptureCursor="Kaappaa kursori" AdvancedSettings="Lisäasetukset" XServer="X-palvelin" diff --git a/plugins/linux-capture/data/locale/fr-FR.ini b/plugins/linux-capture/data/locale/fr-FR.ini index 076efe0f7ab5d5..cc0540b8fd3eb8 100644 --- a/plugins/linux-capture/data/locale/fr-FR.ini +++ b/plugins/linux-capture/data/locale/fr-FR.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Capture d'écran (XSHM)" -Screen="Écran" +X11SharedMemoryDisplayInput="Capture de l'affichage (XSHM)" +Display="Affichage" CaptureCursor="Capturer le curseur" AdvancedSettings="Paramètres avancés" XServer="Serveur X" diff --git a/plugins/linux-capture/data/locale/gd-GB.ini b/plugins/linux-capture/data/locale/gd-GB.ini index 673895aaf0c4ae..645583f643cc97 100644 --- a/plugins/linux-capture/data/locale/gd-GB.ini +++ b/plugins/linux-capture/data/locale/gd-GB.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Glacadh-sgrìn (XSHM)" -Screen="Sgrìn" CaptureCursor="Glac an cùrsair" AdvancedSettings="Roghainnean adhartach" XServer="Frithealaiche X" diff --git a/plugins/linux-capture/data/locale/gl-ES.ini b/plugins/linux-capture/data/locale/gl-ES.ini index 22b57480420501..857b28d61f2058 100644 --- a/plugins/linux-capture/data/locale/gl-ES.ini +++ b/plugins/linux-capture/data/locale/gl-ES.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Captura de pantalla (XSHM)" -Screen="Pantalla" CaptureCursor="Capturar o cursor" AdvancedSettings="Axustes avanzados" XServer="Servidor das X" diff --git a/plugins/linux-capture/data/locale/he-IL.ini b/plugins/linux-capture/data/locale/he-IL.ini index a36c2a47d307ec..e39d30c4977d83 100644 --- a/plugins/linux-capture/data/locale/he-IL.ini +++ b/plugins/linux-capture/data/locale/he-IL.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="הקלטת מסך (XSHM)" -Screen="מסך" +X11SharedMemoryDisplayInput="הקלטת תצוגה (XSHM)" +Display="תצוגה" CaptureCursor="הקלטת סמן" AdvancedSettings="הגדרות מתקדמות" XServer="שרת X" diff --git a/plugins/linux-capture/data/locale/hi-IN.ini b/plugins/linux-capture/data/locale/hi-IN.ini index e85b07009ce96d..80ab60852666c9 100644 --- a/plugins/linux-capture/data/locale/hi-IN.ini +++ b/plugins/linux-capture/data/locale/hi-IN.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="स्क्रीन कैप्चर (XSHM)" -Screen="स्क्रीन" CaptureCursor="कर्सर कैप्चर करें" AdvancedSettings="उन्नत सेटिंग्स" XServer="एक्स सर्वर" diff --git a/plugins/linux-capture/data/locale/hr-HR.ini b/plugins/linux-capture/data/locale/hr-HR.ini index 1add55e200c1f0..4c9450f32548c3 100644 --- a/plugins/linux-capture/data/locale/hr-HR.ini +++ b/plugins/linux-capture/data/locale/hr-HR.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Snimanje ekrana (XSHM)" -Screen="Ekran" CaptureCursor="Snimanje pokazivača" AdvancedSettings="Napredne postavke" XServer="X poslužitelj" diff --git a/plugins/linux-capture/data/locale/hu-HU.ini b/plugins/linux-capture/data/locale/hu-HU.ini index 905ecd25cc0936..3288e81b4749b6 100644 --- a/plugins/linux-capture/data/locale/hu-HU.ini +++ b/plugins/linux-capture/data/locale/hu-HU.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Képernyő felvétele (XSHM)" -Screen="Képernyő" +X11SharedMemoryDisplayInput="Kijelző felvétele (XSHM)" +Display="Kijelző" CaptureCursor="Kurzor felvétele" AdvancedSettings="Speciális beállítások" XServer="X Szerver" diff --git a/plugins/linux-capture/data/locale/hy-AM.ini b/plugins/linux-capture/data/locale/hy-AM.ini index 2d9015ca72de5c..5866e6bc14c9cb 100644 --- a/plugins/linux-capture/data/locale/hy-AM.ini +++ b/plugins/linux-capture/data/locale/hy-AM.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Էկրանի տեսագրիչ (XSHM)" -Screen="Էկրան" CaptureCursor="Կուրսորի գրավում" AdvancedSettings="Ընդլայնված կարգավորումներ" XServer="X-սերվեր" diff --git a/plugins/linux-capture/data/locale/id-ID.ini b/plugins/linux-capture/data/locale/id-ID.ini index 7907e136679538..649aab20bb6d18 100644 --- a/plugins/linux-capture/data/locale/id-ID.ini +++ b/plugins/linux-capture/data/locale/id-ID.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Penangkap Layar (XSHM)" -Screen="Layar" +X11SharedMemoryDisplayInput="Penangkap Layar (XSHM)" +Display="Tampilan" CaptureCursor="Tangkap Kursor" AdvancedSettings="Pengaturan Lanjutan" XCCapture="Penangkap Jendela (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/it-IT.ini b/plugins/linux-capture/data/locale/it-IT.ini index 2a321c9f437b45..16c5131bf8a322 100644 --- a/plugins/linux-capture/data/locale/it-IT.ini +++ b/plugins/linux-capture/data/locale/it-IT.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Cattura schermo (XSHM)" -Screen="Schermo" +X11SharedMemoryDisplayInput="Cattura schermo (XSHM)" +Display="Schermo" CaptureCursor="Cattura il cursore" AdvancedSettings="Impostazioni avanzate" XCCapture="Cattura la finestra (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/ja-JP.ini b/plugins/linux-capture/data/locale/ja-JP.ini index 7f6eecab10586e..c085ddc9bb64b6 100644 --- a/plugins/linux-capture/data/locale/ja-JP.ini +++ b/plugins/linux-capture/data/locale/ja-JP.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="スクリーンキャプチャ (XSHM)" -Screen="画面" +X11SharedMemoryDisplayInput="画面キャプチャ (XSHM)" +Display="ディスプレイ" CaptureCursor="カーソルをキャプチャする" AdvancedSettings="高度な設定" XServer="X サーバー" diff --git a/plugins/linux-capture/data/locale/ka-GE.ini b/plugins/linux-capture/data/locale/ka-GE.ini index 7a0061f683be45..34fd23b5ec0ebb 100644 --- a/plugins/linux-capture/data/locale/ka-GE.ini +++ b/plugins/linux-capture/data/locale/ka-GE.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="ეკრანის გადაღება (XSHM)" -Screen="ეკრანი" CaptureCursor="მაჩვენებლის ასახვა" AdvancedSettings="დამატებითი პარამეტრები" XServer="X სერვერი" diff --git a/plugins/linux-capture/data/locale/kab-KAB.ini b/plugins/linux-capture/data/locale/kab-KAB.ini index 6efb7076f13285..5dc86e1a0788c5 100644 --- a/plugins/linux-capture/data/locale/kab-KAB.ini +++ b/plugins/linux-capture/data/locale/kab-KAB.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Tuṭṭfa n ugdil (XSHM)" -Screen="Agdil" CaptureCursor="Ṭṭef taḥnaccaḍt" AdvancedSettings="Iɣewwaren inaẓiyen" XServer="Aqeddac X" diff --git a/plugins/linux-capture/data/locale/kmr-TR.ini b/plugins/linux-capture/data/locale/kmr-TR.ini index 1cb5db9efcd688..61b84c369b5fb8 100644 --- a/plugins/linux-capture/data/locale/kmr-TR.ini +++ b/plugins/linux-capture/data/locale/kmr-TR.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Girtina wêneya dîmenderê (XSHM)" -Screen="Dîmender" CaptureCursor="Nîşankerê bigire" AdvancedSettings="Sazkariyên pêşketî" XServer="Rajekara X" diff --git a/plugins/linux-capture/data/locale/ko-KR.ini b/plugins/linux-capture/data/locale/ko-KR.ini index 315f31c453b01d..6b938f033e0533 100644 --- a/plugins/linux-capture/data/locale/ko-KR.ini +++ b/plugins/linux-capture/data/locale/ko-KR.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="화면 캡쳐 (XSHM)" -Screen="화면" +X11SharedMemoryDisplayInput="디스플레이 캡처 (XSHM)" +Display="디스플레이" CaptureCursor="커서 캡쳐" AdvancedSettings="고급 설정" XServer="X 서버" diff --git a/plugins/linux-capture/data/locale/lo-LA.ini b/plugins/linux-capture/data/locale/lo-LA.ini index 2d1770ae0b0503..67aa2728a4565e 100644 --- a/plugins/linux-capture/data/locale/lo-LA.ini +++ b/plugins/linux-capture/data/locale/lo-LA.ini @@ -1,2 +1 @@ -Screen="ໜ້າຈໍ" CaptureCursor="ອັດເອົາລູກສອນເມົ້າສ໌ ນຳ" diff --git a/plugins/linux-capture/data/locale/ms-MY.ini b/plugins/linux-capture/data/locale/ms-MY.ini index db7dd3295733d3..7915e8eacdea55 100644 --- a/plugins/linux-capture/data/locale/ms-MY.ini +++ b/plugins/linux-capture/data/locale/ms-MY.ini @@ -1,4 +1,5 @@ -Screen="Skrin" +X11SharedMemoryDisplayInput="Tangkap Paparan (XSHM)" +Display="Paparan" CaptureCursor="Tangkap Kursor" AdvancedSettings="Tetapan Lanjutan" XServer="Pelayan X" diff --git a/plugins/linux-capture/data/locale/nb-NO.ini b/plugins/linux-capture/data/locale/nb-NO.ini index 40fcfec10b8fd4..136bc4d1a65d60 100644 --- a/plugins/linux-capture/data/locale/nb-NO.ini +++ b/plugins/linux-capture/data/locale/nb-NO.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Skjermopptak (XSHM)" -Screen="Skjerm" CaptureCursor="Ta opp musepeker" AdvancedSettings="Avanserte innstillinger" XServer="X-tjener" diff --git a/plugins/linux-capture/data/locale/nl-NL.ini b/plugins/linux-capture/data/locale/nl-NL.ini index b443e24d111b13..3dd47d591f51fd 100644 --- a/plugins/linux-capture/data/locale/nl-NL.ini +++ b/plugins/linux-capture/data/locale/nl-NL.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Schermopname (XSHM)" -Screen="Scherm" +X11SharedMemoryDisplayInput="Beeldschermopname (XSHM)" +Display="Beeldscherm" CaptureCursor="Cursor Opnemen" AdvancedSettings="Geavanceerde instellingen" XCCapture="Venstercapture (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/nn-NO.ini b/plugins/linux-capture/data/locale/nn-NO.ini index 6b4161afe49458..8f82be57187101 100644 --- a/plugins/linux-capture/data/locale/nn-NO.ini +++ b/plugins/linux-capture/data/locale/nn-NO.ini @@ -1,3 +1,2 @@ -Screen="Skjerm" AdvancedSettings="Avanserte innstillingar" Window="Vindauge" diff --git a/plugins/linux-capture/data/locale/pl-PL.ini b/plugins/linux-capture/data/locale/pl-PL.ini index fdc3312c7968f1..0dfaf0af3d1f0a 100644 --- a/plugins/linux-capture/data/locale/pl-PL.ini +++ b/plugins/linux-capture/data/locale/pl-PL.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Przechwytywanie ekranu (XSHM)" -Screen="Ekran" +X11SharedMemoryDisplayInput="Przechwytywanie ekranu (XSHM)" +Display="Ekran" CaptureCursor="Przechwytywanie kursora" AdvancedSettings="Ustawienia zaawansowane" XCCapture="Przechwytywanie okna (XComposite)" diff --git a/plugins/linux-capture/data/locale/pt-BR.ini b/plugins/linux-capture/data/locale/pt-BR.ini index d1ee23427519f2..113bb78ccac41b 100644 --- a/plugins/linux-capture/data/locale/pt-BR.ini +++ b/plugins/linux-capture/data/locale/pt-BR.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Captura de Tela (XSHM)" -Screen="Tela" +X11SharedMemoryDisplayInput="Captura de tela (XSHM)" +Display="Tela" CaptureCursor="Capturar o cursor" AdvancedSettings="Configurações Avançadas" XServer="Servidor x" @@ -10,4 +10,4 @@ CropLeft="Cortar Equerda" CropRight="Cortar Direita" CropBottom="Cortar Inferior" IncludeXBorder="Incluir X Borda" -ExcludeAlpha="Utilizar formato de textura sem alpha (solução para Mesa)" +ExcludeAlpha="Utilizar formato de textura sem alfa (solução para Mesa)" diff --git a/plugins/linux-capture/data/locale/pt-PT.ini b/plugins/linux-capture/data/locale/pt-PT.ini index bb6afc4733df9c..9c48e5b340dc9a 100644 --- a/plugins/linux-capture/data/locale/pt-PT.ini +++ b/plugins/linux-capture/data/locale/pt-PT.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Captura de ecrã (XSHM)" -Screen="Ecrã" +X11SharedMemoryDisplayInput="Captura de ecrã" +Display="Ecrã" CaptureCursor="Capturar cursor" AdvancedSettings="Definições avançadas" XCCapture="Captura de janela (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/ro-RO.ini b/plugins/linux-capture/data/locale/ro-RO.ini index fc9f6e2fed36a3..cce397682ca321 100644 --- a/plugins/linux-capture/data/locale/ro-RO.ini +++ b/plugins/linux-capture/data/locale/ro-RO.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Captură de ecran (XSHM)" -Screen="Ecran" CaptureCursor="Capturează cursorul" AdvancedSettings="Setări avansate" XCCapture="Captură de fereastră (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/ru-RU.ini b/plugins/linux-capture/data/locale/ru-RU.ini index b974757cfb0890..d27c75c861a61e 100644 --- a/plugins/linux-capture/data/locale/ru-RU.ini +++ b/plugins/linux-capture/data/locale/ru-RU.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Захват экрана (XSHM)" -Screen="Экран" +X11SharedMemoryDisplayInput="Захват экрана (XSHM)" +Display="Экран" CaptureCursor="Захватывать курсор" AdvancedSettings="Расширенные настройки" XServer="X-сервер" diff --git a/plugins/linux-capture/data/locale/si-LK.ini b/plugins/linux-capture/data/locale/si-LK.ini index 97aaca788dc9b7..9cb4231a3fa1a4 100644 --- a/plugins/linux-capture/data/locale/si-LK.ini +++ b/plugins/linux-capture/data/locale/si-LK.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="තිරය ග්‍රහණය (XSHM)" -Screen="තිරය" CaptureCursor="ඊතලය ග්‍රහණය" AdvancedSettings="වැඩිදුර සැකසුම්" XServer="X සේවාදායකය" diff --git a/plugins/linux-capture/data/locale/sk-SK.ini b/plugins/linux-capture/data/locale/sk-SK.ini index f787ae8fdc220b..f2bb789bafcdb9 100644 --- a/plugins/linux-capture/data/locale/sk-SK.ini +++ b/plugins/linux-capture/data/locale/sk-SK.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Zachytávanie obrazovky (XSHM)" -Screen="Monitor" +X11SharedMemoryDisplayInput="Zachytávanie monitora (XSHM)" +Display="Monitor" CaptureCursor="Zachytávať kurzor" AdvancedSettings="Rozšírené nastavenia" XCCapture="Zachytávanie okna (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/sl-SI.ini b/plugins/linux-capture/data/locale/sl-SI.ini index dc358d546cdc4c..1aaf8abdd1afe9 100644 --- a/plugins/linux-capture/data/locale/sl-SI.ini +++ b/plugins/linux-capture/data/locale/sl-SI.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Zajemanje zaslona (XSHM)" -Screen="Zaslon" +X11SharedMemoryDisplayInput="Zajem zaslona (XSHM)" +Display="Zaslon" CaptureCursor="Zajemi kazalec" AdvancedSettings="Napredne nastavitve" XServer="X strežnik" diff --git a/plugins/linux-capture/data/locale/sr-CS.ini b/plugins/linux-capture/data/locale/sr-CS.ini index a1acc4699271e8..9cd8d1795b9b48 100644 --- a/plugins/linux-capture/data/locale/sr-CS.ini +++ b/plugins/linux-capture/data/locale/sr-CS.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Snimak ekrana (XSHM)" -Screen="Екран" CaptureCursor="Снимај курсор" AdvancedSettings="Napredna podešavanja" XServer="X server" diff --git a/plugins/linux-capture/data/locale/sr-SP.ini b/plugins/linux-capture/data/locale/sr-SP.ini index 2e96a885025676..7ec264af9bc940 100644 --- a/plugins/linux-capture/data/locale/sr-SP.ini +++ b/plugins/linux-capture/data/locale/sr-SP.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Снимак екрана (XSHM)" -Screen="Ekran" CaptureCursor="Snimaj kursor" AdvancedSettings="Напредна подешавања" XServer="X сервер" diff --git a/plugins/linux-capture/data/locale/sv-SE.ini b/plugins/linux-capture/data/locale/sv-SE.ini index c8001b2a76dcbf..38c310c3545329 100644 --- a/plugins/linux-capture/data/locale/sv-SE.ini +++ b/plugins/linux-capture/data/locale/sv-SE.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Bildskärmskälla (XSHM)" -Screen="Skärm" +X11SharedMemoryDisplayInput="Bildskärmskälla (XSHM)" +Display="Bildskärm" CaptureCursor="Visa muspekare" AdvancedSettings="Avancerade inställningar" XServer="X-servern" diff --git a/plugins/linux-capture/data/locale/szl-PL.ini b/plugins/linux-capture/data/locale/szl-PL.ini index 31356f9cf46b1c..f60c63b6614606 100644 --- a/plugins/linux-capture/data/locale/szl-PL.ini +++ b/plugins/linux-capture/data/locale/szl-PL.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Chytanie ekranu (XSHM)" -Screen="Ekran" CaptureCursor="Chytej kursōr" AdvancedSettings="Zaawansowane ôpcyje" XCCapture="Chytanie ôkna (XComposite)" diff --git a/plugins/linux-capture/data/locale/ta-IN.ini b/plugins/linux-capture/data/locale/ta-IN.ini index 8bb6b9e4955a53..29c5346529418a 100644 --- a/plugins/linux-capture/data/locale/ta-IN.ini +++ b/plugins/linux-capture/data/locale/ta-IN.ini @@ -1,3 +1,2 @@ -Screen="காட்சிதிரை" AdvancedSettings="மேம்பட்ட அமைப்புகள்" Window="சாளரம்" diff --git a/plugins/linux-capture/data/locale/tl-PH.ini b/plugins/linux-capture/data/locale/tl-PH.ini index bf0f76d2d54e20..26132e54175b94 100644 --- a/plugins/linux-capture/data/locale/tl-PH.ini +++ b/plugins/linux-capture/data/locale/tl-PH.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="Pamagat ng Screen (XSHM)" -Screen="Ang Screen" CaptureCursor="Pamagat ng kursor" AdvancedSettings="Ang mga nauunang setting" XServer="X tagapagsilbi" diff --git a/plugins/linux-capture/data/locale/tr-TR.ini b/plugins/linux-capture/data/locale/tr-TR.ini index b68a55c67bd1bc..0c0948f03edcd5 100644 --- a/plugins/linux-capture/data/locale/tr-TR.ini +++ b/plugins/linux-capture/data/locale/tr-TR.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Ekran Yakalama (XSHM)" -Screen="Ekran" +X11SharedMemoryDisplayInput="Ekran Yakalama (XSHM)" +Display="Ekran" CaptureCursor="İmleci Yakala" AdvancedSettings="Gelişmiş Ayarlar" XServer="X Sunucusu" diff --git a/plugins/linux-capture/data/locale/tt-RU.ini b/plugins/linux-capture/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..995c384da62ea6 --- /dev/null +++ b/plugins/linux-capture/data/locale/tt-RU.ini @@ -0,0 +1,2 @@ +AdvancedSettings="Киңәйтелгән көйләүләр" +Window="Тәрәзә" diff --git a/plugins/linux-capture/data/locale/ug-CN.ini b/plugins/linux-capture/data/locale/ug-CN.ini index 9beb9a34c6526d..1f79d44530701a 100644 --- a/plugins/linux-capture/data/locale/ug-CN.ini +++ b/plugins/linux-capture/data/locale/ug-CN.ini @@ -1,5 +1,3 @@ -X11SharedMemoryScreenInput="ئېكران تۇتۇش (XSHM)" -Screen="ئېكران" CaptureCursor="سىنبەلگە تۇت" AdvancedSettings="ئالىي تەڭشەكلەر" XServer="X مۇلازىمېتىر" diff --git a/plugins/linux-capture/data/locale/uk-UA.ini b/plugins/linux-capture/data/locale/uk-UA.ini index 617e22af66169f..222598e95a848f 100644 --- a/plugins/linux-capture/data/locale/uk-UA.ini +++ b/plugins/linux-capture/data/locale/uk-UA.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Захоплення екрану (XSHM)" -Screen="Екран" +X11SharedMemoryDisplayInput="Захоплення екрана (XSHM)" +Display="Екран" CaptureCursor="Захоплювати курсор" AdvancedSettings="Розширені параметри" XCCapture="Захоплення вікна (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/vi-VN.ini b/plugins/linux-capture/data/locale/vi-VN.ini index 24ba4433ede344..e09f3bd2a047ff 100644 --- a/plugins/linux-capture/data/locale/vi-VN.ini +++ b/plugins/linux-capture/data/locale/vi-VN.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="Quay màn hỉnh (XSHM)" -Screen="Màn hình" +X11SharedMemoryDisplayInput="Ghi màn hình (XSHM)" +Display="Màn hình" CaptureCursor="Quay cả con trỏ" AdvancedSettings="Thiết đặt nâng cao" XCCapture="Quay cửa sổ (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/zh-CN.ini b/plugins/linux-capture/data/locale/zh-CN.ini index d5333c12c886a7..aa1fe234b355db 100644 --- a/plugins/linux-capture/data/locale/zh-CN.ini +++ b/plugins/linux-capture/data/locale/zh-CN.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="屏幕采集 (XSHM)" -Screen="屏幕" +X11SharedMemoryDisplayInput="显示器采集 (XSHM)" +Display="显示器" CaptureCursor="显示鼠标指针" AdvancedSettings="高级设置" XCCapture="窗口采集 (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/zh-TW.ini b/plugins/linux-capture/data/locale/zh-TW.ini index be1e002c1de76d..53a83b733765e6 100644 --- a/plugins/linux-capture/data/locale/zh-TW.ini +++ b/plugins/linux-capture/data/locale/zh-TW.ini @@ -1,5 +1,5 @@ -X11SharedMemoryScreenInput="截取螢幕(XSHM)" -Screen="螢幕" +X11SharedMemoryDisplayInput="擷取螢幕輸出 (XSHM)" +Display="顯示器" CaptureCursor="捕捉游標" AdvancedSettings="進階設定" XServer="X server" diff --git a/plugins/linux-jack/data/locale/th-TH.ini b/plugins/linux-jack/data/locale/th-TH.ini new file mode 100644 index 00000000000000..06b15587fe7c4f --- /dev/null +++ b/plugins/linux-jack/data/locale/th-TH.ini @@ -0,0 +1,2 @@ +StartJACKServer="เริ่มเซิร์ฟเวอร์ JACK" +Channels="จำนวนช่อง" diff --git a/plugins/linux-jack/data/locale/tt-RU.ini b/plugins/linux-jack/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..e5190adba432e3 --- /dev/null +++ b/plugins/linux-jack/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Channels="Канал саны" diff --git a/plugins/linux-pipewire/data/locale/ar-SA.ini b/plugins/linux-pipewire/data/locale/ar-SA.ini index b3413e3894f0f4..3d5767d1e461d1 100644 --- a/plugins/linux-pipewire/data/locale/ar-SA.ini +++ b/plugins/linux-pipewire/data/locale/ar-SA.ini @@ -1,5 +1,6 @@ CameraControls="التحكم في الكاميرا" FrameRate="معدل الإطارات" +PipeWireCamera="جهاز التقاط التسجيل(بايب واير) (النسخة الأولية)" PipeWireCameraDevice="الجهاز" PipeWireDesktopCapture="التقاط الشاشة (PipeWire)" PipeWireSelectMonitor="حدد الشاشة" diff --git a/plugins/linux-pipewire/data/locale/be-BY.ini b/plugins/linux-pipewire/data/locale/be-BY.ini new file mode 100644 index 00000000000000..791d68e49b2eff --- /dev/null +++ b/plugins/linux-pipewire/data/locale/be-BY.ini @@ -0,0 +1,12 @@ +CameraControls="Кіраванне камерай" +FrameRate="Частата кадраў" +PipeWireCamera="Прылада відэазахопу (PipeWire) (БЭТА)" +PipeWireCameraDevice="Прылада" +PipeWireDesktopCapture="Захоп экрана (PipeWire)" +PipeWireSelectMonitor="Выбраць манітор" +PipeWireSelectWindow="Выбраць акно" +PipeWireWindowCapture="Захоп акна (PipeWire)" +PipeWireSelectScreenCast="Адкрыць меню выбару" +Resolution="Раздзяляльнасць" +ShowCursor="Паказваць курсор" +VideoFormat="Фармат відэа" diff --git a/plugins/linux-pipewire/data/locale/ca-ES.ini b/plugins/linux-pipewire/data/locale/ca-ES.ini index de596da20d9473..3b15a04ae1c0bb 100644 --- a/plugins/linux-pipewire/data/locale/ca-ES.ini +++ b/plugins/linux-pipewire/data/locale/ca-ES.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Captura de pantalla (PipeWire)" PipeWireSelectMonitor="Seleccioneu el monitor" PipeWireSelectWindow="Seleccioneu la finestra" PipeWireWindowCapture="Captura de finestres (PipeWire)" +PipeWireSelectScreenCast="Obre el selector" Resolution="Resolució" ShowCursor="Mostra el cursor" VideoFormat="Format de vídeo" diff --git a/plugins/linux-pipewire/data/locale/cs-CZ.ini b/plugins/linux-pipewire/data/locale/cs-CZ.ini index 59d84789595b6e..b53cdf965f46f8 100644 --- a/plugins/linux-pipewire/data/locale/cs-CZ.ini +++ b/plugins/linux-pipewire/data/locale/cs-CZ.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Záznam obrazovky (PipeWire)" PipeWireSelectMonitor="Vybrat monitor" PipeWireSelectWindow="Vybrat okno" PipeWireWindowCapture="Záznam okna (PipeWire)" +PipeWireSelectScreenCast="Vybrat" Resolution="Rozlišení" ShowCursor="Zobrazit kurzor" VideoFormat="Formát videa" diff --git a/plugins/linux-pipewire/data/locale/de-DE.ini b/plugins/linux-pipewire/data/locale/de-DE.ini index a7d0131fe0628e..388bcd0348f756 100644 --- a/plugins/linux-pipewire/data/locale/de-DE.ini +++ b/plugins/linux-pipewire/data/locale/de-DE.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Bildschirmaufnahme (PipeWire)" PipeWireSelectMonitor="Bildschirm auswählen" PipeWireSelectWindow="Fenster auswählen" PipeWireWindowCapture="Fensteraufname (PipeWire)" +PipeWireSelectScreenCast="Auswahl öffnen" Resolution="Auflösung" ShowCursor="Mauszeiger anzeigen" VideoFormat="Videoformat" diff --git a/plugins/linux-pipewire/data/locale/es-ES.ini b/plugins/linux-pipewire/data/locale/es-ES.ini index 284737fd304c89..28bf5769e827da 100644 --- a/plugins/linux-pipewire/data/locale/es-ES.ini +++ b/plugins/linux-pipewire/data/locale/es-ES.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Captura de pantalla (PipeWire)" PipeWireSelectMonitor="Seleccionar monitor" PipeWireSelectWindow="Seleccionar ventana" PipeWireWindowCapture="Captura de ventana (PipeWire)" +PipeWireSelectScreenCast="Abrir el selector" Resolution="Resolución" ShowCursor="Mostrar cursor" VideoFormat="Formato de vídeo" diff --git a/plugins/linux-pipewire/data/locale/fa-IR.ini b/plugins/linux-pipewire/data/locale/fa-IR.ini index 36a8be2bd3eef0..4c459ae4d017b8 100644 --- a/plugins/linux-pipewire/data/locale/fa-IR.ini +++ b/plugins/linux-pipewire/data/locale/fa-IR.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="ضبط صفحه (سیم لوله)" PipeWireSelectMonitor="گزینش نمایشگر" PipeWireSelectWindow="گزینش پنجره" PipeWireWindowCapture="ضبط پنجره (پایپ‌وایر)" +PipeWireSelectScreenCast="انتخابگر را باز کنید" Resolution="رزولوشن" ShowCursor="نمایش مکان نما" VideoFormat="فرمت ویدیو" diff --git a/plugins/linux-pipewire/data/locale/fi-FI.ini b/plugins/linux-pipewire/data/locale/fi-FI.ini index 0f14fe65de87c3..57273980463eb9 100644 --- a/plugins/linux-pipewire/data/locale/fi-FI.ini +++ b/plugins/linux-pipewire/data/locale/fi-FI.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Näytönkaappaus (PipeWire)" PipeWireSelectMonitor="Valitse näyttö" PipeWireSelectWindow="Valitse ikkuna" PipeWireWindowCapture="Ikkunankaappaus (PipeWire)" +PipeWireSelectScreenCast="Avaa valitsin" Resolution="Resoluutio" ShowCursor="Näytä kursori" VideoFormat="Videomuoto" diff --git a/plugins/linux-pipewire/data/locale/fr-FR.ini b/plugins/linux-pipewire/data/locale/fr-FR.ini index c79c8760211b50..7e9c1f00c7a1de 100644 --- a/plugins/linux-pipewire/data/locale/fr-FR.ini +++ b/plugins/linux-pipewire/data/locale/fr-FR.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Capture d'écran (PipeWire)" PipeWireSelectMonitor="Sélectionner l'écran" PipeWireSelectWindow="Sélectionner la fenêtre" PipeWireWindowCapture="Capture de fenêtre (PipeWire)" +PipeWireSelectScreenCast="Ouvrir le sélecteur" Resolution="Résolution" ShowCursor="Afficher le curseur" VideoFormat="Format vidéo" diff --git a/plugins/linux-pipewire/data/locale/he-IL.ini b/plugins/linux-pipewire/data/locale/he-IL.ini index 771ef9ce7f5972..e1aec09d679e7a 100644 --- a/plugins/linux-pipewire/data/locale/he-IL.ini +++ b/plugins/linux-pipewire/data/locale/he-IL.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="הקלטת מסך (PipeWire)" PipeWireSelectMonitor="בחירת צג" PipeWireSelectWindow="בחירת חלון" PipeWireWindowCapture="הקלטת חלון (PipeWire)" +PipeWireSelectScreenCast="פתיחת בורר" Resolution="רזולוציה" ShowCursor="הצגת הסמן" VideoFormat="תצורת וידאו" diff --git a/plugins/linux-pipewire/data/locale/hu-HU.ini b/plugins/linux-pipewire/data/locale/hu-HU.ini index a56d8f710018fc..edef160d9d35d3 100644 --- a/plugins/linux-pipewire/data/locale/hu-HU.ini +++ b/plugins/linux-pipewire/data/locale/hu-HU.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Képernyő felvétele (PipeWire)" PipeWireSelectMonitor="Monitor kiválasztása" PipeWireSelectWindow="Ablak kiválasztása" PipeWireWindowCapture="Ablak felvétele (PipeWire)" +PipeWireSelectScreenCast="Kiválasztó megnyitása" Resolution="Felbontás" ShowCursor="Kurzor megjelenítése" VideoFormat="Videóformátum" diff --git a/plugins/linux-pipewire/data/locale/id-ID.ini b/plugins/linux-pipewire/data/locale/id-ID.ini index f2355dc707f5ff..23586fd89b0c59 100644 --- a/plugins/linux-pipewire/data/locale/id-ID.ini +++ b/plugins/linux-pipewire/data/locale/id-ID.ini @@ -5,6 +5,7 @@ PipeWireDesktopCapture="Penangkap Layar (PipeWire)" PipeWireSelectMonitor="Pilih Monitor" PipeWireSelectWindow="Pilih Jendela" PipeWireWindowCapture="Penangkap Jendela (PipeWire)" +PipeWireSelectScreenCast="Buka Penyeleksi" Resolution="Resolusi" ShowCursor="Tampilkan Kursor" VideoFormat="Format Video" diff --git a/plugins/linux-pipewire/data/locale/it-IT.ini b/plugins/linux-pipewire/data/locale/it-IT.ini index 7de110daabda5e..3cd7e9b97b5383 100644 --- a/plugins/linux-pipewire/data/locale/it-IT.ini +++ b/plugins/linux-pipewire/data/locale/it-IT.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Cattura schermo (PipeWire)" PipeWireSelectMonitor="Seleziona monitor" PipeWireSelectWindow="Seleziona finestra" PipeWireWindowCapture="Cattura finestra (PipeWire)" +PipeWireSelectScreenCast="Apri selettore" Resolution="Risoluzione" ShowCursor="Visualizza cursore" VideoFormat="Formato video" diff --git a/plugins/linux-pipewire/data/locale/ja-JP.ini b/plugins/linux-pipewire/data/locale/ja-JP.ini index a2aa3378b4208d..e34c21fe24623c 100644 --- a/plugins/linux-pipewire/data/locale/ja-JP.ini +++ b/plugins/linux-pipewire/data/locale/ja-JP.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="スクリーンキャプチャ (PipeWire)" PipeWireSelectMonitor="モニターを選択" PipeWireSelectWindow="ウィンドウを選択" PipeWireWindowCapture="ウィンドウキャプチャ (PipeWire)" +PipeWireSelectScreenCast="セレクターを開く" Resolution="解像度" ShowCursor="カーソルを表示する" VideoFormat="映像フォーマット" diff --git a/plugins/linux-pipewire/data/locale/ko-KR.ini b/plugins/linux-pipewire/data/locale/ko-KR.ini index 8921a60a9116b6..73fa7c2174ac0d 100644 --- a/plugins/linux-pipewire/data/locale/ko-KR.ini +++ b/plugins/linux-pipewire/data/locale/ko-KR.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="화면 캡쳐 (PipeWire)" PipeWireSelectMonitor="화면 선택" PipeWireSelectWindow="창 선택" PipeWireWindowCapture="윈도우 캡쳐 (PipeWire)" +PipeWireSelectScreenCast="선택기 열기" Resolution="해상도" ShowCursor="커서 표시" VideoFormat="비디오 형식" diff --git a/plugins/linux-pipewire/data/locale/ms-MY.ini b/plugins/linux-pipewire/data/locale/ms-MY.ini index ce5930879c066b..d151111a74c29b 100644 --- a/plugins/linux-pipewire/data/locale/ms-MY.ini +++ b/plugins/linux-pipewire/data/locale/ms-MY.ini @@ -1,5 +1,12 @@ +CameraControls="Kawalan Kamera" +FrameRate="Kadar Bingkai" +PipeWireCamera="Peranti Tangkapan Video Capture (PipeWire) (BETA)" +PipeWireCameraDevice="Peranti" PipeWireDesktopCapture="Tangkap Skrin (PipeWire)" PipeWireSelectMonitor="Pilih Monitor" PipeWireSelectWindow="Pilih Tetingkap" PipeWireWindowCapture="Tangkap Tetingkap (PipeWire)" +PipeWireSelectScreenCast="Buka Pemilih" +Resolution="Resolusi" ShowCursor="Tunjuk Kursor" +VideoFormat="Format Video" diff --git a/plugins/linux-pipewire/data/locale/nl-NL.ini b/plugins/linux-pipewire/data/locale/nl-NL.ini index c0df004199b413..2f2773e3e438e9 100644 --- a/plugins/linux-pipewire/data/locale/nl-NL.ini +++ b/plugins/linux-pipewire/data/locale/nl-NL.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Schermopname (PipeWire)" PipeWireSelectMonitor="Beeldscherm selecteren" PipeWireSelectWindow="Venster selecteren" PipeWireWindowCapture="Vensteropname (PipeWire)" +PipeWireSelectScreenCast="Open keuzemenu" Resolution="Resolutie" ShowCursor="Cursor Weergeven" VideoFormat="Video-indeling" diff --git a/plugins/linux-pipewire/data/locale/pl-PL.ini b/plugins/linux-pipewire/data/locale/pl-PL.ini index 8eaa3c10606702..80a9396e35647d 100644 --- a/plugins/linux-pipewire/data/locale/pl-PL.ini +++ b/plugins/linux-pipewire/data/locale/pl-PL.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Przechwytywanie ekranu (PipeWire)" PipeWireSelectMonitor="Wybierz monitor" PipeWireSelectWindow="Wybierz okno" PipeWireWindowCapture="Przechwytywanie okna (PipeWire)" +PipeWireSelectScreenCast="Wybierz źródło" Resolution="Rozdzielczość" ShowCursor="Pokaż kursor" VideoFormat="Format wideo" diff --git a/plugins/linux-pipewire/data/locale/pt-BR.ini b/plugins/linux-pipewire/data/locale/pt-BR.ini index 0ae724349b4dc7..3b7780ebee613d 100644 --- a/plugins/linux-pipewire/data/locale/pt-BR.ini +++ b/plugins/linux-pipewire/data/locale/pt-BR.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Captura de tela (PipeWire)" PipeWireSelectMonitor="Selecionar Monitor" PipeWireSelectWindow="Selecionar Janela" PipeWireWindowCapture="Captura de Janela (PipeWire)" +PipeWireSelectScreenCast="Abrir Seletor" Resolution="Resolução" ShowCursor="Mostrar Cursor" VideoFormat="Formato de vídeo" diff --git a/plugins/linux-pipewire/data/locale/pt-PT.ini b/plugins/linux-pipewire/data/locale/pt-PT.ini index 3d2252340b4c31..88d0c5287e7cb1 100644 --- a/plugins/linux-pipewire/data/locale/pt-PT.ini +++ b/plugins/linux-pipewire/data/locale/pt-PT.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Captura de ecrã (PipeWire)" PipeWireSelectMonitor="Selecionar monitor" PipeWireSelectWindow="Selecionar janela" PipeWireWindowCapture="Captura de janela (PipeWire)" +PipeWireSelectScreenCast="Abrir seletor" Resolution="Resolução" ShowCursor="Mostrar cursor" VideoFormat="Formato de vídeo" diff --git a/plugins/linux-pipewire/data/locale/ru-RU.ini b/plugins/linux-pipewire/data/locale/ru-RU.ini index 4e577d00a68004..98a88f3bcd8881 100644 --- a/plugins/linux-pipewire/data/locale/ru-RU.ini +++ b/plugins/linux-pipewire/data/locale/ru-RU.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Захват экрана (PipeWire)" PipeWireSelectMonitor="Выбрать монитор" PipeWireSelectWindow="Выбрать окно" PipeWireWindowCapture="Захват окна (PipeWire)" +PipeWireSelectScreenCast="Открыть переключатель" Resolution="Разрешение" ShowCursor="Показывать курсор" VideoFormat="Формат видео" diff --git a/plugins/linux-pipewire/data/locale/sk-SK.ini b/plugins/linux-pipewire/data/locale/sk-SK.ini index 195ecc372c0a96..455658c6b7e568 100644 --- a/plugins/linux-pipewire/data/locale/sk-SK.ini +++ b/plugins/linux-pipewire/data/locale/sk-SK.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Zachytávanie obrazovky (PipeWire)" PipeWireSelectMonitor="Vybrať monitor" PipeWireSelectWindow="Vybrať okno" PipeWireWindowCapture="Zachytávanie okna (PipeWire)" +PipeWireSelectScreenCast="Otvoriť výber" Resolution="Rozlíšenie" ShowCursor="Zobraziť kurzor" VideoFormat="Formát videa" diff --git a/plugins/linux-pipewire/data/locale/sv-SE.ini b/plugins/linux-pipewire/data/locale/sv-SE.ini index dfddf0ec2832e5..b9a176fbfc51c8 100644 --- a/plugins/linux-pipewire/data/locale/sv-SE.ini +++ b/plugins/linux-pipewire/data/locale/sv-SE.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Skärmkälla (PipeWire)" PipeWireSelectMonitor="Välj bildskärm" PipeWireSelectWindow="Välj fönster" PipeWireWindowCapture="Fönsterkälla (PipeWire)" +PipeWireSelectScreenCast="Öppna väljare" Resolution="Upplösning" ShowCursor="Visa muspekaren" VideoFormat="Videoformat" diff --git a/plugins/linux-pipewire/data/locale/tr-TR.ini b/plugins/linux-pipewire/data/locale/tr-TR.ini index 7225109244142d..6558ce11efd64d 100644 --- a/plugins/linux-pipewire/data/locale/tr-TR.ini +++ b/plugins/linux-pipewire/data/locale/tr-TR.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Ekran Yakalama (PipeWire)" PipeWireSelectMonitor="Ekran Seç" PipeWireSelectWindow="Pencere Seç" PipeWireWindowCapture="Pencere Yakalama (PipeWire)" +PipeWireSelectScreenCast="Seçiciyi Aç" Resolution="Çözünürlük" ShowCursor="İmleci Göster" VideoFormat="Video Biçimi" diff --git a/plugins/linux-pipewire/data/locale/tt-RU.ini b/plugins/linux-pipewire/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..eeabca22ba6e7e --- /dev/null +++ b/plugins/linux-pipewire/data/locale/tt-RU.ini @@ -0,0 +1,5 @@ +PipeWireCameraDevice="Җайланма" +PipeWireSelectWindow="Тәрәзәне сайлау" +Resolution="Ачыклык" +ShowCursor="Күрсәткечне күрсәтү" +VideoFormat="Видео форматы" diff --git a/plugins/linux-pipewire/data/locale/uk-UA.ini b/plugins/linux-pipewire/data/locale/uk-UA.ini index 7f87c39acdac65..6a3dacfebd90bf 100644 --- a/plugins/linux-pipewire/data/locale/uk-UA.ini +++ b/plugins/linux-pipewire/data/locale/uk-UA.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Захоплення екрана (PipeWire)" PipeWireSelectMonitor="Вибрати монітор" PipeWireSelectWindow="Вибрати вікно" PipeWireWindowCapture="Захоплення вікна (PipeWire)" +PipeWireSelectScreenCast="Відкрити виділяч" Resolution="Роздільність" ShowCursor="Показувати курсор" VideoFormat="Формат відео" diff --git a/plugins/linux-pipewire/data/locale/vi-VN.ini b/plugins/linux-pipewire/data/locale/vi-VN.ini index f8ea2fb72db0c9..01a9b0ece69dd2 100644 --- a/plugins/linux-pipewire/data/locale/vi-VN.ini +++ b/plugins/linux-pipewire/data/locale/vi-VN.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Quay màn hình (PipeWire)" PipeWireSelectMonitor="Chọn màn hình" PipeWireSelectWindow="Chọn cửa sổ" PipeWireWindowCapture="Quay cửa sổ (PipeWire)" +PipeWireSelectScreenCast="Mở bộ chọn" Resolution="Độ phân giải" ShowCursor="Hiển thị con trỏ" VideoFormat="Định dạng video" diff --git a/plugins/linux-pipewire/data/locale/zh-CN.ini b/plugins/linux-pipewire/data/locale/zh-CN.ini index 81d484aa2692a4..58673502f8da3b 100644 --- a/plugins/linux-pipewire/data/locale/zh-CN.ini +++ b/plugins/linux-pipewire/data/locale/zh-CN.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="屏幕采集(PipeWire)" PipeWireSelectMonitor="选择显示器" PipeWireSelectWindow="选择窗口" PipeWireWindowCapture="窗口采集(PipeWire)" +PipeWireSelectScreenCast="打开选择器" Resolution="分辨率" ShowCursor="显示光标" VideoFormat="视频格式" diff --git a/plugins/linux-pipewire/data/locale/zh-TW.ini b/plugins/linux-pipewire/data/locale/zh-TW.ini index 78ffff8195821e..1fb2c2cc3ae09d 100644 --- a/plugins/linux-pipewire/data/locale/zh-TW.ini +++ b/plugins/linux-pipewire/data/locale/zh-TW.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="畫面擷取 (PipeWire)" PipeWireSelectMonitor="選擇顯示器" PipeWireSelectWindow="選擇視窗" PipeWireWindowCapture="視窗擷取(PipeWire)" +PipeWireSelectScreenCast="開啟選擇器" Resolution="解析度" ShowCursor="顯示游標" VideoFormat="影片格式" diff --git a/plugins/linux-pulseaudio/data/locale/eu-ES.ini b/plugins/linux-pulseaudio/data/locale/eu-ES.ini index 28385e33fa8911..4eab9272c4f40a 100644 --- a/plugins/linux-pulseaudio/data/locale/eu-ES.ini +++ b/plugins/linux-pulseaudio/data/locale/eu-ES.ini @@ -1,3 +1,4 @@ PulseInput="Audioa kapturatzeko gailua (PulseAudio)" PulseOutput="Audio irteeraren kaptura (PulseAudio)" Device="Gailua" +Default="Lehenetsia" diff --git a/plugins/linux-pulseaudio/data/locale/tt-RU.ini b/plugins/linux-pulseaudio/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..3fc7b5f4ddf757 --- /dev/null +++ b/plugins/linux-pulseaudio/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Device="Җайланма" diff --git a/plugins/linux-v4l2/data/locale/hu-HU.ini b/plugins/linux-v4l2/data/locale/hu-HU.ini index d53129870d7168..a7104e412e0d05 100644 --- a/plugins/linux-v4l2/data/locale/hu-HU.ini +++ b/plugins/linux-v4l2/data/locale/hu-HU.ini @@ -6,7 +6,7 @@ VideoStandard="Video szabvány" DVTiming="DV időzítés" Resolution="Felbontás" FrameRate="Képkockasebesség" -LeaveUnchanged="Változatlanul hagyni" +LeaveUnchanged="Változatlanul hagyás" UseBuffering="Pufferelés használata" ColorRange="Színtartomány" ColorRange.Default="Alapértelmezett" diff --git a/plugins/linux-v4l2/data/locale/tt-RU.ini b/plugins/linux-v4l2/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..8316827801e770 --- /dev/null +++ b/plugins/linux-v4l2/data/locale/tt-RU.ini @@ -0,0 +1,6 @@ +Device="Җайланма" +Input="Кертү" +VideoFormat="Видео форматы" +Resolution="Ачыклык" +ColorRange="Төс арасы" +ColorRange.Full="Тулы" diff --git a/plugins/mac-avcapture/data/locale/ar-SA.ini b/plugins/mac-avcapture/data/locale/ar-SA.ini index 09439a62007a32..82aa8dcf396d15 100644 --- a/plugins/mac-avcapture/data/locale/ar-SA.ini +++ b/plugins/mac-avcapture/data/locale/ar-SA.ini @@ -1,4 +1,6 @@ AVCapture="جهاز التقاط الفيديو" +AVCapture_Fast="بطاقة التقاط" +AVCapture_Legacy="بطاقة التقاط الفيديو (قديم)" Device="الجهاز" UsePreset="استعمال عارض ضوئي" Preset="العارض الضوئي" diff --git a/plugins/mac-avcapture/data/locale/be-BY.ini b/plugins/mac-avcapture/data/locale/be-BY.ini index db0bf73bec1ad3..3ba343beea0ef2 100644 --- a/plugins/mac-avcapture/data/locale/be-BY.ini +++ b/plugins/mac-avcapture/data/locale/be-BY.ini @@ -1 +1,17 @@ +AVCapture="Прылада відэазахопу" +AVCapture_Fast="Карта захопу" +AVCapture_Legacy="Прылада відэазахопу (састарэлае)" Device="Прылада" +UsePreset="Выкарыстаць набор налад" +Preset="Набор налад" +Buffering="Буферызацыя" +FrameRate="Частата кадраў" +InputFormat="Фармат уводу" +ColorSpace="Колеравая прастора" +VideoRange="Колеравы дыяпазон" +VideoRange.Partial="Абмежаваны" +VideoRange.Full="Поўны" +Auto="Аўта" +Unknown="Невядомы (%1)" +EnableAudio="Уключыць аўдыя, калі падтрымліваецца прыладай" +Resolution="Раздзяляльнасць" diff --git a/plugins/mac-avcapture/data/locale/eu-ES.ini b/plugins/mac-avcapture/data/locale/eu-ES.ini index d9d8e7e5779f20..22462fbfdbd43b 100644 --- a/plugins/mac-avcapture/data/locale/eu-ES.ini +++ b/plugins/mac-avcapture/data/locale/eu-ES.ini @@ -1,4 +1,6 @@ AVCapture="Bideoa kapturatzeko gailua" +AVCapture_Fast="Kapturatzeko-txartelaren gailua" +AVCapture_Legacy="Bideoa kapturatzeko gailua (Legacy)" Device="Gailua" UsePreset="Erabili aurre-ezarpena" Preset="Aurre-ezarpena" @@ -11,3 +13,4 @@ VideoRange.Partial="Mugatua" VideoRange.Full="Osoa" Unknown="Ezezaguna (%1)" EnableAudio="Audioa aktibatu gailuak onartzen badu" +Resolution="Bereizmena" diff --git a/plugins/mac-avcapture/data/locale/fi-FI.ini b/plugins/mac-avcapture/data/locale/fi-FI.ini index 2dd806cc051ada..7332e809f6edda 100644 --- a/plugins/mac-avcapture/data/locale/fi-FI.ini +++ b/plugins/mac-avcapture/data/locale/fi-FI.ini @@ -1,4 +1,6 @@ -AVCapture="Videokaappauslaite" +AVCapture="Videon kaappauskortti" +AVCapture_Fast="Kaappauskortti" +AVCapture_Legacy="Videon kaappauskortti (vanha)" Device="Laite" UsePreset="Käytä esiasetusta" Preset="Esiasetus" diff --git a/plugins/mac-avcapture/data/locale/hi-IN.ini b/plugins/mac-avcapture/data/locale/hi-IN.ini index 58b3e0c7eae8b8..77ffc708b8cbe6 100644 --- a/plugins/mac-avcapture/data/locale/hi-IN.ini +++ b/plugins/mac-avcapture/data/locale/hi-IN.ini @@ -1,5 +1,6 @@ AVCapture="वीडियो कैप्चर डिवाइस" AVCapture_Fast="कैप्चर कार्ड डिवाइस" +AVCapture_Legacy="वीडियो कैप्चर डिवाइस (Legacy)" Device="डिवाइस" UsePreset="प्रिसेट प्रयोग करें" Preset="प्रिसेट" diff --git a/plugins/mac-avcapture/data/locale/it-IT.ini b/plugins/mac-avcapture/data/locale/it-IT.ini index 83bbb131796a56..f0b7a1ebe6dda3 100644 --- a/plugins/mac-avcapture/data/locale/it-IT.ini +++ b/plugins/mac-avcapture/data/locale/it-IT.ini @@ -1,5 +1,5 @@ AVCapture="Dispositivo di cattura video" -AVCapture_Fast="Dispositivo scheda cattura" +AVCapture_Fast="Dispositivo scheda di acquisizione" AVCapture_Legacy="Scheda cattura video (legacy)" Device="Dispositivo" UsePreset="Utilizza il preset" diff --git a/plugins/mac-avcapture/data/locale/ms-MY.ini b/plugins/mac-avcapture/data/locale/ms-MY.ini index 9275e9ea47a302..107a7ee59c9ffb 100644 --- a/plugins/mac-avcapture/data/locale/ms-MY.ini +++ b/plugins/mac-avcapture/data/locale/ms-MY.ini @@ -1,4 +1,6 @@ AVCapture="Peranti Tangkap Video" +AVCapture_Fast="Peranti Kad Tangkapan" +AVCapture_Legacy="Peranti Tangkap Video (Legasi)" Device="Peranti" UsePreset="Guna Praset" Preset="Praset" diff --git a/plugins/mac-avcapture/data/locale/pt-BR.ini b/plugins/mac-avcapture/data/locale/pt-BR.ini index 62d8089a6ace01..7c7c02b0ffcc40 100644 --- a/plugins/mac-avcapture/data/locale/pt-BR.ini +++ b/plugins/mac-avcapture/data/locale/pt-BR.ini @@ -6,11 +6,12 @@ UsePreset="Usar Predefinição" Preset="Predefinição" Buffering="Usar buffering" FrameRate="Taxa de quadros" -InputFormat="Formato de Entrada" +InputFormat="Formato de entrada" ColorSpace="Espaço de cores" VideoRange="Intervalo de Vídeo" VideoRange.Partial="Limitado" VideoRange.Full="Completo" +Auto="Automático" Unknown="Desconhecido (%1)" EnableAudio="Habilitar áudio se suportado pelo dispositivo" Resolution="Resolução" diff --git a/plugins/mac-avcapture/data/locale/ru-RU.ini b/plugins/mac-avcapture/data/locale/ru-RU.ini index 3fe05c61f232b7..e3654fc1ac5d54 100644 --- a/plugins/mac-avcapture/data/locale/ru-RU.ini +++ b/plugins/mac-avcapture/data/locale/ru-RU.ini @@ -8,7 +8,7 @@ Buffering="Использовать буферизацию" FrameRate="Частота кадров" InputFormat="Формат ввода" ColorSpace="Цветовое пространство" -VideoRange="Видео-диапазон" +VideoRange="Видеодиапазон" VideoRange.Partial="Ограниченный" VideoRange.Full="Полный" Auto="Автоматически" diff --git a/plugins/mac-avcapture/data/locale/tt-RU.ini b/plugins/mac-avcapture/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..e204c028ce2873 --- /dev/null +++ b/plugins/mac-avcapture/data/locale/tt-RU.ini @@ -0,0 +1,8 @@ +AVCapture="Видео яздыру җайланмасы" +AVCapture_Fast="Яздыру карта җайланмасы" +AVCapture_Legacy="Видео яздыру җайланмасы (иске)" +Device="Җайланма" +InputFormat="Кертү форматы" +VideoRange="Видео арасы" +Unknown="Билгесез (%1)" +Resolution="Ачыклык" diff --git a/plugins/mac-capture/data/locale/ar-SA.ini b/plugins/mac-capture/data/locale/ar-SA.ini index fb441e000fe7f2..1bfcecf90d26ca 100644 --- a/plugins/mac-capture/data/locale/ar-SA.ini +++ b/plugins/mac-capture/data/locale/ar-SA.ini @@ -7,6 +7,7 @@ CoreAudio.Channel.Default="افتراضي" CoreAudio.Channel.Unnamed="بدون اسم" CoreAudio.Channel.Device="قناة الجهاز" CoreAudio.None="بدون" +CoreAudio.Downmix="تفعيل الخلط التخفيضي" ApplicationCapture="التقاط التطبيق" ApplicationAudioCapture="التقاط صوت التطبيق" DesktopAudioCapture="التقاط صوت سطح المكتب" diff --git a/plugins/mac-capture/data/locale/be-BY.ini b/plugins/mac-capture/data/locale/be-BY.ini new file mode 100644 index 00000000000000..457d686f507a2f --- /dev/null +++ b/plugins/mac-capture/data/locale/be-BY.ini @@ -0,0 +1,39 @@ +CoreAudio.InputCapture="Захоп уваходнага аўдыя" +CoreAudio.OutputCapture="Захоп выходнага аўдыя" +CoreAudio.Device="Прылада" +CoreAudio.Device.Default="Прадвызн." +CoreAudio.Channel="Канал" +CoreAudio.Channel.Default="Прадвызн." +CoreAudio.Channel.Unnamed="Без назвы" +CoreAudio.Channel.Device="Канал прылады" +CoreAudio.None="Не зададзена" +CoreAudio.Downmix="Змяншэнне колькасці каналаў" +ApplicationCapture="Захоп праграмы" +ApplicationAudioCapture="Захоп гуку праграмы" +DesktopAudioCapture="Захоп гуку камп'ютара" +DisplayCapture="Захоп экрана" +DisplayCapture.Display="Экран" +DisplayCapture.ShowCursor="Паказваць курсор" +DisplayCapture.HideOBS="Не паказваць OBS на запісе" +WindowCapture="Захоп акна" +WindowCapture.ShowShadow="Паказваць цень акна" +Application="Праграма" +WindowUtils.Window="Акно" +WindowUtils.ShowEmptyNames="Паказваць вокны без назвы" +WindowUtils.ShowHidden="Паказваць поўнаэкранныя і схаваныя вокны/праграмы" +CropMode="Абрэзаць" +CropMode.None="Не зададзена" +CropMode.Manual="Уручную" +CropMode.ToWindow="Да акна" +CropMode.ToWindowAndManual="Да акна і ўручную" +Crop.origin.x="Абрэзаць злева" +Crop.origin.y="Абрэзаць уверсе" +Crop.size.width="Абрэзаць справа" +Crop.size.height="Абрэзаць унізе" +SCK.Name="Захоп экрана macOS" +SCK.Name.Beta="Захоп экрана macOS (БЭТА)" +SCK.Audio.Name="Захоп аўдыя macOS" +SCK.AudioUnavailable="Для захопу гуку патрабуецца macOS 13 або навейшай версіі" +SCK.CaptureTypeUnavailable="Для выбранага тыпу захопу патрабуецца macOS 13 або навейшай версіі" +SCK.Method="Метад" +SCK.Restart="Перазапусціць захоп" diff --git a/plugins/mac-capture/data/locale/de-DE.ini b/plugins/mac-capture/data/locale/de-DE.ini index 498168ddeda6d3..cad4369eb549c0 100644 --- a/plugins/mac-capture/data/locale/de-DE.ini +++ b/plugins/mac-capture/data/locale/de-DE.ini @@ -11,8 +11,8 @@ CoreAudio.Downmix="Downmixing aktivieren" ApplicationCapture="Anwendungsaufnahme" ApplicationAudioCapture="Anwendungsaudioaufnahme" DesktopAudioCapture="Desktop-Audioaufnahme" -DisplayCapture="Bildschirmaufnahme" -DisplayCapture.Display="Bildschirm" +DisplayCapture="Monitoraufnahme" +DisplayCapture.Display="Monitor" DisplayCapture.ShowCursor="Mauszeiger anzeigen" DisplayCapture.HideOBS="OBS vor Aufnahme verbergen" WindowCapture="Fensteraufnahme" diff --git a/plugins/mac-capture/data/locale/eu-ES.ini b/plugins/mac-capture/data/locale/eu-ES.ini index 27a294dbf291db..641584d67b7255 100644 --- a/plugins/mac-capture/data/locale/eu-ES.ini +++ b/plugins/mac-capture/data/locale/eu-ES.ini @@ -2,10 +2,15 @@ CoreAudio.InputCapture="Audio sarreraren kaptura" CoreAudio.OutputCapture="Audio irteeraren kaptura" CoreAudio.Device="Gailua" CoreAudio.Device.Default="Lehenetsia" +CoreAudio.Channel="Kanala" +CoreAudio.Channel.Default="Lehenetsia" +CoreAudio.Channel.Unnamed="Izenik gabe" +CoreAudio.None="Bat ere ez" ApplicationCapture="Aplikazio kaptura" DisplayCapture="Pantaila-kaptura" DisplayCapture.Display="Pantaila" DisplayCapture.ShowCursor="Kurtsorea erakutsi" +DisplayCapture.HideOBS="Ezkutatu OBS kapturatik" WindowCapture="Leiho-kaptura" WindowCapture.ShowShadow="Erakutsi leihoaren itzala" Application="Aplikazioa" @@ -23,5 +28,8 @@ Crop.size.width="Moztu eskuina" Crop.size.height="Moztu behea" SCK.Name="MacOSen pantaila-kaptura" SCK.Name.Beta="MacOSen pantaila-kaptura (BETA)" +SCK.Audio.Name="macOSen audio-kaptura" SCK.AudioUnavailable="Audio-kaptura macOS 13 edo berriagoa behar du." +SCK.CaptureTypeUnavailable="Hautatutako kapturaren mota macOS 13 edo berriagoa behar du." SCK.Method="Metodoa" +SCK.Restart="Berrabiarazi kaptura" diff --git a/plugins/mac-capture/data/locale/fi-FI.ini b/plugins/mac-capture/data/locale/fi-FI.ini index 150b8f50fc6450..7aa33d36d7eb95 100644 --- a/plugins/mac-capture/data/locale/fi-FI.ini +++ b/plugins/mac-capture/data/locale/fi-FI.ini @@ -7,6 +7,7 @@ CoreAudio.Channel.Default="Oletus" CoreAudio.Channel.Unnamed="Nimetön" CoreAudio.Channel.Device="Laitekanava" CoreAudio.None="Ei mitään" +CoreAudio.Downmix="Alasmiksaus käyttöön" ApplicationCapture="Kaappaus" ApplicationAudioCapture="Sovelluksen äänenkaappaus" DesktopAudioCapture="Työpöydän äänenkaappaus" diff --git a/plugins/mac-capture/data/locale/ms-MY.ini b/plugins/mac-capture/data/locale/ms-MY.ini index df3c2ba5ad0b81..7afa045e65ed4f 100644 --- a/plugins/mac-capture/data/locale/ms-MY.ini +++ b/plugins/mac-capture/data/locale/ms-MY.ini @@ -2,6 +2,12 @@ CoreAudio.InputCapture="Tangkap Input Audio" CoreAudio.OutputCapture="Tangkap Output Audio" CoreAudio.Device="Peranti" CoreAudio.Device.Default="Lalai" +CoreAudio.Channel="Saluran" +CoreAudio.Channel.Default="Asal" +CoreAudio.Channel.Unnamed="Tiada nama" +CoreAudio.Channel.Device="Saluran Peranti" +CoreAudio.None="Tiada" +CoreAudio.Downmix="Aktifkan Downmixing" ApplicationCapture="Tangkap Aplikasi" ApplicationAudioCapture="Tangkap Audio Aplikasi" DesktopAudioCapture="Tangkap Audio Atas Meja" diff --git a/plugins/mac-capture/data/locale/pt-BR.ini b/plugins/mac-capture/data/locale/pt-BR.ini index c3b6c79a059dcf..89adc7e55b8645 100644 --- a/plugins/mac-capture/data/locale/pt-BR.ini +++ b/plugins/mac-capture/data/locale/pt-BR.ini @@ -5,7 +5,7 @@ CoreAudio.Device.Default="Padrão" CoreAudio.Channel="Canal" CoreAudio.Channel.Default="Padrão" CoreAudio.Channel.Unnamed="Sem nome" -CoreAudio.Channel.Device="Canal do Dispositivo" +CoreAudio.Channel.Device="Canal do dispositivo" CoreAudio.None="Nenhum" CoreAudio.Downmix="Ativar Downmixing" ApplicationCapture="Captura de Aplicação" diff --git a/plugins/mac-capture/data/locale/pt-PT.ini b/plugins/mac-capture/data/locale/pt-PT.ini index 916eca6af0bd7a..6acf49bd2178cd 100644 --- a/plugins/mac-capture/data/locale/pt-PT.ini +++ b/plugins/mac-capture/data/locale/pt-PT.ini @@ -14,7 +14,7 @@ DesktopAudioCapture="Captura de áudio do ambiente de trabalho" DisplayCapture="Captura de ecrã" DisplayCapture.Display="Ecrã" DisplayCapture.ShowCursor="Mostrar cursor" -DisplayCapture.HideOBS="Ocultar da captura do OBS" +DisplayCapture.HideOBS="Ocultar OBS da captura" WindowCapture="Captura de janela" WindowCapture.ShowShadow="Mostrar sombra da janela" Application="Aplicação" @@ -26,7 +26,7 @@ CropMode.None="Nenhum" CropMode.ToWindow="Para a janela" CropMode.ToWindowAndManual="Para a janela e manual" Crop.origin.x="Cortar à esquerda" -Crop.origin.y="Cortar em cima" +Crop.origin.y="Cortar no topo" Crop.size.width="Cortar à direita" Crop.size.height="Cortar em baixo" SCK.Name="Captura de ecrã do macOS" diff --git a/plugins/mac-capture/data/locale/ru-RU.ini b/plugins/mac-capture/data/locale/ru-RU.ini index 14d7dae6accc8e..eecd0fe7025e74 100644 --- a/plugins/mac-capture/data/locale/ru-RU.ini +++ b/plugins/mac-capture/data/locale/ru-RU.ini @@ -14,7 +14,7 @@ DesktopAudioCapture="Захват звука рабочего стола" DisplayCapture="Захват экрана" DisplayCapture.Display="Экран" DisplayCapture.ShowCursor="Показывать курсор" -DisplayCapture.HideOBS="Скрыть OBS на записи" +DisplayCapture.HideOBS="Скрыть OBS от записи" WindowCapture="Захват окна" WindowCapture.ShowShadow="Показывать тень окна" Application="Приложение" diff --git a/plugins/mac-capture/data/locale/tt-RU.ini b/plugins/mac-capture/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..094cd687f5d215 --- /dev/null +++ b/plugins/mac-capture/data/locale/tt-RU.ini @@ -0,0 +1,16 @@ +CoreAudio.InputCapture="Керү аудиосы яздыру" +CoreAudio.OutputCapture="Чыгыш аудиосы яздыру" +CoreAudio.Device="Җайланма" +CoreAudio.Channel="Канал" +CoreAudio.Channel.Unnamed="Исемсез" +CoreAudio.Channel.Device="Җайланма каналы" +CoreAudio.None="Юк" +ApplicationCapture="Кушымта яздыру" +ApplicationAudioCapture="Кушымта аудиосы яздыру" +DisplayCapture.ShowCursor="Күрсәткечне күрсәтү" +DisplayCapture.HideOBS="OBS яздырудан яшерү" +WindowCapture="Тәрәзә яздыру" +Application="Кушымта" +WindowUtils.Window="Тәрәзә" +CropMode.None="Юк" +SCK.Audio.Name="macOS аудиосы яздыру" diff --git a/plugins/mac-syphon/data/locale/tt-RU.ini b/plugins/mac-syphon/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..cdded482475e0f --- /dev/null +++ b/plugins/mac-syphon/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Source="Чыганак" diff --git a/plugins/mac-videotoolbox/data/locale/be-BY.ini b/plugins/mac-videotoolbox/data/locale/be-BY.ini new file mode 100644 index 00000000000000..068a0960aacb87 --- /dev/null +++ b/plugins/mac-videotoolbox/data/locale/be-BY.ini @@ -0,0 +1,19 @@ +VTH264EncHW="Апаратны кадавальнік Apple VT H264" +VTH264EncSW="Праграмны кадавальнік Apple VT H264" +VTHEVCEncHW="Апаратны кадавальнік Apple VT HEVC" +VTHEVCEncT2="Апаратны кадавальнік Apple VT HEVC T2" +VTHEVCEncSW="Праграмны кадавальнік Apple VT HEVC" +VTProResEncHW="Апаратны кадавальнік Apple VT ProRes" +VTProResEncSW="Праграмны кадавальнік Apple VT ProRes" +Bitrate="Бітрэйт" +Quality="Якасць" +UseMaxBitrate="Абмежаваць бітрэйт" +MaxBitrate="Максімальны бітрэйт" +MaxBitrateWindow="Акенца максімальнага бітрэйту" +KeyframeIntervalSec="Інтэрвал ключавых кадраў (0=аўта)" +Profile="Профіль" +UseBFrames="Выкарыстоўваць B-кадры" +RateControl="Кіраванне бітрэйтам" +ColorFormatUnsupported="Выбраны фармат колеру не падтрымліваецца выбраным кадавальнікам Apple TV. Выберыце сумяшчальны фармат колеру ў меню Налады -> Пашыраныя або выкарыстайце іншы кадавальнік." +FullRangeUnsupported="Поўны колеравы дыяпазон не падтрымліваецца 16-бітнымі кадавальнікамі Apple TV. Выберыце абмежаваны дыяпазон у меню Налады -> Пашыраныя." +ProResCodec="Кодэк ProRes" diff --git a/plugins/mac-videotoolbox/data/locale/de-DE.ini b/plugins/mac-videotoolbox/data/locale/de-DE.ini index 4235f21c1cb54b..abd2b96ed14935 100644 --- a/plugins/mac-videotoolbox/data/locale/de-DE.ini +++ b/plugins/mac-videotoolbox/data/locale/de-DE.ini @@ -1,10 +1,10 @@ -VTH264EncHW="Apple-VT-H264-Hardwareencoder" -VTH264EncSW="Apple-VT-H264-Softwareencoder" -VTHEVCEncHW="Apple-VT-HEVC-Hardwareencoder" -VTHEVCEncT2="Apple-VT-HEVC-T2-Hardwareencoder" -VTHEVCEncSW="Apple-VT-HEVC-Softwareencoder" -VTProResEncHW="Apple-VT-ProRes-Hardwareencoder" -VTProResEncSW="Apple-VT-ProRes-Softwareencoder" +VTH264EncHW="Apple-VT-H264-Hardwarekodierer" +VTH264EncSW="Apple-VT-H264-Softwarekodierer" +VTHEVCEncHW="Apple-VT-HEVC-Hardwarekodierer" +VTHEVCEncT2="Apple-VT-HEVC-T2-Hardwarekodierer" +VTHEVCEncSW="Apple-VT-HEVC-Softwarekodierer" +VTProResEncHW="Apple-VT-ProRes-Hardwarekodierer" +VTProResEncSW="Apple-VT-ProRes-Softwarekodierer" Quality="Qualität" UseMaxBitrate="Limitiere Bitrate" MaxBitrate="Max. Bitrate" @@ -13,6 +13,6 @@ KeyframeIntervalSec="Keyframeintervall (0 = automatisch)" Profile="Profil" UseBFrames="B-Frames verwenden" RateControl="Qualitätsregulierungsmethode" -ColorFormatUnsupported="Das ausgewählte Farbformat wird durch den gewählten Apple-VT-Encoder nicht unterstützt. Unter „Einstellungen“ → „Erweitert“ können Sie ein kompatibles Farbformat wählen oder einen anderen Encoder verwenden." -FullRangeUnsupported="Voller Farbbereich wird nicht von den 16-Bit Apple-VT-Encodern unterstützt. Wählen Sie begrenzten Farbbereich in „Einstellungen“ → „Erweitert“." +ColorFormatUnsupported="Das ausgewählte Farbformat wird durch den gewählten Apple-VT-Kodierer nicht unterstützt. Unter „Einstellungen“ → „Erweitert“ können Sie ein kompatibles Farbformat wählen oder einen anderen Kodierer verwenden." +FullRangeUnsupported="Voller Farbbereich wird nicht von den 16-Bit Apple-VT-Kodierern unterstützt. Wählen Sie begrenzten Farbbereich in „Einstellungen“ → „Erweitert“." ProResCodec="ProRes-Codec" diff --git a/plugins/mac-videotoolbox/data/locale/it-IT.ini b/plugins/mac-videotoolbox/data/locale/it-IT.ini index 107a10c27ee9e5..42b3a45dd93f63 100644 --- a/plugins/mac-videotoolbox/data/locale/it-IT.ini +++ b/plugins/mac-videotoolbox/data/locale/it-IT.ini @@ -14,7 +14,7 @@ KeyframeIntervalSec="Intervallo fotogramma chiave (0=automatico)" Profile="Profilo" UseBFrames="Utilizza i B-Frame" RateControl="Controllo flusso" -ColorFormatUnsupported="Il formato colore selezionato non è supportato dall'encoder Apple VT selezionato. \nSeleziona un formato colore compatibile in Impostazioni -> Avanzate o usa un diverso encoder." -FullRangeUnsupported="La gamma completa di colori non è supportata da codifiche Apple VT a 16-bit. \nSeleziona la gamma limitata di colori in Impostazioni -> Avanzate." +ColorFormatUnsupported="Il formato colore selezionato non è supportato dall'encoder Apple VT selezionato. Seleziona un formato colore compatibile in Impostazioni -> Avanzate o usa un diverso encoder." +FullRangeUnsupported="La gamma completa di colori non è supportata da codifiche Apple VT a 16-bit. Seleziona la gamma limitata di colori in Impostazioni -> Avanzate." ProResCodec="Codec ProRes" ProRes422Proxy="Proxy ProRes 422" diff --git a/plugins/mac-videotoolbox/data/locale/pt-PT.ini b/plugins/mac-videotoolbox/data/locale/pt-PT.ini index 102f00631ad36d..d3e3e499bc19e2 100644 --- a/plugins/mac-videotoolbox/data/locale/pt-PT.ini +++ b/plugins/mac-videotoolbox/data/locale/pt-PT.ini @@ -13,7 +13,7 @@ MaxBitrateWindow="Janela de taxa de bits máxima" KeyframeIntervalSec="Intervalo de fotogramas-chave ( 0=automático)" Profile="Perfil" UseBFrames="Usar fotogramas B" -RateControl="Controlo de taxa" +RateControl="Controlo da frequência" ColorFormatUnsupported="O formato de cor selecionado não é suportado pelo codificador Apple VT selecionado. Selecione um formato de cor compatível em Definições → Avançado, ou use um codificador diferente." FullRangeUnsupported="Intervalo de cor completo não é suportado por codificadores VT 16-bit Apple. Selecione um intervalo de cor limitado em Definições → Avançado" ProResCodec="Codec ProRes" diff --git a/plugins/mac-videotoolbox/data/locale/tt-RU.ini b/plugins/mac-videotoolbox/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..bcf0b4d5254faf --- /dev/null +++ b/plugins/mac-videotoolbox/data/locale/tt-RU.ini @@ -0,0 +1,4 @@ +Bitrate="Битрейт" +Quality="Сыйфат" +MaxBitrate="Максималь битрейт" +Profile="Профиль" diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/ar-SA.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/ar-SA.ini index b24cee129a54f8..1aac3b66d6aacd 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/ar-SA.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/ar-SA.ini @@ -1,7 +1,9 @@ Plugin_Name="كاميرا ويب افتراضية macOS" +Error.SystemExtension.NotInstalled="الكاميرا الافتراضية ليست مثبتة.\n\nمن فضلك، اسمح لـ OBS بتثبيت البرمجيات النظامية في إعدادات النظام. → الخصوصية والأمان → الأمان.\n\nقد تحتاج إلى إعادة تشغيل OBS إذا ظهرت هذه الرسالة بعد ذلك." Error.SystemExtension.CameraUnavailable="تعذر العثور على الكاميرا الافتراضية.\n\nحاول مرة اخرى." Error.SystemExtension.CameraNotStarted="غير قادر على بدء الكاميرا الافتراضية.\n\nحاول مرة اخرى." Error.SystemExtension.InstallationError="حدث خطأ أثناء تثبيت الكاميرا الافتراضية:" +Error.SystemExtension.WrongLocation="لا يمكن لـ OBS تثبيت الكاميرا الافتراضية إذا لم تكن في \"/Applications\". يرجى نقل OBS إلى مجلد \"/Applications\"." Error.SystemExtension.CompleteAfterReboot="سيتم إكمال تثبيت الكاميرا الافتراضية بعد إعادة تشغيل النظام." Error.DAL.NotInstalled="تعذر تثبيت الكاميرا الافتراضية أو تحديثها.\n\nالرجاء المحاولة مرة أخرى وإدخال اسم المسؤول وكلمة المرور عند المطالبة بذلك." Error.DAL.NotUninstalled="تعذرت إزالة الكاميرا الافتراضية القديمة.\n\nالرجاء المحاولة مرة أخرى وإدخال اسم المسؤول وكلمة المرور عند المطالبة بذلك." diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/be-BY.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/be-BY.ini index f24e1d1b5f5e78..8e63367af2a465 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/be-BY.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/be-BY.ini @@ -1 +1,9 @@ -Plugin_Name="macOS віртуальная вэб-камера" +Plugin_Name="Віртуальная вэб-камера macOS" +Error.SystemExtension.NotInstalled="Віртуальная камера не ўсталявана.\n\nДазвольце OBS усталёўваць сістэмнае праграмнае забеспячэнне ў сістэмных наладах бяспекі.\n\nКалі вы гэта ўжо зрабілі, магчыма, вам патрабуецца перазапусціць праграму." +Error.SystemExtension.CameraUnavailable="Не атрымалася знайсці віртуальную камеру.\n\nПаспрабуйце яшчэ раз." +Error.SystemExtension.CameraNotStarted="Немагчыма запусціць віртуальную камеру.\n\nПаспрабуйце яшчэ раз." +Error.SystemExtension.InstallationError="Падчас усталявання віртуальнай камеры адбылася памылка:" +Error.SystemExtension.WrongLocation="OBS не можа ўсталяваць віртуальную камеру не з каталога «/Applications». Перамясціце праграму ў гэты каталог." +Error.SystemExtension.CompleteAfterReboot="Усталяванне віртуальнай камеры будзе завершана пасля перазапуску сістэмы." +Error.DAL.NotInstalled="Не атрымалася ўсталяваць або абнавіць віртуальную камеру.\n\nПаспрабуйце яшчэ раз і пры запыце ўвядзіце імя і пароль адміністратара." +Error.DAL.NotUninstalled="Не атрымалася выдаліць састарэлую віртуальную камеру.\n\nПаспрабуйце яшчэ раз і пры запыце ўвядзіце імя і пароль адміністратара." diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/de-DE.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/de-DE.ini index 3716cd15e88ecb..b8a78f7638139a 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/de-DE.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/de-DE.ini @@ -1,5 +1,5 @@ Plugin_Name="Virtuelle Kamera für macOS" -Error.SystemExtension.NotInstalled="Die virtuelle Kamera ist nicht installiert.\n\nBitte erlauben Sie OBS in den Systemeinstellungen → „Datenschutz & Sicherheit“ → „Sicherheit“, Systemsoftware zu installieren.\n\nMöglicherweise müssen Sie OBS neu starten, falls diese Benachrichtigung weiterhin erscheint." +Error.SystemExtension.NotInstalled="Die virtuelle Kamera ist nicht installiert.\n\nBitte erlauben Sie OBS in den Systemeinstellungen → „Datenschutz & Sicherheit“ → „Sicherheit“, Systemsoftware zu installieren.\n\nMöglicherweise müssen Sie OBS neu starten, falls diese Nachricht weiterhin erscheint." Error.SystemExtension.CameraUnavailable="Die virtuelle Kamera konnte nicht erkannt werden.\n\nBitte versuchen Sie es erneut." Error.SystemExtension.CameraNotStarted="Die virtuelle Kamera konnte nicht gestartet werden.\n\nBitte versuchen Sie es erneut." Error.SystemExtension.InstallationError="Ein Fehler ist während der Installation der virtuellen Kamera aufgetreten:" diff --git a/plugins/obs-browser b/plugins/obs-browser index 0f4f6c654e5803..c81851a1d7696c 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 0f4f6c654e5803445dfd1266d8ce72c893acdf26 +Subproject commit c81851a1d7696c2d7ba319122eec387c1568ad44 diff --git a/plugins/obs-ffmpeg/data/locale/de-DE.ini b/plugins/obs-ffmpeg/data/locale/de-DE.ini index 9ba7c3f9b43c1f..10ae4e6b3a9c2c 100644 --- a/plugins/obs-ffmpeg/data/locale/de-DE.ini +++ b/plugins/obs-ffmpeg/data/locale/de-DE.ini @@ -17,18 +17,18 @@ KeyframeIntervalSec="Keyframeintervall (0 = automatisch)" Lossless="Verlustfrei" FilePath="Dateipfad" AMFOpts="AMF-/FFmpeg-Optionen" -AMFOpts.ToolTip="Geben Sie benutzerdefinierte AMF- oder FFmpeg-Optionen an, zum Beispiel: „level=5.2 profile=main“. Genauere Informationen finden Sie in der Dokumentation des AMF-Encoders." +AMFOpts.ToolTip="Geben Sie benutzerdefinierte AMF- oder FFmpeg-Optionen an, zum Beispiel: „level=5.2 profile=main“. Genauere Informationen finden Sie in der Dokumentation des AMF-Kodierers." BFrames="Max. B-Frames" VAAPI.Device="VAAPI-Gerät" NVENC.LookAhead="Lookahead" -NVENC.LookAhead.ToolTip="Aktiviert dynamische B-Frames.\n\nWenn deaktiviert, wird der Encoder die Anzahl der B-Frames der Option „Max. B-Frames“ entnehmen.\n\nWenn aktiviert, wird auf Kosten erhöhter GPU-Nutzung die visuelle Qualität erhöht,\nindem nur so viele B-Frames bis zum Maximum verwendet werden, wie nötig sind." -NVENC.PsychoVisualTuning.ToolTip="Aktiviert Encodereinstellungen, die auf Kosten erhöhter GPU-Nutzung die Verwendung der Bitrate für eine erhöhte wahrgenommene visuelle Qualität optimieren,\ninsbesondere in Situationen mit hoher Bewegung." +NVENC.LookAhead.ToolTip="Aktiviert dynamische B-Frames.\n\nWenn deaktiviert, wird der Kodierer die Anzahl der B-Frames der Option „Max. B-Frames“ entnehmen.\n\nWenn aktiviert, wird auf Kosten erhöhter GPU-Nutzung die visuelle Qualität erhöht,\nindem nur so viele B-Frames bis zum Maximum verwendet werden, wie nötig sind." +NVENC.PsychoVisualTuning.ToolTip="Aktiviert Kodierereinstellungen, die auf Kosten erhöhter GPU-Nutzung die Verwendung der Bitrate für eine erhöhte wahrgenommene visuelle Qualität optimieren,\ninsbesondere in Situationen mit hoher Bewegung." NVENC.CQLevel="CQ-Level" NVENC.8bitUnsupportedHdr="Die Ausgabe von 8-Bit von Rec. 2100 wird nicht unterstützt." NVENC.I010Unsupported="NVENC unterstützt kein I010. Verwenden Sie stattdessen P010." -NVENC.10bitUnsupported="Mit diesem Encoder kann kein 10-Bit encodet werden." -NVENC.16bitUnsupported="Mit diesem Encoder kann kein 16-Bit encodet werden." -NVENC.NoAV1FallbackPossible="AV1-Codierung ist mit den aktuellen Einstellungen nicht verfügbar. Versuchen Sie, eventuell eingestellte Wiederskalierungs- oder GPU-Optionen zu deaktivieren. Überprüfen Sie das Log für mehr Details." +NVENC.10bitUnsupported="Mit diesem Kodierer kann kein 10-Bit kodiert werden." +NVENC.16bitUnsupported="Mit diesem Kodierer kann kein 16-Bit kodiert werden." +NVENC.NoAV1FallbackPossible="AV1-Kodierung ist mit den aktuellen Einstellungen nicht verfügbar. Versuchen Sie, eventuell eingestellte Wiederskalierungs- oder GPU-Optionen zu deaktivieren. Überprüfen Sie das Log für mehr Details." NVENC.Preset2.p1="P1: Am schnellsten (Niedrigste Qualität)" NVENC.Preset2.p2="P2: Schneller (Niedrigere Qualität)" NVENC.Preset2.p3="P3: Schnell (Niedrige Qualität)" @@ -53,7 +53,7 @@ Looping="Endlosschleife" Input="Eingabe" InputFormat="Eingabeformat" BufferingMB="Netzwerkpufferung" -HardwareDecode="Hardwaredecodierung verwenden, falls verfügbar" +HardwareDecode="Hardwaredekodierung verwenden, falls verfügbar" ClearOnMediaEnd="Nichts anzeigen, wenn Wiedergabe endet" RestartWhenActivated="Wiedergabe bei Quellenaktivierung erneut starten" CloseFileWhenInactive="Datei schließen, wenn inaktiv" @@ -79,13 +79,13 @@ HelperProcessFailed="Der Aufnahme-Helfer-Prozess kann nicht gestartet werden. Ü UnableToWritePath="Es kann nicht zu %1 geschrieben werden. Stellen Sie sicher, dass Sie einen Aufnahmepfad verwenden, für den Ihr Konto Schreibrechte hat und genügend Speicherplatz zur Verfügung steht." WarnWindowsDefender="Der Fehler kann auch durch den Windows-10-Ransomware-Schutz verursacht werden, falls dieser aktiviert ist. Versuchen Sie, den überwachten Ordnerzugriff in „Windows-Sicherheit“ → „Viren- & Bedrohungsschutz“ auszuschalten." Encoder.Error="Öffnen von %1 fehlgeschlagen: %2" -Encoder.Timeout="Der Encoder %1 braucht zu lange zum Encoden (Zeitüberschreitung: %2 Sekunden)" +Encoder.Timeout="Der Kodierer %1 braucht zu lange bei der Kodierung (Zeitüberschreitung: %2 Sekunden)" AMF.8bitUnsupportedHdr="Die Ausgabe von 8-Bit von Rec. 2100 wird nicht unterstützt." -AMF.10bitUnsupportedAvc="Mit dem AMD-H.264-Encoder kann kein 10-Bit encodet werden." -AMF.16bitUnsupported="Mit diesem Encoder kann kein 16-Bit encodet werden." +AMF.10bitUnsupportedAvc="Mit dem AMD-H.264-Kodierer kann kein 10-Bit kodiert werden." +AMF.16bitUnsupported="Mit diesem Kodierer kann kein 16-Bit kodiert werden." NVENC.Error="Öffnen des NVENC-Codecs fehlgeschlagen: %1" NVENC.GenericError="Versuchen Sie, den neusten NVIDIA-Treiber zu installieren und andere Aufnahmeprogramme wie NVIDIA ShadowPlay oder Windows Game DVR zu schließen, welche NVENC möglicherweise verwenden." -NVENC.BadGPUIndex="Sie haben die GPU %1 in Ihren Ausgabeencodereinstellungen ausgewählt. Setzen Sie diesen Wert wieder auf 0 und versuchen Sie es erneut." +NVENC.BadGPUIndex="Sie haben die GPU %1 in Ihren Ausgabekodierereinstellungen ausgewählt. Setzen Sie diesen Wert wieder auf 0 und versuchen Sie es erneut." NVENC.OutdatedDriver="Der installierte NVIDIA-Treiber unterstützt diese NVENC-Version nicht. Versuchen Sie, ihn zu aktualisieren." NVENC.UnsupportedDevice="NVENC-Fehler: Gerät nicht unterstützt. Überprüfen Sie, ob Ihre Grafikkarte NVENC unterstützt und versuchen Sie, den Treiber zu aktualisieren." NVENC.TooManySessions="NVENC-Fehler: Zu viele gleichzeitige Sitzungen. Probieren Sie, andere Aufnahmeprogramme wie NVIDIA ShadowPlay oder Windows Game DVR zu schließen, welche NVENC möglicherweise verwenden." diff --git a/plugins/obs-ffmpeg/data/locale/it-IT.ini b/plugins/obs-ffmpeg/data/locale/it-IT.ini index 181d60b93f1295..f405460d862cee 100644 --- a/plugins/obs-ffmpeg/data/locale/it-IT.ini +++ b/plugins/obs-ffmpeg/data/locale/it-IT.ini @@ -17,7 +17,7 @@ KeyframeIntervalSec="Intervallo fotogramma chiave (0=automatico)" Level="Livello" FilePath="Percorso file" AMFOpts="Opzioni AMF/FFmpeg" -AMFOpts.ToolTip="Specifica le opzioni AMF o FFmpeg. per esempio, \"level=5.2 profile=main\". \nPer maggiori dettagli controlla la documentazione dell'encoder AMF." +AMFOpts.ToolTip="Specifica le opzioni AMF o FFmpeg. per esempio, \"level=5.2 profile=main\". Per maggiori dettagli controlla la documentazione dell'encoder AMF." BFrames="B-frame massimi" VAAPI.Device="Dispositivo VAAPI" NVENC.LookAhead="Previsione (look-ahead)" @@ -29,19 +29,19 @@ NVENC.8bitUnsupportedHdr="OBS non supporta l'output Rec. 2100 8bit." NVENC.I010Unsupported="NVENC non supporta I010. Usa invece P010." NVENC.10bitUnsupported="Impossibile eseguire la codifica a 10 bit con questo encoder." NVENC.16bitUnsupported="Impossibile con questo encoder eseguire la codifica a 16bit." -NVENC.NoAV1FallbackPossible="Con le impostazioni attuali la codifica AV1 non è disponibile. \nProva a disabilitare eventuali opzioni di ridimensionamento o GPU che potrebbero essere impostate. \nPer maggiori dettagli controlla il registro." -NVENC.Preset2.p1="P1: Più veloce (qualità più bassa)" -NVENC.Preset2.p2="P1: Più Veloce (Qualità Più Bassa)" -NVENC.Preset2.p3="P3: Veloce (Bassa Qualità)" +NVENC.NoAV1FallbackPossible="Con le impostazioni attuali la codifica AV1 non è disponibile. Prova a disabilitare eventuali opzioni di ridimensionamento o GPU che potrebbero essere impostate. Per maggiori dettagli controlla il registro." +NVENC.Preset2.p1="P1: Il più veloce (qualità minima)" +NVENC.Preset2.p2="P2: Più Veloce (qualità più bassa)" +NVENC.Preset2.p3="P3: Veloce (qualità bassa)" NVENC.Preset2.p4="P4: Medio (qualità media)" NVENC.Preset2.p5="P5: Lento (Qualità Buona)" -NVENC.Preset2.p6="P6: Più lenta (qualità migliore)" -NVENC.Preset2.p7="P7: Più lenta (migliore qualità)" -NVENC.Tuning.hq="Alta Qualità" -NVENC.Tuning.ll="Bassa Latenza" +NVENC.Preset2.p6="P6: Più lento (qualità migliore)" +NVENC.Preset2.p7="P7: Il più lento (qualità massima)" +NVENC.Tuning.hq="Alta qualità" +NVENC.Tuning.ll="Bassa latenza" NVENC.Tuning.ull="Latenza ultra bassa" NVENC.Multipass="Modalità Multipass" -NVENC.Multipass.disabled="Passaggio Singolo" +NVENC.Multipass.disabled="Passaggio singolo" NVENC.Multipass.qres="Due passaggi (risoluzione 1/4)" NVENC.Multipass.fullres="Due Passaggi (Risoluzione Completa)" AMF.Preset.speed="Velocità" diff --git a/plugins/obs-ffmpeg/data/locale/kmr-TR.ini b/plugins/obs-ffmpeg/data/locale/kmr-TR.ini index 017cb5ce1d187c..66cafd43434957 100644 --- a/plugins/obs-ffmpeg/data/locale/kmr-TR.ini +++ b/plugins/obs-ffmpeg/data/locale/kmr-TR.ini @@ -74,7 +74,7 @@ HelperProcessFailed="Nikare dest bi pêvajoya alîkara tomarkirinê bike. Kontro UnableToWritePath="Nekarî binivîse li %1. Ji xwe bawer bin ku tu riyên tomarkirinê bi kar tanî ku hesabê te yê bikarhêner destûrê dide nivîsandinê û têra xwe cihê dîskê heye." WarnWindowsDefender="Ku Parastina Ransomware Windows 10 çalak be, ew dikare bibe sedema vê çewtiyê. Di sazkariyên parastina Windows Security / Vîrus & tehdîdê de têgihîştina peldanka kontrolkirî veke." Encoder.Error="Vekirina %1: %2 bi ser neket" -Encoder.Timeout="Kodker %1 Demeke pir dirêj distîne heya kodkirinê dike\n(Dem: %2 çirke)" +Encoder.Timeout="Kodker %1 Demeke pir dirêj distîne heya kodkirinê dike (Dem: %2 çirke)" AMF.8bitUnsupportedHdr="OBS piştgiriyê nade deranê 8-bit ji Rec. 2100." AMF.10bitUnsupportedAvc="Nikare kodkirina 10-bit li ser kodkerê AMD H.264 pêk bîne." AMF.16bitUnsupported="Nikare kodkirina 16-bit li ser vê kodkerê pêk bîne." diff --git a/plugins/obs-ffmpeg/data/locale/pt-PT.ini b/plugins/obs-ffmpeg/data/locale/pt-PT.ini index e6debf4b97cf31..587d354723e39c 100644 --- a/plugins/obs-ffmpeg/data/locale/pt-PT.ini +++ b/plugins/obs-ffmpeg/data/locale/pt-PT.ini @@ -10,7 +10,7 @@ MaxBitrate="Taxa de bits máxima" Preset="Predefinição" Tuning="Ajustes" Profile="Perfil" -RateControl="Controlo de taxa" +RateControl="Controlo da frequência" KeyframeIntervalSec="Intervalo de fotogramas-chave ( 0=automático)" Lossless="Sem perdas" Level="Nível" @@ -57,7 +57,7 @@ HardwareDecode="Utilizar descodificação por hardware, quando disponível" ClearOnMediaEnd="Não mostrar nada quando a reprodução terminar" RestartWhenActivated="Reiniciar reprodução quando a fonte se tornar ativa" CloseFileWhenInactive="Fechar ficheiro quando inativa" -CloseFileWhenInactive.ToolTip="Fecha o ficheiro quando a fonte não estiver a ser mostrada na transmissão ou\ngravação. Permite assim que o ficheiro seja alterado quando a fonte não estiver ativa,\nmas pode haver algum atraso inicial quando a fonte for reativada." +CloseFileWhenInactive.ToolTip="Fecha o ficheiro quando a fonte não estiver a ser mostrada na transmissão ou\ngravação. Permite assim que o ficheiro seja alterado quando a fonte não estiver ativa,\nmas pode haver algum desfasamento inicial quando a fonte for reativada." ColorRange="Gama de cor YUV" ColorRange.Auto="Automático" ColorRange.Partial="Limitado" @@ -91,4 +91,4 @@ NVENC.UnsupportedDevice="Erro NVENC: Dispositivo não suportado. Verifique se a NVENC.TooManySessions="Erro NVENC: demasiadas sessões simultâneas, tente fechar outros programas que possam estar a usar o NVENC, como o NVIDIA ShadowPlay ou Windows Game DVR." NVENC.CheckDrivers="Tente instalar o driver da NVIDIA mais recente." AV1.8bitUnsupportedHdr="O OBS não suporta saída de 8 bits do Rec. 2100." -ReconnectDelayTime="Atraso de ligação" +ReconnectDelayTime="Desfasamento de religação" diff --git a/plugins/obs-ffmpeg/data/locale/tt-RU.ini b/plugins/obs-ffmpeg/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..573f639da42222 --- /dev/null +++ b/plugins/obs-ffmpeg/data/locale/tt-RU.ini @@ -0,0 +1,24 @@ +FFmpegOutput="FFmpeg чыгышы" +FFmpegOpts="FFmpeg көйләүләре" +Bitrate="Битрейт" +Profile="Профиль" +Level="Дәрәҗә" +FilePath="Файл юлы" +AMFOpts="AMF/FFmpeg көйләүләре" +NVENC.Tuning.hq="Югары сыйфат" +AMF.Preset.speed="Тизлек" +AMF.Preset.quality="Сыйфат" +AMF.Preset.highQuality="Югары сыйфат" +FFmpegSource="Медиа чыганагы" +LocalFile="Локаль файл" +Input="Кертү" +InputFormat="Кертү форматы" +ColorRange.Full="Тулы" +RestartMedia="Яңадан башлау" +SpeedPercentage="Тизлек" +Play="Уйнату" +Pause="Туктатып тору" +Stop="туктату" +MediaFileFilter.VideoFiles="Видео файллары" +MediaFileFilter.AudioFiles="Аудио файллары" +Encoder.Error="%1 ачып булмады: %2" diff --git a/plugins/obs-ffmpeg/data/locale/ug-CN.ini b/plugins/obs-ffmpeg/data/locale/ug-CN.ini index ab4e84482c5b75..4a78e9297a9d91 100644 --- a/plugins/obs-ffmpeg/data/locale/ug-CN.ini +++ b/plugins/obs-ffmpeg/data/locale/ug-CN.ini @@ -1,5 +1,9 @@ FFmpegOutput="FFmpeg چىقىرىش" +FFmpegMuxer="FFmpeg بىرىكتۈرگۈچ" +FFmpegHlsMuxer="FFmpeg HLS بىرىكتۈرگۈچ" +FFmpegMpegtsMuxer="FFmpeg MPEG-TS بىرىكتۈرگۈچ" FFmpegOpts="FFmpeg تاللانما" +FFmpegOpts.ToolTip.Source="FFmpeg تاللانمىسىنى تەڭشىشىڭىزگە يول قويىدۇ. پەقەت option=value پىچىمىدىكى تاللانمىنى قوبۇل قىلىدۇ. \nكۆپ تاللانما تاللاشتا بوشلۇق بىلەن ئايرىلىدۇ.\nمەسىلەن: rtsp_transport=tcp rtsp_flags=prefer_tcp" Bitrate="بايت نىسبىتى" MaxBitrate="ئەڭ چوڭ بىت نىسبىتى" Preset="ئالدىن تەڭشەك" @@ -7,6 +11,7 @@ Tuning="تەڭشەش" Profile="سەپلىمە ھۆججەت" RateControl="نىسبەت تىزگىنى" KeyframeIntervalSec="ئاچقۇچلۇق كېشەك ئارىلىقى (0=ئۆزلۈكىدىن)" +Lossless="زەخىمسىز" Level="دەرىجە" FilePath="ھۆججەت يولى" AMFOpts="AMF/FFmpeg تاللانما" diff --git a/plugins/obs-filters/data/locale/be-BY.ini b/plugins/obs-filters/data/locale/be-BY.ini index 8622d7f851d75d..d1f39df5d6e3cc 100644 --- a/plugins/obs-filters/data/locale/be-BY.ini +++ b/plugins/obs-filters/data/locale/be-BY.ini @@ -9,7 +9,7 @@ ChromaKeyFilter="Храмакей" ColorKeyFilter="Каляровы ключ" SharpnessFilter="Рэзкасць" Sharpness="Рэзкасць" -ScaleFilter="Маштаб / суадносіны бакоў" +ScaleFilter="Маштаб/суадносіны бакоў" GPUDelayFilter="Затрымка рэндару" UndistortCenter="Выпраўляць скажэнне цэнтра выявы падчас маштабавання ультрашырокай раздзяляльнасці" NoiseGate="Глушэнне шуму" diff --git a/plugins/obs-filters/data/locale/de-DE.ini b/plugins/obs-filters/data/locale/de-DE.ini index 81e92f2a1158c3..dc94f7cb2141bd 100644 --- a/plugins/obs-filters/data/locale/de-DE.ini +++ b/plugins/obs-filters/data/locale/de-DE.ini @@ -78,7 +78,7 @@ NoiseSuppress.Method.RNNoise="RNNoise (Gute Qualität, höhere CPU-Nutzung)" NoiseSuppress.Method.Nvafx.Denoiser="NVIDIA-Rauschentfernung" NoiseSuppress.Method.Nvafx.Dereverb="NVIDIA-Raumechoentfernung" NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb="NVIDIA-Rauschentfernung + -Raumechoentfernung" -NoiseSuppress.Method.Nvafx.Deprecation="ACHTUNG: Bitte aktualisieren Sie sowohl Ihr NVIDIA-Video- als auch -Audio-SDK. Die Version Ihres Audio-SDK ist veraltet." +NoiseSuppress.Method.Nvafx.Deprecation="Achtung: Die Version Ihres Audio-SDK ist veraltet. Bitte aktualisieren Sie sowohl Ihr NVIDIA-Video- als auch -Audio-SDK." Saturation="Sättigung" HueShift="Farbtonverschiebung" Amount="Betrag" @@ -104,7 +104,7 @@ Greenscreen.Mode="Modus" Greenscreen.Quality="Qualität (Höhere GPU-Nutzung, bessere Qualität)" Greenscreen.Performance="Performance (Niedrigere GPU-Nutzung, gute Qualität)" Greenscreen.Threshold="Schwellwert" -Greenscreen.Deprecation="ACHTUNG: Bitte aktualisieren Sie sowohl Ihr NVIDIA-Video- als auch -Audio-SDK. Die Version Ihres Video-SDK ist veraltet." +Greenscreen.Deprecation="Achtung: Die Version Ihres Video-SDK ist veraltet. Bitte aktualisieren Sie sowohl Ihr NVIDIA-Video- als auch -Audio-SDK." Greenscreen.Processing="Masken-Aktualisierungshäufigkeit in Frames" Greenscreen.Processing.Hint="Dies verringert die GPU-Auslastung, indem eine Maske nur alle N Frames erzeugt wird (2 auf Voreinstellung)." 3BandEq="3-Band-Equalizer" diff --git a/plugins/obs-filters/data/locale/ms-MY.ini b/plugins/obs-filters/data/locale/ms-MY.ini index efa1fb4a9bdac8..968f2001903652 100644 --- a/plugins/obs-filters/data/locale/ms-MY.ini +++ b/plugins/obs-filters/data/locale/ms-MY.ini @@ -50,6 +50,8 @@ HdrTonemap.ToneTransform="Jelmaan Tona" HdrTonemap.SdrWhiteLevel="Aras Putih SDR" HdrTonemap.HdrInputMaximum="Maksimum Input HDR" HdrTonemap.HdrOutputMaximum="Maksimum Output HDR" +HdrTonemap.SdrInputMaximum="Maksimum Input SDR" +HdrTonemap.SdrOutputMaximum="Maksimum Output SDR" ScrollFilter.SpeedX="Kelajuan Mengufuk" ScrollFilter.SpeedY="Kelajuan Menegak" ScrollFilter.LimitWidth="Lebar Batas" diff --git a/plugins/obs-filters/data/locale/pt-BR.ini b/plugins/obs-filters/data/locale/pt-BR.ini index 5e3968be2dd005..a08446e315f2d1 100644 --- a/plugins/obs-filters/data/locale/pt-BR.ini +++ b/plugins/obs-filters/data/locale/pt-BR.ini @@ -16,8 +16,8 @@ InvertPolarity="Inverter polaridade" Gain="Ganho" DelayMs="Atraso" Type="Tipo" -MaskBlendType.MaskColor="Máscara Alpha (Canal de Cor)" -MaskBlendType.MaskAlpha="Máscara Alpha (Canal Alpha)" +MaskBlendType.MaskColor="Máscara alfa (canal de cor)" +MaskBlendType.MaskAlpha="Máscara alfa (canal alfa)" MaskBlendType.BlendMultiply="Misturar (multiplicar)" MaskBlendType.BlendAddition="Misturar (adicionar)" MaskBlendType.BlendSubtraction="Misturar (subtrair)" @@ -46,11 +46,11 @@ Crop.Height="Altura" Crop.Relative="Relativo" HdrTonemap.Description="O OBS pode executar o mapeamento de tom HDR para SDR automaticamente. Use este filtro somente se você precisa de controle adicional para uma fonte específica.\nEste filtro não tem efeito sobre fontes SDR." HdrTonemap.ToneTransform="Transformação de tons" -HdrTonemap.SdrWhiteLevel="Nível de Branco SDR" -HdrTonemap.HdrInputMaximum="Entrada HDR Máximo" -HdrTonemap.HdrOutputMaximum="Saída HDR Máximo" -HdrTonemap.SdrInputMaximum="Entrada SDR Máxima" -HdrTonemap.SdrOutputMaximum="Saída HDR Máxima" +HdrTonemap.SdrWhiteLevel="Nível de branco do SDR" +HdrTonemap.HdrInputMaximum="Entrada máxima HDR" +HdrTonemap.HdrOutputMaximum="Saída máxima HDR" +HdrTonemap.SdrInputMaximum="Entrada máxima SDR" +HdrTonemap.SdrOutputMaximum="Saída máxima HDR" ScrollFilter.SpeedX="Velocidade horizontal" ScrollFilter.SpeedY="Velocidade vertical" ScrollFilter.LimitWidth="Limitar largura" @@ -78,50 +78,51 @@ NoiseSuppress.SuppressLevel="Nível de supressão" NoiseSuppress.Intensity="Intensidade de supressão" NoiseSuppress.Method="Método" NoiseSuppress.Method.Speex="Speex (baixo uso de CPU, qualidade baixa)" -NoiseSuppress.Method.RNNoise="RNNoise (qualidade boa, maior uso de CPU)" -NoiseSuppress.Method.Nvafx.Denoiser="Remoção de ruído NVIDIA" -NoiseSuppress.Method.Nvafx.Dereverb="Remoção de Eco Ambiente NVIDIA" -NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb="Remoção de ruído + Remoção de Eco Ambiente NVIDIA" -NoiseSuppress.Method.Nvafx.Deprecation="AVISO: Por favor, atualize tanto SDK de vídeo e de áudio NVIDIA. Sua versão atual do SDK de áudio está desatualizada." +NoiseSuppress.Method.RNNoise="RNNoise (maior uso de CPU, qualidade boa)" +NoiseSuppress.Method.Nvafx.Denoiser="Remoção de Ruído NVIDIA" +NoiseSuppress.Method.Nvafx.Dereverb="Remoção do Eco do Ambiente NVIDIA" +NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb="Remoção de Ruído + Remoção do Eco do Ambiente NVIDIA" +NoiseSuppress.Method.Nvafx.Deprecation="AVISO: Por favor, atualize os SDKs de vídeo e áudio da NVIDIA. Sua versão atual do SDK de áudio está desatualizada." Saturation="Saturação" -HueShift="Alteração de matiz" +HueShift="Desvio de matiz" Amount="Valor" -PassthroughAlpha="Passagem Alpha" -Compressor.Ratio="Razão" -Compressor.Threshold="Limiar" -Compressor.AttackTime="Ataque" -Compressor.ReleaseTime="Liberação" +PassthroughAlpha="Transparência alfa" +Compressor.Ratio="Razão (Ratio)" +Compressor.Threshold="Limiar (Threshold)" +Compressor.AttackTime="Ataque (Attack)" +Compressor.ReleaseTime="Liberação (Release)" Compressor.OutputGain="Ganho na saída" -Compressor.SidechainSource="Fonte de Cadeia Lateral/Oscilação de Áudio" +Compressor.SidechainSource="Fonte de Sidechain" Limiter="Limitador" -Limiter.Threshold="Limiar" -Limiter.ReleaseTime="Liberação" +Limiter.Threshold="Limiar (Threshold)" +Limiter.ReleaseTime="Liberação (Release)" Expander="Expansor" -Expander.Ratio="Razão" -Expander.Threshold="Limiar" -Expander.AttackTime="Ataque" -Expander.ReleaseTime="Liberação" -Expander.OutputGain="Ganho na Saída" +Expander.Ratio="Razão (Ratio)" +Expander.Threshold="Limiar (Threshold)" +Expander.AttackTime="Ataque (Attack)" +Expander.ReleaseTime="Liberação (Release)" +Expander.OutputGain="Ganho na saída" Expander.Detector="Detecção" Expander.Peak="Pico" Expander.Presets="Predefinições" Expander.Presets.Expander="Expansor" -Expander.Presets.Gate="Portão" -Expander.Knee.Width="Largura de Joelho" -Luma.LumaMax="Luminância Máxima" -Luma.LumaMin="Luminância Mínima" -Luma.LumaMaxSmooth="Suavização da Luminância Máxima" -Luma.LumaMinSmooth="Suavização da Luminância Mínima" -NvidiaGreenscreenFilter="Remoção de fundo da NVIDIA" +Expander.Presets.Gate="Filtro (Gate)" +Expander.Knee.Width="Largura do Knee" +LumaKeyFilter="Chave Luma" +Luma.LumaMax="Luma máximo" +Luma.LumaMin="Luma mínimo" +Luma.LumaMaxSmooth="Suavização do Luma máximo" +Luma.LumaMinSmooth="Suavização do Luma mínimo" +NvidiaGreenscreenFilter="Remoção de Fundo da NVIDIA" Greenscreen.Mode="Modo" Greenscreen.Quality="Qualidade (maior uso da GPU, melhor qualidade)" Greenscreen.Performance="Desempenho (menor uso da GPU, boa qualidade)" -Greenscreen.Threshold="Limite" -Greenscreen.Deprecation="AVISO: Por favor, atualize tanto SDK de vídeo e de áudio NVIDIA. Sua versão atual do SDK de vídeo está desatualizada." +Greenscreen.Threshold="Limiar (Threshold)" +Greenscreen.Deprecation="AVISO: Por favor, atualize os SDKs de vídeo e áudio da NVIDIA. Sua versão atual do SDK de vídeo está desatualizada." Greenscreen.Processing="Mascarar frequência de atualização em quadros" Greenscreen.Processing.Hint="Isto alivia a carga da GPU gerando uma máscara a cada N quadros apenas (2 por padrão)." -Upward.Compressor="Compressor para Cima" -3BandEq="Equalizador de 3 Bandas" +Upward.Compressor="Compressor para cima" +3BandEq="Equalizador de 3 bandas" 3BandEq.low="Baixo" 3BandEq.mid="Médio" 3BandEq.high="Alto" diff --git a/plugins/obs-filters/data/locale/pt-PT.ini b/plugins/obs-filters/data/locale/pt-PT.ini index 9b8360531d10cc..9774b9a5b2ddb3 100644 --- a/plugins/obs-filters/data/locale/pt-PT.ini +++ b/plugins/obs-filters/data/locale/pt-PT.ini @@ -1,7 +1,7 @@ ColorFilter="Correção de cor" ColorGradeFilter="Aplicar LUT" MaskFilter="Máscara/Mistura de imagem" -AsyncDelayFilter="Atraso de vídeo (Async)" +AsyncDelayFilter="Desfasamento de vídeo (Async)" CropFilter="Recortar/Espaçar" HdrTonemapFilter="Mapeamento de tons HDR (Sobrepor)" ScrollFilter="Rolar" @@ -10,13 +10,13 @@ ColorKeyFilter="Chave de cor" SharpnessFilter="Afinar" Sharpness="Nítidez" ScaleFilter="Proporção/Redimensionamento" -GPUDelayFilter="Atraso de processamento" +GPUDelayFilter="Desfasamento de processamento" UndistortCenter="Remover distorção ao escalar de imagens ultra-largas" NoiseGate="Filtro de ruído" NoiseSuppress="Supressão de ruído" InvertPolarity="Inverter polaridade" Gain="Ganho" -DelayMs="Atraso" +DelayMs="Desfasamento" Type="Tipo" MaskBlendType.MaskColor="Máscara Alfa (canal de cor)" MaskBlendType.MaskAlpha="Máscara Alfa (canal Alfa)" diff --git a/plugins/obs-filters/data/locale/ru-RU.ini b/plugins/obs-filters/data/locale/ru-RU.ini index 02ebed837c8ea8..b8d39f2f6b434c 100644 --- a/plugins/obs-filters/data/locale/ru-RU.ini +++ b/plugins/obs-filters/data/locale/ru-RU.ini @@ -1,6 +1,6 @@ ColorFilter="Коррекция цвета" ColorGradeFilter="Применить LUT" -MaskFilter="Маска изображения/смешивание" +MaskFilter="Маска/наложение изображения" AsyncDelayFilter="Задержка видео (асинхронность)" CropFilter="Кадрирование" HdrTonemapFilter="Тональная компрессия HDR (подмена)" diff --git a/plugins/obs-filters/data/locale/tt-RU.ini b/plugins/obs-filters/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..b96926689dc686 --- /dev/null +++ b/plugins/obs-filters/data/locale/tt-RU.ini @@ -0,0 +1,20 @@ +Gain="Көчәйтү" +Type="Төр" +Path="Юл" +Color="Төс" +ColorAdd="Төсне өстәү" +Opacity="Тоныксызлык" +Brightness="Яктылык" +Crop.Width="Киңлек" +Crop.Height="Биеклек" +Red="Кызыл" +Green="Яшел" +Blue="Зәңгәр" +Magenta="Куе кызыл" +Gain.GainDB="Көчәйтү" +Resolution="Ачыклык" +None="Юк" +Compressor.OutputGain="Чыгышны көчәйтү" +Expander.OutputGain="Чыгышны көчәйтү" +Greenscreen.Mode="Шарт" +Greenscreen.Threshold="Чик" diff --git a/plugins/obs-libfdk/data/locale/tt-RU.ini b/plugins/obs-libfdk/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..5b99874a73fb6d --- /dev/null +++ b/plugins/obs-libfdk/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Bitrate="Битрейт" diff --git a/plugins/obs-outputs/data/locale/be-BY.ini b/plugins/obs-outputs/data/locale/be-BY.ini index 731ad423058c9d..cc987cef47703e 100644 --- a/plugins/obs-outputs/data/locale/be-BY.ini +++ b/plugins/obs-outputs/data/locale/be-BY.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Рэжым нізкай затрымкі" FLVOutput="Вывад файла FLV" FLVOutput.FilePath="Шлях да файла" Default="Прадвызн." +MP4Output="Вывад файла MP4" +MP4Output.FilePath="Шлях да файла" +MP4Output.StartChapter="Пачатак" +MP4Output.UnnamedChapter="Без назвы" IPFamily="Сям'я IP-адрасоў" IPFamily.Both="IPv4 і IPv6 (прадвызн.)" IPFamily.V4Only="Толькі IPv4" diff --git a/plugins/obs-outputs/data/locale/ca-ES.ini b/plugins/obs-outputs/data/locale/ca-ES.ini index feb9d2d5f3e085..3798ea4f9f35a2 100644 --- a/plugins/obs-outputs/data/locale/ca-ES.ini +++ b/plugins/obs-outputs/data/locale/ca-ES.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Mode de baixa latència" FLVOutput="Sortida del fitxer FLV" FLVOutput.FilePath="Camí del fitxer" Default="Per defecte" +MP4Output="Sortida de fitxer MP4" +MP4Output.FilePath="Camí del fitxer" +MP4Output.StartChapter="Inicia" +MP4Output.UnnamedChapter="Sense nom" IPFamily="Família d'adreces IP" IPFamily.Both="IPv4 i IPv6 (per defecte)" IPFamily.V4Only="Només IPv4" diff --git a/plugins/obs-outputs/data/locale/cs-CZ.ini b/plugins/obs-outputs/data/locale/cs-CZ.ini index 60a0dce7d89fd6..cb238caef667ba 100644 --- a/plugins/obs-outputs/data/locale/cs-CZ.ini +++ b/plugins/obs-outputs/data/locale/cs-CZ.ini @@ -5,6 +5,10 @@ RTMPStream.LowLatencyMode="Režim nízké odezvy" FLVOutput="Výstup do FLV souboru" FLVOutput.FilePath="Cesta k souboru" Default="Výchozí" +MP4Output="Výstup do MP4 souboru" +MP4Output.FilePath="Cesta k souboru" +MP4Output.StartChapter="Začátek" +MP4Output.UnnamedChapter="Nepojmenovaný" IPFamily="Rodina IP adres" IPFamily.Both="IPv4 a IPv6 (výchozí)" IPFamily.V4Only="Pouze IPv4" diff --git a/plugins/obs-outputs/data/locale/de-DE.ini b/plugins/obs-outputs/data/locale/de-DE.ini index 8a42629c0c7fcb..8b192ae3fdbbd7 100644 --- a/plugins/obs-outputs/data/locale/de-DE.ini +++ b/plugins/obs-outputs/data/locale/de-DE.ini @@ -6,6 +6,9 @@ RTMPStream.LowLatencyMode="Niedriger Latenzmodus" FLVOutput="FLV-Dateiausgabe" FLVOutput.FilePath="Dateipfad" Default="Standard" +MP4Output="MP4-Dateiausgabe" +MP4Output.FilePath="Dateipfad" +MP4Output.UnnamedChapter="Unbenannt" IPFamily="IP-Adressfamilie" IPFamily.Both="IPv4 und IPv6 (Standard)" IPFamily.V4Only="Nur IPv4" diff --git a/plugins/obs-outputs/data/locale/es-ES.ini b/plugins/obs-outputs/data/locale/es-ES.ini index d91ccc32bd0640..83798206690c7b 100644 --- a/plugins/obs-outputs/data/locale/es-ES.ini +++ b/plugins/obs-outputs/data/locale/es-ES.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Modo de baja latencia" FLVOutput="Archivo de salida FLV" FLVOutput.FilePath="Ruta de archivo" Default="Por defecto" +MP4Output="Archivo de salida MP4" +MP4Output.FilePath="Ruta del archivo" +MP4Output.StartChapter="Iniciar" +MP4Output.UnnamedChapter="Sin nombre" IPFamily="Familia de la dirección IP" IPFamily.Both="IPv4 y IPv6 (per defecto)" IPFamily.V4Only="Sólo IPv4" diff --git a/plugins/obs-outputs/data/locale/fi-FI.ini b/plugins/obs-outputs/data/locale/fi-FI.ini index c8db6b42545347..417d3b283bec35 100644 --- a/plugins/obs-outputs/data/locale/fi-FI.ini +++ b/plugins/obs-outputs/data/locale/fi-FI.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Alhainen latenssi" FLVOutput="FLV-tiedostoulostulo" FLVOutput.FilePath="Tiedostopolku" Default="Oletusarvo" +MP4Output="MP4-tiedosto ulostulo" +MP4Output.FilePath="Tiedostopolku" +MP4Output.StartChapter="Aloita" +MP4Output.UnnamedChapter="Nimetön" IPFamily="IP-osoiteperhe" IPFamily.Both="IPv4 ja IPv6 (oletus)" IPFamily.V4Only="Vain IPv4" diff --git a/plugins/obs-outputs/data/locale/he-IL.ini b/plugins/obs-outputs/data/locale/he-IL.ini index 5c59299a4f4ec9..8ade1bcfa0713f 100644 --- a/plugins/obs-outputs/data/locale/he-IL.ini +++ b/plugins/obs-outputs/data/locale/he-IL.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="מצב השהיה נמוכה" FLVOutput="קובץ פלט FLV" FLVOutput.FilePath="נתיב קובץ" Default="ברירת־מחדל" +MP4Output="פלט קובץ MP4" +MP4Output.FilePath="נתיב קובץ" +MP4Output.StartChapter="התחלה" +MP4Output.UnnamedChapter="ללא שם" IPFamily="משפחת כתובות IP" IPFamily.Both="IPv4 ו־IPv6 (ברירת מחדל)" IPFamily.V4Only="IPv4 בלבד" diff --git a/plugins/obs-outputs/data/locale/hi-IN.ini b/plugins/obs-outputs/data/locale/hi-IN.ini index 7eaf01219afa4e..2567cd67772e55 100644 --- a/plugins/obs-outputs/data/locale/hi-IN.ini +++ b/plugins/obs-outputs/data/locale/hi-IN.ini @@ -8,7 +8,7 @@ FLVOutput.FilePath="फ़ाइल पथ" Default="आरंभिक" IPFamily="IP पता परिवार" IPFamily.Both="IPv4 और IPv6 (डिफ़ॉल्ट)" -IPFamily.V4Only="कुंजी: IPFamily.V4केवल\nIPFamily.V4केवल" +IPFamily.V4Only="केवल IPv4" IPFamily.V6Only="केवल IPv6" ConnectionTimedOut="कनेक्शन का समय समाप्त हो गया. सुनिश्चित करें कि आपने एक मान्य स्ट्रीमिंग सेवा कॉन्फ़िगर की है और कोई फ़ायरवॉल कनेक्शन को अवरुद्ध नहीं कर रहा है." PermissionDenied="कनेक्शन ब्लॉक कर दिया गया था. यह सुनिश्चित करने के लिए कि OBS को पूर्ण इंटरनेट एक्सेस की अनुमति है, अपनी फ़ायरवॉल / एंटी-वायरस सेटिंग्स की जाँच करें." diff --git a/plugins/obs-outputs/data/locale/hu-HU.ini b/plugins/obs-outputs/data/locale/hu-HU.ini index 7970f39c4b008c..5542be2c235a8c 100644 --- a/plugins/obs-outputs/data/locale/hu-HU.ini +++ b/plugins/obs-outputs/data/locale/hu-HU.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Alacsony késleltetésű mód" FLVOutput="FLV kimeneti fájl" FLVOutput.FilePath="Fájl elérési útja" Default="Alapértelmezett" +MP4Output="MP4 kimeneti fájl" +MP4Output.FilePath="Fájl elérési útja" +MP4Output.StartChapter="Kezdés" +MP4Output.UnnamedChapter="Névtelen" IPFamily="IP-címcsalád" IPFamily.Both="IPv4 és IPv6 (alapértelmezett)" IPFamily.V4Only="Csak IPv4" diff --git a/plugins/obs-outputs/data/locale/id-ID.ini b/plugins/obs-outputs/data/locale/id-ID.ini index 6aad21cf1fac30..8c7b8d2fc00149 100644 --- a/plugins/obs-outputs/data/locale/id-ID.ini +++ b/plugins/obs-outputs/data/locale/id-ID.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Mode Latensi Rendah" FLVOutput="Output Berkas FLV" FLVOutput.FilePath="Jalur Berkas" Default="Bawaan" +MP4Output="Keluaran Berkas MP4" +MP4Output.FilePath="Jalur Berkas" +MP4Output.StartChapter="Mulai" +MP4Output.UnnamedChapter="Tanpa Nama" IPFamily="Keluarga Alamat IP" IPFamily.Both="IPv4 dan IPv6 (Bawaan)" IPFamily.V4Only="Hanya IPv4" diff --git a/plugins/obs-outputs/data/locale/it-IT.ini b/plugins/obs-outputs/data/locale/it-IT.ini index 726e0d967352ba..d95fe21567dbfe 100644 --- a/plugins/obs-outputs/data/locale/it-IT.ini +++ b/plugins/obs-outputs/data/locale/it-IT.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Modalità a bassa latenza" FLVOutput="Uscita file FLV" FLVOutput.FilePath="Percorso file" Default="Predefinito" +MP4Output="File MP4 di uscita" +MP4Output.FilePath="Percorso del file" +MP4Output.StartChapter="Inizio" +MP4Output.UnnamedChapter="Senza nome" IPFamily="Famiglia indirizzo IP" IPFamily.Both="IPv4 e IPv6 (predefinito)" IPFamily.V4Only="Solo IPv4" diff --git a/plugins/obs-outputs/data/locale/ja-JP.ini b/plugins/obs-outputs/data/locale/ja-JP.ini index 13a9ead05887f6..530b4b091a0173 100644 --- a/plugins/obs-outputs/data/locale/ja-JP.ini +++ b/plugins/obs-outputs/data/locale/ja-JP.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="低遅延モード" FLVOutput="FLV ファイル出力" FLVOutput.FilePath="ファイルのパス" Default="既定" +MP4Output="MP4ファイル出力" +MP4Output.FilePath="ファイルのパス" +MP4Output.StartChapter="開始" +MP4Output.UnnamedChapter="名称未設定" IPFamily="IP アドレスファミリー" IPFamily.Both="IPv4 および IPv6 (既定値)" IPFamily.V4Only="IPv4 のみ" diff --git a/plugins/obs-outputs/data/locale/ko-KR.ini b/plugins/obs-outputs/data/locale/ko-KR.ini index 692698ffe0d23b..6a0c58132d9886 100644 --- a/plugins/obs-outputs/data/locale/ko-KR.ini +++ b/plugins/obs-outputs/data/locale/ko-KR.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="짧은 지연 시간 방식" FLVOutput="FLV 파일 출력" FLVOutput.FilePath="파일 경로" Default="기본값" +MP4Output="MP4 파일 출력" +MP4Output.FilePath="파일 경로" +MP4Output.StartChapter="시작" +MP4Output.UnnamedChapter="이름 없음" IPFamily="IP 주소 체계" IPFamily.Both="IPv4 및 IPv6 (기본값)" IPFamily.V4Only="IPv4 전용" diff --git a/plugins/obs-outputs/data/locale/nl-NL.ini b/plugins/obs-outputs/data/locale/nl-NL.ini index 5dc4c46a4c021b..75e9e9faa8fcdb 100644 --- a/plugins/obs-outputs/data/locale/nl-NL.ini +++ b/plugins/obs-outputs/data/locale/nl-NL.ini @@ -4,6 +4,9 @@ RTMPStream.LowLatencyMode="Lage latency modus" FLVOutput="FLV Bestandsuitvoer" FLVOutput.FilePath="Bestandspad" Default="Standaard" +MP4Output="MP4 bestandsoutput" +MP4Output.FilePath="Bestandspad" +MP4Output.UnnamedChapter="Naamloos" IPFamily="IP-adres familie" IPFamily.Both="IPv4 en IPv6 (Standaard)" IPFamily.V4Only="Alleen IPv4" diff --git a/plugins/obs-outputs/data/locale/pl-PL.ini b/plugins/obs-outputs/data/locale/pl-PL.ini index 7bf31c65a2a203..cd53109f241047 100644 --- a/plugins/obs-outputs/data/locale/pl-PL.ini +++ b/plugins/obs-outputs/data/locale/pl-PL.ini @@ -5,6 +5,10 @@ RTMPStream.LowLatencyMode="Tryb niskich opóźnień" FLVOutput="Wyjście do pliku FLV" FLVOutput.FilePath="Ścieżka pliku" Default="Domyślne" +MP4Output="Wyjście do pliku MP4" +MP4Output.FilePath="Ścieżka pliku" +MP4Output.StartChapter="Początek" +MP4Output.UnnamedChapter="Bez nazwy" IPFamily="Protokół IP" IPFamily.Both="IPv4 i IPv6 (domyślnie)" IPFamily.V4Only="Tylko IPv4" diff --git a/plugins/obs-outputs/data/locale/pt-BR.ini b/plugins/obs-outputs/data/locale/pt-BR.ini index 90eefb2f02ad9a..49dd9bc0119f22 100644 --- a/plugins/obs-outputs/data/locale/pt-BR.ini +++ b/plugins/obs-outputs/data/locale/pt-BR.ini @@ -6,10 +6,14 @@ RTMPStream.LowLatencyMode="Modo de baixa latência" FLVOutput="Arquivo de Saída FLV" FLVOutput.FilePath="Caminho do Arquivo" Default="Padrão" +MP4Output="Arquivo de Saída MP4" +MP4Output.FilePath="Caminho do arquivo" +MP4Output.StartChapter="Iniciar" +MP4Output.UnnamedChapter="Sem nome" IPFamily="Família de endereços IP" -IPFamily.Both="IPv4 e IPv6 (Padrão)" -IPFamily.V4Only="IPv4 Apenas" -IPFamily.V6Only="IPv6 Apenas" +IPFamily.Both="IPv4 e IPv6 (padrão)" +IPFamily.V4Only="Apenas IPv4" +IPFamily.V6Only="Apenas IPv6" ConnectionTimedOut="A conexão expirou. Verifique se você configurou um serviço de transmissão válido e nenhum firewall está bloqueando a conexão." PermissionDenied="A conexão foi bloqueada. Verifique seu firewall / configurações de antivírus para certificar-se do acesso completo do OBS à internet." ConnectionAborted="A conexão foi abortada. Isso geralmente indica problemas de conexão entre você e o serviço de transmissão." diff --git a/plugins/obs-outputs/data/locale/pt-PT.ini b/plugins/obs-outputs/data/locale/pt-PT.ini index e3a0b813f91a11..e69534746a003b 100644 --- a/plugins/obs-outputs/data/locale/pt-PT.ini +++ b/plugins/obs-outputs/data/locale/pt-PT.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Modo de baixa latência" FLVOutput="Saída de ficheiro FLV" FLVOutput.FilePath="Caminho do ficheiro" Default="Predefinido" +MP4Output="Ficheiro de saída MP4" +MP4Output.FilePath="Caminho do ficheiro" +MP4Output.StartChapter="Início" +MP4Output.UnnamedChapter="Sem nome" IPFamily="Família de endereços IP" IPFamily.Both="IPv4 e IPv6 (Predefinido)" IPFamily.V4Only="Apenas IPv4" diff --git a/plugins/obs-outputs/data/locale/ru-RU.ini b/plugins/obs-outputs/data/locale/ru-RU.ini index 8d60cc0206ed0f..c2ae577305d730 100644 --- a/plugins/obs-outputs/data/locale/ru-RU.ini +++ b/plugins/obs-outputs/data/locale/ru-RU.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Режим низкой задержки" FLVOutput="Выходной файл FLV" FLVOutput.FilePath="Путь файла" Default="По умолчанию" +MP4Output="Выходной файл MP4" +MP4Output.FilePath="Путь файла" +MP4Output.StartChapter="Запустить" +MP4Output.UnnamedChapter="Без имени" IPFamily="Семейство IP-адресов" IPFamily.Both="IPv4 и IPv6 (По умолчанию)" IPFamily.V4Only="Только IPv4" diff --git a/plugins/obs-outputs/data/locale/sv-SE.ini b/plugins/obs-outputs/data/locale/sv-SE.ini index 26a52e832524b8..1428cdc1521eff 100644 --- a/plugins/obs-outputs/data/locale/sv-SE.ini +++ b/plugins/obs-outputs/data/locale/sv-SE.ini @@ -5,6 +5,9 @@ RTMPStream.LowLatencyMode="Låg latens-läge" FLVOutput="FLV-filutmatning" FLVOutput.FilePath="Filsökväg" Default="Standard" +MP4Output="MP4-filutmatning" +MP4Output.FilePath="Filsökväg" +MP4Output.UnnamedChapter="Namnlös" IPFamily="IP-adressfamilj" IPFamily.Both="IPv4 och IPv6 (standard)" IPFamily.V4Only="Endast IPv4" diff --git a/plugins/obs-outputs/data/locale/th-TH.ini b/plugins/obs-outputs/data/locale/th-TH.ini index a06a577334d278..d6ea6ab73e1e30 100644 --- a/plugins/obs-outputs/data/locale/th-TH.ini +++ b/plugins/obs-outputs/data/locale/th-TH.ini @@ -1 +1,6 @@ +RTMPStream.LowLatencyMode="โหมดความหน่วงต่ำ" FLVOutput.FilePath="ตำแหน่งไฟล์" +Default="ค่าเริ่มต้น" +IPFamily.Both="IPv4 และ IPv6 (ค่าเริ่มต้น)" +IPFamily.V4Only="IPv4 เท่านั้น" +IPFamily.V6Only="IPv6 เท่านั้น" diff --git a/plugins/obs-outputs/data/locale/tt-RU.ini b/plugins/obs-outputs/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..bbf8e538c0ca93 --- /dev/null +++ b/plugins/obs-outputs/data/locale/tt-RU.ini @@ -0,0 +1,7 @@ +FLVOutput="FLV чыгыш файлы" +FLVOutput.FilePath="Файл юлы" +MP4Output="MP4 чыгыш файлы" +MP4Output.FilePath="Файл юлы" +MP4Output.UnnamedChapter="Исемсез" +IPFamily.V4Only="IPv4 генә" +IPFamily.V6Only="IPv6 гына" diff --git a/plugins/obs-outputs/data/locale/uk-UA.ini b/plugins/obs-outputs/data/locale/uk-UA.ini index 02878d55c8b8b0..0b69cb1f55a4a9 100644 --- a/plugins/obs-outputs/data/locale/uk-UA.ini +++ b/plugins/obs-outputs/data/locale/uk-UA.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Режим з низькою затримкою" FLVOutput="Вивід FLV файлу" FLVOutput.FilePath="Шлях до файлу" Default="За замовчуванням" +MP4Output="Вивід MP4 файлу" +MP4Output.FilePath="Шлях до файлу" +MP4Output.StartChapter="Початок" +MP4Output.UnnamedChapter="Без назви" IPFamily="IP-адреса Родини" IPFamily.Both="IPv4 та IPv6 (за замовчуванням)" IPFamily.V4Only="Тільки IPv4" diff --git a/plugins/obs-outputs/data/locale/vi-VN.ini b/plugins/obs-outputs/data/locale/vi-VN.ini index 9cacf2dc947afe..ff48942c80f21d 100644 --- a/plugins/obs-outputs/data/locale/vi-VN.ini +++ b/plugins/obs-outputs/data/locale/vi-VN.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Chế độ Độ trễ Thấp" FLVOutput="FLV tập tin đầu ra" FLVOutput.FilePath="Đường dẫn tệp" Default="Mặc định" +MP4Output="Đầu ra tập tin MP4" +MP4Output.FilePath="Đường dẫn tệp" +MP4Output.StartChapter="Bắt đầu" +MP4Output.UnnamedChapter="Không tên" IPFamily="Dòng địa chỉ IP" IPFamily.Both="IPv4 và IPv6 (Mặc định)" IPFamily.V4Only="Chỉ IPv4" diff --git a/plugins/obs-outputs/data/locale/zh-CN.ini b/plugins/obs-outputs/data/locale/zh-CN.ini index b7e7f12b4a92d0..f4bf2cb6377334 100644 --- a/plugins/obs-outputs/data/locale/zh-CN.ini +++ b/plugins/obs-outputs/data/locale/zh-CN.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="低延迟模式" FLVOutput="FLV 文件输出" FLVOutput.FilePath="文件路径" Default="默认" +MP4Output="MP4 文件输出" +MP4Output.FilePath="文件路径" +MP4Output.StartChapter="开始" +MP4Output.UnnamedChapter="未命名" IPFamily="IP 地址族" IPFamily.Both="IPv4 和 IPv6(默认)" IPFamily.V4Only="仅 IPv4" diff --git a/plugins/obs-outputs/data/locale/zh-TW.ini b/plugins/obs-outputs/data/locale/zh-TW.ini index f4c419e07a6010..d238874bf99156 100644 --- a/plugins/obs-outputs/data/locale/zh-TW.ini +++ b/plugins/obs-outputs/data/locale/zh-TW.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="低延遲模式" FLVOutput="FLV 檔案輸出" FLVOutput.FilePath="檔案路徑" Default="預設" +MP4Output="MP4 檔案輸出" +MP4Output.FilePath="檔案路徑" +MP4Output.StartChapter="開始" +MP4Output.UnnamedChapter="未命名" IPFamily="IP 地址家族" IPFamily.Both="IPv4 和 IPv6(預設)" IPFamily.V4Only="僅限 IPv4" diff --git a/plugins/obs-qsv11/data/locale/af-ZA.ini b/plugins/obs-qsv11/data/locale/af-ZA.ini index 63c6cc4b34e45c..ec59f25c5e8fe0 100644 --- a/plugins/obs-qsv11/data/locale/af-ZA.ini +++ b/plugins/obs-qsv11/data/locale/af-ZA.ini @@ -6,7 +6,6 @@ Profile="Profiel" ICQQuality="ICQ-kwaliteit" Latency="Vertraging" Latency.ToolTip="Daar is ’n wisselwerking tussen vertraging en kwaliteit.\nAs die kwaliteit verkies word kies u ‘normaal’, waarby die eind-tot-eind vertraging >2s kan wees.\nAs u <500ms vertraging benodig kies u ‘ultra-laag’." -SubjectiveVideoEnhancements="Subjektiewe videoverbeteringe" 8bitUnsupportedHdr="OBS ondersteun nie 8-bisafvoer van Rec. 2100 nie." 10bitUnsupportedAvc="Kan nie 10-bisenkodering op Intel QSV H.264-enkodeerder uitvoer nie." 16bitUnsupported="Kan nie 16-bisenkodering op hierdie enkodeerder uitvoer nie." diff --git a/plugins/obs-qsv11/data/locale/ar-SA.ini b/plugins/obs-qsv11/data/locale/ar-SA.ini index d1b092e42eafec..12d19107d58742 100644 --- a/plugins/obs-qsv11/data/locale/ar-SA.ini +++ b/plugins/obs-qsv11/data/locale/ar-SA.ini @@ -7,10 +7,10 @@ Profile="الملف الشخصي" ICQQuality="جودة ICQ" Latency="تأخير" Latency.ToolTip="هناك مفاضلة بين الوقت والجودة.\nإذا كنت تفضل الجودة، الرجاء تحديد الوضع \"العادي\"، الذي قد يحصل على أكثر من 2 ثانية من النهاية إلى النهاية.\nإذا كانت حالتك تتطلب مهلة أقل من 500ms من النهاية إلى النهاية، الرجاء تحديد وضع \"فائق الانخفاض\"." -SubjectiveVideoEnhancements="تحسينات الفيديو الذاتية" 8bitUnsupportedHdr="لا يدعم OBS إخراج 8 بت من Rec. 2100." 10bitUnsupportedAvc="لا يمكن تنفيذ تشفير 10 بت على وحدة تشفير Intel QSV H.264." 16bitUnsupported="لا يمكن تشفير 16 بت على هذا المشفر." +BFrames="إطارات B" TargetUsage.TU1="TU1: الأبطأ (أفضل جودة)" TargetUsage.TU2="TU2: أبطأ" TargetUsage.TU3="TU3: بطيء" diff --git a/plugins/obs-qsv11/data/locale/be-BY.ini b/plugins/obs-qsv11/data/locale/be-BY.ini index 99c6924a5972dd..97a5bc5d15a8be 100644 --- a/plugins/obs-qsv11/data/locale/be-BY.ini +++ b/plugins/obs-qsv11/data/locale/be-BY.ini @@ -7,7 +7,6 @@ Profile="Профіль" ICQQuality="Яксаць ICQ" Latency="Затрымка" Latency.ToolTip="Паміж затрымкай і якасцю існуе ўзаемазалежнасць.\nКалі ў вашым выпадку важная якасць, выберыце «нармальны» рэжым (у ім затрымка можа перавышаць 2 с).\nКалі ў вашым выпадку патрабуецца затрымка, меншая за 500 мс, выберыце «ультранізкі» рэжым." -SubjectiveVideoEnhancements="Суб'ектыўныя паляпшэнні відэа" 8bitUnsupportedHdr="OBS не падтрымлівае 8-бітны вывад з Rec. 2100." 10bitUnsupportedAvc="Кадавальнік Intel QSV H.264 не падтрымлівае 10-бітнае кадаванне." 16bitUnsupported="Гэты кадавальнік не падтрымлівае 16-бітнае кадаванне." diff --git a/plugins/obs-qsv11/data/locale/bn-BD.ini b/plugins/obs-qsv11/data/locale/bn-BD.ini index c3c6b86f4f0705..5b58d2295e6bb9 100644 --- a/plugins/obs-qsv11/data/locale/bn-BD.ini +++ b/plugins/obs-qsv11/data/locale/bn-BD.ini @@ -6,4 +6,3 @@ Profile="প্রোফাইল" ICQQuality="ICQ মান" Latency="বিলম্ব" Latency.ToolTip="বিলম্বিতা এবং মানের মধ্যে লাভ-ক্ষতি আছে।\nযদি আপনি মানের দিকে অগ্রাধিকার দেন তবে দয়া করে 'সাধারণ' মোডটি নির্বাচন করুন, যার দরুণ > 2 সেকেন্ড শেষ-থেকে-শেষ বিলম্বতা পেতে পারেন।\nকিন্তু যদি < 500 মিলিসেকেন্ড শেষ-থেকে-শেষ বিলম্বের প্রয়োজন হয় তবে দয়া করে 'খুব-কম' ধরনটি নির্বাচন করুন।" -SubjectiveVideoEnhancements="বিষয়গত ভিডিও বর্ধন" diff --git a/plugins/obs-qsv11/data/locale/ca-ES.ini b/plugins/obs-qsv11/data/locale/ca-ES.ini index 76631acd0aef23..5a21df9ded1465 100644 --- a/plugins/obs-qsv11/data/locale/ca-ES.ini +++ b/plugins/obs-qsv11/data/locale/ca-ES.ini @@ -7,7 +7,6 @@ Profile="Perfil" ICQQuality="Qualitat ICQ" Latency="Latència" Latency.ToolTip="Existeix un equilibri entre la latència i la qualitat.\nSi preferiu qualitat, seleccioneu el mode «normal», que pot arribar a > 2s de latència final.\nSi necessiteu una latència < 500ms, aleshores seleccioneu el mode «ultra baix»." -SubjectiveVideoEnhancements="Millores subjectives de vídeo" 8bitUnsupportedHdr="L'OBS no admet la sortida de 8 bits de Rec. 2100." 10bitUnsupportedAvc="No es pot realitzar la codificació de 10 bits al codificador Intel QSV H.264." 16bitUnsupported="No es pot realitzar la codificació de 16 bits amb aquest codificador." diff --git a/plugins/obs-qsv11/data/locale/cs-CZ.ini b/plugins/obs-qsv11/data/locale/cs-CZ.ini index 520838522c807e..ad2c10a156a716 100644 --- a/plugins/obs-qsv11/data/locale/cs-CZ.ini +++ b/plugins/obs-qsv11/data/locale/cs-CZ.ini @@ -6,7 +6,6 @@ Profile="Profil" ICQQuality="Kvalita ICQ" Latency="Odezva" Latency.ToolTip="Existuje kompromis mezi odezvou a kvalitou.\nPokud preferujete kvalitu, tak prosím vyberte 'normal' mód, který může přidat > 2s odezvy.\nPokud vyžadujete < 500ms odezvy, tak prosím vyberte 'ultra-low' mód." -SubjectiveVideoEnhancements="Subjektivní vylepšení obrazu" 8bitUnsupportedHdr="OBS nepodporuje 8bitový výstup Rec. 2100." 10bitUnsupportedAvc="Nelze provést 10bitové enkódování pomocí Intel QSV H.264 enkodéru." 16bitUnsupported="Nelze provést 16bitové enkódování na tomto enkodéru." diff --git a/plugins/obs-qsv11/data/locale/da-DK.ini b/plugins/obs-qsv11/data/locale/da-DK.ini index aef35834a27a81..0877ec9cdbfa92 100644 --- a/plugins/obs-qsv11/data/locale/da-DK.ini +++ b/plugins/obs-qsv11/data/locale/da-DK.ini @@ -7,6 +7,5 @@ Profile="Profil" ICQQuality="ICQ-kvalitet" Latency="Latenstid" Latency.ToolTip="Der er omkostning mellem latenstid og kvalitet.\nForetrækkes kvalitet i situationen, så vælg tilstanden 'normal', der kan betyde >2 s ende-til-ende latenstid.\nKræver situationen <500 ms ende-til-ende latenstid, så vælg tilstanden 'ultralav'." -SubjectiveVideoEnhancements="Subjektive Videoforbedringer" 8bitUnsupportedHdr="OBS understøtter ikke 8-bit output fra Rec. 2100." 10bitUnsupportedAvc="Kan ikke udføre 10-bit kode på Intel QSV H.264-koder." diff --git a/plugins/obs-qsv11/data/locale/de-DE.ini b/plugins/obs-qsv11/data/locale/de-DE.ini index a8656c0e132897..36a041df91542f 100644 --- a/plugins/obs-qsv11/data/locale/de-DE.ini +++ b/plugins/obs-qsv11/data/locale/de-DE.ini @@ -6,10 +6,9 @@ Profile="Profil" ICQQuality="ICQ-Qualität" Latency="Latenz" Latency.ToolTip="Es gibt einen Kompromiss zwischen Latenz und Qualität.\nWenn Sie für Ihren Fall Qualität bevorzugen, wählen Sie den Modus „normal“\naus, durch den eine End-zu-End-Latenz von mehr als 2 s auftreten kann.\nFalls Sie aber eine Latenz von weniger als 500 ms\nbenötigen, wählen Sie den Modus „ultra-low“." -SubjectiveVideoEnhancements="Subjektive Videoverbesserungen" 8bitUnsupportedHdr="Die Ausgabe von 8-Bit von Rec. 2100 wird nicht unterstützt." -10bitUnsupportedAvc="Mit dem Intel-QSV-H.264-Encoder kann kein 10-Bit encodet werden." -16bitUnsupported="Mit diesem Encoder kann kein 16-Bit encodet werden." +10bitUnsupportedAvc="Mit dem Intel-QSV-H.264-Kodierer kann kein 10-Bit kodiert werden." +16bitUnsupported="Mit diesem Kodierer kann kein 16-Bit kodiert werden." BFrames="B-Frames" TargetUsage.TU1="TU1: Am langsamsten (Beste Qualität)" TargetUsage.TU2="TU2: Langsamer" diff --git a/plugins/obs-qsv11/data/locale/el-GR.ini b/plugins/obs-qsv11/data/locale/el-GR.ini index e9e29adb7ec982..c3c507ffca6d8a 100644 --- a/plugins/obs-qsv11/data/locale/el-GR.ini +++ b/plugins/obs-qsv11/data/locale/el-GR.ini @@ -7,7 +7,6 @@ Profile="Προφίλ" ICQQuality="Ποιότητα ICQ" Latency="Καθυστέρηση" Latency.ToolTip="Υπάρχει αλληλεξάρτηση της καθυστέρησης με την ποιότητα.\nΑν προτιμάτε υψηλή ποιότητα, επιλέξτε την 'κανονική' λειτουργία, η οποία μπορεί να έχει καθυστέρηση πάνω από 2 δευτερόλεπτα.\nΑν χρειάζεστε καθυστέρηση μικρότερη των 500 χιλιοστών του δευτερολέπτου, παρακαλώ επιλέξτε την 'εξαιρετικά χαμηλή' λειτουργία." -SubjectiveVideoEnhancements="Υποκειμενικές Βελτιώσεις Βίντεο" 8bitUnsupportedHdr="Το OBS δεν υποστηρίζει την έξοδο 8-bit του Rec. 2100." 10bitUnsupportedAvc="Αδυναμία εκτέλεσης 10-bit κωδικοποίησης στον κωδικοποιητή Intel QSV H.264." 16bitUnsupported="Αδυναμία εκτέλεσης 16-bit κωδικοποίησης σε αυτόν τον κωδικοποιητή." diff --git a/plugins/obs-qsv11/data/locale/es-ES.ini b/plugins/obs-qsv11/data/locale/es-ES.ini index 951ce806d614b6..3e0121ca63017c 100644 --- a/plugins/obs-qsv11/data/locale/es-ES.ini +++ b/plugins/obs-qsv11/data/locale/es-ES.ini @@ -6,7 +6,6 @@ Profile="Perfíl" ICQQuality="Calidad ICQ" Latency="Latencia" Latency.ToolTip="Hay una compensación entre latencia y calidad.\nSi prefiere calidad, seleccione el modo 'normal', que puede tener una latencia de extremo a extremo de unos 2s.\nEn caso que requiera < 500ms de latencia, por favor seleccione el modo 'ultra-low'." -SubjectiveVideoEnhancements="Mejoras subjetivas de vídeo" 8bitUnsupportedHdr="OBS no soporta salida de 8 bits de Rec. 2100." 10bitUnsupportedAvc="No se puede realizar codificación de 10 bits en el codificador Intel QSV H.264." 16bitUnsupported="No se puede realizar codificación de 16 bits en este codificador." diff --git a/plugins/obs-qsv11/data/locale/et-EE.ini b/plugins/obs-qsv11/data/locale/et-EE.ini index c829c9245899f0..54fcd710b708e2 100644 --- a/plugins/obs-qsv11/data/locale/et-EE.ini +++ b/plugins/obs-qsv11/data/locale/et-EE.ini @@ -7,7 +7,6 @@ Profile="Profiil" ICQQuality="ICQ Kvaliteet" Latency="Latsentsus" Latency.ToolTip="Viivitusaja ja kvaliteedi vahel on kompromiss.\nKui eelistate kvaliteeti, valige palun režiim \"normaalne\", mis võib saavutada > 2 s otsast-otsani viivitusaja.\nKui on vaja < 500 ms otsast-lõpuni viivitusaega, valige palun režiim \"ülimadal\"." -SubjectiveVideoEnhancements="Subjektiivsed video täiustused" 8bitUnsupportedHdr="OBS ei toeta Rec. 2100 8-bitist väljundit." 10bitUnsupportedAvc="Intel QSV H.264 kodeerijal ei ole võimalik 10-bitist kodeerimist teostada." 16bitUnsupported="Selle kodeerijaga ei saa teha 16-bitist kodeerimist." diff --git a/plugins/obs-qsv11/data/locale/eu-ES.ini b/plugins/obs-qsv11/data/locale/eu-ES.ini index 10cf471afbbd93..df489c6d677f81 100644 --- a/plugins/obs-qsv11/data/locale/eu-ES.ini +++ b/plugins/obs-qsv11/data/locale/eu-ES.ini @@ -7,6 +7,5 @@ Profile="Profila" ICQQuality="ICQ Ontasuna" Latency="Latentzia" Latency.ToolTip="Hau latentzia eta kalitatearen arteko oreka da.\n Zure kasua kalitatea nahiago badu, mesedez modu 'normala' hautatu, bi segundo baino gehiagoko latentzia eman daiteke.\n Zure kasua 500ms baino gutxiagoko latentzia behar badu, mesedez 'ultra-baxua' hautatu." -SubjectiveVideoEnhancements="Video subjetiboen arriskuak" 8bitUnsupportedHdr="OBSk ez du 8 biteko irteera onartzen Rec. 2100-rekin." 10bitUnsupportedAvc="Inteleko QSV H.264 kodetzailearekin ezin da 10 biteko kodeketarik egin." diff --git a/plugins/obs-qsv11/data/locale/fa-IR.ini b/plugins/obs-qsv11/data/locale/fa-IR.ini index 5de268d0d4ff02..7f0b5d622c95ff 100644 --- a/plugins/obs-qsv11/data/locale/fa-IR.ini +++ b/plugins/obs-qsv11/data/locale/fa-IR.ini @@ -7,7 +7,6 @@ Profile="نمایه" ICQQuality="کیفیت ICQ" Latency="تأخیر" Latency.ToolTip="معامله بین تأخیر و کیفیت وجود دارد.\nاگر پرونده شما کیفیت را ترجیح می دهد، لطفاً حالت \"عادی\" را انتخاب کنید که ممکن است تاخیر پایان به پایان> 2 ثانیه داشته باشد.\n اگر پرونده شما به <500 میلی ثانیه تاخیر پایان به پایان نیاز دارد، لطفاً حالت \"خیلی کم\" را انتخاب کنید." -SubjectiveVideoEnhancements="پیشرفت های ویدئویی ذهنی" 8bitUnsupportedHdr="OBS خروجی 8 بیتی ضبط را پشتیبانی نمی کند. 2100." 10bitUnsupportedAvc="نمی توان رمزگذاری 10 بیتی را روی رمزگذار Intel QSV H.264 انجام داد." 16bitUnsupported="نمی‌توان رمزگذاری ۱۶ بیتی را روی این رمزگذار انجام داد." diff --git a/plugins/obs-qsv11/data/locale/fi-FI.ini b/plugins/obs-qsv11/data/locale/fi-FI.ini index c7fefc9d67d2bf..7bee14d0075372 100644 --- a/plugins/obs-qsv11/data/locale/fi-FI.ini +++ b/plugins/obs-qsv11/data/locale/fi-FI.ini @@ -7,7 +7,6 @@ Profile="Profiili" ICQQuality="ICQ laatu" Latency="Viive" Latency.ToolTip="Laadun ja viiveen välillä on kompromissi.\nJos tarvitset paremman laadun, valitse \"normaali\" -tila, joka saattaa johtaa yli kahden sekunnin viiveeseen.\nJos tarvitset alle 500ms viiveen, valitse \"ultra-low\" -tila." -SubjectiveVideoEnhancements="Subjektiiviset kuvanparannukset" 8bitUnsupportedHdr="OBS ei tue 8-bittistä käyttöä Rec. 2100:n kanssa." 10bitUnsupportedAvc="Ei voida suorittaa 10-bittistä koodausta Intel QSV H.264 kooderilla." 16bitUnsupported="Ei voida suorittaa 16-bittistä koodausta tälle enkooderille." diff --git a/plugins/obs-qsv11/data/locale/fil-PH.ini b/plugins/obs-qsv11/data/locale/fil-PH.ini index 2073cfd7834a7f..8aaeb9d68aed01 100644 --- a/plugins/obs-qsv11/data/locale/fil-PH.ini +++ b/plugins/obs-qsv11/data/locale/fil-PH.ini @@ -7,6 +7,5 @@ Profile="Anyo sa tagiliran" ICQQuality="Kalidad ng ICQ" Latency="Pagkakahuli" Latency.ToolTip="May trade-off sa pagitan ng latency at kalidad.\nKung mas gusto ng iyong kaso ang kalidad, mangyaring piliin ang 'normal' na mode, na maaaring makakuha ng > 2s end-to-end latency.\nKung ang iyong case ay nangangailangan ng < 500ms end-to-end latency , mangyaring piliin ang 'ultra-low' na mode." -SubjectiveVideoEnhancements="Mgapagpapahusay sa Video" 8bitUnsupportedHdr="Hindi sinusuportahan ng OBS ang 8-bit na output ng Rec. 2100." 10bitUnsupportedAvc="Hindi makapagsagawa ng 10-bit na encode sa Intel QSV H.264 encoder." diff --git a/plugins/obs-qsv11/data/locale/fr-FR.ini b/plugins/obs-qsv11/data/locale/fr-FR.ini index 6a0143458f090e..8f9fd89a720f07 100644 --- a/plugins/obs-qsv11/data/locale/fr-FR.ini +++ b/plugins/obs-qsv11/data/locale/fr-FR.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="Qualité ICQ" Latency="Latence" Latency.ToolTip="Il y a un compromis entre latence et qualité.\nSi vous souhaitez une qualité supérieure, sélectionnez le mode 'normal' qui peut engendrer plus de 2s de latence.\nSi vous avez besoin d'une latence inférieure à 500 ms, sélectionnez le mode 'latence ultra faible'." -SubjectiveVideoEnhancements="Améliorations vidéo subjectives" 8bitUnsupportedHdr="OBS ne prend pas en charge la sortie 8 bits de la Rec. 2100." 10bitUnsupportedAvc="Impossible d'effectuer l'encodage 10 bits sur l'encodeur Intel QSV H.264." 16bitUnsupported="Impossible d'effectuer un encodage sur 16 bits avec cet encodeur." diff --git a/plugins/obs-qsv11/data/locale/gd-GB.ini b/plugins/obs-qsv11/data/locale/gd-GB.ini index f4fff876159390..dd1e49afaed760 100644 --- a/plugins/obs-qsv11/data/locale/gd-GB.ini +++ b/plugins/obs-qsv11/data/locale/gd-GB.ini @@ -6,4 +6,3 @@ Profile="Pròifil" ICQQuality="Càileachd ICQ" Latency="Foillidheachd" Latency.ToolTip="Seo cothromachadh eadar a’ chàileachd ’s an fhoillidheachd.\nMa tha a’ chàileachd as cudromaiche dhut, tagh am modh “àbhaisteach” ach bidh sin buailteach air > 2 diog a dh’fhoillidheachd ceann gu ceann.\nMa tha thu feumach air < 500ms a dh’fhoillidheachd ceann gu ceann, tagh am modh “glè ìosal”." -SubjectiveVideoEnhancements="Pisich air coltas a’ video" diff --git a/plugins/obs-qsv11/data/locale/gl-ES.ini b/plugins/obs-qsv11/data/locale/gl-ES.ini index 62d6215c40cf8c..60f020a27ea0e8 100644 --- a/plugins/obs-qsv11/data/locale/gl-ES.ini +++ b/plugins/obs-qsv11/data/locale/gl-ES.ini @@ -7,6 +7,5 @@ Profile="Perfil" ICQQuality="Calidade ICQ" Latency="Latencia" Latency.ToolTip="Hai unha compensación entre latencia e calidade.\nSe prefire calidade, seleccione o modo «normal», que pode ter unha latencia de extremo a extremo duns 2s.\nNo caso de que requira < 500ms de latencia, seleccione o modo «ultra baixo»." -SubjectiveVideoEnhancements="Melloras subxectivas de vídeo" 8bitUnsupportedHdr="OBS non soporta saída de 8 bits de Rec. 2100." 10bitUnsupportedAvc="Non se pode realizar codificación de 10 bits no codificador Intel QSV H.264." diff --git a/plugins/obs-qsv11/data/locale/he-IL.ini b/plugins/obs-qsv11/data/locale/he-IL.ini index 52771ab2af373e..53e02eda7e1988 100644 --- a/plugins/obs-qsv11/data/locale/he-IL.ini +++ b/plugins/obs-qsv11/data/locale/he-IL.ini @@ -7,7 +7,6 @@ Profile="פרופיל" ICQQuality="איכות ICQ" Latency="זמן שיהוי (Latency)" Latency.ToolTip="קיים יחס בין זמן שיהוי (Latency) לבין איכות.\nאם השימוש שלך מתעדף איכות, נא לבחור במצב 'רגיל', שעשוי לקבל מעל 2 שניות זמן שיהוי קצה-אל-קצה.\nאם השימוש שלך דורש פחות מ-500 מילי שניות זמן שיהוי קצה-אל-קצה, נא לבחור במצב 'אולטרה-נמוך'." -SubjectiveVideoEnhancements="שיפורי וידאו סובייקטיביים." 8bitUnsupportedHdr="OBS לא תומך בפלט 8 סיביות עם Rec. 2100." 10bitUnsupportedAvc="לא ניתן לבצע הצפנה של 10 סיביות במצפין QSV H.264 של אינטל." 16bitUnsupported="לא ניתן לבצע הצפנה של 16 סיביות במצפין הזה." diff --git a/plugins/obs-qsv11/data/locale/hi-IN.ini b/plugins/obs-qsv11/data/locale/hi-IN.ini index eff98003c06c14..ccac4cb606496b 100644 --- a/plugins/obs-qsv11/data/locale/hi-IN.ini +++ b/plugins/obs-qsv11/data/locale/hi-IN.ini @@ -7,7 +7,6 @@ Profile="प्रोफ़ाइल" ICQQuality="ICQ गुणवत्ता" Latency="विलंबता" Latency.ToolTip="विलंबता और गुणवत्ता के बीच समझौता करना पड़ता है.\nयदि आप गुणवत्ता पसंद करते हैं, तो कृपया 'सामान्य' मोड चुनें, जो ओर-छोर की > 2सेकेंड की विलंबता प्राप्त कर सकता है.\nयदि आपके मामले में <500मिलीसेकेंड ओर-छोर के विलंबता की आवश्यकता है , तो कृपया 'अति-नयून' मोड चुनें." -SubjectiveVideoEnhancements="सब्जेक्टिव वीडियो एन्हांसमेंट" 8bitUnsupportedHdr="Rec. 2100 के 8-बिट् आउटपुट को OBS सपोर्ट नहीं करता है." 10bitUnsupportedAvc="Intel QSV H.264 एन्कोडर पर 10-बिट् एन्कोड नहीं किया जा सकता." 16bitUnsupported="इस एन्कोडर पर 16-बिट एन्कोड नहीं किया जा सकता." diff --git a/plugins/obs-qsv11/data/locale/hu-HU.ini b/plugins/obs-qsv11/data/locale/hu-HU.ini index bacbf5177876ce..01cbf9805194dc 100644 --- a/plugins/obs-qsv11/data/locale/hu-HU.ini +++ b/plugins/obs-qsv11/data/locale/hu-HU.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="ICQ minőség" Latency="Késleltetés" Latency.ToolTip="A késés és a minőség között kompromisszum van.\nHa az eset inkább a minőséget részesíti előnyben, akkor válaszd a „normál” módot, amely> 2-es végső látenciát kaphat. \nHa az esetének <500 ms közötti teljes késleltetést igényel, akkor válaszd az „ultra alacsony” módot." -SubjectiveVideoEnhancements="Szubjektív videó fejlesztések" 8bitUnsupportedHdr="Az OBS nem támogatja a Rec. 2100 8 bites kimenetét." 10bitUnsupportedAvc="Nem lehet 10 bites kódolást végezni az Intel QSV H.264 kódolón." 16bitUnsupported="Nem lehet 16 bites kódolást végezni ezen a kódolón." diff --git a/plugins/obs-qsv11/data/locale/hy-AM.ini b/plugins/obs-qsv11/data/locale/hy-AM.ini index 16e3c1438f46c5..e868812a2605c9 100644 --- a/plugins/obs-qsv11/data/locale/hy-AM.ini +++ b/plugins/obs-qsv11/data/locale/hy-AM.ini @@ -7,7 +7,6 @@ Profile="Պրոֆիլ" ICQQuality="ICQ-որակը" Latency="Լատենտություն" Latency.ToolTip="Կա փոխզիջում հապաղման և որակի միջև:\nԵթե ձեր գործն առաջնահերթություն է տալիս որակին, խնդրում ենք ընտրել «Նորմալ» ռեժիմը, որը կարող է ունենալ >2 վրկ վերջից մինչև վերջ հապաղում:\nԵթե ձեր գործը պահանջում է < 500 մս վերջից մինչև վերջ հապաղում: , խնդրում ենք ընտրել «Ուլտրա-ցածր» ռեժիմը:" -SubjectiveVideoEnhancements="Տեսանյութի սուբյեկտիվ բարելավումներ" 8bitUnsupportedHdr="OBS-ը չի աջակցում 8-բիթ Rec. 2100-ի ելքը" 10bitUnsupportedAvc="Հնարավոր չէ կատարել 10-բիթանոց կոդավորում Intel QSV H.264 կոդավորիչի վրա։" 16bitUnsupported="Հնարավոր չէ կատարել 16-բիթանոց կոդավորում։" diff --git a/plugins/obs-qsv11/data/locale/id-ID.ini b/plugins/obs-qsv11/data/locale/id-ID.ini index bae86679fb3226..9035f3ee3e1395 100644 --- a/plugins/obs-qsv11/data/locale/id-ID.ini +++ b/plugins/obs-qsv11/data/locale/id-ID.ini @@ -6,7 +6,6 @@ Profile="Profil" ICQQuality="Kualitas ICQ" Latency="Latensi" Latency.ToolTip="Ada untung-rugi antara latensi dan kualitas.\nJika lebih memilih kualitas, silakan pilih mode 'normal', kemungkinan ada latensi end-to-end > 2 detik.\nJika menginginkan latensi end-to-end < 500 milidetik, silakan pilih mode 'ultra-low'." -SubjectiveVideoEnhancements="Penyempurnaan Subyektif Video" 8bitUnsupportedHdr="OBS tidak mendukung output 8-bit dari Rec. 2100." 10bitUnsupportedAvc="Tidak dapat melakukan meng-kode 10-bit pada pengkodean Intel QSV H.264." 16bitUnsupported="Tidak dapat melakukan meng-kode 16-bit pada pengkodean ini." diff --git a/plugins/obs-qsv11/data/locale/it-IT.ini b/plugins/obs-qsv11/data/locale/it-IT.ini index f548100541a24f..61d232bad0252c 100644 --- a/plugins/obs-qsv11/data/locale/it-IT.ini +++ b/plugins/obs-qsv11/data/locale/it-IT.ini @@ -7,15 +7,14 @@ Profile="Profilo" ICQQuality="Qualità ICQ" Latency="Latenza" Latency.ToolTip="C'è un compromesso tra latenza e qualità.\nSe nel tuo caso si preferisce la qualità, seleziona la modalità 'normale', arrivando a più di 2s di latenza end-to-end.\nSe si vuole una latenza minore di 500ms, seleziona la modalità 'ultra-low'." -SubjectiveVideoEnhancements="Miglioramenti Video Soggettivi" 8bitUnsupportedHdr="OBS non supporta l'uscita Rec. 2100 8bit." 10bitUnsupportedAvc="Impossibile eseguire la codifica a 10 bit con l'encoder Intel QSV H.264." 16bitUnsupported="Impossibile con questo encoder eseguire la codifica a 16bit." BFrames="B-frame" -TargetUsage.TU1="TU1: più lento (migliore qualità)" +TargetUsage.TU1="TU1: Il più lento (qualità massima)" TargetUsage.TU2="TU2: più lento" TargetUsage.TU3="TU3: lento" -TargetUsage.TU4="TU4: bilanciato (media qualità)" +TargetUsage.TU4="TU4: Bilanciato (qualità media)" TargetUsage.TU5="TU5: veloce" TargetUsage.TU6="TU6: più veloce" -TargetUsage.TU7="TU7: più veloce (massima velocità)" +TargetUsage.TU7="TU7: il più veloce (massima velocità)" diff --git a/plugins/obs-qsv11/data/locale/ja-JP.ini b/plugins/obs-qsv11/data/locale/ja-JP.ini index c334198ab69085..0304675da932c9 100644 --- a/plugins/obs-qsv11/data/locale/ja-JP.ini +++ b/plugins/obs-qsv11/data/locale/ja-JP.ini @@ -7,7 +7,6 @@ Profile="プロファイル" ICQQuality="ICQ の品質" Latency="遅延" Latency.ToolTip="遅延と品質の間にはトレードオフがあります。\n品質を優先する場合は 'normal' モードを選択してください (エンドツーエンド遅延が2秒を超える場合があります)。\n500ms未満のエンドツーエンド遅延が必要な場合は 'ultra-low' モードを選択してください。" -SubjectiveVideoEnhancements="主観的なビデオ強化" 8bitUnsupportedHdr="OBSは Rec. 2100 で 8-bit 出力をサポートしていません。" 10bitUnsupportedAvc="QSV H.264 エンコーダ では 10-bit エンコードを実行できません。" 16bitUnsupported="このエンコーダでは 16-bit エンコードを実行できません。" diff --git a/plugins/obs-qsv11/data/locale/ka-GE.ini b/plugins/obs-qsv11/data/locale/ka-GE.ini index fe20481a958470..263fcd4c5caf62 100644 --- a/plugins/obs-qsv11/data/locale/ka-GE.ini +++ b/plugins/obs-qsv11/data/locale/ka-GE.ini @@ -7,7 +7,6 @@ Profile="პროფილი" ICQQuality="ICQ-ხარისხი" Latency="დაყოვნება" Latency.ToolTip="დაყოვნება და ხარისხი ურთიერთსაპირისპირო კავშირშია.\nთუ ხარისხს ამჯობინებთ, აირჩიეთ „normal“ და მაყურებელთან დაყოვნება > 2წმ იქნება.\nთუ გესაჭიროებათ მცირე < 500მწმ დაყოვნება, აირჩიეთ „ultra-low“." -SubjectiveVideoEnhancements="ვიდეოს მოჩვენებითი გაუმჯობესება" 8bitUnsupportedHdr="OBS-ში ვერ იქნება გამოყენებული 8-ბიტიანი გამოტანა და Rec. 2100." 10bitUnsupportedAvc="10-ბიტიანი დაშიფვრა ვერ მოხერხდება Intel-QSV-H.264-მშიფრავით." 16bitUnsupported="16-ბიტიანი დაშიფვრა ვერ ხერხდება ამ მშიფრავით." diff --git a/plugins/obs-qsv11/data/locale/kmr-TR.ini b/plugins/obs-qsv11/data/locale/kmr-TR.ini index 1098167efdd8c0..b33c4d09f5d6fa 100644 --- a/plugins/obs-qsv11/data/locale/kmr-TR.ini +++ b/plugins/obs-qsv11/data/locale/kmr-TR.ini @@ -7,7 +7,6 @@ Profile="Profîl" ICQQuality="Qalîteya ICQ" Latency="Derengbûn" Latency.ToolTip="Di navbera derengbûn û qalîteyê de bazirganî heye. \nKu rewşa te li ser qalîteyê tercîh dike, ji kerema xwe re moda 'normal' hilbijêre, ku dibe ku > 2s ser-bi-ser.\nKu doza te pêdivî bi < 500ms ser-bi-ser ya derengbûnê heye, ji kerema xwe moda 'pir-kêm' hilbijêre." -SubjectiveVideoEnhancements="Baştirkirina vîdeoya kesanekirî" 8bitUnsupportedHdr="OBS piştgiriyê nade deranê 8-bit ji Rec. 2100." 10bitUnsupportedAvc="Nikare kodkirina 10-bit li ser kodkerê Intel QSV H.264 pêk bîne." 16bitUnsupported="Nikare kodkirina 16-bit li ser vê kodkerê pêk bîne." diff --git a/plugins/obs-qsv11/data/locale/ko-KR.ini b/plugins/obs-qsv11/data/locale/ko-KR.ini index 747f7f45499972..86be58017198a6 100644 --- a/plugins/obs-qsv11/data/locale/ko-KR.ini +++ b/plugins/obs-qsv11/data/locale/ko-KR.ini @@ -7,7 +7,6 @@ Profile="프로파일" ICQQuality="ICQ 품질" Latency="지연 시간" Latency.ToolTip="지연시간과 품질은 서로 상충 관계에 있습니다.\n품질을 선호한다면 '보통'을 선택하세요. 2초 이상의 지연이 발생할 수 있습니다.\n500ms 이하의 낮은 지연시간이 필요하다면 '매우 낮음'을 선택하세요." -SubjectiveVideoEnhancements="주관적 비디오 향상" 8bitUnsupportedHdr="OBS는 Rec. 2100을 통한 8비트 출력을 지원하지 않습니다." 10bitUnsupportedAvc="Intel QSV H.264 인코더에서는 10비트 인코딩을 할 수 없습니다." 16bitUnsupported="이 인코더에서는 16비트 인코딩을 할 수 없습니다." diff --git a/plugins/obs-qsv11/data/locale/ms-MY.ini b/plugins/obs-qsv11/data/locale/ms-MY.ini index 3b04e593f21054..466313ac9321d6 100644 --- a/plugins/obs-qsv11/data/locale/ms-MY.ini +++ b/plugins/obs-qsv11/data/locale/ms-MY.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="Kualiti ICQ" Latency="Kependaman" Latency.ToolTip="Terdapat tukar-ganti antara kependaman dengan kualiti.\nJika ada mahukan kualiti, sila pilih mod 'biasa', yang memperoleh >2s kependaman hujung-ke-hujung.\nJika anda mahu <500ms kependaman hujung-ke-hujung, sila pilih mod 'ultra-rendah'." -SubjectiveVideoEnhancements="Penambahbaikan Video Subjektif" 8bitUnsupportedHdr="OBS tidak menyokong penggunaan output 8-bit Rec. 2100." 10bitUnsupportedAvc="Tidak dapat melakukan pengekodan 10-bit dengan pengekod H.264 QSV Intel." 16bitUnsupported="Tidak dapat melakukan pengekodan 16-bit dengan pengekod ini." diff --git a/plugins/obs-qsv11/data/locale/nb-NO.ini b/plugins/obs-qsv11/data/locale/nb-NO.ini index 5ce69e3cd8465b..645e6cd86b42ed 100644 --- a/plugins/obs-qsv11/data/locale/nb-NO.ini +++ b/plugins/obs-qsv11/data/locale/nb-NO.ini @@ -5,7 +5,6 @@ Profile="Profil" ICQQuality="ICQ-kvalitet" Latency="Latens" Latency.ToolTip="Der er et kompromiss mellom latens og kvalitet.\nHvis du foretrekker kvalitet for ditt scenario, vennligst velg 'normal' modus som kan forårsake > 2s ende til ende latens.\nHvis scenarioet ditt krever < 500ms ende til ende latens, vennligst velg 'ultra-lav' modus." -SubjectiveVideoEnhancements="Subjektive videoforbedringer" BFrames="B-rammer" TargetUsage.TU1="TU1: Tregest (Beste kvalitet)" TargetUsage.TU2="TU2: Tregere" diff --git a/plugins/obs-qsv11/data/locale/nl-NL.ini b/plugins/obs-qsv11/data/locale/nl-NL.ini index ebd338dfdd682a..5199c0cf347dff 100644 --- a/plugins/obs-qsv11/data/locale/nl-NL.ini +++ b/plugins/obs-qsv11/data/locale/nl-NL.ini @@ -5,7 +5,6 @@ Profile="Profiel" ICQQuality="ICQ kwaliteit" Latency="Vertraging" Latency.ToolTip="Er is een wisselwerking tussen vertraging en kwaliteit.\nAls de kwaliteit primeert selecteert u 'normaal', waarbij de end-to-end vertraging >2s kan zijn. \nAls u <500ms vertraging nodig heeft selecteert u 'ultra-laag'." -SubjectiveVideoEnhancements="Subjectieve Videoverbeteringen" 8bitUnsupportedHdr="OBS ondersteunt geen 8-bit output van Rec. 2100." 10bitUnsupportedAvc="Kan geen 10-bit encoding doen op Intel QSV H.264 encoder." 16bitUnsupported="Kan niet 16-bit encoden met deze encoder." diff --git a/plugins/obs-qsv11/data/locale/pl-PL.ini b/plugins/obs-qsv11/data/locale/pl-PL.ini index e54cc6c37b144d..9f4ab003929ae3 100644 --- a/plugins/obs-qsv11/data/locale/pl-PL.ini +++ b/plugins/obs-qsv11/data/locale/pl-PL.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="Jakość ICQ" Latency="Opóźnienie" Latency.ToolTip="Istnieje zależność między opóźnieniem a jakością.\nJeżeli preferujesz jakość, wybierz tryb \"normalny\", który skutkuje opóźnieniem nawet powyżej 2 sekund.\nJeżeli zależy Tobie na niskim opóźnieniu (poniżej 500ms), wybierz tryb \"ultra-niski\"." -SubjectiveVideoEnhancements="Subiektywne ulepszenia wideo" 8bitUnsupportedHdr="OBS nie obsługuje 8-bitowego wyjścia z Rec. 2100." 10bitUnsupportedAvc="Nie można użyć 10-bitowego enkodowania z enkoderem Intel QSV H.264." 16bitUnsupported="Wybrany enkoder nie obsługuje 16-bitowego kodowania." diff --git a/plugins/obs-qsv11/data/locale/pt-BR.ini b/plugins/obs-qsv11/data/locale/pt-BR.ini index a2d2db20dd1d99..a8b33669667bb7 100644 --- a/plugins/obs-qsv11/data/locale/pt-BR.ini +++ b/plugins/obs-qsv11/data/locale/pt-BR.ini @@ -5,7 +5,6 @@ Profile="Perfil" ICQQuality="Qualidade ICQ" Latency="Latência" Latency.ToolTip="Existe uma troca entre latência e qualidade.\nSe você prefere qualidade, por favor selecione o modo 'normal', que pode obter > 2s de latência de ponta a ponta.\nSe você prefere < 500ms de latência de ponta a ponta, por favor selecione o modo 'ultra-low'." -SubjectiveVideoEnhancements="Aprimoramentos Subjetivos de Vídeo" 8bitUnsupportedHdr="O OBS não suporta saída de 8 bits do Rec. 2100." 10bitUnsupportedAvc="Não é possível executar a codificação de 10 bits no codificador Intel QSV H.264." 16bitUnsupported="Codificação 16-bit não pode ser feita neste codificador." diff --git a/plugins/obs-qsv11/data/locale/pt-PT.ini b/plugins/obs-qsv11/data/locale/pt-PT.ini index 7923b7a1e26d67..8dba6da38211fe 100644 --- a/plugins/obs-qsv11/data/locale/pt-PT.ini +++ b/plugins/obs-qsv11/data/locale/pt-PT.ini @@ -1,13 +1,12 @@ TargetUsage="Uso do alvo" Bitrate="Taxa de bits" MaxBitrate="Taxa de bits máxima" -RateControl="Controlo de taxa" +RateControl="Controlo da frequência" KeyframeIntervalSec="Intervalo de fotogramas-chave ( 0=automático)" Profile="Perfil" ICQQuality="Qualidade ICQ" Latency="Latência" Latency.ToolTip="Existe uma escolha a fazer entre latência e qualidade.\nSe prefere qualidade, selecione o modo \"Normal\" e pode obter > 2s de latência de ponta a ponta.\nSe requer latência < 500ms de ponta a ponta, selecione o modo \"Ultra-baixo\"." -SubjectiveVideoEnhancements="Melhorias de vídeo subjetivas" 8bitUnsupportedHdr="O OBS não suporta saída de 8 bits do Rec. 2100." 10bitUnsupportedAvc="Não é possível executar a codificação de 10 bits no codificador Intel QSV H.264." 16bitUnsupported="Não é possível executar a codificação de 16 bits neste codificador." diff --git a/plugins/obs-qsv11/data/locale/ro-RO.ini b/plugins/obs-qsv11/data/locale/ro-RO.ini index c898e3ddca130e..c0fb344e8c8cc1 100644 --- a/plugins/obs-qsv11/data/locale/ro-RO.ini +++ b/plugins/obs-qsv11/data/locale/ro-RO.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="Calitate ICQ" Latency="Latență" Latency.ToolTip="Există un compromis între latență și calitate.\nDacă în cazul tău se preferă calitatea, te rugăm să selectezi modul „normal” care poate duce la peste 2s latență de la un capăt la altul.\nDacă cazul tău necesită sub 500ms latență de la un capăt la altul, te rugăm să selectezi modul „ultrascăzut”." -SubjectiveVideoEnhancements="Îmbunătățiri video subiective" 8bitUnsupportedHdr="OBS nu suportă outputul pe 8 biți a Rec. 2100." 10bitUnsupportedAvc="Nu se poate efectua codificarea de 10 biți pe codificatorul Intel QSV H.264." 16bitUnsupported="Nu se poate efectua codarea pe 16 biți pe acest codificator." diff --git a/plugins/obs-qsv11/data/locale/ru-RU.ini b/plugins/obs-qsv11/data/locale/ru-RU.ini index 600b892e76f556..f77c9fe16cfc66 100644 --- a/plugins/obs-qsv11/data/locale/ru-RU.ini +++ b/plugins/obs-qsv11/data/locale/ru-RU.ini @@ -7,15 +7,14 @@ Profile="Профиль" ICQQuality="Качество ICQ" Latency="Задержка" Latency.ToolTip="Существует компромисс между задержкой и качеством.\nЕсли ваше дело отдаёт предпочтение качеству, выберите «нормальный» режим, который может иметь сквозную задержку>2 с.\nЕсли ваше дело требует сквозной задержки <500 мс, пожалуйста, выберите режим «сверхнизкий»." -SubjectiveVideoEnhancements="Субъективные улучшения видео" 8bitUnsupportedHdr="OBS не поддерживает 8-битный вывод Rec. 2100." 10bitUnsupportedAvc="Не удаётся выполнить 10-битное кодирование в кодировщике Intel QSV H.264." 16bitUnsupported="Не удаётся выполнить 16-битное кодирование в этом кодировщике." BFrames="B-кадры" -TargetUsage.TU1="ЦИ1: Самая медленная (лучшее качество)" -TargetUsage.TU2="ЦИ2: Более медленная" -TargetUsage.TU3="ЦИ3: Медленная" -TargetUsage.TU4="ЦИ4: Сбалансированная (среднее качество)" -TargetUsage.TU5="ЦИ5: Быстрая" -TargetUsage.TU6="ЦИ6: Более быстрая" -TargetUsage.TU7="ЦИ7: Самая быстрая (лучшая скорость)" +TargetUsage.TU1="ЦИ1: Самое медленное (лучшее качество)" +TargetUsage.TU2="ЦИ2: Более медленное" +TargetUsage.TU3="ЦИ3: Медленное" +TargetUsage.TU4="ЦИ4: Сбалансированное (среднее качество)" +TargetUsage.TU5="ЦИ5: Быстрое" +TargetUsage.TU6="ЦИ6: Более быстрое" +TargetUsage.TU7="ЦИ7: Самое быстрое (лучшая скорость)" diff --git a/plugins/obs-qsv11/data/locale/sk-SK.ini b/plugins/obs-qsv11/data/locale/sk-SK.ini index 518303406bafe3..4698fb805d21df 100644 --- a/plugins/obs-qsv11/data/locale/sk-SK.ini +++ b/plugins/obs-qsv11/data/locale/sk-SK.ini @@ -6,7 +6,6 @@ Profile="Profil" ICQQuality="ICQ Kvalita" Latency="Odozva" Latency.ToolTip="Medzi odozvou a kvalitou existuje kompromis. \nAk uprednostňujete kvalitu, vyberte prosím 'normálny' režim, ktorý môže mať > 2s odozvu. \nAk potrebujete odozvu < 500ms, vyberte režim 'nízka odozva'." -SubjectiveVideoEnhancements="Subjektívne vylepšenia videa" 8bitUnsupportedHdr="OBS nepodporuje 8-bitový výstup z Rec. 2100." 10bitUnsupportedAvc="Nemožno vykonať 10-bitové enkódovanie na enkodéri Intel QSV H.264." 16bitUnsupported="Nemožno vykonať 16-bitové enkódovanie na tomto enkodéri." diff --git a/plugins/obs-qsv11/data/locale/sl-SI.ini b/plugins/obs-qsv11/data/locale/sl-SI.ini index 7566b572c5c1e4..fc5ff2c09c0a34 100644 --- a/plugins/obs-qsv11/data/locale/sl-SI.ini +++ b/plugins/obs-qsv11/data/locale/sl-SI.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="Kakovost ICQ" Latency="Zakasnitev" Latency.ToolTip="Razmerje med zamikom/ latenco in kakovstjo je kompromi.\nČe potrebujete kakovost, izbertie način 'normalno', ki lahko doseže latenco večjo od dveh sekund.\nČe potrebujete latenco manjšo od 500ms, izberite način 'ultra-nizka'." -SubjectiveVideoEnhancements="Subjektivne izboljšave videa" 8bitUnsupportedHdr="OBS ne podpira rabe 8-bitnega izhoda z Rec. 2100." 10bitUnsupportedAvc="Na kodirniku Intel QSV H.264 ni mogoče izvesti 10-bitnega kodiranja." 16bitUnsupported="S tem kodirnikom ni mogoče izvesti 16-bitnega kodiranja." diff --git a/plugins/obs-qsv11/data/locale/sv-SE.ini b/plugins/obs-qsv11/data/locale/sv-SE.ini index 5681814ccbfb6c..0d9a5ad7f30b43 100644 --- a/plugins/obs-qsv11/data/locale/sv-SE.ini +++ b/plugins/obs-qsv11/data/locale/sv-SE.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="ICQ-kvalitet" Latency="Latens" Latency.ToolTip="Man kan välja mellan latens och kvalitet.\nOm du föredrar kvalitet väljer du läget \"normal\", som ger dig en latenstid på >2s för slutpunkt till slutpunkt.\nOm du kräver en latenstid på <500 ms för slutpunkt till slutpunkt väljer du läget \"ultralåg\"." -SubjectiveVideoEnhancements="Subjektiva videoförbättringar" 8bitUnsupportedHdr="OBS saknar stöd för 8-bitars utmatning av Rec. 2100." 10bitUnsupportedAvc="Kan inte utföra 10-bitars kodning på Intel QSV H.264-kodare." 16bitUnsupported="Kan inte utföra kodning med 16-bitar på denna kodare." diff --git a/plugins/obs-qsv11/data/locale/tr-TR.ini b/plugins/obs-qsv11/data/locale/tr-TR.ini index 8c397b0e53a11a..f8d596ed11e289 100644 --- a/plugins/obs-qsv11/data/locale/tr-TR.ini +++ b/plugins/obs-qsv11/data/locale/tr-TR.ini @@ -7,7 +7,6 @@ Profile="Profil" ICQQuality="ICQ Kalitesi" Latency="Gecikme süresi" Latency.ToolTip="Gecikme ve kalite arasında bir denge vardır.\nKaliteyi tercih ediyorsanız, lütfen 2s'den uzun uçtan uca gecikme süresine çıkabilen 'normal' modunu seçin.\nDurumunuz 500ms'den düşük uçtan uca gecikme gerektiriyorsa, lütfen 'ultra düşük' modunu seçin." -SubjectiveVideoEnhancements="Öznel Video Geliştirmeleri" 8bitUnsupportedHdr="OBS Rec. 2100'ün 8-bit olarak çıkışını desteklemiyor." 10bitUnsupportedAvc="Intel QSV H.264 kodlayıcıda 10-bit kodlama gerçekleştirilemiyor." 16bitUnsupported="Bu kodlayıcıda 16 bit kodlama gerçekleştirilemiyor." diff --git a/plugins/obs-qsv11/data/locale/tt-RU.ini b/plugins/obs-qsv11/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..083f1cf3881bd2 --- /dev/null +++ b/plugins/obs-qsv11/data/locale/tt-RU.ini @@ -0,0 +1,3 @@ +Bitrate="Битрейт" +MaxBitrate="Максималь битрейт" +Profile="Профиль" diff --git a/plugins/obs-qsv11/data/locale/uk-UA.ini b/plugins/obs-qsv11/data/locale/uk-UA.ini index af3282a84d7bf8..08a79f62150ed3 100644 --- a/plugins/obs-qsv11/data/locale/uk-UA.ini +++ b/plugins/obs-qsv11/data/locale/uk-UA.ini @@ -7,7 +7,6 @@ Profile="Профіль" ICQQuality="Якість ICQ" Latency="Затримка" Latency.ToolTip="Є компроміс між затримкою і якістю.\nЯкщо у вашому випадку надається перевага якості, тоді виберіть «нормальний» режим, який може надати понад 2 с кінцевої затримки.\nЯкщо у вашому потрібно менше ніж 500 мс кінцевої затримки – виберіть, «ультранизький» режим." -SubjectiveVideoEnhancements="Суб'єктивні покращення відео" 8bitUnsupportedHdr="OBS не підтримує 8-розрядний вивід з Rec. 2100." 10bitUnsupportedAvc="Не вдалося виконати 10-розрядне кодування на кодувальнику Intel QSV H.264." 16bitUnsupported="Не можна виконати 16-розрядний код на цьому кодувальнику." diff --git a/plugins/obs-qsv11/data/locale/vi-VN.ini b/plugins/obs-qsv11/data/locale/vi-VN.ini index 8c6909543b051b..1a871b3b7fbcbd 100644 --- a/plugins/obs-qsv11/data/locale/vi-VN.ini +++ b/plugins/obs-qsv11/data/locale/vi-VN.ini @@ -6,7 +6,6 @@ Profile="Hồ sơ" ICQQuality="ICQ chất lượng" Latency="Độ trễ" Latency.ToolTip="Có sự đánh đổi giữa độ trễ và chất lượng.\nNếu trường hợp của bạn ưu tiên chất lượng, hãy chọn chế độ 'trung bình', cái có thể có độ trễ cuối-cuối > 2 giây.\nNếu trường hợp của bạn yêu cầu độ trễ cuối-cuối < 500ms, hãy chọn chế độ 'siêu thấp'." -SubjectiveVideoEnhancements="Cải tiến video chủ quan" 8bitUnsupportedHdr="OBS không hỗ trợ đầu ra 8-bit của Rec. 2100." 10bitUnsupportedAvc="Không thể thực hiện mã hóa 10-bit trên bộ mã hóa Intel QSV H.264." 16bitUnsupported="Không thể thực hiện mã hóa 16-bit trên encoder này." diff --git a/plugins/obs-qsv11/data/locale/zh-CN.ini b/plugins/obs-qsv11/data/locale/zh-CN.ini index 94f9530ece3635..f1f912e2988bfd 100644 --- a/plugins/obs-qsv11/data/locale/zh-CN.ini +++ b/plugins/obs-qsv11/data/locale/zh-CN.ini @@ -7,7 +7,6 @@ Profile="配置文件" ICQQuality="ICQ 质量" Latency="延迟" Latency.ToolTip="延迟和质量之间需要权衡利弊。\n如果您喜欢更高质量,请选择“正常”模式,这种模式会有 > 2s 的端到端延迟。\n如果您需要 < 500ms 的端到端延迟,请选择“超低”模式。" -SubjectiveVideoEnhancements="主观视频强化" 8bitUnsupportedHdr="OBS不支持Rec. 2100的 8-bit 输出。" 10bitUnsupportedAvc="无法在 Intel QSV H.264 编码器上执行 10-bit 编码。" 16bitUnsupported="无法在此编码器上进行 16-bit 编码。" diff --git a/plugins/obs-qsv11/data/locale/zh-TW.ini b/plugins/obs-qsv11/data/locale/zh-TW.ini index d9724d6cb43279..0c226bc7f55ddc 100644 --- a/plugins/obs-qsv11/data/locale/zh-TW.ini +++ b/plugins/obs-qsv11/data/locale/zh-TW.ini @@ -7,7 +7,6 @@ Profile="設定檔" ICQQuality="ICQ 品質" Latency="延遲" Latency.ToolTip="此為延遲與品質之間的取捨。\n如果您的環境偏好品質,請選擇 [一般] (normal) 模式,端對端延遲可能會超過 2 秒。\n如果您的環境要求端對端延遲小於 500 毫秒,請選擇 [極低] (ultra-low) 模式。" -SubjectiveVideoEnhancements="影片主觀增強" 8bitUnsupportedHdr="OBS 不支援 Rec. 2100 的 8 位輸出。" 10bitUnsupportedAvc="無法在 Intel QSV H.264 編碼器上進行 10 位編碼。" 16bitUnsupported="無法在此編碼器進行 16 位編碼。" diff --git a/plugins/obs-text/data/locale/pt-BR.ini b/plugins/obs-text/data/locale/pt-BR.ini index d344b1c3cc20a2..bc3fb679fffe9c 100644 --- a/plugins/obs-text/data/locale/pt-BR.ini +++ b/plugins/obs-text/data/locale/pt-BR.ini @@ -15,7 +15,7 @@ BkColor="Cor do fundo" BkOpacity="Opacidade do fundo" Alignment="Alinhamento" Alignment.Left="Esquerda" -Alignment.Center="Centralizado" +Alignment.Center="Centro" Alignment.Right="Direita" VerticalAlignment="Alinhamento vertical" VerticalAlignment.Top="Em cima" @@ -26,13 +26,13 @@ Outline.Color="Cor do contorno" Outline.Opacity="Opacidade do contorno" ChatlogMode="Modo de chat" ChatlogMode.Lines="Limite de linhas do chat" -UseCustomExtents="Usar extensões de texto personalizadas" -UseCustomExtents.Wrap="Ajustar" +UseCustomExtents="Usar dimensões de texto personalizadas" +UseCustomExtents.Wrap="Quebrar texto" Width="Largura" Height="Altura" Transform="Transformação" Transform.None="Nenhuma" Transform.Uppercase="Letras maiúsculas" Transform.Lowercase="Letras minúsculas" -Transform.Startcase="Capitalizar" +Transform.Startcase="Iniciais maiúsculas" Antialiasing="Ativar anti-aliasing" diff --git a/plugins/obs-text/data/locale/tt-RU.ini b/plugins/obs-text/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..0c1a4100cc45c2 --- /dev/null +++ b/plugins/obs-text/data/locale/tt-RU.ini @@ -0,0 +1,15 @@ +TextGDIPlus="Текст (GDI+)" +Font="Хәрефләр" +Text="Текст" +ReadFromFile="Файлдан уку" +TextFile="Текстлы файл (UTF-8)" +Filter.TextFiles="Текстлы файллар" +Filter.AllFiles="Барлык файллар" +Color="Төс" +Opacity="Тоныксызлык" +Gradient="Градиент" +Gradient.Color="Градиент төсе" +Gradient.Direction="Градиент юнәлеше" +Width="Киңлек" +Height="Биеклек" +Transform.None="Юк" diff --git a/plugins/obs-transitions/data/locale/de-DE.ini b/plugins/obs-transitions/data/locale/de-DE.ini index 1b3c2c3af2735a..e69aa7045b2e07 100644 --- a/plugins/obs-transitions/data/locale/de-DE.ini +++ b/plugins/obs-transitions/data/locale/de-DE.ini @@ -25,7 +25,7 @@ TrackMatteLayoutVertical="Gleiche Datei, übereinander (Stinger oben, Spurmaske TrackMatteLayoutSeparateFile="Separate Datei (Achtung: Maske kann asynchron werden)" TrackMatteLayoutMask="Nur Maske" PreloadVideoToRam="Video in RAM vorladen" -PreloadVideoToRam.Description="Den gesamten Stinger in den RAM laden, um Echtzeit-Decodierung während der Wiedergabe zu vermeiden.\nBenötigt viel RAM (Ein typisches 5-sekündiges Video in 1080p60 benötigt ~1 GB)." +PreloadVideoToRam.Description="Den gesamten Stinger in den RAM laden, um Echtzeit-Dekodierung während der Wiedergabe zu vermeiden.\nBenötigt viel RAM (Ein typisches 5-sekündiges Video in 1080p60 benötigt ~1 GB)." AudioFadeStyle="Audioübergangsstil" AudioFadeStyle.FadeOutFadeIn="Zu Übergangspunkt aus- und dann einblenden" AudioFadeStyle.CrossFade="Überblendung" @@ -69,4 +69,4 @@ AudioMonitoring="Audiomonitoring" AudioMonitoring.None="Monitoring aus" AudioMonitoring.MonitorOnly="Nur Monitoring an (keine Ausgabe)" AudioMonitoring.Both="Monitoring und Ausgabe an" -HardwareDecode="Hardwaredecodierung verwenden, falls verfügbar" +HardwareDecode="Hardwaredekodierung verwenden, falls verfügbar" diff --git a/plugins/obs-transitions/data/locale/hr-HR.ini b/plugins/obs-transitions/data/locale/hr-HR.ini index c33683e47edfcb..2324bc433e7142 100644 --- a/plugins/obs-transitions/data/locale/hr-HR.ini +++ b/plugins/obs-transitions/data/locale/hr-HR.ini @@ -1,21 +1,25 @@ -FadeTransition="Iščezavanje" +FadeTransition="Izbljeđivanje" CutTransition="Rez" SwipeTransition="Prevlačenje" SlideTransition="Klizanje" -FadeToColorTransition="Iščezavanje u boju" -Direction="Pravac" -Direction.Left="Levo" +FadeToColorTransition="Izblijeđivanje do boje" +Direction="Usmjereno" +Direction.Left="Lijevo" Direction.Right="Desno" Direction.Up="Gore" Direction.Down="Dole" SwipeIn="Uvlačenje" Color="Boja" +VideoFile="Video datoteka" +FileFilter.VideoFiles="Video datoteke" +FileFilter.AllFiles="Sve datoteke" TransitionPoint="Točka prijelaza" TransitionPointFrame="Točka prijelaza (kadar)" TransitionPointType="Vrsta točke prijelaza" AudioTransitionPointType="Vrsta točke audio prijelaza" TransitionPointTypeFrame="Kadar" TransitionPointTypeTime="Vrijeme" +TrackMatteEnabled="Koristi zamućivanje trake" PreloadVideoToRam="Unaprijed učitaj video u RAM" LumaWipeTransition="Luma brisanje" LumaWipe.Image="Slika" @@ -31,26 +35,31 @@ LumaWipe.Type.BoxBottomRight="Kutija odozdo sdesna" LumaWipe.Type.BoxTopLeft="Kutija odozgo sleva" LumaWipe.Type.BoxTopRight="Kutija odozgo sdesna" LumaWipe.Type.Burst="Rafal" -LumaWipe.Type.CheckerboardSmall="Šahovska tabla mala" +LumaWipe.Type.CheckerboardSmall="Mala šahovnica" LumaWipe.Type.Circles="Krugovi" LumaWipe.Type.Clock="Sat" LumaWipe.Type.Cloud="Oblak" -LumaWipe.Type.Curtain="Zavesa" +LumaWipe.Type.Curtain="Zavjesa" LumaWipe.Type.Fan="Ventilator" LumaWipe.Type.Fractal="Fraktal" -LumaWipe.Type.Iris="Žiža" -LumaWipe.Type.LinearHorizontal="Linijski horizontalno" -LumaWipe.Type.LinearTopLeft="Linijski odozgo sleva" +LumaWipe.Type.Iris="Dijafragma" +LumaWipe.Type.LinearHorizontal="Linearno vodoravno" +LumaWipe.Type.LinearTopLeft="Linearno gore lijevo" LumaWipe.Type.LinearTopRight="Linijski odozgo sdesna" -LumaWipe.Type.LinearVertical="Linijski vertikalno" -LumaWipe.Type.ParallelZigzagHorizontal="Paralelni cik-cak horizontalno" -LumaWipe.Type.ParallelZigzagVertical="Paralelni cik-cak vertikalni" -LumaWipe.Type.Spiral="Spiralno" +LumaWipe.Type.LinearVertical="Linearno okomito" +LumaWipe.Type.ParallelZigzagHorizontal="Paralelni vodoravni cik-cak" +LumaWipe.Type.ParallelZigzagVertical="Paralelni okomiti cik-cak" +LumaWipe.Type.Spiral="Spirala" LumaWipe.Type.Square="Kvadrat" LumaWipe.Type.Squares="Kvadrati" LumaWipe.Type.Stripes="Trake" -LumaWipe.Type.StripsHorizontal="Trake horizontalno" -LumaWipe.Type.StripsVertical="Trake vertikalno" -LumaWipe.Type.Watercolor="Vodene bojice" -LumaWipe.Type.ZigzagHorizontal="Cik-cak horizontalno" -LumaWipe.Type.ZigzagVertical="Cik-cak vertikalno" +LumaWipe.Type.StripsHorizontal="Vodoravne trake" +LumaWipe.Type.StripsVertical="Okomite trake" +LumaWipe.Type.Watercolor="Akvarel" +LumaWipe.Type.ZigzagHorizontal="Cik-cak vodoravno" +LumaWipe.Type.ZigzagVertical="Cik-cak okomito" +AudioMonitoring="Kontrola zvuka" +AudioMonitoring.None="Nadzor isključen" +AudioMonitoring.MonitorOnly="Samo nadziranje (bez zvuka)" +AudioMonitoring.Both="Kontrola i izlaz" +HardwareDecode="Koristi hardversko enkodiranje kada je dostupno" diff --git a/plugins/obs-transitions/data/locale/it-IT.ini b/plugins/obs-transitions/data/locale/it-IT.ini index 2a4019c246798e..6f094c0cc9d436 100644 --- a/plugins/obs-transitions/data/locale/it-IT.ini +++ b/plugins/obs-transitions/data/locale/it-IT.ini @@ -73,5 +73,5 @@ LumaWipe.Type.ZigzagVertical="Zig-zag verticale" AudioMonitoring="Monitoraggio audio" AudioMonitoring.None="Disattivato" AudioMonitoring.MonitorOnly="Solo monitoraggio (uscita disattivata)" -AudioMonitoring.Both="Monitora audio e invia all'uscita" +AudioMonitoring.Both="Monitora l'audio e invia all'uscita" HardwareDecode="Utilizza l'accellerazione hardware quando disponibile" diff --git a/plugins/obs-transitions/data/locale/sk-SK.ini b/plugins/obs-transitions/data/locale/sk-SK.ini index 0673ba063c72f0..bd65d0751807a1 100644 --- a/plugins/obs-transitions/data/locale/sk-SK.ini +++ b/plugins/obs-transitions/data/locale/sk-SK.ini @@ -1,4 +1,4 @@ -FadeTransition="Miznutie" +FadeTransition="Prelínať" CutTransition="Strih" SwipeTransition="Potiahnutie" SlideTransition="Posunutie" @@ -6,14 +6,14 @@ FadeToColorTransition="Miznutie do farby" Direction="Smer" Direction.Left="Vľavo" Direction.Right="Vpravo" -Direction.Up="Nahor" +Direction.Up="Hore" Direction.Down="Nadol" SwipeIn="Potiahnutie" Color="Farba" VideoFile="Video súbor" FileFilter.VideoFiles="Video súbory" FileFilter.AllFiles="Všetky súbory" -TransitionPoint="Bod Prechodu" +TransitionPoint="Bod prechodu" TransitionPointFrame="Bod prechodu (v snímkoch)" TransitionPointType="Typ bodu prechodu" AudioTransitionPointType="Typ bodu prechodu zvuku" diff --git a/plugins/obs-transitions/data/locale/tt-RU.ini b/plugins/obs-transitions/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..0eca685e26b29f --- /dev/null +++ b/plugins/obs-transitions/data/locale/tt-RU.ini @@ -0,0 +1,9 @@ +Color="Төс" +VideoFile="Видео файлы" +FileFilter.VideoFiles="Видео файллары" +FileFilter.AllFiles="Барлык файллар" +TransitionPointTypeFrame="Кадр" +TransitionPointTypeTime="Вакыт" +LumaWipe.Image="Сурәт" +LumaWipe.Type.Clock="Сәгать" +LumaWipe.Type.Cloud="Болыт" diff --git a/plugins/obs-webrtc/data/locale/ar-SA.ini b/plugins/obs-webrtc/data/locale/ar-SA.ini new file mode 100644 index 00000000000000..56603d601f804e --- /dev/null +++ b/plugins/obs-webrtc/data/locale/ar-SA.ini @@ -0,0 +1,5 @@ +Output.Name="مخرج WHIP" +Service.Name="خدمة WHIP" +Service.BearerToken="رمز حامل" +Error.InvalidSDP="استجاب خادم WHIP بـ SDP غير صالح:%1" +Error.NoRemoteDescription="أخفق تعيين الوصف عن بعد: %1" diff --git a/plugins/obs-webrtc/data/locale/de-DE.ini b/plugins/obs-webrtc/data/locale/de-DE.ini index f74fce047a46d6..81c7af87a17ab0 100644 --- a/plugins/obs-webrtc/data/locale/de-DE.ini +++ b/plugins/obs-webrtc/data/locale/de-DE.ini @@ -2,3 +2,4 @@ Output.Name="WHIP-Ausgabe" Service.Name="WHIP-Dienst" Service.BearerToken="Geheimschlüssel" Error.InvalidSDP="WHIP-Server hat mit ungültigem SDP geantwortet: %1" +Error.NoRemoteDescription="Remote-Beschreibung konnte nicht gesetzt werden: %1" diff --git a/plugins/obs-webrtc/data/locale/fa-IR.ini b/plugins/obs-webrtc/data/locale/fa-IR.ini index e6c237a30d3add..73b747c9c7bc38 100644 --- a/plugins/obs-webrtc/data/locale/fa-IR.ini +++ b/plugins/obs-webrtc/data/locale/fa-IR.ini @@ -1,3 +1,5 @@ Output.Name="خروجی WHIP" Service.Name="سرویس WHIP" Service.BearerToken="توکن رمز" +Error.InvalidSDP="سرور WHIP با SDP نامعتبر پاسخ داد: %1" +Error.NoRemoteDescription="توضیحات راه دور تنظیم نشد: %1" diff --git a/plugins/obs-webrtc/data/locale/fi-FI.ini b/plugins/obs-webrtc/data/locale/fi-FI.ini index a3f7eecb4fd30e..44ce78fd80fdd2 100644 --- a/plugins/obs-webrtc/data/locale/fi-FI.ini +++ b/plugins/obs-webrtc/data/locale/fi-FI.ini @@ -1,3 +1,5 @@ Output.Name="WHIP ulostulo" Service.Name="WHIP-palvelu" Service.BearerToken="Kantoaalto" +Error.InvalidSDP="WHIP-palvelin vastasi virheellisellä SDP:llä: %1" +Error.NoRemoteDescription="Kuvauksen asettaminen epäonnistui: %1" diff --git a/plugins/obs-webrtc/data/locale/hi-IN.ini b/plugins/obs-webrtc/data/locale/hi-IN.ini index 9044f8042d0439..f4f3c74ea9944b 100644 --- a/plugins/obs-webrtc/data/locale/hi-IN.ini +++ b/plugins/obs-webrtc/data/locale/hi-IN.ini @@ -2,3 +2,4 @@ Output.Name="WHIP आउटपुट" Service.Name="WHIP सेवा" Service.BearerToken="बियरर टोकन" Error.InvalidSDP="WHIP सर्वर ने अमान्य SDP के साथ प्रतिक्रिया दी : %1" +Error.NoRemoteDescription="इसके लिए चैनल जानकारी लोड करने में विफल : %1" diff --git a/plugins/obs-webrtc/data/locale/ka-GE.ini b/plugins/obs-webrtc/data/locale/ka-GE.ini index f9f51c41b08d29..7961b9a2c9a19d 100644 --- a/plugins/obs-webrtc/data/locale/ka-GE.ini +++ b/plugins/obs-webrtc/data/locale/ka-GE.ini @@ -1,3 +1,4 @@ Output.Name="WHIP-გამოტანა" Service.Name="WHIP-მომსახურება" Service.BearerToken="საშუამავლო საცნობი" +Error.NoRemoteDescription="დისტანციური აღწერილობის დაყენება ვერ მოხერხდა: %1" diff --git a/plugins/obs-webrtc/data/locale/ms-MY.ini b/plugins/obs-webrtc/data/locale/ms-MY.ini index 86a7687c1010b0..a4e84da7a2f404 100644 --- a/plugins/obs-webrtc/data/locale/ms-MY.ini +++ b/plugins/obs-webrtc/data/locale/ms-MY.ini @@ -1,2 +1,4 @@ Output.Name="Output WHIP" Service.Name="Perkhidmatan WHIP" +Error.InvalidSDP="Server WHIP menjawab dengan tidak sah SDP: %1" +Error.NoRemoteDescription="Gagal untuk menetapkan deskripsi kawalan: %1" diff --git a/plugins/obs-webrtc/data/locale/pt-BR.ini b/plugins/obs-webrtc/data/locale/pt-BR.ini index e54729f849a8be..c6743764601346 100644 --- a/plugins/obs-webrtc/data/locale/pt-BR.ini +++ b/plugins/obs-webrtc/data/locale/pt-BR.ini @@ -1,3 +1,5 @@ Output.Name="Saída WHIP" Service.Name="Serviço WHIP" Service.BearerToken="Token Bearer" +Error.InvalidSDP="O servidor WHIP respondeu com um SDP inválido: %1" +Error.NoRemoteDescription="Falha ao definir a descrição remota: %1" diff --git a/plugins/obs-webrtc/data/locale/tr-TR.ini b/plugins/obs-webrtc/data/locale/tr-TR.ini index dbc651862da995..bf0e9a1c415524 100644 --- a/plugins/obs-webrtc/data/locale/tr-TR.ini +++ b/plugins/obs-webrtc/data/locale/tr-TR.ini @@ -1,2 +1,4 @@ Output.Name="WHIP Çıkışı" Service.Name="WHIP Servisi" +Error.InvalidSDP="WHIP sunucusu geçersiz SDP ile yanıt verdi: %1" +Error.NoRemoteDescription="Uzak açıklama ayarlanamadı: %1" diff --git a/plugins/obs-x264/data/locale/ko-KR.ini b/plugins/obs-x264/data/locale/ko-KR.ini index 7d7d6ad6549217..3e4ca825a438a8 100644 --- a/plugins/obs-x264/data/locale/ko-KR.ini +++ b/plugins/obs-x264/data/locale/ko-KR.ini @@ -2,7 +2,7 @@ Bitrate="비트레이트" CustomBufsize="사용자 정의 버퍼 크기 설정" BufferSize="버퍼 크기" RateControl="데이터율 제어" -KeyframeIntervalSec="카프레임 간격 (0=자동)" +KeyframeIntervalSec="키프레임 간격 (0=자동)" CPUPreset="CPU 사용량 사전 설정 (높을수록 = 낮은 CPU부담)" Profile="프로파일" Tune="조정" diff --git a/plugins/obs-x264/data/locale/pt-PT.ini b/plugins/obs-x264/data/locale/pt-PT.ini index 7681c24a9d05fc..054981b7d2d0f2 100644 --- a/plugins/obs-x264/data/locale/pt-PT.ini +++ b/plugins/obs-x264/data/locale/pt-PT.ini @@ -1,7 +1,7 @@ Bitrate="Taxa de bits" CustomBufsize="Utilizar tamanho de memória personalizado" BufferSize="Tamanho da memória temporária" -RateControl="Controlo de taxa" +RateControl="Controlo da frequência" KeyframeIntervalSec="Intervalo de fotogramas-chave ( 0=automático)" CPUPreset="Predefinição de uso do CPU (maior = menos CPU)" Profile="Perfil" diff --git a/plugins/obs-x264/data/locale/tt-RU.ini b/plugins/obs-x264/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..213c3775a64ae5 --- /dev/null +++ b/plugins/obs-x264/data/locale/tt-RU.ini @@ -0,0 +1,3 @@ +Bitrate="Битрейт" +Profile="Профиль" +None="(Юк)" diff --git a/plugins/rtmp-services/data/locale/be-BY.ini b/plugins/rtmp-services/data/locale/be-BY.ini index 38f1a0701c96cf..3872debdf07858 100644 --- a/plugins/rtmp-services/data/locale/be-BY.ini +++ b/plugins/rtmp-services/data/locale/be-BY.ini @@ -8,3 +8,5 @@ UseAuth="Выкарыстоўваць аўтарызацыю" Username="Імя карыстальніка" Password="Пароль" ShowAll="Паказаць усе сервісы" +MultitrackVideo.Disclaimer="%1 аўтаматычна аптымізуе вашы налады пад кадаванне і адпраўленне сэрвісу %2 відэа ў некалькіх якасцях. Пасля выбару гэтай опцыі сэрвісу %2 будуць адпраўлены звесткі аб вашым камп'ютары і ПЗ." +MultitrackVideo.LearnMoreLink="Даведацца больш" diff --git a/plugins/rtmp-services/data/locale/ca-ES.ini b/plugins/rtmp-services/data/locale/ca-ES.ini index 32e1adcf6be300..218fa556d6e9f7 100644 --- a/plugins/rtmp-services/data/locale/ca-ES.ini +++ b/plugins/rtmp-services/data/locale/ca-ES.ini @@ -8,3 +8,5 @@ UseAuth="Usa autenticació" Username="Nom d’usuari" Password="Contrasenya" ShowAll="Mostra tots els serveis" +MultitrackVideo.Disclaimer="%1 optimitza automàticament la vostra configuració per a codificar i enviar múltiples qualitats de vídeo a %2. Si seleccioneu aquesta opció, s'enviarà a %2 informació relativa al vostre ordinador i la configuració del programari." +MultitrackVideo.LearnMoreLink=" Més informació" diff --git a/plugins/rtmp-services/data/locale/cs-CZ.ini b/plugins/rtmp-services/data/locale/cs-CZ.ini index 96b247701d021c..454d98758f68ef 100644 --- a/plugins/rtmp-services/data/locale/cs-CZ.ini +++ b/plugins/rtmp-services/data/locale/cs-CZ.ini @@ -7,3 +7,5 @@ UseAuth="Použít ověření" Username="Uživatelské jméno" Password="Heslo" ShowAll="Zobrazit všechny služby" +MultitrackVideo.Disclaimer="%1 automaticky optimalizuje nastavení pro enkódování a odesílání videí v několika kvalitách do %2. Tato možnost odešle %2 informace o vašem počítači a nainstalovaném softwaru." +MultitrackVideo.LearnMoreLink=" Více informací" diff --git a/plugins/rtmp-services/data/locale/de-DE.ini b/plugins/rtmp-services/data/locale/de-DE.ini index 1e394d6b62b053..b5dcc2933fe947 100644 --- a/plugins/rtmp-services/data/locale/de-DE.ini +++ b/plugins/rtmp-services/data/locale/de-DE.ini @@ -7,3 +7,5 @@ UseAuth="Authentifizierung verwenden" Username="Benutzername" Password="Passwort" ShowAll="Alle Plattformen anzeigen" +MultitrackVideo.Disclaimer="%1 optimiert automatisch Ihre Einstellungen, um mehrere Videoqualitäten zu kodieren und an %2 zu senden. %2 werden dann Informationen über die Konfiguration Ihres Computers und Ihrer Software mitgeteilt." +MultitrackVideo.LearnMoreLink=" Mehr darüber erfahren" diff --git a/plugins/rtmp-services/data/locale/en-GB.ini b/plugins/rtmp-services/data/locale/en-GB.ini index 4287ca8617970f..0892771be58152 100644 --- a/plugins/rtmp-services/data/locale/en-GB.ini +++ b/plugins/rtmp-services/data/locale/en-GB.ini @@ -1 +1 @@ -# \ No newline at end of file +MultitrackVideo.Disclaimer="%1 automatically optimises your settings to encode and send multiple video qualities to %2. Selecting this option will send %2 information about your computer and software setup." diff --git a/plugins/rtmp-services/data/locale/es-ES.ini b/plugins/rtmp-services/data/locale/es-ES.ini index 9c9625c27714e2..734bd7fb16e48d 100644 --- a/plugins/rtmp-services/data/locale/es-ES.ini +++ b/plugins/rtmp-services/data/locale/es-ES.ini @@ -8,3 +8,5 @@ UseAuth="Usar la autentificación" Username="Nombre de usuario" Password="Contraseña" ShowAll="Mostrar todos los servicios" +MultitrackVideo.Disclaimer=" %1 optimiza automáticamente su configuración para codificar y enviar múltiples calidades de vídeo a %2. Al seleccionar esta opción se enviará a %2 información sobre la configuración de su ordenador y software." +MultitrackVideo.LearnMoreLink="Aprende más" diff --git a/plugins/rtmp-services/data/locale/fa-IR.ini b/plugins/rtmp-services/data/locale/fa-IR.ini index ea6adf454bb916..930517255dfbc6 100644 --- a/plugins/rtmp-services/data/locale/fa-IR.ini +++ b/plugins/rtmp-services/data/locale/fa-IR.ini @@ -8,3 +8,5 @@ UseAuth="استفاده از تایید اعتبار" Username="نام کاربری" Password="رمزعبور" ShowAll="همه سرویس ها را نمایش بده" +MultitrackVideo.Disclaimer="% 1 به طور خودکار تنظیمات شما را برای رمزگذاری و ارسال چند کیفیت ویدیو به %2 بهینه می کند. با انتخاب این گزینه اطلاعات %2 درباره رایانه و تنظیم نرم‌افزار شما ارسال می شود." +MultitrackVideo.LearnMoreLink="بیشتر بیاموزید" diff --git a/plugins/rtmp-services/data/locale/fi-FI.ini b/plugins/rtmp-services/data/locale/fi-FI.ini index 204006e7ab5b44..bbd426251625e3 100644 --- a/plugins/rtmp-services/data/locale/fi-FI.ini +++ b/plugins/rtmp-services/data/locale/fi-FI.ini @@ -8,3 +8,5 @@ UseAuth="Käytä todennusta" Username="Käyttäjätunnus" Password="Salasana" ShowAll="Näytä kaikki palvelut" +MultitrackVideo.Disclaimer="%1 optimoi asetuksesi automaattisesti koodaukselle ja lähettää useita videolaatuja kohteeseen %2. Tämän valitseminen lähettää tietoja %2 tietokoneestasi ja ohjelmiston asetuksista." +MultitrackVideo.LearnMoreLink="Lue lisää" diff --git a/plugins/rtmp-services/data/locale/fr-FR.ini b/plugins/rtmp-services/data/locale/fr-FR.ini index fc8924d231ef7e..ce43ffce9ffaae 100644 --- a/plugins/rtmp-services/data/locale/fr-FR.ini +++ b/plugins/rtmp-services/data/locale/fr-FR.ini @@ -7,3 +7,5 @@ UseAuth="Utiliser l'authentification" Username="Nom d'utilisateur" Password="Mot de passe" ShowAll="Afficher tous les services" +MultitrackVideo.Disclaimer="%1 optimise automatiquement vos paramètres pour encoder et envoyer plusieurs qualités vidéo à %2. Sélectionner cette option enverra à %2 des informations sur votre ordinateur et votre configuration logicielle." +MultitrackVideo.LearnMoreLink="En savoir plus" diff --git a/plugins/rtmp-services/data/locale/he-IL.ini b/plugins/rtmp-services/data/locale/he-IL.ini index a5a78610dcb6a0..81fe8237be7909 100644 --- a/plugins/rtmp-services/data/locale/he-IL.ini +++ b/plugins/rtmp-services/data/locale/he-IL.ini @@ -8,3 +8,5 @@ UseAuth="שימוש באימות" Username="שם משתמש" Password="סיסמא" ShowAll="הצגת כל השירותים" +MultitrackVideo.Disclaimer="%1 ממטב את ההגדרות שלך אוטומטית כדי לקודד ולשלוח מגוון איכויות וידאו אל %2. בחירה באפשרות הזאת תשלח פרטים אל %2 על תצורת המחשב והתוכנה שלך." +MultitrackVideo.LearnMoreLink=" מידע נוסף" diff --git a/plugins/rtmp-services/data/locale/hu-HU.ini b/plugins/rtmp-services/data/locale/hu-HU.ini index 0aa9cdf4cfede6..ebc6e851d2fcdb 100644 --- a/plugins/rtmp-services/data/locale/hu-HU.ini +++ b/plugins/rtmp-services/data/locale/hu-HU.ini @@ -8,3 +8,5 @@ UseAuth="Hitelesítés használata" Username="Felhasználó" Password="Jelszó" ShowAll="Összes szolgáltató mutatása" +MultitrackVideo.Disclaimer="Az %1 automatikusan optimalizálja a beállításait, hogy többféle minőségű videót kódoljon és küldjön a következőnek: %2. A beallítás választása információkat küld a(z) %2 számára a számítógépéről és szoftverbeállításairól." +MultitrackVideo.LearnMoreLink="További tudnivalók" diff --git a/plugins/rtmp-services/data/locale/id-ID.ini b/plugins/rtmp-services/data/locale/id-ID.ini index e861bf1fef87b4..6a95cff4137d69 100644 --- a/plugins/rtmp-services/data/locale/id-ID.ini +++ b/plugins/rtmp-services/data/locale/id-ID.ini @@ -7,3 +7,5 @@ UseAuth="Gunakan autentikasi" Username="Nama pengguna" Password="Kata sandi" ShowAll="Tampilkan semua layanan" +MultitrackVideo.Disclaimer="%1 otomatis mengoptimisasikan setelan Anda untuk mengkodekan dan mengirimkan beberapa kualitas video ke %2. Memilih opsi ini akan mengirimkan informasi %2 tentang komputer dan pengaturan perangkat lunak Anda." +MultitrackVideo.LearnMoreLink=" Pelajari Lebih Lanjut" diff --git a/plugins/rtmp-services/data/locale/it-IT.ini b/plugins/rtmp-services/data/locale/it-IT.ini index b88b3e43da52c4..44e54bb525c151 100644 --- a/plugins/rtmp-services/data/locale/it-IT.ini +++ b/plugins/rtmp-services/data/locale/it-IT.ini @@ -6,3 +6,5 @@ StreamKey="Codice delle dirette" UseAuth="Utilizza l'autenticazione" Username="Nome utente" ShowAll="Mostra tutti i servizi" +MultitrackVideo.Disclaimer="%1 ottimizza automaticamente le tue impostazioni per codificare e inviare qualità del video multiple a %2. Selezionando questa opzione saranno inviate a %2 informazioni sulla configurazione del tuo computer e del software." +MultitrackVideo.LearnMoreLink=" Scopri di più" diff --git a/plugins/rtmp-services/data/locale/ja-JP.ini b/plugins/rtmp-services/data/locale/ja-JP.ini index 90a1de3e3c9f08..51b5894fe33b69 100644 --- a/plugins/rtmp-services/data/locale/ja-JP.ini +++ b/plugins/rtmp-services/data/locale/ja-JP.ini @@ -8,3 +8,5 @@ UseAuth="認証を使用する" Username="ユーザー名" Password="パスワード" ShowAll="すべてのサービスを表示" +MultitrackVideo.Disclaimer="%1 は設定を自動的に最適化し複数のビデオ品質をエンコードして %2 に送信します。 このオプションを選択するとコンピュータとソフトウェアのセットアップに関する情報が %2 に送信されます。" +MultitrackVideo.LearnMoreLink="詳しくはこちら" diff --git a/plugins/rtmp-services/data/locale/ko-KR.ini b/plugins/rtmp-services/data/locale/ko-KR.ini index c85923033d6201..4e8bcd9d384dad 100644 --- a/plugins/rtmp-services/data/locale/ko-KR.ini +++ b/plugins/rtmp-services/data/locale/ko-KR.ini @@ -8,3 +8,5 @@ UseAuth="접속 시 인증 필요" Username="사용자 이름" Password="암호" ShowAll="모든 방송 서비스 표시" +MultitrackVideo.Disclaimer="%1은 여러 영상을 여러 품질로 인코딩하여 %2로 전송할 수 있도록 자동으로 설정을 최적화합니다. 이 옵션을 선택하면 %2 에 컴퓨터 본체와 소프트웨어 설정에 관한 정보를 전송합니다." +MultitrackVideo.LearnMoreLink=" 더 알아보기" diff --git a/plugins/rtmp-services/data/locale/ms-MY.ini b/plugins/rtmp-services/data/locale/ms-MY.ini index 533668e93fc9b4..f1176fc41515d0 100644 --- a/plugins/rtmp-services/data/locale/ms-MY.ini +++ b/plugins/rtmp-services/data/locale/ms-MY.ini @@ -8,3 +8,5 @@ UseAuth="Guna pengesahihan" Username="Nama Pengguna" Password="Kata Laluan" ShowAll="Tunjuk semua perkhidmatan" +MultitrackVideo.Disclaimer="%1 mengoptimakan tetapan secara automatik bagi meng-encode dan menghantar beberapa kualiti video ke %2. Memilih pilihan ini dan menghantar %2 informasi tentang komputer anda dan penyediaan perisian." +MultitrackVideo.LearnMoreLink=" Ketahui Lebih Lanjut" diff --git a/plugins/rtmp-services/data/locale/nl-NL.ini b/plugins/rtmp-services/data/locale/nl-NL.ini index a013b242ae6dc9..4f1c9fce8430d5 100644 --- a/plugins/rtmp-services/data/locale/nl-NL.ini +++ b/plugins/rtmp-services/data/locale/nl-NL.ini @@ -6,3 +6,5 @@ UseAuth="Gebruik authenticatie" Username="Gebruikersnaam" Password="Wachtwoord" ShowAll="Laat alle services zien" +MultitrackVideo.Disclaimer="%1 optimaliseert automatisch jouw instellingen voor encoden en versturen van meerdere video kwaliteiten naar %2. Selecteren van deze optie stuurt informatie over jouw computer en drivers naar %2." +MultitrackVideo.LearnMoreLink="Meer informatie." diff --git a/plugins/rtmp-services/data/locale/pl-PL.ini b/plugins/rtmp-services/data/locale/pl-PL.ini index 8a55a481a09d0d..76c318ac666e0e 100644 --- a/plugins/rtmp-services/data/locale/pl-PL.ini +++ b/plugins/rtmp-services/data/locale/pl-PL.ini @@ -8,3 +8,5 @@ UseAuth="Użyj uwierzytelniania" Username="Użytkownik" Password="Hasło" ShowAll="Pokaż wszystkie serwisy" +MultitrackVideo.Disclaimer="%1 automatycznie optymalizuje ustawienia enkodera i wysyłki video o różnych jakościach do %2. Wybranie tej opcji spowoduje wysłanie informacji o Twoim komputerze i oprogramowaniu do %2." +MultitrackVideo.LearnMoreLink=" Dowiedz się więcej" diff --git a/plugins/rtmp-services/data/locale/pt-BR.ini b/plugins/rtmp-services/data/locale/pt-BR.ini index 4278484d44fe67..16c47f62d68dd4 100644 --- a/plugins/rtmp-services/data/locale/pt-BR.ini +++ b/plugins/rtmp-services/data/locale/pt-BR.ini @@ -8,3 +8,5 @@ UseAuth="Utilizar autenticação" Username="Usuário" Password="Senha" ShowAll="Mostrar todos os serviços" +MultitrackVideo.Disclaimer="%1 Automaticamente optimiza suas definições para codificar e enviar múltiplas qualidades de védeo para %2. Selecionando esta opção enviará informações sobre configurações de seu computador para %2." +MultitrackVideo.LearnMoreLink=" Saiba Mais" diff --git a/plugins/rtmp-services/data/locale/pt-PT.ini b/plugins/rtmp-services/data/locale/pt-PT.ini index 5a9fc92c1b33ea..d7f2196481a8cf 100644 --- a/plugins/rtmp-services/data/locale/pt-PT.ini +++ b/plugins/rtmp-services/data/locale/pt-PT.ini @@ -5,6 +5,8 @@ Server="Servidor" Server.Auto="Automático (recomendado)" StreamKey="Chave da transmissão" UseAuth="Utilizar autenticação" -Username="Utilizador" +Username="Nome de utilizador" Password="Senha" ShowAll="Mostrar todos os serviços" +MultitrackVideo.Disclaimer="%1 otimiza automaticamente as suas configurações para codificar e enviar múltiplas qualidades de vídeo para %2. Selecionar esta opção enviará a %2 informações sobre o software e a configuração do seu computador." +MultitrackVideo.LearnMoreLink="Saiba mais." diff --git a/plugins/rtmp-services/data/locale/ru-RU.ini b/plugins/rtmp-services/data/locale/ru-RU.ini index 7ba8409c6fbb42..d3aae72ab45e3d 100644 --- a/plugins/rtmp-services/data/locale/ru-RU.ini +++ b/plugins/rtmp-services/data/locale/ru-RU.ini @@ -8,3 +8,5 @@ UseAuth="Использовать авторизацию" Username="Имя пользователя" Password="Пароль" ShowAll="Показать все службы" +MultitrackVideo.Disclaimer="%1 автоматически оптимизирует ваши настройки для кодирования и отправки видео разного качества на %2. При выборе этого параметра на %2 будет отправлена информация о настройках вашего компьютера и программного обеспечения." +MultitrackVideo.LearnMoreLink=" Подробнее" diff --git a/plugins/rtmp-services/data/locale/sk-SK.ini b/plugins/rtmp-services/data/locale/sk-SK.ini index c1decb7654491f..ee282ecbde4617 100644 --- a/plugins/rtmp-services/data/locale/sk-SK.ini +++ b/plugins/rtmp-services/data/locale/sk-SK.ini @@ -7,3 +7,5 @@ UseAuth="Použiť overenie" Username="Užívateľské meno" Password="Heslo" ShowAll="Zobraziť všetky služby" +MultitrackVideo.Disclaimer="%1 automaticky optimalizuje vaše nastavenia pre enkódovanie a pošle viaceré video kvality do %2. Vybratím tejto možnosti pošle %2 informácie of vašom počítači a nastavenie softvéru." +MultitrackVideo.LearnMoreLink=" Zistiť viac" diff --git a/plugins/rtmp-services/data/locale/sl-SI.ini b/plugins/rtmp-services/data/locale/sl-SI.ini index 7aa340206112d6..02f360d921c99a 100644 --- a/plugins/rtmp-services/data/locale/sl-SI.ini +++ b/plugins/rtmp-services/data/locale/sl-SI.ini @@ -8,3 +8,5 @@ UseAuth="Uporabi overitev" Username="Uporabniško ime" Password="Geslo" ShowAll="Prikaži vse storitve" +MultitrackVideo.Disclaimer="%1 samodejno optimizira nastavitve za kodiranje in pošiljanje več kakovosti videa na %2.\nTa izbira bo poslala %2 podatkov o vaši računalniški in programski opremi." +MultitrackVideo.LearnMoreLink=" Več o tem" diff --git a/plugins/rtmp-services/data/locale/sv-SE.ini b/plugins/rtmp-services/data/locale/sv-SE.ini index 3e5e0be4686d3b..498fb0c505117e 100644 --- a/plugins/rtmp-services/data/locale/sv-SE.ini +++ b/plugins/rtmp-services/data/locale/sv-SE.ini @@ -7,3 +7,5 @@ UseAuth="Använd autentisering" Username="Användarnamn" Password="Lösenord" ShowAll="Visa alla tjänster" +MultitrackVideo.Disclaimer="%1 optimerar automatiskt dina inställningar för att koda och skicka flera videokvaliteter till %2. Om detta alternativ väljs kommer %2-information om din dator och mjukvaruinstallation att skickas." +MultitrackVideo.LearnMoreLink=" Läs mer" diff --git a/plugins/rtmp-services/data/locale/tr-TR.ini b/plugins/rtmp-services/data/locale/tr-TR.ini index 729f4a0ce8a13a..5fe5d687a0fb7c 100644 --- a/plugins/rtmp-services/data/locale/tr-TR.ini +++ b/plugins/rtmp-services/data/locale/tr-TR.ini @@ -8,3 +8,5 @@ UseAuth="Kimlik doğrulaması kullan" Username="Kullanıcı Adı" Password="Şifre" ShowAll="Tüm hizmetleri göster" +MultitrackVideo.Disclaimer="%1, birden fazla video kalitesini kodlamak ve %2 hizmetine göndermek için ayarlarınızı otomatik olarak optimize eder. Bu seçeneğin seçilmesi, bilgisayarınız ve yazılım kurulumunuz hakkında bilgilerinizi %2 hizmetine gönderecektir." +MultitrackVideo.LearnMoreLink="Daha Fazlasını Öğren." diff --git a/plugins/rtmp-services/data/locale/tt-RU.ini b/plugins/rtmp-services/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..ead71318c95e6c --- /dev/null +++ b/plugins/rtmp-services/data/locale/tt-RU.ini @@ -0,0 +1,6 @@ +StreamingServices="Агым хезмәтләре" +Service="Хезмәт" +Server="Сервер" +StreamKey="Агым ачкычы" +Username="Кулланучы исеме" +Password="Серсүз" diff --git a/plugins/rtmp-services/data/locale/uk-UA.ini b/plugins/rtmp-services/data/locale/uk-UA.ini index 028efd5edd120a..b956e11d9c146a 100644 --- a/plugins/rtmp-services/data/locale/uk-UA.ini +++ b/plugins/rtmp-services/data/locale/uk-UA.ini @@ -8,3 +8,5 @@ UseAuth="Використовувати автентифікацію" Username="Ім’я користувача" Password="Пароль" ShowAll="Показати всі сервіси" +MultitrackVideo.Disclaimer="%1 автоматично оптимізує ваші налаштування для кодування та надсилання відео різної якості на %2. Вибравши цей параметр, ви надішлете %2 інформацію про ваш пристрій і налаштування програмного забезпечення." +MultitrackVideo.LearnMoreLink=" Докладніше" diff --git a/plugins/rtmp-services/data/locale/vi-VN.ini b/plugins/rtmp-services/data/locale/vi-VN.ini index 4d2fc4f7f6ce20..3537dce9b21580 100644 --- a/plugins/rtmp-services/data/locale/vi-VN.ini +++ b/plugins/rtmp-services/data/locale/vi-VN.ini @@ -8,3 +8,5 @@ UseAuth="Xác thực sử dụng" Username="Tài khoản" Password="Mật khẩu" ShowAll="Hiển tất cả các dịch vụ" +MultitrackVideo.Disclaimer="%1 tự động tối ưu hóa cài đặt của bạn để mã hóa và gửi nhiều chất lượng video tới %2. Việc chọn tùy chọn này sẽ gửi %2 thông tin về máy tính và cài đặt phần mềm của bạn." +MultitrackVideo.LearnMoreLink="Tìm hiểu thêm" diff --git a/plugins/rtmp-services/data/locale/zh-CN.ini b/plugins/rtmp-services/data/locale/zh-CN.ini index a4e10ef3710dca..7afd7acbd4f02e 100644 --- a/plugins/rtmp-services/data/locale/zh-CN.ini +++ b/plugins/rtmp-services/data/locale/zh-CN.ini @@ -8,3 +8,5 @@ UseAuth="使用身份认证" Username="用户名" Password="密码" ShowAll="显示所有服务" +MultitrackVideo.Disclaimer="%1 会自动优化您的设置,以便对多个视频质量进行编码并将其发送到 %2。选择此选项将向 %2 发送有关您的计算机和软件设置的信息。" +MultitrackVideo.LearnMoreLink=" 了解更多" diff --git a/plugins/rtmp-services/data/locale/zh-TW.ini b/plugins/rtmp-services/data/locale/zh-TW.ini index 5ccf4a5747f408..bd804252435bc1 100644 --- a/plugins/rtmp-services/data/locale/zh-TW.ini +++ b/plugins/rtmp-services/data/locale/zh-TW.ini @@ -8,3 +8,5 @@ UseAuth="使用身份驗證" Username="使用者名稱" Password="密碼" ShowAll="顯示所有服務" +MultitrackVideo.Disclaimer="%1 會對多種視訊品質進行編碼,以自動最佳化設定,並傳送到 %2。選擇本選項將會將電腦和軟體設定的資訊傳送給 %2。" +MultitrackVideo.LearnMoreLink=" 深入瞭解" diff --git a/plugins/sndio/data/locale/tt-RU.ini b/plugins/sndio/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..3fc7b5f4ddf757 --- /dev/null +++ b/plugins/sndio/data/locale/tt-RU.ini @@ -0,0 +1 @@ +Device="Җайланма" diff --git a/plugins/text-freetype2/data/locale/it-IT.ini b/plugins/text-freetype2/data/locale/it-IT.ini index 885797515051d2..71a3206d214c4d 100644 --- a/plugins/text-freetype2/data/locale/it-IT.ini +++ b/plugins/text-freetype2/data/locale/it-IT.ini @@ -14,4 +14,5 @@ Color2="Colore 2" Outline="Contorno del testo" DropShadow="Ombreggiatura del testo" CustomWidth="Larghezza del testo personalizzata" +WordWrap="A capo automatico" Antialiasing="Abilita Antialiasing" diff --git a/plugins/text-freetype2/data/locale/tt-RU.ini b/plugins/text-freetype2/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..6fb252464b9257 --- /dev/null +++ b/plugins/text-freetype2/data/locale/tt-RU.ini @@ -0,0 +1,9 @@ +TextFreetype2="Текст (FreeType 2)" +Font="Хәрефләр" +Text="Текст" +TextFile="Текстлы файл" +TextFile.Encoding="UTF-8 яки UTF-16" +TextInputMode.FromFile="Файлдан" +TextFileFilter="Текстлы файллар (*.txt);;" +Color1="Төс 1" +Color2="Төс 2" diff --git a/plugins/vlc-video/data/locale/tt-RU.ini b/plugins/vlc-video/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..9186d099643bd3 --- /dev/null +++ b/plugins/vlc-video/data/locale/tt-RU.ini @@ -0,0 +1,6 @@ +VLCSource="VLC видео чыганагы" +PlayPause="Уйнату/туктатып тору" +Restart="Яңадан башлау" +Stop="туктату" +PlaylistNext="Киләсе" +PlaylistPrev="Элеккеге" diff --git a/plugins/win-capture/data/locale/ar-SA.ini b/plugins/win-capture/data/locale/ar-SA.ini index 0aae0558e04e9b..a434a050bc4524 100644 --- a/plugins/win-capture/data/locale/ar-SA.ini +++ b/plugins/win-capture/data/locale/ar-SA.ini @@ -38,6 +38,7 @@ GameCapture.HookRate.Fastest="الأسرع" GameCapture.Rgb10a2Space="مساحة الألوان RGB10A2" Mode="الوضع" CaptureAudio="التقط الصوت (بيتا)" +CaptureAudio.TT="عند تمكينه ينيء مصدر \"التقاط صوت تطبيق\" الذي يحدّث تِلْقائيًا إلى النافذة/التطبيق الملتقَط حاليا. لاحظ أنه إذا كان صوت سطح المكتب معدّاً فقد يتسبب في صوت مضاعَف." AudioSuffix="صوت" Compatibility.GameCapture.Admin="%name% قد يتطلب تشغيل OBS بصلاحيات مشرف من أجل التقاط اللعبة." Compatibility.GameCapture.Blocked="لا يمكن التقاط %name% عن طريق التقاط اللعبة. استخدم التقاط النافذة أو التقاط الشاشة بدلا من ذلك." diff --git a/plugins/win-capture/data/locale/be-BY.ini b/plugins/win-capture/data/locale/be-BY.ini index ed15a22cce4aa6..0c2abf565ed36d 100644 --- a/plugins/win-capture/data/locale/be-BY.ini +++ b/plugins/win-capture/data/locale/be-BY.ini @@ -9,7 +9,7 @@ WindowCapture.Priority="Прыярытэт адпаведнасці акна" WindowCapture.Priority.Title="Загаловак акна павінен супадаць" WindowCapture.Priority.Class="Супадзенне загалоўка, у іншым выпадку знаходзіць акно таго ж тыпу" WindowCapture.Priority.Exe="Супадзенне загалоўка, у іншым выпадку знаходзіць акно таго ж выканальнага файла" -CaptureCursor="Захват курсору" +CaptureCursor="Запісваць курсор" Compatibility="Сумяшчальнасць з мультыадаптарам" ClientArea="Не запісваць межы акна" ForceSdr="Прымусовы SDR" diff --git a/plugins/win-capture/data/locale/de-DE.ini b/plugins/win-capture/data/locale/de-DE.ini index 3f2dbe48522dec..93e3e5c6ee5e37 100644 --- a/plugins/win-capture/data/locale/de-DE.ini +++ b/plugins/win-capture/data/locale/de-DE.ini @@ -1,4 +1,4 @@ -MonitorCapture="Bildschirmaufnahme" +MonitorCapture="Monitoraufnahme" WindowCapture="Fensteraufnahme" WindowCapture.Window="Fenster" WindowCapture.Method="Aufnahmemethode" @@ -16,7 +16,7 @@ ForceSdr="SDR erzwingen" SLIFix="SLI-/Crossfire-Aufnahmemodus (Langsam)" AllowTransparency="Transparenz erlauben" PremultipliedAlpha="Vormultipliziertes Alpha" -Monitor="Bildschirm" +Monitor="Monitor" PrimaryMonitor="Hauptbildschirm" Method="Aufnahmemethode" Method.DXGI="DXGI-Desktopduplikation" @@ -40,9 +40,9 @@ Mode="Modus" CaptureAudio="Audio erfassen (Beta)" CaptureAudio.TT="Erstellt eine „Anwendungsaudioaufnahme“ für das erfasste Fenster/die erfasste Anwendung.\nWenn Desktop-Audio konfiguriert ist, kann dies zu doppelter Audio führen." Compatibility.GameCapture.Admin="Für %name% kann es erforderlich sein, OBS als Administrator auszuführen, um die Spieleaufnahme verwenden zu können." -Compatibility.GameCapture.Blocked="%name% kann nicht über die Spielaufnahme aufgenommen werden. Verwenden Sie stattdessen die Fenster- oder Bildschirmaufnahme." -Compatibility.GameCapture.Blocked.Applications="%name%-Anwendungen können nicht über die Spielaufnahme aufgenommen werden. Verwenden Sie stattdessen die Fenster- oder Bildschirmaufnahme." -Compatibility.GameCapture.Blocked.Applications.Built="%name%-basierte Spiele können nicht über die Spielaufnahme aufgenommen werden. Verwenden Sie stattdessen die Fenster- oder Bildschirmaufnahme." +Compatibility.GameCapture.Blocked="%name% kann nicht über die Spielaufnahme aufgenommen werden. Verwenden Sie stattdessen die Fenster- oder Monitoraufnahme." +Compatibility.GameCapture.Blocked.Applications="%name%-Anwendungen können nicht über die Spielaufnahme aufgenommen werden. Verwenden Sie stattdessen die Fenster- oder Monitoraufnahme." +Compatibility.GameCapture.Blocked.Applications.Built="%name%-basierte Spiele können nicht über die Spielaufnahme aufgenommen werden. Verwenden Sie stattdessen die Fenster- oder Monitoraufnahme." Compatibility.GameCapture.WrongGPU="Falls die Vorschau leer ist, sollten Sie sicherstellen, dass %name% auf derselben GPU wie OBS läuft." Compatibility.WindowCapture.BitBlt="%name% kann möglicherweise nicht mit der ausgewählten Aufnahmemethode (BitBlt) aufgenommen werden." Compatibility.WindowCapture.BitBlt.Applications="%name%-Anwendungen können möglicherweise nicht mit der ausgewählten Aufnahmemethode (BitBlt) aufgenommen werden." diff --git a/plugins/win-capture/data/locale/hi-IN.ini b/plugins/win-capture/data/locale/hi-IN.ini index 2d9ed3f3587113..9d3aee44aafb40 100644 --- a/plugins/win-capture/data/locale/hi-IN.ini +++ b/plugins/win-capture/data/locale/hi-IN.ini @@ -38,6 +38,7 @@ GameCapture.HookRate.Fastest="शीघ्रतम" GameCapture.Rgb10a2Space="RGB10A2 रंग स्पेस" Mode="मोड" CaptureAudio="ऑडियो कैप्चर (बीटा)" +CaptureAudio.TT="सक्षम होने पर, एक \"एप्लिकेशन ऑडियो कैप्चर\" स्रोत बनाता है जो वर्तमान में कैप्चर की गई विंडो/एप्लिकेशन पर स्वचालित रूप से अपडेट हो जाता है.\nध्यान दें कि यदि डेस्कटॉप ऑडियो कॉन्फ़िगर किया गया है, तो इसके परिणामस्वरूप ऑडियो दोगुना हो सकता है." AudioSuffix="ऑडियो" Compatibility.GameCapture.Admin="OBS में गेम कैप्चर का उपयोग करने के लिए %name% को व्यवस्थापक के रूप में चलाने के लिए की आवश्यकता हो सकती है." Compatibility.GameCapture.Blocked="गेम कैप्चर के माध्यम से %name% को कैप्चर नहीं किया जा सकता है. इसके बजाय विंडो कैप्चर या डिस्प्ले कैप्चर का उपयोग करें." diff --git a/plugins/win-capture/data/locale/id-ID.ini b/plugins/win-capture/data/locale/id-ID.ini index ca890f326ff542..978dc82bc84c0f 100644 --- a/plugins/win-capture/data/locale/id-ID.ini +++ b/plugins/win-capture/data/locale/id-ID.ini @@ -1,4 +1,4 @@ -MonitorCapture="Tangkapan Layar" +MonitorCapture="Penangkap Layar" WindowCapture="Tangkapan Jendela" WindowCapture.Window="Jendela" WindowCapture.Method="Metode Tangkapan" diff --git a/plugins/win-capture/data/locale/it-IT.ini b/plugins/win-capture/data/locale/it-IT.ini index 45fb11e443378e..2088a98941dd17 100644 --- a/plugins/win-capture/data/locale/it-IT.ini +++ b/plugins/win-capture/data/locale/it-IT.ini @@ -38,7 +38,7 @@ GameCapture.HookRate.Fastest="La più veloce" GameCapture.Rgb10a2Space="Spazio colore RGB10A2" Mode="Modalità" CaptureAudio="Cattura audio (BETA)" -CaptureAudio.TT="Se abilitato, crea una sorgente 'Acquisizione audio applicazione' che si aggiorna automaticamente la finestra/applicazione attualmente catturata.\nNota che se è configurato l'audio del desktop, ciò potrebbe comportare un audio raddoppiato." +CaptureAudio.TT="Quando abilitato, crea una sorgente \"Acquisizione audio applicazione\" che si aggiorna automaticamente alla finestra/applicazione attualmente catturata.\nNota che se l'Audio del desktop è configurato, ciò potrebbe comportare un audio raddoppiato." Compatibility.GameCapture.Admin="%name% potrebbe richiedere che OBS sia eseguito come amministratore per utilizzare Cattura Gioco" Compatibility.GameCapture.Blocked="'%name%' non può essere catturato tramite 'Cattura gioco'. Usa invece 'Cattura finestra' o 'Cattura schermo'." Compatibility.GameCapture.Blocked.Applications="%name% applicazioni non possono essere catturate tramite 'Cattura gioco'; usa invece 'Cattura finestra' o 'Cattura schermo'." diff --git a/plugins/win-capture/data/locale/ja-JP.ini b/plugins/win-capture/data/locale/ja-JP.ini index 446bcf607083c3..4425fdf63f35eb 100644 --- a/plugins/win-capture/data/locale/ja-JP.ini +++ b/plugins/win-capture/data/locale/ja-JP.ini @@ -16,7 +16,7 @@ ForceSdr="SDRを強制する" SLIFix="SLI/Crossfire キャプチャモード (遅い)" AllowTransparency="透過を許可" PremultipliedAlpha="乗算済みアルファ" -Monitor="画面" +Monitor="ディスプレイ" PrimaryMonitor="プライマリモニター" Method="キャプチャ方法" Method.DXGI="DXGI デスクトップ複製" diff --git a/plugins/win-capture/data/locale/ms-MY.ini b/plugins/win-capture/data/locale/ms-MY.ini index b4d212cd034463..2abb120d4224be 100644 --- a/plugins/win-capture/data/locale/ms-MY.ini +++ b/plugins/win-capture/data/locale/ms-MY.ini @@ -15,6 +15,7 @@ ClientArea="Kawasan Klien" ForceSdr="Paksa SDR" SLIFix="Mod Tangkap SLI/Crossfire (Perlahan)" AllowTransparency="Benarkan Kelutsinaran" +PremultipliedAlpha="Pra-darab Alpha" Monitor="Paparan" PrimaryMonitor="Pemantau Utama" Method="Kaedah Tangkap" @@ -36,6 +37,8 @@ GameCapture.HookRate.Fast="Pantas" GameCapture.HookRate.Fastest="Terpantas" GameCapture.Rgb10a2Space="Ruang Warna RGB10A2" Mode="Mod" +CaptureAudio="Tangkap Audio (BETA)" +CaptureAudio.TT="Apbila diaktifkan, cipta sumber \"Aplikasi Tangkapan Audio\" yang akan secara automatik mengemaskini kepada tangkapan tetingkap/aplikasi.\nHarap maklum, jika Audio Desktop telah ditetapkan, ini mungkin akan menyebabkan audio berganda." Compatibility.GameCapture.Admin="%name% mungkin memerlukan OBS dijalankan sebagai admin untuk menggunakan Game Capture." Compatibility.GameCapture.Blocked="%name% tidak dapat dirakam melalui Game Capture. Sila gunakan Window Capture atau Display Capture sebagai ganti." Compatibility.GameCapture.Blocked.Applications="Aplikasi %name% tidak dapat dirakam melalui Game Capture. Sila gunakan Window Capture atau Display Capture sebagai ganti." diff --git a/plugins/win-capture/data/locale/pt-PT.ini b/plugins/win-capture/data/locale/pt-PT.ini index f3b5c188ea2fde..65adbde293a27a 100644 --- a/plugins/win-capture/data/locale/pt-PT.ini +++ b/plugins/win-capture/data/locale/pt-PT.ini @@ -1,4 +1,4 @@ -MonitorCapture="Captura do monitor" +MonitorCapture="Captura de ecrã" WindowCapture="Captura de janela" WindowCapture.Window="Janela" WindowCapture.Method="Método de captura" @@ -41,9 +41,9 @@ CaptureAudio="Capturar áudio (Beta)" CaptureAudio.TT="Quando ativado, cria uma fonte de \"Captura de áudio de aplicações\" que é atualizada automaticamente para a janela/aplicação atualmente capturada.\nNote que se estiver configurado para usar o \"Áudio do ambiente de trabalho\" isto poderá resultar em áudio duplicado." AudioSuffix="Áudio" Compatibility.GameCapture.Admin="%name% pode implicar que o OBS seja executado como administrador para poder usar o modo Captura de jogo." -Compatibility.GameCapture.Blocked="%name% não pode ser capturado através do modo de captura de jogo. Em alternativa, use captura de janela ou captura de visualização." -Compatibility.GameCapture.Blocked.Applications="Aplicações %name% não podem ser capturadas através do modo de captura de jogo. Em alternativa, use captura de janela ou captura de visualização." -Compatibility.GameCapture.Blocked.Applications.Built="Jogos criados em %name% não podem ser capturados com o modo de captura de Jogo. Em alternativa, use o modo de captura de janela ou captura de visualização." +Compatibility.GameCapture.Blocked="%name% não pode ser capturado através do modo de captura de jogo. Em alternativa, use captura de janela ou captura de ecrã." +Compatibility.GameCapture.Blocked.Applications="Aplicações %name% não podem ser capturadas através do modo de captura de jogo. Em alternativa, use captura de janela ou captura de ecrã." +Compatibility.GameCapture.Blocked.Applications.Built="Jogos criados em %name% não podem ser capturados com o modo de captura de Jogo. Em alternativa, use o modo de captura de janela ou captura de ecrã." Compatibility.GameCapture.WrongGPU="Se a antevisão estiver em branco, verifique se %name% está a funcionar na mesma GPU que o OBS" Compatibility.WindowCapture.BitBlt="%name% podem não ser capturáveis com o modo de captura selecionado (BitBlt)." Compatibility.WindowCapture.BitBlt.Applications="Aplicações %name% podem não ser capturáveis usando o modo de captura selecionado (BitBlt)." diff --git a/plugins/win-capture/data/locale/sv-SE.ini b/plugins/win-capture/data/locale/sv-SE.ini index 500f5b0077086c..bef40966e97cb2 100644 --- a/plugins/win-capture/data/locale/sv-SE.ini +++ b/plugins/win-capture/data/locale/sv-SE.ini @@ -44,7 +44,7 @@ Compatibility.GameCapture.Admin="%name% kan kräva att OBS körs som administrat Compatibility.GameCapture.Blocked="%name% kan inte fångas med spelkälla. Använd i stället fönsterkälla eller skärmkälla." Compatibility.GameCapture.Blocked.Applications="%name%-applikationer kan inte fångas med spelkälla. Använd i stället fönsterkälla eller skärmkälla." Compatibility.GameCapture.Blocked.Applications.Built="Spel som byggs på %name% kan inte fångas med hjälp av spelkälla. Använd i stället fönsterkälla eller skärmkälla." -Compatibility.GameCapture.WrongGPU="Se till att %name% körs på samma grafikprocessor som OBS om förhandsgranskningen är tom." +Compatibility.GameCapture.WrongGPU="Se till att %name% körs på samma grafikprocessor som OBS om förhandsvisningen är tom." Compatibility.WindowCapture.BitBlt="%name% kanske inte kan fångas med hjälp av den valda källan (BitBlt)." Compatibility.WindowCapture.BitBlt.Applications="%name%-applikationer kanske inte kan fångas med hjälp av den valda källan (BitBlt)." Compatibility.WindowCapture.BitBlt.Applications.Based="%name%-baserade applikationer kanske inte kan fångas med hjälp av den valda källan (BitBlt)." diff --git a/plugins/win-capture/data/locale/tt-RU.ini b/plugins/win-capture/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..40d86d70e0d014 --- /dev/null +++ b/plugins/win-capture/data/locale/tt-RU.ini @@ -0,0 +1,7 @@ +WindowCapture="Тәрәзә яздыру" +WindowCapture.Window="Тәрәзә" +CaptureCursor="Күрсәткечне яздыру" +GameCapture="Уен яздыру" +GameCapture.HookRate.Fast="Тиз" +CaptureAudio="Аудио яздыру (БЕТА)" +AudioSuffix="Аудио" diff --git a/plugins/win-dshow/data/locale/de-DE.ini b/plugins/win-dshow/data/locale/de-DE.ini index 8a32ea974ef320..395d80d872ab16 100644 --- a/plugins/win-dshow/data/locale/de-DE.ini +++ b/plugins/win-dshow/data/locale/de-DE.ini @@ -32,7 +32,7 @@ Activate="Aktivieren" Deactivate="Deaktivieren" FlipVertically="Vertikal spiegeln" Autorotation="Rotationsdaten von Kamera übernehmen" -HardwareDecode="Hardwaredecodierung verwenden, falls verfügbar" +HardwareDecode="Hardwaredekodierung verwenden, falls verfügbar" DeactivateWhenNotShowing="Deaktivieren, wenn die Quelle nicht gezeigt wird" -Encoder.C985="AVerMedia-H.264-Encoder (c985)" -Encoder.C353="AVerMedia-H.264-Encoder" +Encoder.C985="AVerMedia-H.264-Kodierer (c985)" +Encoder.C353="AVerMedia-H.264-Kodierer" diff --git a/plugins/win-dshow/data/locale/ja-JP.ini b/plugins/win-dshow/data/locale/ja-JP.ini index 72a51cc305b194..8068f561ab2d7b 100644 --- a/plugins/win-dshow/data/locale/ja-JP.ini +++ b/plugins/win-dshow/data/locale/ja-JP.ini @@ -6,8 +6,8 @@ ColorRange="色範囲" ColorRange.Default="既定" ColorRange.Partial="リミテッド" ColorRange.Full="フル" -ConfigureVideo="映像を構成" -ConfigureCrossbar="クロスバーを構成" +ConfigureVideo="ビデオの設定" +ConfigureCrossbar="クロスバーの設定" ResFPSType="解像度/FPS タイプ" ResFPSType.Custom="カスタム" ResFPSType.DevPreferred="デバイスの既定値" diff --git a/plugins/win-dshow/data/locale/pt-PT.ini b/plugins/win-dshow/data/locale/pt-PT.ini index e1c438ced7f3b2..55e493592e937b 100644 --- a/plugins/win-dshow/data/locale/pt-PT.ini +++ b/plugins/win-dshow/data/locale/pt-PT.ini @@ -24,7 +24,7 @@ AudioOutputMode.WaveOut="Áudio do ambiente de trabalho (WaveOut)" UseCustomAudioDevice="Utilizar dispositivo de áudio personalizado" AudioDevice="Dispositivo de áudio" Buffering="Memória temporária" -Buffering.ToolTip="Quando ativa, esta memória de dados de vídeo/áudio garante a melhor e mais suave reprodução possível, mas com o custo de uma maior latência. Ao usar memória de dados com uma placa de captura de vídeo, para melhores resultados, recomenda-se definir a placa para a mesma taxa de fotogramas.\n\nQuando inativa, garante a menor latência de reprodução, mas com custos na precisão de reprodução de frames. É ideal para câmeras de rosto, ou quando utilizar a janela de antevisão do programa para reproduzir uma consola.\n\nAutodeteção (predefinição) define-a como ativa, se o dispositivo tiver atraso e inativa se não tiver atraso." +Buffering.ToolTip="Quando ativa, esta memória de dados de vídeo/áudio garante a melhor e mais suave reprodução possível, mas com o custo de uma maior latência. Ao usar memória de dados com uma placa de captura de vídeo, para melhores resultados, recomenda-se definir a placa para a mesma taxa de fotogramas.\n\nQuando inativa, garante a menor latência de reprodução, mas com custos na precisão de reprodução de frames. É ideal para câmeras de rosto, ou quando utilizar a janela de antevisão do programa para reproduzir uma consola.\n\nAutodeteção (predefinição) define-a como ativa, se o dispositivo tiver desfasamento e inativa se o não tiver." Buffering.AutoDetect="Deteção automática" Buffering.Enable="Ativar" Buffering.Disable="Desativar" diff --git a/plugins/win-dshow/data/locale/tt-RU.ini b/plugins/win-dshow/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..dd964cc2ec3cb6 --- /dev/null +++ b/plugins/win-dshow/data/locale/tt-RU.ini @@ -0,0 +1,12 @@ +Device="Җайланма" +ColorRange="Төс арасы" +ColorRange.Full="Тулы" +ConfigureVideo="Видеоны көйләү" +Resolution="Ачыклык" +VideoFormat="Видео форматы" +VideoFormat.Unknown="Билгесез (%1)" +AudioDevice="Аудио җайланмасы" +Buffering.Enable="Кушу" +Buffering.Disable="Сүндерү" +Activate="Активлаштыру" +Bitrate="Битрейт" diff --git a/plugins/win-dshow/data/locale/ug-CN.ini b/plugins/win-dshow/data/locale/ug-CN.ini index 393d24f5ce6902..3b5381f239dfee 100644 --- a/plugins/win-dshow/data/locale/ug-CN.ini +++ b/plugins/win-dshow/data/locale/ug-CN.ini @@ -24,7 +24,7 @@ AudioOutputMode.WaveOut="ئۈستەلئۈستى ئۈنىنى چىقار (WaveOut UseCustomAudioDevice="ئىختىيارى ئۈن ئۈسكۈنىسى ئىشلىتىدۇ" AudioDevice="ئۈن ئۈسكۈنىسى" Buffering="غەملەش" -Buffering.ToolTip="قوزغىتىلغاندا، سىن/ئۈن سانلىق مەلۇماتى غەملىنىپ راۋان ۋە ئەڭ توغرا ھالەتتە قويۇشقا كاپالەتلىك قىلىدۇ، ئەمما بەدىلى كېچىكىشنى ئۇزارتىۋېتىدۇ. سىن تۇتۇش كارتىسىنى ئىشلىتىپ غەملىگەندە، سىن تۇتۇش كارتىسى بىلەن پىروگرامما تەڭشىكىنى ئوخشاش كاندۇك نىسبىتىگە تەڭشەش تەۋسىيە قىلىنىدۇ.\nچەكلىگەندە، قويغاندا كېچىكىش ئەڭ ئاز بولىدۇ، ئەمما كاندۇكنى توغرا قويۇشنى قۇربان قىلىدۇ. بۇ چىراي كامېراسى ياكى پىروگراممىنىڭ ئالدىن كۆزىتىش كۆزنىكىدە ئويۇننى قويغاندا كۆڭۈلدىكىدەك تاللاش بولالايدۇ.\nئۆزلۈكىدىن بايقا (كۆڭۈلدىكى) ئۈسكۈنىدە كېچىكىش بار يوقلۇقىغا ئاساسەن غەملەشنى قوزغىتىش ياكى قوزغاتماسلىقنى تەڭشەيدۇ. ئەگەر ئۈسكۈنىدە كېچىكىش بولسا ئۇنداقتا غەملەشنى قوزغىتىدۇ؛ ئەگەر ئۈسكۈنىدە كېچىكىش بولمىسا ئۇنداقتا غەملەشنى چەكلەيدۇ.\n" +Buffering.ToolTip="قوزغىتىلغاندا، سىن/ئۈن سانلىق مەلۇماتى غەملىنىپ راۋان ۋە ئەڭ توغرا ھالەتتە قويۇشقا كاپالەتلىك قىلىدۇ، ئەمما بەدىلى كېچىكىشنى ئۇزارتىۋېتىدۇ. سىن تۇتۇش كارتىسىنى ئىشلىتىپ غەملىگەندە، سىن تۇتۇش كارتىسى بىلەن پىروگرامما تەڭشىكىنى ئوخشاش كاندۇك نىسبىتىگە تەڭشەش تەۋسىيە قىلىنىدۇ.\n\nچەكلىگەندە، قويغاندا كېچىكىش ئەڭ ئاز بولىدۇ، ئەمما كاندۇكنى توغرا قويۇشنى قۇربان قىلىدۇ. بۇ چىراي كامېراسى ياكى پىروگراممىنىڭ ئالدىن كۆزىتىش كۆزنىكىدە ئويۇننى قويغاندا كۆڭۈلدىكىدەك تاللاش بولالايدۇ.\n\nئۆزلۈكىدىن بايقا (كۆڭۈلدىكى) ئۈسكۈنىدە كېچىكىش بار يوقلۇقىغا ئاساسەن غەملەشنى قوزغىتىش ياكى قوزغاتماسلىقنى تەڭشەيدۇ. ئەگەر ئۈسكۈنىدە كېچىكىش بولسا ئۇنداقتا غەملەشنى قوزغىتىدۇ؛ ئەگەر ئۈسكۈنىدە كېچىكىش بولمىسا ئۇنداقتا غەملەشنى چەكلەيدۇ." Buffering.AutoDetect="ئۆزلۈكىدىن بايقا" Buffering.Enable="قوزغات" Buffering.Disable="چەكلە" diff --git a/plugins/win-wasapi/data/locale/ms-MY.ini b/plugins/win-wasapi/data/locale/ms-MY.ini index 06b796fb15c497..3efac5e50dad15 100644 --- a/plugins/win-wasapi/data/locale/ms-MY.ini +++ b/plugins/win-wasapi/data/locale/ms-MY.ini @@ -1,5 +1,6 @@ AudioInput="Tangkap Input Audio" AudioOutput="Tangkap Output Audio" +ApplicationAudioCapture="Aplikasi Tangkapan Audio (BETA)" Device="Peranti" Default="Lalai" UseDeviceTiming="Guna Setem Masa Peranti" diff --git a/plugins/win-wasapi/data/locale/tt-RU.ini b/plugins/win-wasapi/data/locale/tt-RU.ini new file mode 100644 index 00000000000000..b0ec46e7e727e3 --- /dev/null +++ b/plugins/win-wasapi/data/locale/tt-RU.ini @@ -0,0 +1,4 @@ +AudioInput="Керү аудиосы яздыру" +AudioOutput="Чыгыш аудиосы яздыру" +Device="Җайланма" +Window="Тәрәзә" From 9d610316cba4c169a32fb51538ca551fe8e82424 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 7 Jun 2024 03:20:40 -0700 Subject: [PATCH 0199/1073] obs-websocket: Update version to 5.5.0 See commit obsproject/obs-studio@2055104 for the changelog --- plugins/obs-websocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-websocket b/plugins/obs-websocket index d2d4bfb3e78cf2..3b7c1c53815c59 160000 --- a/plugins/obs-websocket +++ b/plugins/obs-websocket @@ -1 +1 @@ -Subproject commit d2d4bfb3e78cf2b02c8e2f5dda1d805eda8d8f32 +Subproject commit 3b7c1c53815c59fb2498caf9831b0f6a0bc35025 From bd36daa39559da8278825c9983c42ef84c942fdf Mon Sep 17 00:00:00 2001 From: jcm Date: Thu, 6 Jun 2024 14:59:55 -0500 Subject: [PATCH 0200/1073] UI: Address logging buffer size discrepancies --- UI/obs-app.cpp | 5 +++-- UI/qt-wrappers.cpp | 2 +- libobs/util/base.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 389f7df5258cc5..9c5a6c31cca9f3 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -350,7 +350,7 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg, static mutex log_mutex; static const char *last_msg_ptr = nullptr; static int last_char_sum = 0; - static char cmp_str[4096]; + static char cmp_str[8192]; static int rep_count = 0; int new_sum = sum_chars(output_str); @@ -376,7 +376,8 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg, } last_msg_ptr = msg; - strcpy(cmp_str, output_str); + strncpy(cmp_str, output_str, sizeof(cmp_str)); + cmp_str[sizeof(cmp_str) - 1] = 0; last_char_sum = new_sum; rep_count = 0; diff --git a/UI/qt-wrappers.cpp b/UI/qt-wrappers.cpp index 51c062b5a40f4c..c91fab5fa6ebbf 100644 --- a/UI/qt-wrappers.cpp +++ b/UI/qt-wrappers.cpp @@ -42,7 +42,7 @@ static inline void OBSErrorBoxva(QWidget *parent, const char *msg, va_list args) { - char full_message[4096]; + char full_message[8192]; vsnprintf(full_message, sizeof(full_message), msg, args); QMessageBox::critical(parent, "Error", full_message); diff --git a/libobs/util/base.c b/libobs/util/base.c index 831ad47cd4a984..1800727b18ec75 100644 --- a/libobs/util/base.c +++ b/libobs/util/base.c @@ -27,7 +27,7 @@ static void *crash_param = NULL; static void def_log_handler(int log_level, const char *format, va_list args, void *param) { - char out[4096]; + char out[8192]; vsnprintf(out, sizeof(out), format, args); switch (log_level) { From 5db2ff9413fbc74a5f1768d3946bc57664ced115 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 7 Jun 2024 18:34:20 +0200 Subject: [PATCH 0201/1073] UI: Remove dead code from multitrack output --- UI/multitrack-video-output.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 320070c9a6ba8c..d10afd8f4d5816 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -144,34 +144,6 @@ create_service(const GoLiveApi::Config &go_live_config, return service; } -static void ensure_directory_exists(std::string &path) -{ - replace(path.begin(), path.end(), '\\', '/'); - - size_t last = path.rfind('/'); - if (last == std::string::npos) - return; - - std::string directory = path.substr(0, last); - os_mkdirs(directory.c_str()); -} - -std::string GetOutputFilename(const std::string &path, const char *format) -{ - std::string strPath; - strPath += path; - - char lastChar = strPath.back(); - if (lastChar != '/' && lastChar != '\\') - strPath += "/"; - - strPath += BPtr{ - os_generate_formatted_filename("flv", false, format)}; - ensure_directory_exists(strPath); - - return strPath; -} - static OBSOutputAutoRelease create_output() { OBSOutputAutoRelease output = obs_output_create( From 512db59c44a70744893ddbaf9ed776e9c910d1ea Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Thu, 6 Jun 2024 14:27:06 -0400 Subject: [PATCH 0202/1073] obs-webrtc: Do not use curl_easy_nextheader API not available in Ubuntu 22.04 which ships 7.81 this API was first available in 7.83 --- plugins/obs-webrtc/whip-output.cpp | 46 +++++++++++++++++------------- plugins/obs-webrtc/whip-utils.h | 40 +++++++++++++------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index 935824ea98a664..8786fc16959589 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -362,7 +362,7 @@ bool WHIPOutput::Connect() } std::string read_buffer; - std::vector location_headers; + std::vector http_headers; auto offer_sdp = std::string(peer_connection->localDescription().value()); @@ -379,9 +379,8 @@ bool WHIPOutput::Connect() CURL *c = curl_easy_init(); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, curl_writefunction); curl_easy_setopt(c, CURLOPT_WRITEDATA, (void *)&read_buffer); - curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, - curl_header_location_function); - curl_easy_setopt(c, CURLOPT_HEADERDATA, (void *)&location_headers); + curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, curl_header_function); + curl_easy_setopt(c, CURLOPT_HEADERDATA, (void *)&http_headers); curl_easy_setopt(c, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(c, CURLOPT_URL, endpoint_url.c_str()); curl_easy_setopt(c, CURLOPT_POST, 1L); @@ -428,7 +427,18 @@ bool WHIPOutput::Connect() long redirect_count = 0; curl_easy_getinfo(c, CURLINFO_REDIRECT_COUNT, &redirect_count); - if (location_headers.size() < static_cast(redirect_count) + 1) { + std::string last_location_header; + size_t location_header_count = 0; + for (auto &http_header : http_headers) { + auto value = value_for_header("location", http_header); + if (value.empty()) + continue; + + location_header_count++; + last_location_header = value; + } + + if (location_header_count < static_cast(redirect_count) + 1) { do_log(LOG_ERROR, "WHIP server did not provide a resource URL via the Location header"); cleanup(); @@ -437,25 +447,21 @@ bool WHIPOutput::Connect() } CURLU *url_builder = curl_url(); - auto last_location_header = location_headers.back(); // Parse Link headers to extract STUN/TURN server configuration URLs - struct curl_header *prev = nullptr; - struct curl_header *h = nullptr; std::vector iceServers; - while ((h = curl_easy_nextheader(c, CURLH_HEADER, 0, prev))) { - if (astrcmpi("Link", h->name) == 0) { - auto value = std::string(h->value); - // Parse multiple links separated by ',' - for (auto end = value.find(","); - end != std::string::npos; end = value.find(",")) { - this->ParseLinkHeader(value.substr(0, end), - iceServers); - value = value.substr(end + 1); - } - this->ParseLinkHeader(value, iceServers); + for (auto &http_header : http_headers) { + auto value = value_for_header("link", http_header); + if (value.empty()) + continue; + + // Parse multiple links separated by ',' + for (auto end = value.find(","); end != std::string::npos; + end = value.find(",")) { + this->ParseLinkHeader(value.substr(0, end), iceServers); + value = value.substr(end + 1); } - prev = h; + this->ParseLinkHeader(value, iceServers); } // If Location header doesn't start with `http` it is a relative URL. diff --git a/plugins/obs-webrtc/whip-utils.h b/plugins/obs-webrtc/whip-utils.h index ed1bd0d1071071..64338d6edd95ce 100644 --- a/plugins/obs-webrtc/whip-utils.h +++ b/plugins/obs-webrtc/whip-utils.h @@ -26,6 +26,22 @@ static std::string trim_string(const std::string &source) return ret; } +static std::string value_for_header(const std::string &header, + const std::string &val) +{ + if (val.size() <= header.size() || + astrcmpi_n(header.c_str(), val.c_str(), header.size()) != 0) { + return ""; + } + + auto delimiter = val.find_first_of(" "); + if (delimiter == std::string::npos) { + return ""; + } + + return val.substr(delimiter + 1); +} + static size_t curl_writefunction(char *data, size_t size, size_t nmemb, void *priv_data) { @@ -37,28 +53,12 @@ static size_t curl_writefunction(char *data, size_t size, size_t nmemb, return real_size; } -#define LOCATION_HEADER_LENGTH 10 - -static size_t curl_header_location_function(char *data, size_t size, - size_t nmemb, void *priv_data) +static size_t curl_header_function(char *data, size_t size, size_t nmemb, + void *priv_data) { auto header_buffer = static_cast *>(priv_data); - - size_t real_size = size * nmemb; - - if (real_size < LOCATION_HEADER_LENGTH) - return real_size; - - if (!astrcmpi_n(data, "location: ", LOCATION_HEADER_LENGTH)) { - char *val = data + LOCATION_HEADER_LENGTH; - auto header_temp = - std::string(val, real_size - LOCATION_HEADER_LENGTH); - - header_temp = trim_string(header_temp); - header_buffer->push_back(header_temp); - } - - return real_size; + header_buffer->push_back(trim_string(std::string(data, size * nmemb))); + return size * nmemb; } static inline std::string generate_user_agent() From e223e7b5cacc34187ea6b6a5606bf2430ba6ba39 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 7 Jun 2024 18:46:19 +0200 Subject: [PATCH 0203/1073] UI: Add config option to use MP4 for debug recording --- UI/multitrack-video-output.cpp | 18 ++++++++++++++---- UI/window-basic-main-outputs.cpp | 14 ++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index d10afd8f4d5816..559b42897f718d 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -161,11 +161,21 @@ static OBSOutputAutoRelease create_output() static OBSOutputAutoRelease create_recording_output(obs_data_t *settings) { - OBSOutputAutoRelease output = obs_output_create( - "flv_output", "flv multitrack video", settings, nullptr); + OBSOutputAutoRelease output; + bool useMP4 = obs_data_get_bool(settings, "use_mp4"); - if (!output) - blog(LOG_ERROR, "Failed to create multitrack video flv output"); + if (useMP4) { + output = obs_output_create("mp4_output", "mp4 multitrack video", + settings, nullptr); + } else { + output = obs_output_create("flv_output", "flv multitrack video", + settings, nullptr); + } + + if (!output) { + blog(LOG_ERROR, "Failed to create multitrack video %s output", + useMP4 ? "mp4" : "flv"); + } return output; } diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index b9a7e3d8ac8f26..fc1a760dbc3ded 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -2854,16 +2854,26 @@ OBSDataAutoRelease BasicOutputHandler::GenerateMultitrackVideoStreamDumpConfig() "FilenameFormatting"); bool overwriteIfExists = config_get_bool(main->Config(), "Output", "OverwriteIfExists"); + bool useMP4 = config_get_bool(main->Config(), "Stream1", + "MultitrackVideoStreamDumpAsMP4"); string f; OBSDataAutoRelease settings = obs_data_create(); f = GetFormatString(filenameFormat, nullptr, nullptr); - string strPath = GetRecordingFilename(path, "flv", noSpace, - overwriteIfExists, f.c_str(), + string strPath = GetRecordingFilename(path, useMP4 ? "mp4" : "flv", + noSpace, overwriteIfExists, + f.c_str(), // never remux stream dump false); obs_data_set_string(settings, "path", strPath.c_str()); + + if (useMP4) { + obs_data_set_bool(settings, "use_mp4", true); + obs_data_set_string(settings, "muxer_settings", + "write_encoder_info=1"); + } + return settings; } From c1fdbf064fa272874b2a0c7981778c2a76b8c152 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 7 Jun 2024 17:40:49 +0200 Subject: [PATCH 0204/1073] UI: Simplify multitrack encoder availability check --- UI/multitrack-video-output.cpp | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 559b42897f718d..80f42126c22718 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -241,30 +241,16 @@ static void adjust_encoder_frame_rate_divisor( obs_encoder_set_frame_rate_divisor(video_encoder, divisor); } -static const std::vector &get_available_encoders() -{ - // encoders are currently only registered during startup, so keeping - // a static vector around shouldn't be a problem - static std::vector available_encoders = [] { - std::vector available_encoders; - for (size_t i = 0;; i++) { - const char *id = nullptr; - if (!obs_enum_encoder_types(i, &id)) - break; - available_encoders.push_back(id); - } - return available_encoders; - }(); - return available_encoders; -} - static bool encoder_available(const char *type) { - auto &encoders = get_available_encoders(); - return std::find_if(std::begin(encoders), std::end(encoders), - [=](const char *encoder) { - return strcmp(type, encoder) == 0; - }) != std::end(encoders); + const char *id = nullptr; + + for (size_t idx = 0; obs_enum_encoder_types(idx, &id); idx++) { + if (strcmp(id, type) == 0) + return true; + } + + return false; } static OBSEncoderAutoRelease create_video_encoder( From 6d3a645bbf88dc5193e7e4e9a5cc27783081cb82 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 7 Jun 2024 22:05:37 +0200 Subject: [PATCH 0205/1073] CI: Restrict PVS-Studio analysis to obsproject repo --- .github/workflows/analyze-project.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/analyze-project.yaml b/.github/workflows/analyze-project.yaml index 4eb7ae92d69713..3bc5d3a8b48005 100644 --- a/.github/workflows/analyze-project.yaml +++ b/.github/workflows/analyze-project.yaml @@ -5,6 +5,7 @@ jobs: windows: name: Windows 🪟 (PVS-Studio) runs-on: windows-2022 + if: github.repository_owner == 'obsproject' defaults: run: shell: pwsh From 28f056882a1332c5139730f0817c1f67fab896ea Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 7 Jun 2024 22:37:55 +0200 Subject: [PATCH 0206/1073] UI: Fix multitrack stream key query parameter concatenation --- UI/multitrack-video-output.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 80f42126c22718..97363bb60d99c7 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -114,23 +114,29 @@ create_service(const GoLiveApi::Config &go_live_config, str->len - (found - str->array)); } + /* The stream key itself may contain query parameters, such as + * "bandwidthtest" that need to be carried over. */ + QUrl parsed_key{stream_key}; + QUrlQuery key_query{parsed_key}; + QUrl parsed_url{url}; QUrlQuery parsed_query{parsed_url}; + for (const auto &[key, value] : key_query.queryItems()) + parsed_query.addQueryItem(key, value); + if (!go_live_config.meta.config_id.empty()) { parsed_query.addQueryItem( "obsConfigId", QString::fromStdString(go_live_config.meta.config_id)); } - auto key_with_param = stream_key; - if (!parsed_query.isEmpty()) - key_with_param += "?" + parsed_query.toString(); + parsed_key.setQuery(parsed_query); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_string(settings, "server", str->array); obs_data_set_string(settings, "key", - key_with_param.toUtf8().constData()); + parsed_key.toString().toUtf8().constData()); auto service = obs_service_create( "rtmp_custom", "multitrack video service", settings, nullptr); From 5ef97920c3c6b2e9b8c75583b17764db2728856d Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 7 Jun 2024 17:55:37 -0400 Subject: [PATCH 0207/1073] libobs: Update version to 30.2.0 --- libobs/obs-config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 21f9c15d7807ed..7f2890d4a5d408 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -34,14 +34,14 @@ * * Reset to zero each major version */ -#define LIBOBS_API_MINOR_VER 1 +#define LIBOBS_API_MINOR_VER 2 /* * Increment if backward-compatible bug fix * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 2 +#define LIBOBS_API_PATCH_VER 0 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From dab4349e05a4c1ea8b6ec6e9e80ba7e41f18b7cb Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 7 Jun 2024 19:34:24 -0400 Subject: [PATCH 0208/1073] CI: Fix incorrect filenames in release creation Follow-up to fb4d65875e27ed58ce545875b2591c46fc324d12. --- .github/workflows/push.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index ad107e55be2f12..ffe91fd8bc9533 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -281,13 +281,13 @@ jobs: "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel.dmg mv -v "${macos_intel_dsym_artifact_name}/"obs-studio-*-macos-intel-dSYMs.tar.xz \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel-dSYMs.tar.xz - mv -v "${ubuntu_2204_x86_64_artifact_name}/"obs-studio-*-x86_64-linux-gnu.deb \ + mv -v "${ubuntu_2204_x86_64_artifact_name}/"obs-studio-*-x86_64-ubuntu-gnu.deb \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-22.04-x86_64.deb - mv -v "${ubuntu_2204_x86_64_debug_name}/"obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb \ + mv -v "${ubuntu_2204_x86_64_debug_name}/"obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-22.04-x86_64-dbsym.ddeb - mv -v "${ubuntu_2404_x86_64_artifact_name}/"obs-studio-*-x86_64-linux-gnu.deb \ + mv -v "${ubuntu_2404_x86_64_artifact_name}/"obs-studio-*-x86_64-ubuntu-gnu.deb \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64.deb - mv -v "${ubuntu_2404_x86_64_debug_name}/"obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb \ + mv -v "${ubuntu_2404_x86_64_debug_name}/"obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64-dbsym.ddeb mv -v "${ubuntu_2404_sources_name}/"obs-studio-*-sources.tar.gz \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz From 30f174b8bb691ffb3abff1b9fde813042b4d0a91 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 8 Jun 2024 19:20:16 +0200 Subject: [PATCH 0209/1073] CI: Fix input variable name when getting Windows release notes --- .github/actions/windows-patches/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml index fb87ed5c7575e0..76a4a3cfd2ab45 100644 --- a/.github/actions/windows-patches/action.yaml +++ b/.github/actions/windows-patches/action.yaml @@ -86,7 +86,7 @@ runs: run: | # Release notes are just the tag body on Windows Set-Location repo - git tag -l --format='%(contents:body)' ${{ inputs.version }} > "${{ github.workspace }}/notes.rst" + git tag -l --format='%(contents:body)' ${{ inputs.tagName }} > "${{ github.workspace }}/notes.rst" - name: Run bouf shell: pwsh From 51e3bd5e3deece2146ea61b12d4fb706d938a58b Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 8 Jun 2024 16:47:21 +0200 Subject: [PATCH 0210/1073] UI: Remove unnecessary string copy from log filter --- UI/obs-app.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 9c5a6c31cca9f3..b2a65b38282cf4 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -350,7 +350,6 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg, static mutex log_mutex; static const char *last_msg_ptr = nullptr; static int last_char_sum = 0; - static char cmp_str[8192]; static int rep_count = 0; int new_sum = sum_chars(output_str); @@ -376,8 +375,6 @@ static inline bool too_many_repeated_entries(fstream &logFile, const char *msg, } last_msg_ptr = msg; - strncpy(cmp_str, output_str, sizeof(cmp_str)); - cmp_str[sizeof(cmp_str) - 1] = 0; last_char_sum = new_sum; rep_count = 0; From f9f4171d56022a3cf74f554802d84960adf78e02 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Sat, 8 Jun 2024 17:44:29 +0200 Subject: [PATCH 0211/1073] UI: Add null checks before doing some API calls Harmless, but generated a debug warning for null pointers passed into the API. --- UI/window-basic-main-transitions.cpp | 3 ++- UI/window-basic-main.cpp | 2 +- UI/window-basic-preview.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 60d1943206fa43..2894c6387b6903 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -445,7 +445,8 @@ void OBSBasic::SetTransition(OBSSource transition) ui->transitionDurationLabel->setVisible(!fixed); ui->transitionDuration->setVisible(!fixed); - bool configurable = obs_source_configurable(transition); + bool configurable = transition ? obs_source_configurable(transition) + : false; ui->transitionRemove->setEnabled(configurable); ui->transitionProps->setEnabled(configurable); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index a931db27a23a0c..220bed16204c7c 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -8639,7 +8639,7 @@ void OBSBasic::UpdateEditMenu() const bool canTransformSingle = videoCount == 1 && totalCount == 1; OBSSceneItem curItem = GetCurrentSceneItem(); - bool locked = obs_sceneitem_locked(curItem); + bool locked = curItem && obs_sceneitem_locked(curItem); ui->actionCopySource->setEnabled(totalCount > 0); ui->actionEditTransform->setEnabled(canTransformSingle && !locked); diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 042dd458a0b6b2..848ce2ad8e2c9c 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -654,7 +654,7 @@ void OBSBasicPreview::mousePressEvent(QMouseEvent *event) void OBSBasicPreview::UpdateCursor(uint32_t &flags) { - if (obs_sceneitem_locked(stretchItem)) { + if (!stretchItem || obs_sceneitem_locked(stretchItem)) { unsetCursor(); return; } From 7a4cb085ba2688a8b8ee80789318ee2aa0d8762c Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 8 Jun 2024 15:42:01 +0200 Subject: [PATCH 0212/1073] cmake: Fix script plugin path on Linux with CMake 3 --- cmake/linux/defaults.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/linux/defaults.cmake b/cmake/linux/defaults.cmake index a495f019816f19..f057b09bdd0d75 100644 --- a/cmake/linux/defaults.cmake +++ b/cmake/linux/defaults.cmake @@ -31,7 +31,7 @@ set(OBS_CMAKE_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake") # Set additional paths used by OBS for self-discovery set(OBS_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/obs-plugins") -set(OBS_SCRIPT_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/obs-scripting") +set(OBS_SCRIPT_PLUGIN_PATH "../${CMAKE_INSTALL_LIBDIR}/obs-scripting") set(OBS_DATA_PATH "${OBS_DATA_DESTINATION}") set(OBS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") From 298e858f635699abe1d48686e170583f79c6d73c Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 8 Jun 2024 17:46:07 +0200 Subject: [PATCH 0213/1073] UI: Improve macOS properties tooltip spacing workaround QMacStyle appears to have an issue where it messes up the positions of some widgets. The previous workaround added extra spacing to force the icon further to the right. Forcing the widget rectangle to be used instead of the one made by the style also fixes this, arguably in a nicer way. See also b760b24ff00e844c34be0d26222976ee99281472 which does this for checkboxes in the source tree. --- UI/properties-view.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 1f9658d06aed76..69f176b1474d10 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -1640,18 +1640,15 @@ void OBSPropertiesView::AddProperty(obs_property_t *property, QHBoxLayout *boxLayout = new QHBoxLayout(newWidget); boxLayout->setContentsMargins(0, 0, 0, 0); boxLayout->setAlignment(Qt::AlignLeft); -#ifdef __APPLE__ - /* TODO: This fixes the issue of tooltip not aligning - * correcty on macOS, the root cause needs further - * investigation. */ - boxLayout->setSpacing(10); -#else boxLayout->setSpacing(0); -#endif + QCheckBox *check = qobject_cast(widget); check->setText(desc); check->setToolTip( obs_property_long_description(property)); +#ifdef __APPLE__ + check->setAttribute(Qt::WA_LayoutUsesWidgetRect); +#endif QLabel *help = new QLabel(check); help->setText(bStr.arg(file)); From 0680b642e9cb85101971e8e03bbcd9b574cff6f7 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 8 Jun 2024 19:13:24 +0200 Subject: [PATCH 0214/1073] UI: Always show chapter marker hotkey --- UI/data/locale/en-US.ini | 2 +- UI/window-basic-main.cpp | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 28a5e28545d3d8..54496d97220b9e 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -736,7 +736,7 @@ Basic.Main.StopRecording="Stop Recording" Basic.Main.PauseRecording="Pause Recording" Basic.Main.UnpauseRecording="Unpause Recording" Basic.Main.SplitFile="Split Recording File" -Basic.Main.AddChapterMarker="Add Chapter Marker" +Basic.Main.AddChapterMarker="Add Chapter Marker (Hybrid MP4 only)" Basic.Main.StoppingRecording="Stopping Recording..." Basic.Main.StopReplayBuffer="Stop Replay Buffer" Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..." diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 220bed16204c7c..b8e9d4f281d9a1 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2889,22 +2889,14 @@ void OBSBasic::CreateHotkeys() this); LoadHotkey(splitFileHotkey, "OBSBasic.SplitFile"); - /* Adding chapters is only supported by the native MP4 output */ - const string_view output_id = - obs_output_get_id(outputHandler->fileOutput); - if (output_id == "mp4_output") { - addChapterHotkey = obs_hotkey_register_frontend( - "OBSBasic.AddChapterMarker", - Str("Basic.Main.AddChapterMarker"), - [](void *, obs_hotkey_id, obs_hotkey_t *, - bool pressed) { - if (pressed) - obs_frontend_recording_add_chapter( - nullptr); - }, - this); - LoadHotkey(addChapterHotkey, "OBSBasic.AddChapterMarker"); - } + addChapterHotkey = obs_hotkey_register_frontend( + "OBSBasic.AddChapterMarker", Str("Basic.Main.AddChapterMarker"), + [](void *, obs_hotkey_id, obs_hotkey_t *, bool pressed) { + if (pressed) + obs_frontend_recording_add_chapter(nullptr); + }, + this); + LoadHotkey(addChapterHotkey, "OBSBasic.AddChapterMarker"); replayBufHotkeys = obs_hotkey_pair_register_frontend( "OBSBasic.StartReplayBuffer", From 14fa71f7499f9b34ea74ef45d8ab98d8ea791b7e Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 8 Jun 2024 23:46:40 +0200 Subject: [PATCH 0215/1073] UI: Connect replay buffer stopping to signal instead of slot --- UI/basic-controls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/basic-controls.cpp b/UI/basic-controls.cpp index 00145281cc1fe2..0f216a3d59d868 100644 --- a/UI/basic-controls.cpp +++ b/UI/basic-controls.cpp @@ -117,7 +117,7 @@ OBSBasicControls::OBSBasicControls(OBSBasic *main) connect(main, &OBSBasic::ReplayBufStarted, this, &OBSBasicControls::ReplayBufferStarted); - connect(main, &OBSBasic::ReplayBufferStopping, this, + connect(main, &OBSBasic::ReplayBufStopping, this, &OBSBasicControls::ReplayBufferStopping); connect(main, &OBSBasic::ReplayBufStopped, this, &OBSBasicControls::ReplayBufferStopped); From 7d559426018ba7d5f90c2e5090d80efee7dfdcdd Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Wed, 5 Jun 2024 19:21:48 -0400 Subject: [PATCH 0216/1073] UI: Clean up Settings widget structure and styling --- UI/data/themes/Yami.obt | 33 +- UI/forms/OBSBasicSettings.ui | 1797 ++++++++++++++++++---------------- UI/window-basic-settings.cpp | 14 + 3 files changed, 968 insertions(+), 876 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 51e85a62a88532..0694674a551b05 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -127,13 +127,14 @@ --padding_base_border: calc(var(--padding_base) + 1px); - --spinbox_button_height: calc(var(--input_height) - 2px); + --spinbox_button_height: calc(var(--input_height_half) - 1px); --volume_slider: calc(calc(6px + var(--font_base_value)) / 2); --volume_slider_box: calc(var(--volume_slider) * 4); --volume_slider_label: calc(var(--volume_slider) * 6); --scrollbar_size: 12px; + --settings_scrollbar_size: calc(var(--scrollbar_size) + 9px); /* Inputs / Controls */ --border_color: var(--grey4); @@ -451,6 +452,16 @@ OBSBasicSettings QListWidget::item { padding: var(--padding_large); } +OBSBasicSettings QScrollBar:vertical { + width: var(--settings_scrollbar_size); + margin-left: 9px; +} + +OBSBasicSettings QScrollBar:horizontal { + height: var(--settings_scrollbar_size); + margin-top: 9px; +} + /* Settings properties view */ OBSBasicSettings #PropertiesContainer { background-color: var(--bg_base); @@ -798,7 +809,6 @@ QDateTimeEdit { border-radius: var(--border_radius); padding: var(--padding_large) var(--padding_large); padding-left: 10px; - max-height: var(--input_height); } QComboBox QAbstractItemView::item:selected, @@ -861,6 +871,7 @@ QPlainTextEdit { border: none; border-radius: var(--border_radius); padding: var(--input_padding) var(--padding_small) var(--input_padding) var(--input_padding); + padding-left: 8px; border: 1px solid var(--input_bg); height: var(--input_height); } @@ -890,10 +901,9 @@ QDoubleSpinBox { background-color: var(--input_bg); border: 1px solid var(--input_bg); border-radius: var(--border_radius); - margin-right: var(--spacing_base); padding: var(--input_padding) 0px var(--input_padding) var(--input_padding); - height: var(--spinbox_button_height); - max-height: var(--spinbox_button_height); + padding-left: 8px; + max-height: var(--input_height); } QSpinBox:hover, @@ -914,10 +924,11 @@ QDoubleSpinBox::up-button { subcontrol-position: top right; /* position at the top right corner */ width: 32px; + height: var(--spinbox_button_height); border-left: 1px solid var(--grey6); border-bottom: 1px solid transparent; border-radius: 0px; - margin-top: -1px; + border-top-right-radius: var(--border_radius_small); } QSpinBox::down-button, @@ -926,10 +937,11 @@ QDoubleSpinBox::down-button { subcontrol-position: bottom right; /* position at the top right corner */ width: 32px; + height: var(--spinbox_button_height); border-left: 1px solid var(--grey6); border-top: 1px solid var(--grey6); border-radius: 0px; - margin-bottom: -1px; + border-bottom-right-radius: var(--border_radius_small); } QSpinBox::up-button:hover, @@ -1384,9 +1396,14 @@ QLabel#errorLabel { font-weight: bold; } -QFrame [themeID="notice"] { +QFrame [noticeFrame="true"] { background: var(--bg_preview); border-radius: var(--border_radius); + padding: var(--padding_xlarge) var(--padding_large); +} + +QFrame [noticeFrame="true"] QLabel { + padding: var(--padding_large) 0px; } /* About dialog */ diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 45af7e598cb486..985f153536e2dc 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -28,7 +28,7 @@ true - + @@ -151,7 +151,7 @@ - 0 + 9 0 @@ -170,6 +170,9 @@ QFrame::Plain + + 0 + true @@ -178,8 +181,8 @@ 0 0 - 764 - 1298 + 755 + 1260 @@ -193,17 +196,20 @@ 0 - 9 + 0 - 9 + 0 0 + + 0 + 0 @@ -213,9 +219,6 @@ Basic.Settings.General - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -277,12 +280,12 @@ Basic.Settings.General.Updater - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 2 + @@ -335,8 +338,8 @@ Basic.Settings.Output - - QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 @@ -420,12 +423,6 @@ true - - - 0 - 0 - - Basic.Settings.General.Snapping @@ -433,9 +430,6 @@ false - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -530,8 +524,8 @@ Basic.Settings.General.Projectors - - QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 @@ -589,8 +583,8 @@ Basic.Settings.General.SysTray - - QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 2 @@ -647,9 +641,6 @@ StudioMode.Preview - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -713,9 +704,6 @@ Basic.Settings.General.Importers - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -751,9 +739,6 @@ Basic.TogglePreviewProgramMode - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -806,9 +791,6 @@ Basic.Settings.General.Multiview - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -877,6 +859,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -889,7 +884,7 @@ - 0 + 9 0 @@ -908,6 +903,9 @@ QFrame::Plain + + 0 + true @@ -916,7 +914,7 @@ 0 0 - 781 + 772 680 @@ -936,6 +934,9 @@ + + 0 + 0 @@ -986,6 +987,19 @@ + + + + Qt::Horizontal + + + + 170 + 0 + + + + @@ -1027,29 +1041,23 @@ - - - 0 - 0 - - - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - 0 + 9 0 + + 0 + 0 - + @@ -1068,7 +1076,7 @@ - + @@ -1111,97 +1119,128 @@ - - - 0 - 0 - - 0 - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 170 - 19 - + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Basic.Settings.Stream.Destination - - - - - - - - Basic.AutoConfig.StreamPage.ConnectAccount - - - - - - - Qt::Horizontal - - - - 40 - 10 - - - - - + + + 2 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 170 + 19 + + + + + + + + + + Basic.AutoConfig.StreamPage.ConnectAccount + + + + + + + Qt::Horizontal + + + + 40 + 10 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 170 + 10 + + + + + + + + + + Basic.AutoConfig.StreamPage.UseStreamKey + + + + + + + Qt::Horizontal + + + + 40 + 10 + + + + + + + + - - + + - Qt::Horizontal - - - QSizePolicy::Fixed + Qt::Vertical - 170 - 19 + 20 + 0 - - - - - - Basic.AutoConfig.StreamPage.UseStreamKey - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -1211,25 +1250,36 @@ QFrame::Plain + + 0 + true - - - 0 - 0 - + + + 0 + 0 + 987 + 809 + + + 0 + + + 0 + + + 0 + + + 0 + - - - 0 - 0 - - Basic.Settings.Stream.Destination @@ -1388,17 +1438,11 @@ - - - Qt::Horizontal - - - - 170 - 0 - + + + Basic.AutoConfig.StreamPage.ConnectedAccount - + @@ -1412,39 +1456,6 @@ - - - - Qt::Horizontal - - - - 40 - 0 - - - - - -
- - - - Basic.AutoConfig.StreamPage.ConnectedAccount - - - - - - - 8 - - - 7 - - - 7 - @@ -1463,7 +1474,7 @@ - + Qt::Horizontal @@ -1477,6 +1488,19 @@ + + + + Qt::Horizontal + + + + 170 + 0 + + + + @@ -1568,22 +1592,10 @@ - - - 0 - 0 - - Basic.Settings.Stream.MultitrackVideoLabel - - - QFormLayout::AllNonFixedFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + 9 @@ -1596,142 +1608,201 @@ 9 - - - - Basic.Settings.Stream.EnableMultitrackVideo - - - - - - - MultitrackVideo.Info - - - Qt::RichText - - - true - - - true - - - - - - - Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate - - - - - + + - - - Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto + + + Qt::Horizontal - - QSizePolicy::Minimum, QSizePolicy::Minimum + + QSizePolicy::Fixed - + + + 170 + 10 + + + - - - QSizePolicy::MinimumExpanding, QSizePolicy::Minimum + + + MultitrackVideo.Info - - 500 + + Qt::RichText - - 1000000 + + true - - 8000 + + true - - - - Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks - - - - - - - + + + + - Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto + Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate - - QSizePolicy::Minimum, QSizePolicy::Minimum + + + + + + + + Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto + + + + + + + + 0 + 0 + + + + 500 + + + 1000000 + + + 8000 + + + + + + + + + Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks - - - - QSizePolicy::MinimumExpanding, QSizePolicy::Minimum + + + + + + + 0 + 0 + + + + Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto + + + + + + + + 0 + 0 + + + + 0 + + + 100 + + + 0 + + + + + + + + + Qt::Horizontal - - 0 + + QSizePolicy::Fixed - - 100 + + + 170 + 10 + - - 0 + + + + + + Basic.Settings.Stream.MultitrackVideoStreamDumpEnable + + + + + + + Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable + + + + + + + Basic.Settings.Stream.MultitrackVideoConfigOverride + + + + + + + + 0 + 0 + + + + + + + + Basic.Settings.Stream.EnableMultitrackVideo + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 170 + 10 + + + + - - - - Basic.Settings.Stream.MultitrackVideoStreamDumpEnable - - - - - - - Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable - - - - - - - Basic.Settings.Stream.MultitrackVideoConfigOverride - - - - - - - QSizePolicy::Preferred, QSizePolicy::MinimumExpanding - - - - - - 0 - 0 - - Basic.Settings.Stream.AdvancedOptions @@ -1810,6 +1881,19 @@ + + + + Qt::Vertical + + + + 20 + 0 + + + + @@ -1848,10 +1932,22 @@ - - notice + + true + + 0 + + + 0 + + + 0 + + + 0 + @@ -1867,24 +1963,18 @@ - - - 0 - 0 - - QFormLayout::AllNonFixedFieldsGrow - 0 + 9 0 - 9 + 0 0 @@ -1964,6 +2054,9 @@ QFrame::Plain + + 0 + true @@ -1972,7 +2065,7 @@ 0 0 - 518 + 509 609 @@ -1990,26 +2083,17 @@ 0 - 9 + 0 0 - - - 0 - 0 - - Basic.Settings.Output.Adv.Streaming - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2199,19 +2283,10 @@ - - - 0 - 0 - - Basic.Settings.Output.Adv.Recording - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2578,9 +2653,6 @@ true - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2684,7 +2756,7 @@ 20 - 10 + 0 @@ -2696,16 +2768,16 @@ - 10 + 9 - 10 + 9 - 10 + 9 - 10 + 9 @@ -2713,9 +2785,6 @@ - - 6 - 0 @@ -2723,7 +2792,7 @@ 0 - 9 + 0 0 @@ -2745,7 +2814,7 @@ 0 - 6 + 9 0 @@ -2761,6 +2830,9 @@ QFrame::Plain + + 0 + true @@ -2769,8 +2841,8 @@ 0 0 - 431 - 180 + 424 + 175 @@ -2781,42 +2853,33 @@ 0 - 9 + 0 0 - - - 0 - 0 - - Basic.Settings.Output.Adv.Streaming.Settings - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - 8 + 9 2 - 8 + 9 - 8 + 9 - + @@ -2832,7 +2895,7 @@ - + @@ -2841,15 +2904,9 @@ - 0 + 1 - - - 0 - 0 - - 0 @@ -2970,34 +3027,28 @@ - + Basic.Settings.Output.Encoder.Audio - + - + Basic.Settings.Output.Encoder.Video - + - + - - - 0 - 0 - - Qt::RightToLeft @@ -3006,7 +3057,7 @@ - + @@ -3028,12 +3079,6 @@ - - - 0 - 0 - - Basic.Settings.Output.Adv.Encoder @@ -3061,7 +3106,7 @@ 20 - 40 + 0 @@ -3077,14 +3122,11 @@ Basic.Settings.Output.Adv.Recording - - 0 - 0 - 0 + 9 0 @@ -3095,9 +3137,6 @@ - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -3105,13 +3144,13 @@ 9 - 6 + 0 0 - 6 + 0 @@ -3202,7 +3241,7 @@ 0 0 - 518 + 509 371 @@ -3220,26 +3259,17 @@ 0 - 9 + 0 0 - - - 0 - 0 - - Basic.Settings.Output.Adv.Recording.Settings - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -3381,7 +3411,7 @@ - 0 + 1 @@ -3799,7 +3829,7 @@ 20 - 40 + 0 @@ -3849,7 +3879,7 @@ 0 0 - 634 + 625 467 @@ -3861,7 +3891,7 @@ 0 - 9 + 0 0 @@ -4296,7 +4326,7 @@ 20 - 40 + 0 @@ -4319,7 +4349,7 @@ 0 - 0 + 9 0 @@ -4338,7 +4368,7 @@ 0 - 0 + 9 0 @@ -4359,7 +4389,7 @@ 0 - 6 + 0 0 @@ -4386,7 +4416,7 @@ 0 0 - 267 + 258 510 @@ -4401,7 +4431,7 @@ 0 - 9 + 0 0 @@ -4418,9 +4448,6 @@ Basic.Settings.Output.Adv.Audio.Track1 - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4558,9 +4585,6 @@ Basic.Settings.Output.Adv.Audio.Track2 - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4698,9 +4722,6 @@ Basic.Settings.Output.Adv.Audio.Track3 - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4838,9 +4859,6 @@ Basic.Settings.Output.Adv.Audio.Track4 - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4978,9 +4996,6 @@ Basic.Settings.Output.Adv.Audio.Track5 - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5118,9 +5133,6 @@ Basic.Settings.Output.Adv.Audio.Track6 - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5256,8 +5268,8 @@ - 10 - 10 + 20 + 0 @@ -5280,7 +5292,7 @@ 0 - 0 + 9 0 @@ -5295,16 +5307,16 @@ - 9 + 0 - 6 + 0 0 - 9 + 0 @@ -5332,6 +5344,9 @@ + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 0 @@ -5342,7 +5357,7 @@ 0 - 8 + 0 @@ -5411,7 +5426,7 @@ 20 - 20 + 0 @@ -5432,7 +5447,7 @@ - 0 + 9 0 @@ -5451,6 +5466,9 @@ QFrame::Plain + + 0 + true @@ -5459,8 +5477,8 @@ 0 0 - 608 - 520 + 590 + 511 @@ -5474,17 +5492,20 @@ 0 - 9 + 0 - 9 + 0 0 + + 0 + 0 @@ -5494,9 +5515,6 @@ Basic.Settings.General - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5606,9 +5624,6 @@ Basic.Settings.Audio.Devices - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5738,9 +5753,6 @@ Basic.Settings.Audio.Meters - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5827,9 +5839,6 @@ Basic.Settings.Advanced - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5861,6 +5870,9 @@ Qt::Horizontal + + QSizePolicy::Expanding + 170 @@ -5885,9 +5897,6 @@ Basic.Settings.Hotkeys - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5946,310 +5955,374 @@ - - - QFormLayout::AllNonFixedFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + 9 0 + + 0 + 0 - - - - - 170 - 0 - - - - Basic.Settings.Video.BaseResolution - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - baseResolution - - - - - - - 6 - - - - - - 0 - 0 - - - - true - - - - - - false - - - true - - - - - - - AspectRatio - - - - - - - - - Basic.Settings.Video.ScaledResolution - - - outputResolution - - - - - - - Basic.Settings.Video.DownscaleFilter - - - downscaleFilter - - - - - - - true - - - - - + + - + 0 0 - - Basic.Settings.Video.FPSCommon - - - QComboBox::AdjustToContents + + Basic.Settings.General - - - Basic.Settings.Video.FPSCommon - - - - - Basic.Settings.Video.FPSInteger + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Basic.Settings.Video.FPSFraction + + 2 - - - - - - - 1 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 10 - - - 0 - - - - 10 + + + + + 170 + 0 + + + + Basic.Settings.Video.BaseResolution + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + baseResolution + + + + + + + 6 + + + + + + 0 + 0 + - - - - 20 + + true - - - - 24 NTSC + + - - - - 25 PAL + + false - - - - 29.97 + + true - - + + + + - 30 + AspectRatio - - - - 48 + + + + + + + + Basic.Settings.Video.ScaledResolution + + + outputResolution + + + + + + + 6 + + + + + + 0 + 0 + - - + + true + + + + + + + + - 50 PAL + AspectRatio + + + + + + + + + Basic.Settings.Video.DownscaleFilter + + + downscaleFilter + + + + + + + + + + Basic.Settings.Video.FPSCommon + + + + Basic.Settings.Video.FPSCommon + + + + + Basic.Settings.Video.FPSInteger + + + + + Basic.Settings.Video.FPSFraction + + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 10 + + + 0 + + + + 10 + + + + + 20 + + + + + 24 NTSC + + + + + 25 PAL + + + + + 29.97 + + + + + 30 + + + + + 48 + + + + + 50 PAL + + + + + 59.94 + + + + + 60 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 120 + + + 30 + + + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 - - - - 59.94 + + 0 - - - - 60 + + 0 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 120 - - - 30 - - - - - - - - - QFormLayout::ExpandingFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1000000 - - - 30 - - - - - - - 1 - - - 1000000 - - - - - - - Basic.Settings.Video.Numerator - - - - - - - Basic.Settings.Video.Denominator - + + 0 + + + + + Basic.Settings.Video.Numerator + + + + + + + 1 + + + 1000000 + + + 30 + + + + + + + Basic.Settings.Video.Denominator + + + + + + + 1 + + + 1000000 + + + + - - - + + + + + + Qt::Horizontal + + + + 170 + 10 + + + + + - + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 0 + + + + + - + 0 0 @@ -6265,36 +6338,6 @@ - - - - 6 - - - - - - 0 - 0 - - - - true - - - - - - - - - - AspectRatio - - - - - @@ -6305,6 +6348,9 @@ 0 + + 0 + 0 @@ -6392,46 +6438,55 @@ 0 0 - 196 - 28 + 178 + 16 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 0 - + - 9 + 0 - 2 + 0 - 9 + 0 - 9 + 0 - - - - - 0 - 0 - - - - Basic.Settings.Hotkeys.PleaseWait - - - false - - - Qt::AlignCenter - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Basic.Settings.Hotkeys.PleaseWait + + + Qt::AlignCenter + + + + @@ -6443,7 +6498,7 @@ - 0 + 9 0 @@ -6462,6 +6517,9 @@ QFrame::Plain + + 0 + true @@ -6470,7 +6528,7 @@ 0 0 - 704 + 696 347 @@ -6490,19 +6548,13 @@ - 9 + 0 0 - - - 0 - 0 - - Basic.Settings.Accessibility.ColorOverrides @@ -7336,7 +7388,7 @@ 20 - 40 + 0 @@ -7352,7 +7404,7 @@ - 0 + 9 0 @@ -7371,6 +7423,9 @@ QFrame::Plain + + 0 + true @@ -7379,8 +7434,8 @@ 0 0 - 713 - 955 + 695 + 952 @@ -7394,17 +7449,20 @@ 0 - 9 + 0 - 9 + 0 0 + + 0 + 0 @@ -7414,9 +7472,6 @@ Basic.Settings.General - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7465,9 +7520,6 @@ Basic.Settings.Video - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7704,9 +7756,6 @@ Basic.Settings.Output.Adv.Recording - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7817,9 +7866,6 @@ Basic.Settings.Advanced.StreamDelay - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7934,9 +7980,6 @@ Basic.Settings.Output.Reconnect - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -8040,9 +8083,6 @@ Basic.Settings.Advanced.Network - - QFormLayout::AllNonFixedFieldsGrow - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -8131,6 +8171,9 @@ Basic.Main.Sources + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 2 @@ -8195,6 +8238,22 @@ + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 0 + + + + @@ -8203,46 +8262,48 @@ - - - 10 - - - 10 - - - 10 - - - 10 - - - - - - - - true - - - error - - - - - - - - - - true - - - error - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + true + + + error + + + + + + + + + + true + + + error + + + + + diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 02b9d1dc8778f3..55cd1a05b4d36f 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -1021,6 +1021,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) UpdateAudioWarnings(); UpdateAdvNetworkGroup(); + + ui->audioMsg->setVisible(false); + ui->advancedMsg->setVisible(false); + ui->advancedMsg2->setVisible(false); } OBSBasicSettings::~OBSBasicSettings() @@ -2836,24 +2840,30 @@ void OBSBasicSettings::UpdateColorFormatSpaceWarning() if ((format == "P010") || (format == "P216") || (format == "P416")) { ui->advancedMsg2->clear(); + ui->advancedMsg2->setVisible(false); } else if (format == "I010") { ui->advancedMsg2->setText( QTStr("Basic.Settings.Advanced.FormatWarning")); + ui->advancedMsg2->setVisible(true); } else { ui->advancedMsg2->setText(QTStr( "Basic.Settings.Advanced.FormatWarning2100")); + ui->advancedMsg2->setVisible(true); } break; default: if (format == "NV12") { ui->advancedMsg2->clear(); + ui->advancedMsg2->setVisible(false); } else if ((format == "I010") || (format == "P010") || (format == "P216") || (format == "P416")) { ui->advancedMsg2->setText(QTStr( "Basic.Settings.Advanced.FormatWarningPreciseSdr")); + ui->advancedMsg2->setVisible(true); } else { ui->advancedMsg2->setText( QTStr("Basic.Settings.Advanced.FormatWarning")); + ui->advancedMsg2->setVisible(true); } } } @@ -4680,6 +4690,8 @@ void OBSBasicSettings::AudioChanged() void OBSBasicSettings::AudioChangedRestart() { + ui->audioMsg->setVisible(false); + if (!loading) { int currentChannelIndex = ui->channelSetup->currentIndex(); int currentSampleRateIndex = ui->sampleRate->currentIndex(); @@ -4691,6 +4703,7 @@ void OBSBasicSettings::AudioChangedRestart() currentLLAudioBufVal != llBufferingEnabled) { ui->audioMsg->setText( QTStr("Basic.Settings.ProgramRestart")); + ui->audioMsg->setVisible(true); } else { ui->audioMsg->setText(""); } @@ -6022,6 +6035,7 @@ void OBSBasicSettings::UpdateAudioWarnings() } ui->audioMsg_2->setText(text); + ui->audioMsg_2->setVisible(!text.isEmpty()); } void OBSBasicSettings::LowLatencyBufferingChanged(bool checked) From 4b187ed38c766070529ad98f816117b2c4f5f1f0 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 9 Jun 2024 10:19:30 +0200 Subject: [PATCH 0217/1073] cmake: Copy shared library soname file to rundir on Linux Soname files are required since 1d8c377240dbeb601c8cc3c22bdd1888f685dcb7 --- cmake/linux/helpers.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/linux/helpers.cmake b/cmake/linux/helpers.cmake index 7446f7f0e3afb2..05808c2b26e566 100644 --- a/cmake/linux/helpers.cmake +++ b/cmake/linux/helpers.cmake @@ -74,6 +74,8 @@ function(set_target_properties_obs target) COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}/" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" + "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}/" COMMENT "Copy ${target} to library directory (${OBS_LIBRARY_DESTINATION})" VERBATIM) From 56d6fb4c6248593093bacad98156cae164df0e6d Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 7 Jun 2024 16:15:32 -0400 Subject: [PATCH 0218/1073] UI: Group horizontal audio mixer buttons --- UI/volume-control.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 06cb587edcfd2b..25410f9174341c 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -257,6 +257,8 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) config->setProperty("themeID", "menuIconSmall"); config->setAutoDefault(false); + config->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + config->setAccessibleName( QTStr("VolControl.Properties").arg(sourceName)); @@ -338,9 +340,10 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) setMaximumWidth(110); } else { QHBoxLayout *textLayout = new QHBoxLayout; + QHBoxLayout *controlLayout = new QHBoxLayout; QFrame *meterFrame = new QFrame; - QHBoxLayout *meterLayout = new QHBoxLayout; - QHBoxLayout *botLayout = new QHBoxLayout; + QVBoxLayout *meterLayout = new QVBoxLayout; + QVBoxLayout *buttonLayout = new QVBoxLayout; volMeter = new VolumeMeter(nullptr, obs_volmeter, false); volMeter->setSizePolicy(QSizePolicy::MinimumExpanding, @@ -362,23 +365,25 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) meterLayout->setContentsMargins(0, 0, 0, 0); meterLayout->setSpacing(0); - if (showConfig) { - meterLayout->addWidget(config); - meterLayout->setAlignment(config, Qt::AlignVCenter); - } meterLayout->addWidget(volMeter); + meterLayout->addWidget(slider); - botLayout->setContentsMargins(0, 0, 0, 0); - botLayout->setSpacing(0); - botLayout->addWidget(mute); - botLayout->addWidget(slider); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->setSpacing(0); - botLayout->setAlignment(slider, Qt::AlignVCenter); - botLayout->setAlignment(mute, Qt::AlignVCenter); + if (showConfig) { + buttonLayout->addWidget(config); + } + buttonLayout->addItem( + new QSpacerItem(0, 0, QSizePolicy::Minimum, + QSizePolicy::MinimumExpanding)); + buttonLayout->addWidget(mute); + + controlLayout->addItem(buttonLayout); + controlLayout->addWidget(meterFrame); mainLayout->addItem(textLayout); - mainLayout->addWidget(meterFrame); - mainLayout->addItem(botLayout); + mainLayout->addItem(controlLayout); volMeter->setFocusProxy(slider); } From 86c337d4e767e319d988c424a241fe1519c6c81b Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 7 Jun 2024 16:15:51 -0400 Subject: [PATCH 0219/1073] UI: Adjust audio mixer slider size and spacing --- UI/data/themes/Yami.obt | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 0694674a551b05..00ad65d241570c 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -129,9 +129,9 @@ --spinbox_button_height: calc(var(--input_height_half) - 1px); - --volume_slider: calc(calc(6px + var(--font_base_value)) / 2); + --volume_slider: calc(calc(10px + var(--font_base_value)) / 4); --volume_slider_box: calc(var(--volume_slider) * 4); - --volume_slider_label: calc(var(--volume_slider) * 6); + --volume_slider_label: calc(var(--volume_slider_box) * 2); --scrollbar_size: 12px; --settings_scrollbar_size: calc(var(--scrollbar_size) + 9px); @@ -1209,7 +1209,7 @@ QSlider::handle:disabled { height: var(--icon_base); background-color: var(--button_bg); padding: var(--padding_base_border) var(--padding_base_border); - margin: 0px var(--spacing_base); + margin: 0px; border: 1px solid var(--button_border); border-radius: var(--border_radius); icon-size: var(--icon_base), var(--icon_base); @@ -1244,12 +1244,12 @@ VolControl #volLabel { /* Horizontal Mixer */ #hMixerScrollArea VolControl { - padding: 0px var(--padding_large); + padding: 0px var(--padding_xlarge) var(--padding_base); border-bottom: 1px solid var(--border_color); } #hMixerScrollArea VolControl QSlider { - margin: 0px 0px; + margin: 0px 0px var(--padding_base); } #hMixerScrollArea VolControl QSlider::groove:horizontal { @@ -1257,10 +1257,6 @@ VolControl #volLabel { height: var(--volume_slider); } -#hMixerScrollArea VolControl QPushButton { - margin-right: var(--padding_xlarge); -} - /* Vertical Mixer */ #stackedMixerArea QScrollBar:vertical { border-left: 1px solid var(--border_color); @@ -1273,13 +1269,14 @@ VolControl #volLabel { #vMixerScrollArea VolControl QSlider { width: var(--volume_slider_box); + margin: 0px var(--padding_xlarge); } #vMixerScrollArea VolControl #volLabel { padding: var(--padding_base) 0px var(--padding_base); min-width: var(--volume_slider_label); max-width: var(--volume_slider_label); - margin-right: 0; + margin-left: var(--padding_xlarge); text-align: center; } @@ -1289,7 +1286,7 @@ VolControl #volLabel { } #vMixerScrollArea VolControl #volMeterFrame { - padding: var(--padding_large) var(--padding_xlarge); + padding: var(--padding_large) var(--padding_xlarge) var(--padding_large) 0px; } #vMixerScrollArea VolControl QLabel { @@ -1603,7 +1600,7 @@ MuteCheckBox::indicator:unchecked { height: var(--icon_base); background-color: var(--button_bg); padding: var(--padding_base_border) var(--padding_base_border); - margin: 0px var(--spacing_base); + margin: 0px; border: 1px solid var(--button_border); border-radius: var(--border_radius); icon-size: var(--icon_base), var(--icon_base); @@ -1613,7 +1610,7 @@ MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { background-color: var(--button_bg_hover); padding: var(--padding_base_border) var(--padding_base_border); - margin: 0px var(--spacing_base); + margin: 0px; border: 1px solid var(--button_border_hover); icon-size: var(--icon_base), var(--icon_base); } From a989fefa0b9d478919b1fcae117bdc227085fbb7 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 7 Jun 2024 16:16:20 -0400 Subject: [PATCH 0220/1073] UI: Fix audio meter and slider blocking mousewheel --- UI/absolute-slider.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/UI/absolute-slider.cpp b/UI/absolute-slider.cpp index 449e2c4906fff8..7efde7c0f59b0e 100644 --- a/UI/absolute-slider.cpp +++ b/UI/absolute-slider.cpp @@ -65,9 +65,6 @@ bool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event) } } - if (event->type() == QEvent::Wheel) - return true; - return QSlider::eventFilter(obj, event); } From c7dc09e8624fa5bae36c755cc82ff86fb16b9623 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 8 Jun 2024 15:43:41 +0200 Subject: [PATCH 0221/1073] UI: Fix mute checkbox positioning on macOS Likely related to QTBUG-2699, QMacStyle appears to screw up the size of this checkbox. Ignoring the style's layout rect fixes this. --- UI/volume-control.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 25410f9174341c..29e068551f50c4 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -249,6 +249,10 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) volLabel->setObjectName("volLabel"); volLabel->setAlignment(Qt::AlignCenter); +#ifdef __APPLE__ + mute->setAttribute(Qt::WA_LayoutUsesWidgetRect); +#endif + QString sourceName = obs_source_get_name(source); setObjectName(sourceName); From 4e13cff8f1529c035b5b29822c45e086360ea610 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Mon, 10 Jun 2024 14:01:31 -0400 Subject: [PATCH 0222/1073] UI: Center widgets in vertical mixer layout --- UI/volume-control.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 29e068551f50c4..ab9b7f095fda4c 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -300,24 +300,16 @@ VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical) controlLayout->setContentsMargins(0, 0, 0, 0); controlLayout->setSpacing(0); - controlLayout->setAlignment(mute, Qt::AlignVCenter); // Add Headphone (audio monitoring) widget here controlLayout->addWidget(mute); - controlLayout->addItem( - new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, - QSizePolicy::Minimum)); if (showConfig) { controlLayout->addWidget(config); - controlLayout->setAlignment(config, Qt::AlignVCenter); } meterLayout->setContentsMargins(0, 0, 0, 0); meterLayout->setSpacing(0); meterLayout->addWidget(slider); - meterLayout->addItem( - new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, - QSizePolicy::Minimum)); meterLayout->addWidget(volMeter); meterFrame->setLayout(meterLayout); From 00c68981ab905a6a5aadf0271ff9fa0fa4bb3c52 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 9 Jun 2024 03:13:06 +0200 Subject: [PATCH 0223/1073] UI: Check VC++ Runtime version on startup --- UI/obs-app.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index b2a65b38282cf4..9a7e9fd1063bd2 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -57,6 +57,7 @@ #ifdef _WIN32 #include #include +#include #else #include #include @@ -2901,6 +2902,35 @@ void OBSApp::commitData(QSessionManager &manager) } #endif +#ifdef _WIN32 +static constexpr char vcRunErrorTitle[] = "Outdated Visual C++ Runtime"; +static constexpr char vcRunErrorMsg[] = + "OBS Studio requires a newer version of the Microsoft Visual C++ " + "Redistributables.\n\nYou will now be directed to the download page."; +static constexpr char vcRunInstallerUrl[] = + "https://obsproject.com/visual-studio-2022-runtimes"; + +static bool vc_runtime_outdated() +{ + win_version_info ver; + if (!get_dll_ver(L"msvcp140.dll", &ver)) + return true; + /* Major is always 14 (hence 140.dll), so we only care about minor. */ + if (ver.minor >= 40) + return false; + + int choice = MessageBoxA(NULL, vcRunErrorMsg, vcRunErrorTitle, + MB_OKCANCEL | MB_ICONERROR | MB_TASKMODAL); + if (choice == IDOK) { + /* Open the URL in the default browser. */ + ShellExecuteA(NULL, "open", vcRunInstallerUrl, NULL, NULL, + SW_SHOWNORMAL); + } + + return true; +} +#endif + int main(int argc, char *argv[]) { #ifndef _WIN32 @@ -2927,6 +2957,9 @@ int main(int argc, char *argv[]) #endif #ifdef _WIN32 + // Abort as early as possible if MSVC runtime is outdated + if (vc_runtime_outdated()) + return 1; // Try to keep this as early as possible install_dll_blocklist_hook(); From c677bac875e6b3b6e4d3358aa8b38679ff4fcb1f Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 9 Jun 2024 02:00:25 +0200 Subject: [PATCH 0224/1073] UI: Avoid allowing to override provided themes --- UI/obs-app-theming.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 6571c9529fee65..669594eff350ed 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -425,6 +425,16 @@ void OBSApp::FindThemes() << "*.oha" // OBS High-contrast Adjustment layer ; + GetDataFilePath("themes/", themeDir); + QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); + while (it.hasNext()) { + OBSTheme *theme = ParseThemeMeta(it.next()); + if (theme && !themes.contains(theme->id)) + themes[theme->id] = std::move(*theme); + else + delete theme; + } + if (GetConfigPath(themeDir.data(), themeDir.capacity(), "obs-studio/themes/") > 0) { QDirIterator it(QT_UTF8(themeDir.c_str()), filters, @@ -439,16 +449,6 @@ void OBSApp::FindThemes() } } - GetDataFilePath("themes/", themeDir); - QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); - while (it.hasNext()) { - OBSTheme *theme = ParseThemeMeta(it.next()); - if (theme && !themes.contains(theme->id)) - themes[theme->id] = std::move(*theme); - else - delete theme; - } - /* Build dependency tree for all themes, removing ones that have items missing. */ QSet invalid; From 86502764b98470ec7deb8ef42bf7f21e25d36dbb Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 9 Jun 2024 23:57:23 +0200 Subject: [PATCH 0225/1073] obs-ffmpeg: Set encoder error message for CUDA errors --- plugins/obs-ffmpeg/obs-nvenc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-nvenc.c b/plugins/obs-ffmpeg/obs-nvenc.c index be735e20294762..62e5a4ca228006 100644 --- a/plugins/obs-ffmpeg/obs-nvenc.c +++ b/plugins/obs-ffmpeg/obs-nvenc.c @@ -270,14 +270,22 @@ static inline bool cuda_error_check(struct nvenc_data *enc, CUresult res, if (res == CUDA_SUCCESS) return true; + struct dstr message = {0}; + const char *name, *desc; if (cuda_get_error_desc(res, &name, &desc)) { - error("%s: CUDA call \"%s\" failed with %s (%d): %s", func, - call, name, res, desc); + dstr_printf(&message, + "%s: CUDA call \"%s\" failed with %s (%d): %s", + func, call, name, res, desc); } else { - error("%s: CUDA call \"%s\" failed with %d", func, call, res); + dstr_printf(&message, "%s: CUDA call \"%s\" failed with %d", + func, call, res); } + error("%s", message.array); + obs_encoder_set_last_error(enc->encoder, message.array); + + dstr_free(&message); return false; } From d2b05a6e0c6a0f51c42f247efd581bed8f4a1877 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 10 Jun 2024 01:28:41 +0200 Subject: [PATCH 0226/1073] CI: Update bouf release in Windows Signing action --- .github/actions/windows-signing/action.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/windows-signing/action.yaml b/.github/actions/windows-signing/action.yaml index e7b175c991cc58..57371b0ddb2668 100644 --- a/.github/actions/windows-signing/action.yaml +++ b/.github/actions/windows-signing/action.yaml @@ -27,9 +27,9 @@ runs: - name: Setup bouf shell: pwsh env: - BOUF_TAG: 'v0.6.3' - BOUF_HASH: '7f1d266467620aa553a705391ee06128e8ee14af66129a0e64a282997fb6fd83' - BOUF_NSIS_HASH: 'a234126de89f122b6a552df3416de3eabcb4195217626c7f4eaec71b20fe36eb' + BOUF_TAG: 'v0.6.3-nsis-update' + BOUF_HASH: 'c5b58f613e7d7ad8090900cbd6b5f4b057528ee9f0b7e4ba52038ddfdd50b924' + BOUF_NSIS_HASH: 'd77cda42d33af77774beddb3fd6edb89e75050f03c174260df7d66bc8247334f' GH_TOKEN: ${{ github.token }} run: | # Download bouf release From 5bc1d31a9896c9b149eec136c8a181b4c94ba35a Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 10 Jun 2024 01:29:05 +0200 Subject: [PATCH 0227/1073] CI: Update windows signing workflow commit --- .github/workflows/push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index ffe91fd8bc9533..4f2fb4d53fa759 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -207,7 +207,7 @@ jobs: sign-windows-build: name: Windows Signing ✍️ - uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@b5b457d7b059397b70f6e3dd09b65e172ad734c3 + uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@d2b05a6e0c6a0f51c42f247efd581bed8f4a1877 if: github.repository_owner == 'obsproject' && github.ref_type == 'tag' needs: build-project permissions: From 051c11e7b2fde964863edf35d10938490a4ce044 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Mon, 10 Jun 2024 18:43:51 -0400 Subject: [PATCH 0228/1073] UI: Translate Multitrack Video error dialog buttons The Yes and No standard buttons are not translated unless we manually set the translated text ourselves. --- UI/multitrack-video-error.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/UI/multitrack-video-error.cpp b/UI/multitrack-video-error.cpp index 036a80f89fdca6..1786131c61a971 100644 --- a/UI/multitrack-video-error.cpp +++ b/UI/multitrack-video-error.cpp @@ -1,6 +1,7 @@ #include "multitrack-video-error.hpp" #include +#include #include "obs-app.hpp" MultitrackVideoError MultitrackVideoError::critical(QString error) @@ -31,9 +32,12 @@ bool MultitrackVideoError::ShowDialog( QTStr("FailedToStartStream.WarningRetryNonMultitrackVideo") .arg(multitrack_video_name)); mb.setIcon(QMessageBox::Warning); - mb.setStandardButtons(QMessageBox::StandardButton::Yes | - QMessageBox::StandardButton::No); - return mb.exec() == QMessageBox::StandardButton::Yes; + QAbstractButton *yesButton = + mb.addButton(QTStr("Yes"), QMessageBox::YesRole); + mb.addButton(QTStr("No"), QMessageBox::NoRole); + mb.exec(); + + return mb.clickedButton() == yesButton; } else if (type == Type::Critical) { mb.setText(error); mb.setIcon(QMessageBox::Critical); From 677905290258d813c5b59e133625dd511e9c2963 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 9 Jun 2024 23:31:44 +0200 Subject: [PATCH 0229/1073] updater: Check and update VC runtime version --- UI/win-update/updater/CMakeLists.txt | 12 ++- UI/win-update/updater/cmake/legacy.cmake | 12 ++- UI/win-update/updater/updater.cpp | 115 ++++++++++------------- 3 files changed, 70 insertions(+), 69 deletions(-) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 1f38168e2e41a8..635e9029ae6674 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -26,8 +26,16 @@ target_compile_definitions(updater PRIVATE NOMINMAX "PSAPI_VERSION=2") target_include_directories(updater PRIVATE "${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_SOURCE_DIR}/UI/win-update") -target_link_libraries(updater PRIVATE OBS::blake2 nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32 - winhttp) +target_link_libraries( + updater + PRIVATE OBS::blake2 + nlohmann_json::nlohmann_json + zstd::libzstd_static + comctl32 + shell32 + version + winhttp + wintrust) # zstd is hardcoded with /DEFAULTLIB:LIBCMT target_link_options(updater PRIVATE /NODEFAULTLIB:LIBCMT) diff --git a/UI/win-update/updater/cmake/legacy.cmake b/UI/win-update/updater/cmake/legacy.cmake index 07f7f1cbb2037b..f394e8d922a8e5 100644 --- a/UI/win-update/updater/cmake/legacy.cmake +++ b/UI/win-update/updater/cmake/legacy.cmake @@ -36,7 +36,15 @@ if(MSVC) target_link_options(updater PRIVATE "LINKER:/IGNORE:4098") endif() -target_link_libraries(updater PRIVATE OBS::blake2 nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32 - winhttp) +target_link_libraries( + updater + PRIVATE OBS::blake2 + nlohmann_json::nlohmann_json + zstd::libzstd_static + comctl32 + shell32 + version + winhttp + wintrust) set_target_properties(updater PROPERTIES FOLDER "frontend") diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index a6ec91fd591048..a18cb2141f2d6b 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -18,6 +18,8 @@ #include "manifest.hpp" #include +#include +#include #include @@ -39,6 +41,9 @@ constexpr const wchar_t *kCDNUpdateBaseUrl = L"https://cdn-fastly.obsproject.com/update_studio"; constexpr const wchar_t *kPatchManifestURL = L"https://obsproject.com/update_studio/getpatchmanifest"; +constexpr const wchar_t *kVSRedistURL = + L"https://aka.ms/vs/17/release/vc_redist.x64.exe"; +constexpr const wchar_t *kMSHostname = L"aka.ms"; /* ----------------------------------------------------------------------- */ @@ -72,44 +77,28 @@ void FreeWinHttpHandle(HINTERNET handle) /* ----------------------------------------------------------------------- */ -static inline bool HasVS2019Redist2() +static bool IsVSRedistOutdated() { - wchar_t base[MAX_PATH]; - wchar_t path[MAX_PATH]; - WIN32_FIND_DATAW wfd; - HANDLE handle; - - SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, base); + VS_FIXEDFILEINFO *info = nullptr; + UINT len = 0; + vector buf; -#define check_dll_installed(dll) \ - do { \ - StringCbCopyW(path, sizeof(path), base); \ - StringCbCatW(path, sizeof(path), L"\\" dll ".dll"); \ - handle = FindFirstFileW(path, &wfd); \ - if (handle == INVALID_HANDLE_VALUE) { \ - return false; \ - } else { \ - FindClose(handle); \ - } \ - } while (false) + const wchar_t vc_dll[] = L"msvcp140"; - check_dll_installed(L"msvcp140"); - check_dll_installed(L"vcruntime140"); - check_dll_installed(L"vcruntime140_1"); + auto size = GetFileVersionInfoSize(vc_dll, nullptr); + if (!size) + return true; -#undef check_dll_installed + buf.resize(size); + if (!GetFileVersionInfo(vc_dll, 0, size, buf.data())) + return true; - return true; -} + bool success = VerQueryValue(buf.data(), L"\\", + reinterpret_cast(&info), &len); + if (!success || !info || !len) + return true; -static bool HasVS2019Redist() -{ - PVOID old = nullptr; - bool redirect = !!Wow64DisableWow64FsRedirection(&old); - bool success = HasVS2019Redist2(); - if (redirect) - Wow64RevertWow64FsRedirection(old); - return success; + return LOWORD(info->dwFileVersionMS) < 40; } static void Status(const wchar_t *fmt, ...) @@ -1139,7 +1128,7 @@ try { return false; } -static bool UpdateVS2019Redists(const string &vc_redist_hash) +static bool UpdateVSRedists() { /* ------------------------------------------ * * Initialize session */ @@ -1153,7 +1142,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash) WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) { - Status(L"Update failed: Couldn't open obsproject.com"); + Status(L"VC Redist Update failed: Couldn't create session"); return false; } @@ -1163,10 +1152,10 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash) WinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION, (LPVOID)&compressionFlags, sizeof(compressionFlags)); - HttpHandle hConnect = WinHttpConnect(hSession, kCDNHostname, + HttpHandle hConnect = WinHttpConnect(hSession, kMSHostname, INTERNET_DEFAULT_HTTPS_PORT, 0); if (!hConnect) { - Status(L"Update failed: Couldn't connect to %S", kCDNHostname); + Status(L"Update failed: Couldn't connect to %S", kMSHostname); return false; } @@ -1180,54 +1169,50 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash) /* ------------------------------------------ * * Download redist */ - Status(L"Downloading Visual C++ 2019 Redistributable"); - - wstring sourceURL = - L"https://cdn-fastly.obsproject.com/downloads/VC_redist.x64.exe"; + Status(L"Downloading Visual C++ Redistributable"); wstring destPath; destPath += tempPath; destPath += L"\\VC_redist.x64.exe"; - if (!HTTPGetFile(hConnect, sourceURL.c_str(), destPath.c_str(), + if (!HTTPGetFile(hConnect, kVSRedistURL, destPath.c_str(), L"Accept-Encoding: gzip", &responseCode)) { DeleteFile(destPath.c_str()); Status(L"Update failed: Could not download " L"%s (error code %d)", - L"Visual C++ 2019 Redistributable", responseCode); + L"Visual C++ Redistributable", responseCode); return false; } /* ------------------------------------------ * - * Get expected hash */ + * Verify file signature */ - B2Hash expectedHash; - StringToHash(vc_redist_hash, expectedHash); + GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2; - /* ------------------------------------------ * - * Get download hash */ + WINTRUST_FILE_INFO fileInfo = {}; + fileInfo.cbStruct = sizeof(fileInfo); + fileInfo.pcwszFilePath = destPath.c_str(); - B2Hash downloadHash; - if (!CalculateFileHash(destPath.c_str(), downloadHash)) { - DeleteFile(destPath.c_str()); - Status(L"Update failed: Couldn't verify integrity of %s", - L"Visual C++ 2019 Redistributable"); - return false; - } + WINTRUST_DATA data = {}; + data.cbStruct = sizeof(data); + data.dwUIChoice = WTD_UI_NONE; + data.dwUnionChoice = WTD_CHOICE_FILE; + data.dwStateAction = WTD_STATEACTION_VERIFY; + data.pFile = &fileInfo; - /* ------------------------------------------ * - * If hashes do not match, integrity failed */ + LONG result = WinVerifyTrust(nullptr, &action, &data); - if (downloadHash == expectedHash) { + if (result != ERROR_SUCCESS) { + Status(L"Update failed: Signature verification failed for " + L"%s (error code %d / %d)", + L"Visual C++ Redistributable", result, GetLastError()); DeleteFile(destPath.c_str()); - Status(L"Update failed: Couldn't verify integrity of %s", - L"Visual C++ 2019 Redistributable"); return false; } /* ------------------------------------------ * - * If hashes match, install redist */ + * If verification succeeded, install redist */ wchar_t commandline[MAX_PATH + MAX_PATH]; StringCbPrintf(commandline, sizeof(commandline), @@ -1241,7 +1226,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash) nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi); if (success) { - Status(L"Installing %s...", L"Visual C++ 2019 Redistributable"); + Status(L"Installing %s...", L"Visual C++ Redistributable"); CloseHandle(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); @@ -1249,7 +1234,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash) } else { Status(L"Update failed: Could not execute " L"%s (error code %d)", - L"Visual C++ 2019 Redistributable", (int)GetLastError()); + L"Visual C++ Redistributable", (int)GetLastError()); } DeleteFile(destPath.c_str()); @@ -1514,10 +1499,10 @@ static bool Update(wchar_t *cmdLine) } /* ------------------------------------- * - * Check for VS2019 redistributables */ + * Check VS redistributables version */ - if (!HasVS2019Redist()) { - if (!UpdateVS2019Redists(manifest.vc2019_redist_x64)) { + if (IsVSRedistOutdated()) { + if (!UpdateVSRedists()) { return false; } } From 0cfae862cd8e9d826bfdb793cc9877374fd9229b Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 9 Jun 2024 23:35:40 +0200 Subject: [PATCH 0230/1073] updater: Compile with static VC runtime --- UI/win-update/updater/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 635e9029ae6674..1438172d519a17 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -40,4 +40,8 @@ target_link_libraries( # zstd is hardcoded with /DEFAULTLIB:LIBCMT target_link_options(updater PRIVATE /NODEFAULTLIB:LIBCMT) -set_target_properties(updater PROPERTIES FOLDER frontend OUTPUT_NAME updater) +set_target_properties( + updater + PROPERTIES FOLDER frontend + OUTPUT_NAME updater + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") From 0f4e33c33e1627aa0dc6ca21519c19e724973ca9 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 7 Jun 2024 15:41:36 +0200 Subject: [PATCH 0231/1073] UI: Discard state if stream attempt is canceled Repro steps: 1. Enable an incompatible setting in settings 2. Start stream, hit cancel on incompatible settings dialog 3. Disable incompatible setting in settings 4. Start stream -> crash --- UI/multitrack-video-output.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 97363bb60d99c7..09f723969ac0ff 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -688,6 +688,9 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings( return true; } + MultitrackVideoOutput::ReleaseOnMainThread(take_current()); + MultitrackVideoOutput::ReleaseOnMainThread(take_current_stream_dump()); + return false; } From cfd692ca153b8c8c5b4f21576872baf34f6037db Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 11 Jun 2024 16:30:10 -0400 Subject: [PATCH 0232/1073] updater: Fix defaultlib conflict Trying to build in Debug fails due a default lib conflict: LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library Instead, we can set the updater to always use /MT instead of /MTd. --- UI/win-update/updater/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 1438172d519a17..88491015e161a9 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -44,4 +44,4 @@ set_target_properties( updater PROPERTIES FOLDER frontend OUTPUT_NAME updater - MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + MSVC_RUNTIME_LIBRARY "MultiThreaded") From 21adf0930f232e48021f61890e182f36e7030a71 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 11 Jun 2024 20:24:28 +0200 Subject: [PATCH 0233/1073] UI: Prevent recursion in theme dependencies --- UI/obs-app-theming.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 669594eff350ed..514f1c414adfc1 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -485,6 +485,16 @@ void OBSApp::FindThemes() break; } + if (parent->id == theme.id || + theme.dependencies.contains(parent->id)) { + blog(LOG_ERROR, + R"(Dependency chain of "%s" ("%s") contains recursion!)", + QT_TO_UTF8(theme.id), + QT_TO_UTF8(parent->id)); + invalid.insert(theme.id); + break; + } + /* Mark this theme as a variant of first parent that is a base theme. */ if (!theme.isBaseTheme && parent->isBaseTheme && theme.parent.isEmpty()) From e454f488aa8da1b2d22fae3062d94eb592d90f36 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 11 Jun 2024 19:05:02 -0400 Subject: [PATCH 0234/1073] obs-websocket: Update version to 5.5.1 Changelog: obsproject/obs-websocket@f8bc7c4f593c7957ecdb45a24faaff3e95f24e8b --- plugins/obs-websocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-websocket b/plugins/obs-websocket index 3b7c1c53815c59..f8bc7c4f593c79 160000 --- a/plugins/obs-websocket +++ b/plugins/obs-websocket @@ -1 +1 @@ -Subproject commit 3b7c1c53815c59fb2498caf9831b0f6a0bc35025 +Subproject commit f8bc7c4f593c7957ecdb45a24faaff3e95f24e8b From 4a46d2d7226f5cbf4b28545fbc77aefbb995d22b Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 12 Jun 2024 22:03:23 +0200 Subject: [PATCH 0235/1073] UI: Fix themeDir buffer being resized incorrectly c677bac875e6b3b6e4d3358aa8b38679ff4fcb1f changed the order here, but this also resulted in the string having whatever size was necessary for the install data path, rather than being large enough to fit a userdata path. To fix this, move the resize operaetion *after* the buit-in themes are searched, and also bump it to 1024 just to be sure. This resulted in a crash due to a bug in os_get_path_internal() which will need to be fixed separately. --- UI/obs-app-theming.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 514f1c414adfc1..8e79bcc05571f2 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -417,7 +417,6 @@ static vector ParseThemeVariables(const char *themeData) void OBSApp::FindThemes() { string themeDir; - themeDir.resize(512); QStringList filters; filters << "*.obt" // OBS Base Theme @@ -435,6 +434,7 @@ void OBSApp::FindThemes() delete theme; } + themeDir.resize(1024); if (GetConfigPath(themeDir.data(), themeDir.capacity(), "obs-studio/themes/") > 0) { QDirIterator it(QT_UTF8(themeDir.c_str()), filters, From 718bd0b265d2844e1d5f3ef604bc406bea73ce74 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 12 Jun 2024 23:57:50 +0200 Subject: [PATCH 0236/1073] UI: Use unique_ptr for theme objects --- UI/obs-app-theming.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 8e79bcc05571f2..790235b149abf6 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -417,6 +417,7 @@ static vector ParseThemeVariables(const char *themeData) void OBSApp::FindThemes() { string themeDir; + unique_ptr theme; QStringList filters; filters << "*.obt" // OBS Base Theme @@ -427,11 +428,9 @@ void OBSApp::FindThemes() GetDataFilePath("themes/", themeDir); QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); while (it.hasNext()) { - OBSTheme *theme = ParseThemeMeta(it.next()); + theme.reset(ParseThemeMeta(it.next())); if (theme && !themes.contains(theme->id)) themes[theme->id] = std::move(*theme); - else - delete theme; } themeDir.resize(1024); @@ -441,11 +440,9 @@ void OBSApp::FindThemes() QDir::Files); while (it.hasNext()) { - OBSTheme *theme = ParseThemeMeta(it.next()); + theme.reset(ParseThemeMeta(it.next())); if (theme && !themes.contains(theme->id)) themes[theme->id] = std::move(*theme); - else - delete theme; } } From 8fcdfb815fdf78911b7f0c9f8f8d7e92e81a6cb0 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 12 Jun 2024 19:04:24 -0400 Subject: [PATCH 0237/1073] CI: Remove redundant checkout step in Windows Patches job --- .github/workflows/publish.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 0b023e3f94d096..96c554f0045dbf 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -180,7 +180,6 @@ jobs: if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag) runs-on: windows-latest steps: - - uses: actions/checkout@v4 - uses: ./.github/actions/windows-patches with: tagName: ${{ github.ref_name }} From 0ea90380b4c128e46b1df96cf2534771825f426c Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 12 Jun 2024 19:40:58 -0400 Subject: [PATCH 0238/1073] Revert "CI: Remove redundant checkout step in Windows Patches job" This reverts commit 8fcdfb815fdf78911b7f0c9f8f8d7e92e81a6cb0. This checkout is not redundant. It is required so that the windows-patches action files can actually be found. --- .github/workflows/publish.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 96c554f0045dbf..0b023e3f94d096 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -180,6 +180,7 @@ jobs: if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag) runs-on: windows-latest steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/windows-patches with: tagName: ${{ github.ref_name }} From 70307a5d255a1320b15315b39ab54805390f26d4 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 12 Jun 2024 21:44:44 -0400 Subject: [PATCH 0239/1073] CI: Add updateChannel to check-tag in Publish workflow This was missing even though we try to set it later. --- .github/workflows/publish.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 0b023e3f94d096..6efe0abb9a011b 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -20,6 +20,7 @@ jobs: outputs: validTag: ${{ steps.check.outputs.validTag }} flatpakMatrix: ${{ steps.check.outputs.flatpakMatrix }} + updateChannel: ${{ steps.check.outputs.updateChannel }} steps: - name: Check Release Tag ☑️ id: check From 77d31fa33fa25fc3b66f823776ddbd3c1b9b3c96 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 12 Jun 2024 22:25:19 -0400 Subject: [PATCH 0240/1073] UI: Restore visibility of Advanced Settings warning The warning message when changing Advanced settings that require a restart was lost. Restore it. Amends commit 7d559426018ba7d5f90c2e5090d80efee7dfdcdd. --- UI/window-basic-settings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 55cd1a05b4d36f..10ff33f764f807 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -4829,10 +4829,13 @@ void RestrictResetBitrates(initializer_list boxes, int maxbitrate) void OBSBasicSettings::AdvancedChangedRestart() { + ui->advancedMsg->setVisible(false); + if (!loading) { advancedChanged = true; ui->advancedMsg->setText( QTStr("Basic.Settings.ProgramRestart")); + ui->advancedMsg->setVisible(true); sender()->setProperty("changed", QVariant(true)); EnableApplyButton(true); } From ce4171908b90f5392c7042ce0b55e1107918c6dd Mon Sep 17 00:00:00 2001 From: Andrew Francis Date: Tue, 4 Jun 2024 00:14:54 -0700 Subject: [PATCH 0241/1073] UI: Remove default values from multitrack-video.hpp These are always overridden in goliveapi-postdata.cpp Co-authored-by: Ruwen Hahn --- UI/models/multitrack-video.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index 66c6d9eeb48d11..00045ebad15ebd 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -202,8 +202,8 @@ struct Preferences { }; struct PostData { - string service = "IVS"; - string schema_version = "2023-05-10"; + string service; + string schema_version; string authentication; Capabilities capabilities; From 14d2c80560206cab2d5c810d77a14d3a324d4ebd Mon Sep 17 00:00:00 2001 From: Andrew Francis Date: Mon, 3 Jun 2024 22:25:39 -0700 Subject: [PATCH 0242/1073] UI: Re-arrange multitrack-video schema to version 2024-06-04 Co-authored-by: Ruwen Hahn --- UI/goliveapi-postdata.cpp | 21 +++++------ UI/models/multitrack-video.hpp | 63 +++++++++++---------------------- UI/multitrack-video-output.cpp | 29 +++++++-------- UI/window-basic-auto-config.cpp | 13 +++++-- 4 files changed, 56 insertions(+), 70 deletions(-) diff --git a/UI/goliveapi-postdata.cpp b/UI/goliveapi-postdata.cpp index 090fec75d4fb4e..27a62e0dd44886 100644 --- a/UI/goliveapi-postdata.cpp +++ b/UI/goliveapi-postdata.cpp @@ -14,29 +14,30 @@ constructGoLivePost(QString streamKey, { GoLiveApi::PostData post_data{}; post_data.service = "IVS"; - post_data.schema_version = "2023-05-10"; + post_data.schema_version = "2024-06-04"; post_data.authentication = streamKey.toStdString(); system_info(post_data.capabilities); - auto &client = post_data.capabilities.client; + auto &client = post_data.client; client.name = "obs-studio"; client.version = obs_get_version_string(); - client.vod_track_audio = vod_track_enabled; + + auto &preferences = post_data.preferences; + preferences.vod_track_audio = vod_track_enabled; obs_video_info ovi; if (obs_get_video_info(&ovi)) { - client.width = ovi.output_width; - client.height = ovi.output_height; - client.fps_numerator = ovi.fps_num; - client.fps_denominator = ovi.fps_den; + preferences.width = ovi.output_width; + preferences.height = ovi.output_height; + preferences.framerate.numerator = ovi.fps_num; + preferences.framerate.denominator = ovi.fps_den; - client.canvas_width = ovi.base_width; - client.canvas_height = ovi.base_height; + preferences.canvas_width = ovi.base_width; + preferences.canvas_height = ovi.base_height; } - auto &preferences = post_data.preferences; if (maximum_aggregate_bitrate.has_value()) preferences.maximum_aggregate_bitrate = maximum_aggregate_bitrate.value(); diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index 00045ebad15ebd..4999bd3911391b 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -109,18 +109,8 @@ using json = nlohmann::json; struct Client { string name = "obs-studio"; string version; - bool vod_track_audio; - uint32_t width; - uint32_t height; - uint32_t fps_numerator; - uint32_t fps_denominator; - uint32_t canvas_width; - uint32_t canvas_height; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version, vod_track_audio, - width, height, fps_numerator, - fps_denominator, canvas_width, - canvas_height) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version) }; struct Cpu { @@ -182,23 +172,30 @@ struct System { }; struct Capabilities { - Client client; Cpu cpu; Memory memory; optional gaming_features; System system; optional> gpu; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, client, cpu, memory, + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, cpu, memory, gaming_features, system, gpu) }; struct Preferences { optional maximum_aggregate_bitrate; optional maximum_video_tracks; + bool vod_track_audio; + uint32_t width; + uint32_t height; + media_frames_per_second framerate; + uint32_t canvas_width; + uint32_t canvas_height; NLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate, - maximum_video_tracks) + maximum_video_tracks, vod_track_audio, + width, height, framerate, canvas_width, + canvas_height) }; struct PostData { @@ -206,11 +203,12 @@ struct PostData { string schema_version; string authentication; + Client client; Capabilities capabilities; Preferences preferences; NLOHMANN_DEFINE_TYPE_INTRUSIVE(PostData, service, schema_version, - authentication, capabilities, + authentication, client, capabilities, preferences) }; @@ -259,47 +257,29 @@ struct VideoEncoderConfiguration { string type; uint32_t width; uint32_t height; - uint32_t bitrate; optional framerate; optional gpu_scale_type; + json settings; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VideoEncoderConfiguration, type, width, height, - bitrate, framerate, - gpu_scale_type) + framerate, gpu_scale_type, + settings) }; struct AudioEncoderConfiguration { string codec; uint32_t track_id; uint32_t channels; - uint32_t bitrate; + json settings; NLOHMANN_DEFINE_TYPE_INTRUSIVE(AudioEncoderConfiguration, codec, - track_id, channels, bitrate) -}; - -template struct EncoderConfiguration { - T config; - json data; - - friend void to_json(nlohmann::json &nlohmann_json_j, - const EncoderConfiguration &nlohmann_json_t) - { - nlohmann_json_j = nlohmann_json_t.data; - to_json(nlohmann_json_j, nlohmann_json_t.config); - } - friend void from_json(const nlohmann::json &nlohmann_json_j, - EncoderConfiguration &nlohmann_json_t) - { - nlohmann_json_t.data = nlohmann_json_j; - nlohmann_json_j.get_to(nlohmann_json_t.config); - } + track_id, channels, settings) }; struct AudioConfigurations { - std::vector> live; - std::vector> vod; + std::vector live; + std::vector vod; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live, vod) @@ -309,8 +289,7 @@ struct Config { Meta meta; optional status; std::vector ingest_endpoints; - std::vector> - encoder_configurations; + std::vector encoder_configurations; AudioConfigurations audio_configurations; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, meta, status, diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 09f723969ac0ff..29b92f0d54a34e 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -259,12 +259,11 @@ static bool encoder_available(const char *type) return false; } -static OBSEncoderAutoRelease create_video_encoder( - DStr &name_buffer, size_t encoder_index, - const GoLiveApi::EncoderConfiguration< - GoLiveApi::VideoEncoderConfiguration> &encoder_config) +static OBSEncoderAutoRelease +create_video_encoder(DStr &name_buffer, size_t encoder_index, + const GoLiveApi::VideoEncoderConfiguration &encoder_config) { - auto encoder_type = encoder_config.config.type.c_str(); + auto encoder_type = encoder_config.type.c_str(); if (!encoder_available(encoder_type)) { blog(LOG_ERROR, "Encoder type '%s' not available", encoder_type); @@ -276,8 +275,8 @@ static OBSEncoderAutoRelease create_video_encoder( dstr_printf(name_buffer, "multitrack video video encoder %zu", encoder_index); - OBSDataAutoRelease encoder_settings = - obs_data_create_from_json(encoder_config.data.dump().c_str()); + OBSDataAutoRelease encoder_settings = obs_data_create_from_json( + encoder_config.settings.dump().c_str()); obs_data_set_bool(encoder_settings, "disable_scenecut", true); OBSEncoderAutoRelease video_encoder = obs_video_encoder_create( @@ -301,22 +300,19 @@ static OBSEncoderAutoRelease create_video_encoder( .arg(name_buffer->array, encoder_type)); } - adjust_video_encoder_scaling(ovi, video_encoder, encoder_config.config, + adjust_video_encoder_scaling(ovi, video_encoder, encoder_config, encoder_index); - adjust_encoder_frame_rate_divisor(ovi, video_encoder, - encoder_config.config, encoder_index); + adjust_encoder_frame_rate_divisor(ovi, video_encoder, encoder_config, + encoder_index); return video_encoder; } static OBSEncoderAutoRelease create_audio_encoder(const char *name, const char *audio_encoder_id, - uint32_t audio_bitrate, + obs_data_t *settings, size_t mixer_idx) { - OBSDataAutoRelease settings = obs_data_create(); - obs_data_set_int(settings, "bitrate", audio_bitrate); - OBSEncoderAutoRelease audio_encoder = obs_audio_encoder_create( audio_encoder_id, name, settings, mixer_idx, nullptr); if (!audio_encoder) { @@ -758,10 +754,11 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config, for (size_t i = 0; i < configs.size(); i++) { dstr_printf(encoder_name_buffer, "%s %zu", name_prefix, i); + OBSDataAutoRelease settings = obs_data_create_from_json( + configs[i].settings.dump().c_str()); OBSEncoderAutoRelease audio_encoder = create_audio_encoder(encoder_name_buffer->array, - audio_encoder_id, - configs[i].config.bitrate, + audio_encoder_id, settings, mixer_idx); obs_output_set_audio_encoder(output, audio_encoder, output_encoder_index); diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index da2ecb0cdc705b..243dce9720f3f1 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -456,8 +456,17 @@ bool AutoConfigStreamPage::validatePage() int multitrackVideoBitrate = 0; for (auto &encoder_config : config.encoder_configurations) { - multitrackVideoBitrate += - encoder_config.config.bitrate; + auto it = + encoder_config.settings.find("bitrate"); + if (it == encoder_config.settings.end()) + continue; + + if (!it->is_number_integer()) + continue; + + int bitrate = 0; + it->get_to(bitrate); + multitrackVideoBitrate += bitrate; } // grab a streamkey from the go live config if we can From b1643c2ac945f678ff8c30576c348a16999c6105 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 14 Jun 2024 02:26:24 -0400 Subject: [PATCH 0243/1073] UI: Add unit suffix to Multitrack Video bitrate limit control --- UI/forms/OBSBasicSettings.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 985f153536e2dc..2c14032b0484c7 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1670,6 +1670,9 @@ 0 + + Kbps + 500 From 021adac2d0c258be64117d35eebda23d48f62454 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 13 Jun 2024 15:50:10 -0400 Subject: [PATCH 0244/1073] UI: Fix submenu arrow indicator position in menus Commit b11d61c89f822502f8c543304a7c32081a44a2c7 added padding-right to provide some minimal padding for this element. This was seemingly accounted for in the Yami Base Theme (Yami.obt), but was missed in the Yami Classic Variant Theme (Yami_Classic.ovt). Re-add the padding-right to restore the padding. --- UI/data/themes/Yami_Classic.ovt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index 828bec6efa26b3..ebd73bec708f1a 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -113,6 +113,10 @@ QMenu::item { padding: var(--padding_menu_y) var(--padding_menu); } +QMenu::item { + padding-right: 20px; +} + QGroupBox { background: var(--bg_window); border: 1px solid var(--border_color); From 40bf8b3c067663f7729831e30a394405c685ecfc Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 14 Jun 2024 15:42:04 +0200 Subject: [PATCH 0245/1073] UI: Make `audio_configurations.vod` optional This is only required if VOD is supported by the service and VOD track is enabled; otherwise there's no need to supply the key or an empty array --- UI/models/multitrack-video.hpp | 2 +- UI/multitrack-video-output.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index 4999bd3911391b..0613fc1133c343 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -279,7 +279,7 @@ struct AudioEncoderConfiguration { struct AudioConfigurations { std::vector live; - std::vector vod; + optional> vod; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live, vod) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 29b92f0d54a34e..c7f1751655b8a7 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -777,8 +777,10 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config, if (!vod_track_mixer.has_value()) return; + // we already check for empty inside of `create_encoders` + encoder_configs_type empty = {}; create_encoders("multitrack video vod audio", - go_live_config.audio_configurations.vod, + go_live_config.audio_configurations.vod.value_or(empty), *vod_track_mixer); return; From b16516a3fa6a15f1f949c859af1b4fe46599902a Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 13 Jun 2024 11:29:31 +0200 Subject: [PATCH 0246/1073] Revert "cmake: Fix script plugin path on Linux with CMake 3" This reverts commit 7a4cb085ba2688a8b8ee80789318ee2aa0d8762c. --- cmake/linux/defaults.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/linux/defaults.cmake b/cmake/linux/defaults.cmake index f057b09bdd0d75..a495f019816f19 100644 --- a/cmake/linux/defaults.cmake +++ b/cmake/linux/defaults.cmake @@ -31,7 +31,7 @@ set(OBS_CMAKE_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake") # Set additional paths used by OBS for self-discovery set(OBS_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/obs-plugins") -set(OBS_SCRIPT_PLUGIN_PATH "../${CMAKE_INSTALL_LIBDIR}/obs-scripting") +set(OBS_SCRIPT_PLUGIN_PATH "${CMAKE_INSTALL_LIBDIR}/obs-scripting") set(OBS_DATA_PATH "${OBS_DATA_DESTINATION}") set(OBS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") From 508f9c2e3cdc3e249e49502420777dafc9c52a06 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 13 Jun 2024 14:46:52 +0200 Subject: [PATCH 0247/1073] obs-scripting: Refactor Lua C paths --- deps/obs-scripting/obs-scripting-lua.c | 30 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/deps/obs-scripting/obs-scripting-lua.c b/deps/obs-scripting/obs-scripting-lua.c index 585f7b11fbffcf..b0cb540e746b0c 100644 --- a/deps/obs-scripting/obs-scripting-lua.c +++ b/deps/obs-scripting/obs-scripting-lua.c @@ -39,13 +39,11 @@ #define SO_EXT "so" #endif -static const char *startup_script_template = - "\ +static const char *startup_script_template = "\ for val in pairs(package.preload) do\n\ package.preload[val] = nil\n\ end\n\ -package.cpath = package.cpath .. \";\" .. \"%s/?." SO_EXT - "\" .. \";\" .. \"%s\" .. \"/?." SO_EXT "\"\n\ +%s\ require \"obslua\"\n"; static const char *get_script_path_func = "\ @@ -1345,6 +1343,11 @@ void obs_lua_script_save(obs_script_t *s) /* -------------------------------------------- */ +static inline void add_package_cpath(struct dstr *cpath, const char *path) +{ + dstr_catf(cpath, " .. \";\" .. \"%s\" .. \"/?." SO_EXT "\"", path); +} + void obs_lua_load(void) { struct dstr tmp = {0}; @@ -1360,6 +1363,7 @@ void obs_lua_load(void) #define PATH_MAX MAX_PATH #endif + struct dstr package_cpath = {0}; char import_path[PATH_MAX]; #ifdef __APPLE__ @@ -1377,7 +1381,23 @@ void obs_lua_load(void) #else strcpy(import_path, "./"); #endif - dstr_printf(&tmp, startup_script_template, import_path, SCRIPT_DIR); + dstr_cat(&package_cpath, "package.cpath = package.cpath"); + + add_package_cpath(&package_cpath, import_path); + +#if !defined(_WIN32) && !defined(__APPLE__) + char *relative_script_path = + os_get_executable_path_ptr("../" SCRIPT_DIR); + if (relative_script_path) + add_package_cpath(&package_cpath, relative_script_path); + bfree(relative_script_path); +#endif + + add_package_cpath(&package_cpath, SCRIPT_DIR); + dstr_cat(&package_cpath, "\n"); + + dstr_printf(&tmp, startup_script_template, package_cpath.array); + dstr_free(&package_cpath); startup_script = tmp.array; obs_add_tick_callback(lua_tick, NULL); From e4305b0a504867bda969265a53e4c4c0978352ee Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 14 Jun 2024 16:04:34 +0200 Subject: [PATCH 0248/1073] UI: Hide multitrack video options for custom output Custom output doesn't currently allow specifying a config URL, so disable relevant settings for now --- UI/window-basic-settings.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 10ff33f764f807..9efc86951a93b6 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -6325,6 +6325,9 @@ void OBSBasicSettings::UpdateMultitrackVideo() ui->enableMultitrackVideo->setChecked(false); } + if (IsCustomService()) + available = available && MultitrackVideoDeveloperModeEnabled(); + ui->multitrackVideoGroupBox->setVisible(available); ui->enableMultitrackVideo->setEnabled(toggle_available); From 2d489fc54e196964ce5502eb2c28e699290bb662 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 13 Jun 2024 01:46:07 +0200 Subject: [PATCH 0249/1073] deps/blake2: Add static blake2 library for Windows updater --- deps/blake2/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/deps/blake2/CMakeLists.txt b/deps/blake2/CMakeLists.txt index e39c1a8d4e9df1..3df7d3bd8a0d90 100644 --- a/deps/blake2/CMakeLists.txt +++ b/deps/blake2/CMakeLists.txt @@ -11,3 +11,16 @@ target_sources( target_include_directories(blake2 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") set_target_properties(blake2 PROPERTIES FOLDER deps) + +if(OS_WINDOWS) + add_library(blake2_static OBJECT) + add_library(OBS::blake2_static ALIAS blake2_static) + + target_sources( + blake2_static + PRIVATE src/blake2-impl.h src/blake2b-ref.c + PUBLIC src/blake2.h) + + target_include_directories(blake2_static PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") + set_target_properties(blake2_static PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +endif() From d2a7f0129553effd67b3b1de3545cda9c0c42ea7 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 13 Jun 2024 01:48:41 +0200 Subject: [PATCH 0250/1073] updater: Use static blake2 and fix building with Debug runtimes --- UI/win-update/updater/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 88491015e161a9..4e49fcfa5cc3ba 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -28,7 +28,7 @@ target_include_directories(updater PRIVATE "${CMAKE_SOURCE_DIR}/libobs" "${CMAKE target_link_libraries( updater - PRIVATE OBS::blake2 + PRIVATE OBS::blake2_static nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 @@ -38,10 +38,10 @@ target_link_libraries( wintrust) # zstd is hardcoded with /DEFAULTLIB:LIBCMT -target_link_options(updater PRIVATE /NODEFAULTLIB:LIBCMT) +target_link_options(updater PRIVATE $<$:/NODEFAULTLIB:LIBCMT>) set_target_properties( updater PROPERTIES FOLDER frontend OUTPUT_NAME updater - MSVC_RUNTIME_LIBRARY "MultiThreaded") + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") From d7e2636316db4ac9e73e761a79aae0210b6ee2ca Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Thu, 13 Jun 2024 16:50:15 -0400 Subject: [PATCH 0251/1073] UI: Change multitrack video configId Schema has changed this field from "obsConfigId" to "clientConfigId". Updated the name to match. --- UI/multitrack-video-output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index c7f1751655b8a7..905524591f3e28 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -127,7 +127,7 @@ create_service(const GoLiveApi::Config &go_live_config, if (!go_live_config.meta.config_id.empty()) { parsed_query.addQueryItem( - "obsConfigId", + "clientConfigId", QString::fromStdString(go_live_config.meta.config_id)); } From 600a5640394f2b7d8db54c004f5f3c4d305a3fe9 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 14 Jun 2024 18:41:47 +0200 Subject: [PATCH 0252/1073] UI: Add `composition_gpu_index` to multitrack video postdata --- UI/goliveapi-postdata.cpp | 2 ++ UI/models/multitrack-video.hpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/UI/goliveapi-postdata.cpp b/UI/goliveapi-postdata.cpp index 27a62e0dd44886..22491c1ff8a191 100644 --- a/UI/goliveapi-postdata.cpp +++ b/UI/goliveapi-postdata.cpp @@ -36,6 +36,8 @@ constructGoLivePost(QString streamKey, preferences.canvas_width = ovi.base_width; preferences.canvas_height = ovi.base_height; + + preferences.composition_gpu_index = ovi.adapter; } if (maximum_aggregate_bitrate.has_value()) diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index 0613fc1133c343..b353ad19f5ee92 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -191,11 +191,12 @@ struct Preferences { media_frames_per_second framerate; uint32_t canvas_width; uint32_t canvas_height; + optional composition_gpu_index; NLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate, maximum_video_tracks, vod_track_audio, width, height, framerate, canvas_width, - canvas_height) + canvas_height, composition_gpu_index) }; struct PostData { From f462ffb224252477d035c265bb665b7f864045b5 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 9 Jun 2024 13:56:02 +0200 Subject: [PATCH 0253/1073] UI: Initialize max length of LineEditAutoResize in constructor LineEditAutoResize didn't have its maxLength initialized in the constructor, leaving it to be a random value until set via setMaxLength. The one place where LineEditAutoResize was used immediately set this after calling the constructor, but if we use this anywhere else in the future it makes sense to have this initialized. As it is meant to mostly behave like a QLineEdit, lets use the same default value of 32767. Detected by PVS-Studio. --- UI/lineedit-autoresize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/lineedit-autoresize.cpp b/UI/lineedit-autoresize.cpp index adc5d23c42e478..9ada354ad486bd 100644 --- a/UI/lineedit-autoresize.cpp +++ b/UI/lineedit-autoresize.cpp @@ -1,6 +1,6 @@ #include "lineedit-autoresize.hpp" -LineEditAutoResize::LineEditAutoResize() +LineEditAutoResize::LineEditAutoResize() : m_maxLength(32767) { connect(this, &LineEditAutoResize::textChanged, this, &LineEditAutoResize::checkTextLength); From 7cd5ede1e037f9be07604138f45c7f8fcb16018c Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 9 Jun 2024 14:09:57 +0200 Subject: [PATCH 0254/1073] UI: Initialize YoutubeChatDock chat input members in constructor Currently, the chat input elements (lineEdit, sendButton, and chatLayout) are initialized when the QCefWidget gets set. This is problematic behavior that only happened to work because we're a bit lucky: The chat is only enabled after a widget is set, and it's only set once. Without those conditions, the chat dock would crash when enabling the chat before a widget is set, and the elements would get recreated if the widget is set a second time, resulting in the original elements not getting freed and leaking. Moving the element creation to the constructor fixes both of these problems, as now they're created immediately and only once. Detected by PVS-Studio. --- UI/auth-youtube.cpp | 15 +++++++++------ UI/auth-youtube.hpp | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/UI/auth-youtube.cpp b/UI/auth-youtube.cpp index 11587cc2df5952..cd07c74cc79759 100644 --- a/UI/auth-youtube.cpp +++ b/UI/auth-youtube.cpp @@ -350,7 +350,7 @@ std::shared_ptr YoutubeAuth::Login(QWidget *owner, } #ifdef BROWSER_AVAILABLE -void YoutubeChatDock::SetWidget(QCefWidget *widget_) +YoutubeChatDock::YoutubeChatDock(const QString &title) : BrowserDock(title) { lineEdit = new LineEditAutoResize(); lineEdit->setVisible(false); @@ -364,6 +364,14 @@ void YoutubeChatDock::SetWidget(QCefWidget *widget_) chatLayout->addWidget(lineEdit, 1); chatLayout->addWidget(sendButton); + QWidget::connect(lineEdit, &LineEditAutoResize::returnPressed, this, + &YoutubeChatDock::SendChatMessage); + QWidget::connect(sendButton, &QPushButton::pressed, this, + &YoutubeChatDock::SendChatMessage); +} + +void YoutubeChatDock::SetWidget(QCefWidget *widget_) +{ QVBoxLayout *layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(widget_, 1); @@ -373,11 +381,6 @@ void YoutubeChatDock::SetWidget(QCefWidget *widget_) widget->setLayout(layout); setWidget(widget); - QWidget::connect(lineEdit, &LineEditAutoResize::returnPressed, this, - &YoutubeChatDock::SendChatMessage); - QWidget::connect(sendButton, &QPushButton::pressed, this, - &YoutubeChatDock::SendChatMessage); - cefWidget.reset(widget_); } diff --git a/UI/auth-youtube.hpp b/UI/auth-youtube.hpp index 28e17148d847d0..2e51cb48afdc59 100644 --- a/UI/auth-youtube.hpp +++ b/UI/auth-youtube.hpp @@ -21,7 +21,7 @@ class YoutubeChatDock : public BrowserDock { QHBoxLayout *chatLayout; public: - inline YoutubeChatDock(const QString &title) : BrowserDock(title) {} + YoutubeChatDock(const QString &title); void SetWidget(QCefWidget *widget_); void SetApiChatId(const std::string &id); From dcd2f19c83003f1a1e19065fd48a2a17138ecfea Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 11 Jun 2024 04:40:02 -0500 Subject: [PATCH 0255/1073] UI: Remove redundant addAction call This removes a redundant addAction call in the studio mode program context menu. --- UI/window-basic-main.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index b8e9d4f281d9a1..eb3ea40ab1c905 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -8407,13 +8407,8 @@ void OBSBasic::ProgramViewContextMenuRequested() &OBSBasic::OpenStudioProgramProjector); popup.addMenu(studioProgramProjector); - - QAction *studioProgramWindow = - popup.addAction(QTStr("StudioProgramWindow"), this, - &OBSBasic::OpenStudioProgramWindow); - - popup.addAction(studioProgramWindow); - + popup.addAction(QTStr("StudioProgramWindow"), this, + &OBSBasic::OpenStudioProgramWindow); popup.addAction(QTStr("Screenshot.StudioProgram"), this, &OBSBasic::ScreenshotProgram); From 62830cd8c79369ebef9a60694eca9f743054c4ab Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 11 Jun 2024 14:43:44 -0400 Subject: [PATCH 0256/1073] UI: Fix icon-size values in theme files The Qt docs on icon-size say its Type is Length, which is further defined as, "A number followed by a measurement unit." https://doc.qt.io/qt-6/stylesheet-reference.html#icon-size https://doc.qt.io/qt-6/stylesheet-reference.html#length This fixes the following logged Qt warning: QCssParser::sizeValue: Too many values provided --- UI/data/themes/System.obt | 4 ++-- UI/data/themes/Yami.obt | 18 +++++++++--------- UI/data/themes/Yami_Classic.ovt | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/UI/data/themes/System.obt b/UI/data/themes/System.obt index eaa41bf78007bf..dea5a0ca0e4074 100644 --- a/UI/data/themes/System.obt +++ b/UI/data/themes/System.obt @@ -396,13 +396,13 @@ QCalendarWidget QToolButton { QCalendarWidget #qt_calendar_prevmonth { padding: 2px; qproperty-icon: url(theme:Dark/left.svg); - icon-size: 16px, 16px; + icon-size: 16px; } QCalendarWidget #qt_calendar_nextmonth { padding: 2px; qproperty-icon: url(theme:Dark/right.svg); - icon-size: 16px, 16px; + icon-size: 16px; } /* Status Bar */ diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 00ad65d241570c..6c3a92e7de953a 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -649,12 +649,12 @@ QScrollBar::handle:horizontal { QPushButton#sourcePropertiesButton { qproperty-icon: url(theme:Dark/settings/general.svg); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } QPushButton#sourceFiltersButton { qproperty-icon: url(theme:Dark/filter.svg); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } /* Scenes and Sources toolbar */ @@ -1041,7 +1041,7 @@ QPushButton { height: var(--input_height); max-height: var(--input_height); padding: var(--input_padding) var(--padding_wide); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } QPushButton { @@ -1059,7 +1059,7 @@ QPushButton[toolButton="true"] { margin: 0px var(--spacing_base); border: 1px solid var(--button_border); border-radius: var(--border_radius); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } QToolButton:last-child, @@ -1212,7 +1212,7 @@ QSlider::handle:disabled { margin: 0px; border: 1px solid var(--button_border); border-radius: var(--border_radius); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } /* This is an incredibly cursed but necessary fix */ @@ -1603,7 +1603,7 @@ MuteCheckBox::indicator:unchecked { margin: 0px; border: 1px solid var(--button_border); border-radius: var(--border_radius); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } MuteCheckBox::indicator:hover, @@ -1612,7 +1612,7 @@ MuteCheckBox::indicator:unchecked:hover { padding: var(--padding_base_border) var(--padding_base_border); margin: 0px; border: 1px solid var(--button_border_hover); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } MuteCheckBox::indicator:pressed, @@ -1883,13 +1883,13 @@ QCalendarWidget QToolButton { QCalendarWidget #qt_calendar_prevmonth { padding: var(--padding_small); qproperty-icon: url(theme:Dark/left.svg); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } QCalendarWidget #qt_calendar_nextmonth { padding: var(--padding_small); qproperty-icon: url(theme:Dark/right.svg); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } QCalendarWidget QToolButton:hover { diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index ebd73bec708f1a..1203e33a795115 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -202,7 +202,7 @@ QPushButton[toolButton="true"] { #stackedMixerArea QPushButton { min-width: var(--icon_base); padding: var(--padding_large) var(--padding_large); - icon-size: var(--icon_base), var(--icon_base); + icon-size: var(--icon_base); } #stackedMixerArea QPushButton:!hover { @@ -221,7 +221,7 @@ MuteCheckBox::indicator:unchecked { border: none; width: var(--icon_base_mixer); height: var(--icon_base_mixer); - icon-size: var(--icon_base_mixer), var(--icon_base_mixer); + icon-size: var(--icon_base_mixer); } MuteCheckBox::indicator:checked { @@ -235,7 +235,7 @@ MuteCheckBox::indicator:unchecked:hover { MuteCheckBox::indicator:hover, MuteCheckBox::indicator:unchecked:hover { - icon-size: var(--icon_base_mixer), var(--icon_base_mixer); + icon-size: var(--icon_base_mixer); border: none; } From d1bf6f951a43f6825119891d35c7167396aa75df Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 14 Jun 2024 00:33:23 -0700 Subject: [PATCH 0257/1073] obs-outputs: Add multitrack flag to null output --- plugins/obs-outputs/null-output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-outputs/null-output.c b/plugins/obs-outputs/null-output.c index c8997b3c32a63c..cd38fb113e9756 100644 --- a/plugins/obs-outputs/null-output.c +++ b/plugins/obs-outputs/null-output.c @@ -89,7 +89,7 @@ static void null_output_data(void *data, struct encoder_packet *packet) struct obs_output_info null_output_info = { .id = "null_output", - .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED, + .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_MULTI_TRACK_AV, .get_name = null_output_getname, .create = null_output_create, .destroy = null_output_destroy, From 0d7478c01756fb23f3de2e636f92e50b3fee4117 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 14 Jun 2024 18:06:13 -0400 Subject: [PATCH 0258/1073] UI: Set Multitrack Video maximum bitrate limit default to 0/Auto There were some reports that the default value of 8000 was confusing because it implies that the automaticaly selected and used value is 8000 Kbps. Set it to 0 which should hopefully make it more obvious that OBS is not sending 0 Kbps. --- UI/forms/OBSBasicSettings.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 2c14032b0484c7..d455afcd9d396a 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1674,13 +1674,13 @@ Kbps - 500 + 0 1000000 - 8000 + 0
From 4eef796f8090d67ed6c1917f830a84734454ddaa Mon Sep 17 00:00:00 2001 From: tt2468 Date: Sat, 15 Jun 2024 17:04:18 -0700 Subject: [PATCH 0259/1073] deps/media-playback: Fix init of swscale with hw decode Checking the format of the AVCodecContext will result in using the format of the hardware-side frames, not the software-side frames. This uses the software frame parameters itself to initialize the swscale context. --- deps/media-playback/media-playback/media.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/deps/media-playback/media-playback/media.c b/deps/media-playback/media-playback/media.c index 07b040dd8235cc..79f76e9af8a806 100644 --- a/deps/media-playback/media-playback/media.c +++ b/deps/media-playback/media-playback/media.c @@ -246,15 +246,14 @@ static inline int get_sws_range(enum AVColorRange r) static bool mp_media_init_scaling(mp_media_t *m) { - int space = get_sws_colorspace(m->v.decoder->colorspace); - int range = get_sws_range(m->v.decoder->color_range); + int space = get_sws_colorspace(m->v.frame->colorspace); + int range = get_sws_range(m->v.frame->color_range); const int *coeff = sws_getCoefficients(space); - m->swscale = sws_getCachedContext(NULL, m->v.decoder->width, - m->v.decoder->height, - m->v.decoder->pix_fmt, - m->v.decoder->width, - m->v.decoder->height, m->scale_format, + m->swscale = sws_getCachedContext(NULL, m->v.frame->width, + m->v.frame->height, + m->v.frame->format, m->v.frame->width, + m->v.frame->height, m->scale_format, SWS_POINT, NULL, NULL, NULL); if (!m->swscale) { blog(LOG_WARNING, "MP: Failed to initialize scaler"); @@ -265,7 +264,7 @@ static bool mp_media_init_scaling(mp_media_t *m) FIXED_1_0, FIXED_1_0); int ret = av_image_alloc(m->scale_pic, m->scale_linesizes, - m->v.decoder->width, m->v.decoder->height, + m->v.frame->width, m->v.frame->height, m->scale_format, 32); if (ret < 0) { blog(LOG_WARNING, "MP: Failed to create scale pic data"); From 9d88b632ae0ad720093be22eecac37f8d1d78843 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Sat, 15 Jun 2024 21:40:36 -0400 Subject: [PATCH 0260/1073] UI: Add step value to Multitrack Video maximum bitrate control All other bitrate setting fields in the UI have a single-step value of 50. Without setting this, the single-step value is 1, which makes using the scroll wheel a bit tedious. While this could arguably be higher, let's make this consistent first and then consider changing the values across the application later. --- UI/forms/OBSBasicSettings.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index d455afcd9d396a..9d25ceef8c35ad 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -1679,6 +1679,9 @@ 1000000 + + 50 + 0 From 751dbdad10907f1b0911a79796b3bdcb65f42d17 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 7 May 2024 20:49:23 -0700 Subject: [PATCH 0261/1073] libobs: Update video encoder group struct member names Updates the struct member names of the video encoder group to be more like what is commonly seen in OBS elsewhere. --- libobs/obs-encoder.c | 39 ++++++++++++++++++----------------- libobs/obs-internal.h | 8 +++---- libobs/obs-output.c | 2 +- libobs/obs-video-gpu-encode.c | 2 +- libobs/obs-video.c | 6 ++++-- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 4ad69def463639..7499e0dd02eacb 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -325,11 +325,10 @@ static void add_connection(struct obs_encoder *encoder) } if (encoder->encoder_group) { - bool ready = false; pthread_mutex_lock(&encoder->encoder_group->mutex); - encoder->encoder_group->encoders_started += 1; - ready = encoder->encoder_group->encoders_started == - encoder->encoder_group->encoders_added; + encoder->encoder_group->num_encoders_started += 1; + bool ready = encoder->encoder_group->num_encoders_started == + encoder->encoder_group->num_encoders; pthread_mutex_unlock(&encoder->encoder_group->mutex); if (ready) add_ready_encoder_group(encoder); @@ -353,8 +352,8 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown) if (encoder->encoder_group) { pthread_mutex_lock(&encoder->encoder_group->mutex); - encoder->encoder_group->encoders_started -= 1; - if (encoder->encoder_group->encoders_started == 0) + encoder->encoder_group->num_encoders_started -= 1; + if (encoder->encoder_group->num_encoders_started == 0) encoder->encoder_group->start_timestamp = 0; pthread_mutex_unlock(&encoder->encoder_group->mutex); @@ -396,14 +395,15 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder) encoder->context.name); if (encoder->encoder_group) { - struct encoder_group *group = encoder->encoder_group; + struct obs_encoder_group *group = + encoder->encoder_group; bool release = false; encoder->encoder_group = NULL; pthread_mutex_lock(&group->mutex); - group->encoders_added -= 1; - release = group->encoders_added == 0; + group->num_encoders -= 1; + release = group->num_encoders == 0; pthread_mutex_unlock(&group->mutex); if (release) { @@ -1461,7 +1461,7 @@ static void receive_video(void *param, struct video_data *frame) struct encoder_frame enc_frame; if (encoder->encoder_group && !encoder->start_ts) { - struct encoder_group *group = encoder->encoder_group; + struct obs_encoder_group *group = encoder->encoder_group; bool ready = false; pthread_mutex_lock(&group->mutex); ready = group->start_timestamp == frame->timestamp; @@ -2093,7 +2093,8 @@ bool obs_encoder_group_keyframe_aligned_encoders( bool unlock = false; if (!encoder->encoder_group) { - encoder->encoder_group = bzalloc(sizeof(struct encoder_group)); + encoder->encoder_group = + bzalloc(sizeof(struct obs_encoder_group)); if (pthread_mutex_init(&encoder->encoder_group->mutex, NULL) < 0) { bfree(encoder->encoder_group); @@ -2101,11 +2102,11 @@ bool obs_encoder_group_keyframe_aligned_encoders( return false; } - encoder->encoder_group->encoders_added = 1; + encoder->encoder_group->num_encoders = 1; } else { pthread_mutex_lock(&encoder->encoder_group->mutex); unlock = true; - if (encoder->encoder_group->encoders_started != 0) { + if (encoder->encoder_group->num_encoders_started != 0) { blog(LOG_ERROR, "obs_encoder_group_keyframe_aligned_encoders: " "Can't add encoder '%s' to active group " @@ -2117,7 +2118,7 @@ bool obs_encoder_group_keyframe_aligned_encoders( } } - encoder->encoder_group->encoders_added += 1; + encoder->encoder_group->num_encoders += 1; encoder_to_be_grouped->encoder_group = encoder->encoder_group; if (unlock) @@ -2156,12 +2157,12 @@ bool obs_encoder_group_remove_keyframe_aligned_encoder( return false; } - struct encoder_group *current_group = encoder->encoder_group; - struct encoder_group *free_group = NULL; + struct obs_encoder_group *current_group = encoder->encoder_group; + struct obs_encoder_group *free_group = NULL; pthread_mutex_lock(¤t_group->mutex); - if (current_group->encoders_started != 0) { + if (current_group->num_encoders_started != 0) { blog(LOG_ERROR, "obs_encoder_group_remove_keyframe_aligned_encoder: " "could not ungroup encoder '%s' from '%s' while " @@ -2172,9 +2173,9 @@ bool obs_encoder_group_remove_keyframe_aligned_encoder( return false; } - current_group->encoders_added -= 1; + current_group->num_encoders -= 1; encoder_to_be_ungrouped->encoder_group = NULL; - if (current_group->encoders_added == 1) { + if (current_group->num_encoders == 1) { free_group = current_group; encoder->encoder_group = NULL; } diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 25108083b1d2c2..c96b2ec2ebbfdb 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -1242,10 +1242,10 @@ struct encoder_callback { void *param; }; -struct encoder_group { +struct obs_encoder_group { pthread_mutex_t mutex; - uint32_t encoders_added; - uint32_t encoders_started; + uint32_t num_encoders; + uint32_t num_encoders_started; uint64_t start_timestamp; }; @@ -1314,7 +1314,7 @@ struct obs_encoder { uint64_t start_ts; /* track encoders that are part of a gop-aligned multi track group */ - struct encoder_group *encoder_group; + struct obs_encoder_group *encoder_group; pthread_mutex_t outputs_mutex; DARRAY(obs_output_t *) outputs; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 14413c204399ba..6394d7c1af4672 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -2226,7 +2226,7 @@ check_encoder_group_keyframe_alignment(obs_output_t *output, pthread_mutex_lock(&packet->encoder->encoder_group->mutex); insert_data.required_tracks = - packet->encoder->encoder_group->encoders_started; + packet->encoder->encoder_group->num_encoders_started; pthread_mutex_unlock(&packet->encoder->encoder_group->mutex); da_insert(output->keyframe_group_tracking, idx, &insert_data); diff --git a/libobs/obs-video-gpu-encode.c b/libobs/obs-video-gpu-encode.c index d9f5dc49ac1468..96bd081ad8ba18 100644 --- a/libobs/obs-video-gpu-encode.c +++ b/libobs/obs-video-gpu-encode.c @@ -92,7 +92,7 @@ static void *gpu_encode_thread(void *data) pkt.encoder = encoder; if (encoder->encoder_group && !encoder->start_ts) { - struct encoder_group *group = + struct obs_encoder_group *group = encoder->encoder_group; bool ready = false; pthread_mutex_lock(&group->mutex); diff --git a/libobs/obs-video.c b/libobs/obs-video.c index fe34d4b4469150..3618f2b69e5ecb 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -932,9 +932,11 @@ static inline void video_sleep(struct obs_core_video *video, uint64_t *p_time, continue; if (encoder->encoder_group) { - struct encoder_group *group = encoder->encoder_group; + struct obs_encoder_group *group = + encoder->encoder_group; pthread_mutex_lock(&group->mutex); - if (group->encoders_added == group->encoders_started && + if (group->num_encoders == + group->num_encoders_started && !group->start_timestamp) { group->start_timestamp = *p_time; } From e215502b620ce9d97f4d0191adef24856682b9e9 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Tue, 7 May 2024 22:31:23 -0700 Subject: [PATCH 0262/1073] libobs, UI: Normalize encoder group API Modifies the encoder group API added previously to better follow the existing libobs API naming paradigms. This also produces much more readable code, and allows a few small benefits like only needing to hold a reference to the encoder group, instead of every encoder. --- UI/multitrack-video-output.cpp | 40 +++---- UI/multitrack-video-output.hpp | 2 +- libobs/obs-encoder.c | 193 +++++++++++++-------------------- libobs/obs-internal.h | 7 +- libobs/obs-video.c | 7 +- libobs/obs.h | 16 ++- 6 files changed, 113 insertions(+), 152 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 905524591f3e28..ce68cb5ca28ab5 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -332,7 +332,7 @@ static OBSOutputs SetupOBSOutput(obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, - std::vector &video_encoders, + std::shared_ptr &video_encoder_group, const char *audio_encoder_id, std::optional vod_track_mixer); static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, @@ -461,10 +461,10 @@ void MultitrackVideoOutput::PrepareStreaming( const auto &output_config = custom ? *custom : *go_live_config; const auto &service_config = go_live_config ? *go_live_config : *custom; - auto audio_encoders = std::vector(); - auto video_encoders = std::vector(); + std::vector audio_encoders; + std::shared_ptr video_encoder_group; auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config, - audio_encoders, video_encoders, + audio_encoders, video_encoder_group, audio_encoder_id, vod_track_mixer); auto output = std::move(outputs.output); auto recording_output = std::move(outputs.recording_output); @@ -496,13 +496,6 @@ void MultitrackVideoOutput::PrepareStreaming( start_recording, stop_recording, deactivate_recording); - decltype(video_encoders) recording_video_encoders; - recording_video_encoders.reserve(video_encoders.size()); - for (auto &encoder : video_encoders) { - recording_video_encoders.emplace_back( - obs_encoder_get_ref(encoder)); - } - decltype(audio_encoders) recording_audio_encoders; recording_audio_encoders.reserve(audio_encoders.size()); for (auto &encoder : audio_encoders) { @@ -515,7 +508,7 @@ void MultitrackVideoOutput::PrepareStreaming( current_stream_dump_mutex}; current_stream_dump.emplace(OBSOutputObjects{ std::move(recording_output), - std::move(recording_video_encoders), + video_encoder_group, std::move(recording_audio_encoders), nullptr, std::move(start_recording), @@ -528,7 +521,7 @@ void MultitrackVideoOutput::PrepareStreaming( const std::lock_guard current_lock{current_mutex}; current.emplace(OBSOutputObjects{ std::move(output), - std::move(video_encoders), + video_encoder_group, std::move(audio_encoders), std::move(multitrack_video_service), std::move(start_streaming), @@ -692,11 +685,10 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings( static bool create_video_encoders(const GoLiveApi::Config &go_live_config, - std::vector &video_encoders, + std::shared_ptr &video_encoder_group, obs_output_t *output, obs_output_t *recording_output) { DStr video_encoder_name_buffer; - obs_encoder_t *first_encoder = nullptr; if (go_live_config.encoder_configurations.empty()) { blog(LOG_WARNING, "MultitrackVideoOutput: Missing video encoder configurations"); @@ -704,6 +696,11 @@ create_video_encoders(const GoLiveApi::Config &go_live_config, QTStr("FailedToStartStream.MissingEncoderConfigs")); } + std::shared_ptr encoder_group( + obs_encoder_group_create(), obs_encoder_group_destroy); + if (!encoder_group) + return false; + for (size_t i = 0; i < go_live_config.encoder_configurations.size(); i++) { auto encoder = create_video_encoder( @@ -712,19 +709,16 @@ create_video_encoders(const GoLiveApi::Config &go_live_config, if (!encoder) return false; - if (!first_encoder) - first_encoder = encoder; - else - obs_encoder_group_keyframe_aligned_encoders( - first_encoder, encoder); + if (!obs_encoder_set_group(encoder, encoder_group.get())) + return false; obs_output_set_video_encoder2(output, encoder, i); if (recording_output) obs_output_set_video_encoder2(recording_output, encoder, i); - video_encoders.emplace_back(std::move(encoder)); } + video_encoder_group = encoder_group; return true; } @@ -790,7 +784,7 @@ static OBSOutputs SetupOBSOutput(obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, - std::vector &video_encoders, + std::shared_ptr &video_encoder_group, const char *audio_encoder_id, std::optional vod_track_mixer) { @@ -801,7 +795,7 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config, recording_output = create_recording_output(dump_stream_to_file_config); - if (!create_video_encoders(go_live_config, video_encoders, output, + if (!create_video_encoders(go_live_config, video_encoder_group, output, recording_output)) return {nullptr, nullptr}; diff --git a/UI/multitrack-video-output.hpp b/UI/multitrack-video-output.hpp index 586432429e2285..01f67c1714d0ae 100644 --- a/UI/multitrack-video-output.hpp +++ b/UI/multitrack-video-output.hpp @@ -53,7 +53,7 @@ struct MultitrackVideoOutput { private: struct OBSOutputObjects { OBSOutputAutoRelease output_; - std::vector video_encoders_; + std::shared_ptr video_encoder_group_; std::vector audio_encoders_; OBSServiceAutoRelease multitrack_video_service_; OBSSignal start_signal, stop_signal, deactivate_signal; diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 7499e0dd02eacb..c90786a0972c9e 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -327,8 +327,8 @@ static void add_connection(struct obs_encoder *encoder) if (encoder->encoder_group) { pthread_mutex_lock(&encoder->encoder_group->mutex); encoder->encoder_group->num_encoders_started += 1; - bool ready = encoder->encoder_group->num_encoders_started == - encoder->encoder_group->num_encoders; + bool ready = encoder->encoder_group->num_encoders_started >= + encoder->encoder_group->encoders.num; pthread_mutex_unlock(&encoder->encoder_group->mutex); if (ready) add_ready_encoder_group(encoder); @@ -337,6 +337,7 @@ static void add_connection(struct obs_encoder *encoder) set_encoder_active(encoder, true); } +void obs_encoder_group_actually_destroy(obs_encoder_group_t *group); static void remove_connection(struct obs_encoder *encoder, bool shutdown) { if (encoder->info.type == OBS_ENCODER_AUDIO) { @@ -352,11 +353,15 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown) if (encoder->encoder_group) { pthread_mutex_lock(&encoder->encoder_group->mutex); - encoder->encoder_group->num_encoders_started -= 1; - if (encoder->encoder_group->num_encoders_started == 0) + if (--encoder->encoder_group->num_encoders_started == 0) { encoder->encoder_group->start_timestamp = 0; + if (encoder->encoder_group->destroy_on_stop) + obs_encoder_group_actually_destroy( + encoder->encoder_group); + } - pthread_mutex_unlock(&encoder->encoder_group->mutex); + if (encoder->encoder_group) + pthread_mutex_unlock(&encoder->encoder_group->mutex); } /* obs_encoder_shutdown locks init_mutex, so don't call it on encode @@ -394,23 +399,7 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder) blog(LOG_DEBUG, "encoder '%s' destroyed", encoder->context.name); - if (encoder->encoder_group) { - struct obs_encoder_group *group = - encoder->encoder_group; - bool release = false; - - encoder->encoder_group = NULL; - - pthread_mutex_lock(&group->mutex); - group->num_encoders -= 1; - release = group->num_encoders == 0; - pthread_mutex_unlock(&group->mutex); - - if (release) { - pthread_mutex_destroy(&group->mutex); - bfree(group); - } - } + obs_encoder_set_group(encoder, NULL); free_audio_buffers(encoder); @@ -2058,133 +2047,99 @@ uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder) return encoder->roi_increment; } -bool obs_encoder_group_keyframe_aligned_encoders( - obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_grouped) +bool obs_encoder_set_group(obs_encoder_t *encoder, obs_encoder_group_t *group) { - if (!obs_encoder_valid(encoder, - "obs_encoder_group_keyframe_aligned_encoders") || - !obs_encoder_valid(encoder_to_be_grouped, - "obs_encoder_group_keyframe_aligned_encoders")) + if (!obs_encoder_valid(encoder, "obs_encoder_set_group")) return false; - if (obs_encoder_active(encoder) || - obs_encoder_active(encoder_to_be_grouped)) { - obs_encoder_t *active = obs_encoder_active(encoder) - ? encoder - : encoder_to_be_grouped; - obs_encoder_t *other = active == encoder ? encoder_to_be_grouped - : encoder; + if (obs_encoder_active(encoder)) { blog(LOG_ERROR, - "obs_encoder_group_keyframe_aligned_encoders: encoder '%s' " - "is already active, could not group with '%s'", - obs_encoder_get_name(active), obs_encoder_get_name(other)); - return false; - } - - if (encoder_to_be_grouped->encoder_group) { - blog(LOG_ERROR, - "obs_encoder_group_keyframe_aligned_encoders: encoder '%s' " - "is already part of a keyframe aligned group while trying " - "to group with encoder '%s'", - obs_encoder_get_name(encoder_to_be_grouped), + "obs_encoder_set_group: encoder '%s' is already active", obs_encoder_get_name(encoder)); return false; } - bool unlock = false; - if (!encoder->encoder_group) { - encoder->encoder_group = - bzalloc(sizeof(struct obs_encoder_group)); - if (pthread_mutex_init(&encoder->encoder_group->mutex, NULL) < - 0) { - bfree(encoder->encoder_group); - encoder->encoder_group = NULL; - return false; - } - - encoder->encoder_group->num_encoders = 1; - } else { - pthread_mutex_lock(&encoder->encoder_group->mutex); - unlock = true; - if (encoder->encoder_group->num_encoders_started != 0) { + if (encoder->encoder_group) { + struct obs_encoder_group *old_group = encoder->encoder_group; + pthread_mutex_lock(&old_group->mutex); + if (old_group->num_encoders_started) { + pthread_mutex_unlock(&old_group->mutex); blog(LOG_ERROR, - "obs_encoder_group_keyframe_aligned_encoders: " - "Can't add encoder '%s' to active group " - "from encoder '%s'", - obs_encoder_get_name(encoder_to_be_grouped), + "obs_encoder_set_group: encoder '%s' existing group has started encoders", obs_encoder_get_name(encoder)); - pthread_mutex_unlock(&encoder->encoder_group->mutex); return false; } + da_erase_item(old_group->encoders, &encoder); + obs_encoder_release(encoder); + pthread_mutex_unlock(&old_group->mutex); } - encoder->encoder_group->num_encoders += 1; - encoder_to_be_grouped->encoder_group = encoder->encoder_group; - - if (unlock) - pthread_mutex_unlock(&encoder->encoder_group->mutex); - - return true; -} + if (!group) + return true; -bool obs_encoder_group_remove_keyframe_aligned_encoder( - obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_ungrouped) -{ - if (!obs_encoder_valid( - encoder, - "obs_encoder_group_remove_keyframe_aligned_encoder") || - !obs_encoder_valid( - encoder_to_be_ungrouped, - "obs_encoder_group_remove_keyframe_aligned_encoder")) - return false; + pthread_mutex_lock(&group->mutex); - if (obs_encoder_active(encoder) || - obs_encoder_active(encoder_to_be_ungrouped)) { + if (group->num_encoders_started) { + pthread_mutex_unlock(&group->mutex); blog(LOG_ERROR, - "obs_encoder_group_remove_keyframe_aligned_encoder: encoders are active, " - "could not ungroup encoder '%s' from '%s'", - obs_encoder_get_name(encoder_to_be_ungrouped), - obs_encoder_get_name(encoder)); + "obs_encoder_set_group: specified group has started encoders"); return false; } - if (encoder->encoder_group != encoder_to_be_ungrouped->encoder_group) { - blog(LOG_ERROR, - "obs_encoder_group_remove_keyframe_aligned_encoder: " - "encoder '%s' does not belong to the same group as encoder '%s'", - obs_encoder_get_name(encoder_to_be_ungrouped), - obs_encoder_get_name(encoder)); + obs_encoder_t *ref = obs_encoder_get_ref(encoder); + if (!ref) { + pthread_mutex_unlock(&group->mutex); return false; } + da_push_back(group->encoders, &ref); + encoder->encoder_group = group; - struct obs_encoder_group *current_group = encoder->encoder_group; - struct obs_encoder_group *free_group = NULL; + pthread_mutex_unlock(&group->mutex); - pthread_mutex_lock(¤t_group->mutex); + return true; +} - if (current_group->num_encoders_started != 0) { - blog(LOG_ERROR, - "obs_encoder_group_remove_keyframe_aligned_encoder: " - "could not ungroup encoder '%s' from '%s' while " - "the group contains active encoders", - obs_encoder_get_name(encoder_to_be_ungrouped), - obs_encoder_get_name(encoder)); - pthread_mutex_unlock(¤t_group->mutex); - return false; +obs_encoder_group_t *obs_encoder_group_create() +{ + struct obs_encoder_group *group = + bzalloc(sizeof(struct obs_encoder_group)); + + pthread_mutex_init_value(&group->mutex); + if (pthread_mutex_init(&group->mutex, NULL) != 0) { + bfree(group); + return NULL; } - current_group->num_encoders -= 1; - encoder_to_be_ungrouped->encoder_group = NULL; - if (current_group->num_encoders == 1) { - free_group = current_group; + return group; +} + +void obs_encoder_group_actually_destroy(obs_encoder_group_t *group) +{ + for (size_t i = 0; i < group->encoders.num; i++) { + struct obs_encoder *encoder = group->encoders.array[i]; encoder->encoder_group = NULL; + obs_encoder_release(encoder); } - pthread_mutex_unlock(¤t_group->mutex); - if (free_group) { - pthread_mutex_destroy(&free_group->mutex); - bfree(free_group); + da_free(group->encoders); + pthread_mutex_unlock(&group->mutex); + pthread_mutex_destroy(&group->mutex); + + bfree(group); +} + +void obs_encoder_group_destroy(obs_encoder_group_t *group) +{ + if (!group) + return; + + pthread_mutex_lock(&group->mutex); + + if (group->num_encoders_started) { + group->destroy_on_stop = true; + pthread_mutex_unlock(&group->mutex); + return; } - return true; + obs_encoder_group_actually_destroy(group); } diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index c96b2ec2ebbfdb..6c3ca8aa133157 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -1244,7 +1244,12 @@ struct encoder_callback { struct obs_encoder_group { pthread_mutex_t mutex; - uint32_t num_encoders; + /* allows group to be destroyed even if some encoders are active */ + bool destroy_on_stop; + + /* holds strong references to all encoders */ + DARRAY(struct obs_encoder *) encoders; + uint32_t num_encoders_started; uint64_t start_timestamp; }; diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 3618f2b69e5ecb..1cd566ac8716ed 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -935,11 +935,10 @@ static inline void video_sleep(struct obs_core_video *video, uint64_t *p_time, struct obs_encoder_group *group = encoder->encoder_group; pthread_mutex_lock(&group->mutex); - if (group->num_encoders == - group->num_encoders_started && - !group->start_timestamp) { + if (group->num_encoders_started >= + group->encoders.num && + !group->start_timestamp) group->start_timestamp = *p_time; - } pthread_mutex_unlock(&group->mutex); } obs_encoder_release(encoder); diff --git a/libobs/obs.h b/libobs/obs.h index 5e65084f6e9b34..a8b6d4dc1a7d78 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -46,6 +46,7 @@ struct obs_scene; struct obs_scene_item; struct obs_output; struct obs_encoder; +struct obs_encoder_group; struct obs_service; struct obs_module; struct obs_fader; @@ -59,6 +60,7 @@ typedef struct obs_scene obs_scene_t; typedef struct obs_scene_item obs_sceneitem_t; typedef struct obs_output obs_output_t; typedef struct obs_encoder obs_encoder_t; +typedef struct obs_encoder_group obs_encoder_group_t; typedef struct obs_service obs_service_t; typedef struct obs_module obs_module_t; typedef struct obs_fader obs_fader_t; @@ -2607,10 +2609,16 @@ EXPORT void obs_encoder_set_last_error(obs_encoder_t *encoder, EXPORT uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder); -EXPORT bool obs_encoder_group_keyframe_aligned_encoders( - obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_grouped); -EXPORT bool obs_encoder_group_remove_keyframe_aligned_encoder( - obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_ungrouped); +/** + * Creates an "encoder group", allowing synchronized startup of encoders within + * the group. Encoder groups are single owner, and hold strong references to + * encoders within the group. Calling destroy on an active group will not actually + * destroy the group until it becomes completely inactive. + */ +EXPORT bool obs_encoder_set_group(obs_encoder_t *encoder, + obs_encoder_group_t *group); +EXPORT obs_encoder_group_t *obs_encoder_group_create(); +EXPORT void obs_encoder_group_destroy(obs_encoder_group_t *group); /* ------------------------------------------------------------------------- */ /* Stream Services */ From 608d3bfc26b5ff88ea6467d0183826c344bb1bd5 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 16 Jun 2024 02:55:47 +0200 Subject: [PATCH 0263/1073] UI: Set default container for beta builds to hybrid MP4 --- UI/window-basic-main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index eb3ea40ab1c905..8f5fc43c1b357b 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1549,7 +1549,7 @@ extern void CheckExistingCookieId(); #elif OBS_RELEASE_CANDIDATE == 0 && OBS_BETA == 0 #define DEFAULT_CONTAINER "mkv" #else -#define DEFAULT_CONTAINER "fragmented_mp4" +#define DEFAULT_CONTAINER "hybrid_mp4" #endif bool OBSBasic::InitBasicConfigDefaults() From 48b1298fafb941f70edfa3b17bd2441e9124c3c9 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Mon, 17 Jun 2024 20:57:40 -0400 Subject: [PATCH 0264/1073] UI: Fix parsing of Multitrack Video stream key query parameters The code was checking stream_key, but stream_key could be the user-supplied value (in_stream_key) or the server-supplied value (endpoint.authentication). The server-supplied value may lack the query parameters set in the user-supplied value. To ensure that user-specified query parameters (such as bandwidthtest) are passed along, parse the user-supplied key instead of the server-supplied key. --- UI/multitrack-video-output.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index ce68cb5ca28ab5..0d8a31d7c0afde 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -116,13 +116,15 @@ create_service(const GoLiveApi::Config &go_live_config, /* The stream key itself may contain query parameters, such as * "bandwidthtest" that need to be carried over. */ + QUrl parsed_user_key{in_stream_key}; + QUrlQuery user_key_query{parsed_user_key}; + QUrl parsed_key{stream_key}; - QUrlQuery key_query{parsed_key}; QUrl parsed_url{url}; QUrlQuery parsed_query{parsed_url}; - for (const auto &[key, value] : key_query.queryItems()) + for (const auto &[key, value] : user_key_query.queryItems()) parsed_query.addQueryItem(key, value); if (!go_live_config.meta.config_id.empty()) { From fb3e571ce8592c080f9a9d346c76e361b96bb531 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Tue, 18 Jun 2024 15:12:57 +0200 Subject: [PATCH 0265/1073] UI: Use advanced mode audio track in multitrack video output --- UI/multitrack-video-output.cpp | 16 +++++++++------- UI/multitrack-video-output.hpp | 1 + UI/window-basic-main-outputs.cpp | 20 ++++++++++++-------- UI/window-basic-main-outputs.hpp | 7 +++---- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 0d8a31d7c0afde..d9888a2c5827e6 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -335,7 +335,7 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, std::shared_ptr &video_encoder_group, - const char *audio_encoder_id, + const char *audio_encoder_id, size_t main_audio_mixer, std::optional vod_track_mixer); static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, obs_output_t *output, OBSSignal &start, @@ -348,7 +348,7 @@ void MultitrackVideoOutput::PrepareStreaming( std::optional maximum_aggregate_bitrate, std::optional maximum_video_tracks, std::optional custom_config, - obs_data_t *dump_stream_to_file_config, + obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer, std::optional vod_track_mixer) { { @@ -467,7 +467,8 @@ void MultitrackVideoOutput::PrepareStreaming( std::shared_ptr video_encoder_group; auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config, audio_encoders, video_encoder_group, - audio_encoder_id, vod_track_mixer); + audio_encoder_id, main_audio_mixer, + vod_track_mixer); auto output = std::move(outputs.output); auto recording_output = std::move(outputs.recording_output); if (!output) @@ -728,7 +729,7 @@ static void create_audio_encoders(const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, obs_output_t *output, obs_output_t *recording_output, - const char *audio_encoder_id, + const char *audio_encoder_id, size_t main_audio_mixer, std::optional vod_track_mixer) { using encoder_configs_type = @@ -768,7 +769,8 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config, }; create_encoders("multitrack video live audio", - go_live_config.audio_configurations.live, 0); + go_live_config.audio_configurations.live, + main_audio_mixer); if (!vod_track_mixer.has_value()) return; @@ -787,7 +789,7 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, std::shared_ptr &video_encoder_group, - const char *audio_encoder_id, + const char *audio_encoder_id, size_t main_audio_mixer, std::optional vod_track_mixer) { @@ -803,7 +805,7 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config, create_audio_encoders(go_live_config, audio_encoders, output, recording_output, audio_encoder_id, - vod_track_mixer); + main_audio_mixer, vod_track_mixer); return {std::move(output), std::move(recording_output)}; } diff --git a/UI/multitrack-video-output.hpp b/UI/multitrack-video-output.hpp index 01f67c1714d0ae..83aa48e182ac43 100644 --- a/UI/multitrack-video-output.hpp +++ b/UI/multitrack-video-output.hpp @@ -35,6 +35,7 @@ struct MultitrackVideoOutput { std::optional maximum_video_tracks, std::optional custom_config, obs_data_t *dump_stream_to_file_config, + size_t main_audio_mixer, std::optional vod_track_mixer); signal_handler_t *StreamingSignalHandler(); void StartedStreaming(); diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index fc1a760dbc3ded..3bf83e1d52295e 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1138,7 +1138,7 @@ FutureHolder SimpleOutput::SetupStreaming(obs_service_t *service) : std::nullopt; auto holder = SetupMultitrackVideo( - service, GetSimpleAACEncoderForBitrate(audio_bitrate), + service, GetSimpleAACEncoderForBitrate(audio_bitrate), 0, vod_track_mixer); auto future = PreventFutureDeadlock(holder.future) @@ -2286,9 +2286,13 @@ FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) const char *audio_encoder_id = config_get_string(main->Config(), "AdvOut", "AudioEncoder"); + int streamTrackIndex = + config_get_int(main->Config(), "AdvOut", "TrackIndex"); - auto holder = SetupMultitrackVideo(service, audio_encoder_id, - VodTrackMixerIdx(service)); + auto holder = + SetupMultitrackVideo(service, audio_encoder_id, + static_cast(streamTrackIndex), + VodTrackMixerIdx(service)); auto future = PreventFutureDeadlock(holder.future) .then(main, [&](std::optional @@ -2701,10 +2705,9 @@ std::string BasicOutputHandler::GetRecordingFilename( extern std::string DeserializeConfigText(const char *text); -FutureHolder> -BasicOutputHandler::SetupMultitrackVideo(obs_service_t *service, - std::string audio_encoder_id, - std::optional vod_track_mixer) +FutureHolder> BasicOutputHandler::SetupMultitrackVideo( + obs_service_t *service, std::string audio_encoder_id, + size_t main_audio_mixer, std::optional vod_track_mixer) { if (!multitrackVideo) return {[] {}, CreateFuture().then([] { @@ -2784,7 +2787,8 @@ BasicOutputHandler::SetupMultitrackVideo(obs_service_t *service, audio_encoder_id.c_str(), maximum_aggregate_bitrate, maximum_video_tracks, custom_config, - stream_dump_config, vod_track_mixer); + stream_dump_config, main_audio_mixer, + vod_track_mixer); } catch (const MultitrackVideoError &error) { return error; } diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp index 5dd86df461f9c1..abf40d66ff4d87 100644 --- a/UI/window-basic-main-outputs.hpp +++ b/UI/window-basic-main-outputs.hpp @@ -98,10 +98,9 @@ struct BasicOutputHandler { bool overwrite, const char *format, bool ffmpeg); - FutureHolder> - SetupMultitrackVideo(obs_service_t *service, - std::string audio_encoder_id, - std::optional vod_track_mixer); + FutureHolder> SetupMultitrackVideo( + obs_service_t *service, std::string audio_encoder_id, + size_t main_audio_mixer, std::optional vod_track_mixer); OBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig(); }; From 32b53ea9361a6d30281275a60e98eb1cfe5e640b Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 18 Jun 2024 20:11:27 -0400 Subject: [PATCH 0266/1073] CI: Fix Windows Patches action release notes generation By not specifying a checkout ref, actions/checkout does a second checkout when this action is invoked by the Publish workflow (release event). When this happens, it checks out the commit object from the tag, and git can no longer locate the annotated tag that contains the release notes. This then causes the release notes to be just the commit message and not the annotated tag message. The sparkle-appcast action in general and this actioo when invoked via the Dispatch workflow do not have this issue, and they both specify the tag as the ref. --- .github/actions/windows-patches/action.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml index 76a4a3cfd2ab45..f4ce585f21a836 100644 --- a/.github/actions/windows-patches/action.yaml +++ b/.github/actions/windows-patches/action.yaml @@ -25,6 +25,7 @@ runs: with: path: "repo" fetch-depth: 0 + ref: ${{ inputs.tagName }} - name: Download Release Artifact shell: pwsh From fc05ca601ab5da4e29233d888f25918e6d5cea3c Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 19 Jun 2024 13:21:30 +0200 Subject: [PATCH 0267/1073] CI: Update Windows patch creation bouf version --- .github/actions/windows-patches/action.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml index f4ce585f21a836..3c7dec67acb38d 100644 --- a/.github/actions/windows-patches/action.yaml +++ b/.github/actions/windows-patches/action.yaml @@ -41,9 +41,9 @@ runs: - name: Setup bouf shell: pwsh env: - BOUF_TAG: 'v0.6.3' - BOUF_HASH: '7f1d266467620aa553a705391ee06128e8ee14af66129a0e64a282997fb6fd83' - BOUF_NSIS_HASH: 'a234126de89f122b6a552df3416de3eabcb4195217626c7f4eaec71b20fe36eb' + BOUF_TAG: 'v0.6.4' + BOUF_HASH: 'aca6810e741dc38ff843fab7b25a0ad8570ee84f5595132cf0cc4a5b0131b4c4' + BOUF_NSIS_HASH: 'ed453784486556bd959d56743a8478ad3f68fe0305e9b43ac19d8771d0515257' GH_TOKEN: ${{ github.token }} run: | # Download bouf release From a9b59685527d4ccdc81b9b8e8e3b45be96217a2f Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 19 Jun 2024 13:21:38 +0200 Subject: [PATCH 0268/1073] CI: Add tag subject to Windows patch notes --- .github/actions/windows-patches/action.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml index 3c7dec67acb38d..95d5688bef92a6 100644 --- a/.github/actions/windows-patches/action.yaml +++ b/.github/actions/windows-patches/action.yaml @@ -87,7 +87,10 @@ runs: run: | # Release notes are just the tag body on Windows Set-Location repo - git tag -l --format='%(contents:body)' ${{ inputs.tagName }} > "${{ github.workspace }}/notes.rst" + git tag -l --format='%(contents:subject)' ${{ inputs.tagName }} > "${{ github.workspace }}/notes.rst" + Write-Output "###################################################" >> "${{ github.workspace }}/notes.rst" + Write-Output "" >> "${{ github.workspace }}/notes.rst" + git tag -l --format='%(contents:body)' ${{ inputs.tagName }} >> "${{ github.workspace }}/notes.rst" - name: Run bouf shell: pwsh From 8a8019db3f287db6b11db545d61108f5383d2b98 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 7 Jun 2024 17:31:25 +0200 Subject: [PATCH 0269/1073] UI: Only cache multitrack config URL startup argument --- UI/goliveapi-network.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/UI/goliveapi-network.cpp b/UI/goliveapi-network.cpp index 34b751eb47a06a..fb787108216fa6 100644 --- a/UI/goliveapi-network.cpp +++ b/UI/goliveapi-network.cpp @@ -128,7 +128,8 @@ GoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, QString MultitrackVideoAutoConfigURL(obs_service_t *service) { - static const QString url = [service]() -> QString { + static const std::optional cli_url = + []() -> std::optional { auto args = qApp->arguments(); for (int i = 0; i < args.length() - 1; i++) { if (args[i] == "--config-url" && @@ -136,11 +137,18 @@ QString MultitrackVideoAutoConfigURL(obs_service_t *service) return args[i + 1]; } } - OBSDataAutoRelease settings = obs_service_get_settings(service); - return obs_data_get_string( - settings, "multitrack_video_configuration_url"); + return std::nullopt; }(); + QString url; + if (cli_url.has_value()) { + url = *cli_url; + } else { + OBSDataAutoRelease settings = obs_service_get_settings(service); + url = obs_data_get_string(settings, + "multitrack_video_configuration_url"); + } + blog(LOG_INFO, "Go live URL: %s", url.toUtf8().constData()); return url; } From 9d1ac8816eedc55d36125e48947a0648679631a2 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Tue, 18 Jun 2024 17:39:02 +0200 Subject: [PATCH 0270/1073] UI: Add supported codecs to GetClientConfiguration request --- UI/goliveapi-postdata.cpp | 26 ++++++++++++++++++++++++++ UI/models/multitrack-video.hpp | 3 ++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/UI/goliveapi-postdata.cpp b/UI/goliveapi-postdata.cpp index 22491c1ff8a191..e67d2dbded272c 100644 --- a/UI/goliveapi-postdata.cpp +++ b/UI/goliveapi-postdata.cpp @@ -24,6 +24,32 @@ constructGoLivePost(QString streamKey, client.name = "obs-studio"; client.version = obs_get_version_string(); + auto add_codec = [&](const char *codec) { + auto it = std::find(std::begin(client.supported_codecs), + std::end(client.supported_codecs), codec); + if (it != std::end(client.supported_codecs)) + return; + + client.supported_codecs.push_back(codec); + }; + + const char *encoder_id = nullptr; + for (size_t i = 0; obs_enum_encoder_types(i, &encoder_id); i++) { + auto codec = obs_get_encoder_codec(encoder_id); + if (!codec) + continue; + + if (qstricmp(codec, "h264") == 0) { + add_codec("h264"); +#ifdef ENABLE_HEVC + } else if (qstricmp(codec, "hevc")) { + add_codec("h265"); +#endif + } else if (qstricmp(codec, "av1")) { + add_codec("av1"); + } + } + auto &preferences = post_data.preferences; preferences.vod_track_audio = vod_track_enabled; diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index b353ad19f5ee92..30b2218da08284 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -109,8 +109,9 @@ using json = nlohmann::json; struct Client { string name = "obs-studio"; string version; + std::vector supported_codecs; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version) + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version, supported_codecs) }; struct Cpu { From aa096e2ad04814af4ae21a31f95053b4fb659780 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 19 Jun 2024 13:55:45 +0200 Subject: [PATCH 0271/1073] UI: Disable multitrack video settings on non-win32 platforms --- UI/window-basic-settings.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 9efc86951a93b6..dfcd2dd653ca91 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -6325,6 +6325,10 @@ void OBSBasicSettings::UpdateMultitrackVideo() ui->enableMultitrackVideo->setChecked(false); } +#ifndef _WIN32 + available = available && MultitrackVideoDeveloperModeEnabled(); +#endif + if (IsCustomService()) available = available && MultitrackVideoDeveloperModeEnabled(); From a189489dd29558f3163d6ad269389a626e6c688b Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Wed, 19 Jun 2024 11:48:22 -0400 Subject: [PATCH 0272/1073] CI: Fix FreeBSD package installation FreeBSD's package tool is pkg(8), and install is the command verb passed to it. Fixes: fb4d65875e27 ("CI: Update Linux build scripts to use CMake p...") --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index c2d2ca7d94394e..03ca9af58401f2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -9,7 +9,7 @@ env: task: install_script: - - pkg-install -y + - pkg install -y cmake ninja binutils pkgconf curl ffmpeg qt6-base qt6-svg jansson libsysinfo e2fsprogs-libuuid pulseaudio alsa-lib pipewire v4l_compat libpci librist srt nlohmann-json uthash From 92352f18b8df90fe4c9d003544c96bccd1b9ebe0 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Tue, 18 Jun 2024 22:59:10 +0200 Subject: [PATCH 0273/1073] cmake: Add obs-config.h to libobs headers Adds the header to make it findable in IDEs --- libobs/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 3d759a13d0a4c7..fe9445f0f7670a 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -49,6 +49,7 @@ target_sources( obs-av1.h obs-avc.c obs-avc.h + obs-config.h obs-data.c obs-data.h obs-defs.h From 6457d7b42934ff0627168c9cc4cf216d3715b0c9 Mon Sep 17 00:00:00 2001 From: Fabien Lavocat <4154532+FabienLavocat@users.noreply.github.com> Date: Tue, 18 Jun 2024 08:32:22 -0700 Subject: [PATCH 0274/1073] rtmp-services: Add Dolby Millicast --- plugins/rtmp-services/data/package.json | 4 +- plugins/rtmp-services/data/services.json | 97 ++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index f05a13c2a13191..bdd34a73ab07ad 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 255, + "version": 256, "files": [ { "name": "services.json", - "version": 255 + "version": 256 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 5c5a717bb5c2f5..341de1a848ba37 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2922,6 +2922,103 @@ "keyint": 2, "x264opts": "scenecut=0" } + }, + { + "name": "Dolby Millicast", + "common": false, + "more_info_link": "https://docs.dolby.io/streaming-apis/docs/using-obs", + "stream_key_link": "https://streaming.dolby.io", + "servers": [ + { + "name": "Global (RTMPS)", + "url": "rtmps://rtmp-auto.millicast.com:443/v2/pub" + }, + { + "name": "Global (RTMP)", + "url": "rtmp://rtmp-auto.millicast.com:1935/v2/pub" + }, + { + "name": "Amsterdam, Netherlands (RTMPS)", + "url": "rtmps://rtmp-ams-1.millicast.com:443/v2/pub" + }, + { + "name": "Amsterdam, Netherlands (RTMP)", + "url": "rtmp://rtmp-ams-1.millicast.com:1935/v2/pub" + }, + { + "name": "Bangalore, India (RTMPS)", + "url": "rtmps://rtmp-blr-1.millicast.com:443/v2/pub" + }, + { + "name": "Bangalore, India (RTMP)", + "url": "rtmp://rtmp-blr-1.millicast.com:1935/v2/pub" + }, + { + "name": "Frankfurt, Germany (RTMPS)", + "url": "rtmps://rtmp-fra-1.millicast.com:443/v2/pub" + }, + { + "name": "Frankfurt, Germany (RTMP)", + "url": "rtmp://rtmp-fra-1.millicast.com:1935/v2/pub" + }, + { + "name": "Ashburn, Virginia, USA (RTMPS)", + "url": "rtmps://rtmp-iad-1.millicast.com:443/v2/pub" + }, + { + "name": "Ashburn, Virginia, USA (RTMP)", + "url": "rtmp://rtmp-iad-1.millicast.com:1935/v2/pub" + }, + { + "name": "London, England (RTMPS)", + "url": "rtmps://rtmp-lon-1.millicast.com:443/v2/pub" + }, + { + "name": "London, England (RTMP)", + "url": "rtmp://rtmp-lon-1.millicast.com:1935/v2/pub" + }, + { + "name": "Phoenix, AZ, USA (RTMPS)", + "url": "rtmps://rtmp-phx-1.millicast.com:443/v2/pub" + }, + { + "name": "Phoenix, AZ, USA (RTMP)", + "url": "rtmp://rtmp-phx-1.millicast.com:1935/v2/pub" + }, + { + "name": "Sao Paulo, Brazil (RTMPS)", + "url": "rtmps://rtmp-sao-1.millicast.com:443/v2/pub" + }, + { + "name": "Sao Paulo, Brazil (RTMP)", + "url": "rtmp://rtmp-sao-1.millicast.com:1935/v2/pub" + }, + { + "name": "Singapore (RTMPS)", + "url": "rtmps://rtmp-sgp-1.millicast.com:443/v2/pub" + }, + { + "name": "Singapore (RTMP)", + "url": "rtmp://rtmp-sgp-1.millicast.com:1935/v2/pub" + }, + { + "name": "Sydney, Australia (RTMPS)", + "url": "rtmps://rtmp-syd-1.millicast.com:443/v2/pub" + }, + { + "name": "Sydney, Australia (RTMP)", + "url": "rtmp://rtmp-syd-1.millicast.com:1935/v2/pub" + } + ], + "supported video codecs": [ + "h264", + "hevc", + "av1" + ], + "recommended": { + "keyint": 1, + "bframes": 0 + } } ] } From 6c389271b368ccd81ca1f31ac956eac1185dbe4d Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 19 Jun 2024 17:54:39 -0700 Subject: [PATCH 0275/1073] obs-ffmpeg: Close VAAPI device on vaInitialize fail On some systems (eg. mine), VAAPI fails on vaInitialize. Valgrind was able to spot that the device was not being closed, and it appears to have been correct. This fixes a memory leak. --- plugins/obs-ffmpeg/vaapi-utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/obs-ffmpeg/vaapi-utils.c b/plugins/obs-ffmpeg/vaapi-utils.c index d636558b709d48..46ac79c185d9fe 100644 --- a/plugins/obs-ffmpeg/vaapi-utils.c +++ b/plugins/obs-ffmpeg/vaapi-utils.c @@ -99,6 +99,7 @@ VADisplay vaapi_open_device(int *fd, const char *device_path, if (va_status != VA_STATUS_SUCCESS) { blog(LOG_ERROR, "VAAPI: Failed to initialize display in %s", func_name); + vaapi_close_device(fd, va_dpy); return NULL; } From 5f98d34e2c4d81a43eafd3d87ae65e67a3d04e06 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Wed, 19 Jun 2024 15:07:59 -0400 Subject: [PATCH 0276/1073] UI: Fix multitrack-video audio track index Fix a minor oversight from a recent commit. Audio track indexing in the UI is 1-based while underlying code uses 0-based indexing. --- UI/window-basic-main-outputs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 3bf83e1d52295e..3473ac2fd50219 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -2287,7 +2287,7 @@ FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) const char *audio_encoder_id = config_get_string(main->Config(), "AdvOut", "AudioEncoder"); int streamTrackIndex = - config_get_int(main->Config(), "AdvOut", "TrackIndex"); + config_get_int(main->Config(), "AdvOut", "TrackIndex") - 1; auto holder = SetupMultitrackVideo(service, audio_encoder_id, From fd34bab6153dc73df352434abe0e553dd48e5826 Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Wed, 19 Jun 2024 15:45:58 -0400 Subject: [PATCH 0277/1073] UI: Link Qt::DBus on FreeBSD As with Linux we need to link Qt::DBus on FreeBSD now that there's a HighContrastEnabled implementation that makes use of it. Fixes: 41ba8bdfdd02 ("UI: Add HighContrastEnabled implementation fo...") --- UI/cmake/os-freebsd.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/cmake/os-freebsd.cmake b/UI/cmake/os-freebsd.cmake index 323600567c7a56..ed4a019063b8ea 100644 --- a/UI/cmake/os-freebsd.cmake +++ b/UI/cmake/os-freebsd.cmake @@ -1,6 +1,6 @@ target_sources(obs-studio PRIVATE platform-x11.cpp) target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}") -target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate procstat) +target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate Qt::DBus procstat) target_sources(obs-studio PRIVATE system-info-posix.cpp) From ed2478535f073b6db678d18972377f8689d74a01 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 21 Jun 2024 08:31:33 +0200 Subject: [PATCH 0278/1073] obs-outputs: Do not create MP4 track chunks without samples --- plugins/obs-outputs/mp4-mux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-outputs/mp4-mux.c b/plugins/obs-outputs/mp4-mux.c index 3d9d0ed07259a9..2d6c1de4756541 100644 --- a/plugins/obs-outputs/mp4-mux.c +++ b/plugins/obs-outputs/mp4-mux.c @@ -2409,7 +2409,7 @@ static void write_packets(struct mp4_mux *mux, struct mp4_track *track) struct serializer *s = mux->serializer; size_t count = track->packets.size / sizeof(struct encoder_packet); - if (!count) + if (!count || !track->fragment_samples.num) return; struct chunk *chk = da_push_back_new(track->chunks); From 6cc0e2b803f2bc1c635ba84dc9ef739885079c0d Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 21 Jun 2024 08:33:00 +0200 Subject: [PATCH 0279/1073] obs-outputs: Fix file splitting ts offset using video DTS instead of PTS --- plugins/obs-outputs/mp4-output.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/obs-outputs/mp4-output.c b/plugins/obs-outputs/mp4-output.c index ef3b54423ad090..fc7d2cbac423a2 100644 --- a/plugins/obs-outputs/mp4-output.c +++ b/plugins/obs-outputs/mp4-output.c @@ -112,20 +112,23 @@ static inline void ts_offset_update(struct mp4_output *out, struct encoder_packet *packet) { int64_t *offset; + int64_t ts; bool *found; if (packet->type == OBS_ENCODER_VIDEO) { offset = &out->video_pts_offsets[packet->track_idx]; found = &out->found_video[packet->track_idx]; + ts = packet->pts; } else { offset = &out->audio_dts_offsets[packet->track_idx]; found = &out->found_audio[packet->track_idx]; + ts = packet->dts; } if (*found) return; - *offset = packet->dts; + *offset = ts; *found = true; } From 4517918c7adaa3453e575c223d52c1b001fbcf23 Mon Sep 17 00:00:00 2001 From: Penwywern Date: Wed, 26 Jun 2024 00:58:16 +0200 Subject: [PATCH 0280/1073] cmake: Fix FFmpeg version regex --- cmake/finders/FindFFmpeg.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/finders/FindFFmpeg.cmake b/cmake/finders/FindFFmpeg.cmake index bf28f8ab95b5dc..e4e746915971f9 100644 --- a/cmake/finders/FindFFmpeg.cmake +++ b/cmake/finders/FindFFmpeg.cmake @@ -277,7 +277,7 @@ endif() if(EXISTS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h") file(STRINGS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h" _version_string - REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\.-]+\"[ \t]*$") + REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\~.-]+\"[ \t]*$") string(REGEX REPLACE ".*FFMPEG_VERSION[ \t]+\"n?([0-9]+\\.[0-9]).*\".*" "\\1" FFmpeg_VERSION "${_version_string}") endif() From b34fbb116e19ac06b92834ff99bfe1b9dacbc02e Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 23 Jun 2024 01:09:05 +0200 Subject: [PATCH 0281/1073] obs-ffmpeg: Check if current NVENC configuration supports 4:4:4 encode --- plugins/obs-ffmpeg/data/locale/en-US.ini | 1 + plugins/obs-ffmpeg/obs-nvenc.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/plugins/obs-ffmpeg/data/locale/en-US.ini b/plugins/obs-ffmpeg/data/locale/en-US.ini index 9652be1600d248..e8b73293c2cea6 100644 --- a/plugins/obs-ffmpeg/data/locale/en-US.ini +++ b/plugins/obs-ffmpeg/data/locale/en-US.ini @@ -40,6 +40,7 @@ NVENC.8bitUnsupportedHdr="OBS does not support 8-bit output of Rec. 2100." NVENC.I010Unsupported="NVENC does not support I010. Use P010 instead." NVENC.10bitUnsupported="Cannot perform 10-bit encode on this encoder." NVENC.16bitUnsupported="Cannot perform 16-bit encode on this encoder." +NVENC.444Unsupported="Cannot perform 4:4:4 encode on this encoder." NVENC.NoAV1FallbackPossible="AV1 encoding is not available with the current settings. Try disabling any re-scaling or GPU options that may be set. Check the log for more details." NVENC.Preset2.p1="P1: Fastest (Lowest Quality)" NVENC.Preset2.p2="P2: Faster (Lower Quality)" diff --git a/plugins/obs-ffmpeg/obs-nvenc.c b/plugins/obs-ffmpeg/obs-nvenc.c index 62e5a4ca228006..54436daeecbbc6 100644 --- a/plugins/obs-ffmpeg/obs-nvenc.c +++ b/plugins/obs-ffmpeg/obs-nvenc.c @@ -1371,12 +1371,19 @@ static bool init_encoder(struct nvenc_data *enc, enum codec_type codec, int bf = (int)obs_data_get_int(settings, "bf"); const bool support_10bit = nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_10BIT_ENCODE); + const bool support_444 = + nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_YUV444_ENCODE); const int bf_max = nv_get_cap(enc, NV_ENC_CAPS_NUM_MAX_BFRAMES); video_t *video = obs_encoder_video(enc->encoder); const struct video_output_info *voi = video_output_get_info(video); enc->in_format = get_preferred_format(voi->format); + if (enc->in_format == VIDEO_FORMAT_I444 && !support_444) { + NV_FAIL(obs_module_text("NVENC.444Unsupported")); + return false; + } + if (is_10_bit(enc) && !support_10bit) { NV_FAIL(obs_module_text("NVENC.10bitUnsupported")); return false; From 2e6e79b4f587bc72315dd047c2a7da11e7a1d5b2 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 23 Jun 2024 01:19:16 +0200 Subject: [PATCH 0282/1073] obs-outputs: Skip trak box if track has no data --- plugins/obs-outputs/mp4-mux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/obs-outputs/mp4-mux.c b/plugins/obs-outputs/mp4-mux.c index 2d6c1de4756541..ea86d8ee5c553c 100644 --- a/plugins/obs-outputs/mp4-mux.c +++ b/plugins/obs-outputs/mp4-mux.c @@ -1718,6 +1718,10 @@ static size_t mp4_write_trak(struct mp4_mux *mux, struct mp4_track *track, struct serializer *s = mux->serializer; int64_t start = serializer_get_pos(s); + /* If track has no data, omit it from full moov. */ + if (!fragmented && !track->chunks.num) + return 0; + write_box(s, 0, "trak"); // tkhd From edcda5a825a530a0d6f7bc9ee501e130ff0b6193 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 22 Jun 2024 17:29:41 +0200 Subject: [PATCH 0283/1073] obs-x264: Ignore stats/qp file and multipass options --- plugins/obs-x264/obs-x264.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c index 9a8c3cb77f12bc..5d95adb0940deb 100644 --- a/plugins/obs-x264/obs-x264.c +++ b/plugins/obs-x264/obs-x264.c @@ -309,7 +309,9 @@ static inline void set_param(struct obs_x264 *obsx264, struct obs_option option) if (strcmp(name, "preset") != 0 && strcmp(name, "profile") != 0 && strcmp(name, "tune") != 0 && strcmp(name, "fps") != 0 && strcmp(name, "force-cfr") != 0 && strcmp(name, "width") != 0 && - strcmp(name, "height") != 0 && strcmp(name, "opencl") != 0) { + strcmp(name, "height") != 0 && strcmp(name, "opencl") != 0 && + strcmp(name, "stats") != 0 && strcmp(name, "qpfile") != 0 && + strcmp(name, "pass") != 0) { if (strcmp(option.name, OPENCL_ALIAS) == 0) name = "opencl"; if (x264_param_parse(&obsx264->params, name, val) != 0) From 47fb19422321dd10ff1a42671e276dd4f14885fb Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Tue, 11 Jun 2024 23:05:16 -0400 Subject: [PATCH 0284/1073] UI: Adjust Yami (Classic) styling --- UI/data/themes/Yami.obt | 15 +++++---- UI/data/themes/Yami_Classic.ovt | 59 +++++++++++++++++++++++++++++++-- UI/forms/OBSBasicFilters.ui | 24 ++++++++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 6c3a92e7de953a..015c006a79db75 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -477,7 +477,7 @@ OBSDock > QWidget { } #transitionsFrame { - padding: 4px 8px; + padding: var(--padding_large); } OBSDock QLabel { @@ -827,7 +827,7 @@ QComboBox::drop-down, QDateTimeEdit::drop-down { border:none; border-left: 1px solid var(--grey6); - width: 32px; + width: var(--input_height); } QComboBox::down-arrow, @@ -923,7 +923,7 @@ QDoubleSpinBox::up-button { subcontrol-origin: padding; subcontrol-position: top right; /* position at the top right corner */ - width: 32px; + width: var(--input_height); height: var(--spinbox_button_height); border-left: 1px solid var(--grey6); border-bottom: 1px solid transparent; @@ -936,7 +936,7 @@ QDoubleSpinBox::down-button { subcontrol-origin: padding; subcontrol-position: bottom right; /* position at the top right corner */ - width: 32px; + width: var(--input_height); height: var(--spinbox_button_height); border-left: 1px solid var(--grey6); border-top: 1px solid var(--grey6); @@ -1078,6 +1078,8 @@ QPushButton:hover { QToolButton:hover, QToolButton:focus, +QPushButton[toolButton="true"]:hover, +QPushButton[toolButton="true"]:focus, MuteCheckBox::indicator:hover, MuteCheckBox::indicator:focus { border-color: var(--button_border); @@ -1104,7 +1106,9 @@ QPushButton:pressed:hover { } QToolButton:pressed, -QToolButton:pressed:hover { +QToolButton:pressed:hover, +QPushButton[toolButton="true"]:pressed, +QPushButton[toolButton="true"]:pressed:hover { background-color: var(--button_bg_down); border-color: var(--button_border); } @@ -1275,7 +1279,6 @@ VolControl #volLabel { #vMixerScrollArea VolControl #volLabel { padding: var(--padding_base) 0px var(--padding_base); min-width: var(--volume_slider_label); - max-width: var(--volume_slider_label); margin-left: var(--padding_xlarge); text-align: center; } diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index 1203e33a795115..05b79419ef7e1b 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -11,7 +11,7 @@ --grey2: rgb(134,135,134); --grey3: rgb(122,121,122); --grey4: rgb(76,76,76); - --grey5: rgb(88,87,88); + --grey5: rgb(70,69,70); --grey6: rgb(31,30,31); --grey7: rgb(58,57,58); --grey8: rgb(46,45,46); @@ -31,8 +31,12 @@ /* OS Fixes */ --os_mac_font_base_value: 11; + --font_small: calc(0.8pt * var(--font_base_value)); + --icon_base: calc(6px + var(--font_base_value)); + --padding_xlarge: calc(2px + calc(0.5px * var(--padding_base_value))); + --padding_wide: calc(18px + calc(0.25 * var(--padding_base_value))); --padding_menu: calc(8px + calc(1 * var(--padding_base_value))); @@ -45,14 +49,14 @@ --border_radius_large: 2px; --input_bg: var(--grey4); - --input_bg_hover: var(--grey5); + --input_bg_hover: var(--grey1); --input_bg_focus: var(--grey6); --list_item_bg_selected: var(--primary); --list_item_bg_hover: var(--primary_light); --input_border: var(--grey4); - --input_border_hover: var(--grey5); + --input_border_hover: var(--grey1); --input_border_focus: var(--grey6); --spacing_input: var(--spacing_base); @@ -91,11 +95,17 @@ QStatusBar { background-color: var(--bg_window); } +OBSDock > QWidget { + border-top: 1px solid var(--border_color); + padding-top: var(--spacing_large); +} + QDockWidget { font-weight: normal; } QDockWidget::title { + background-color: var(--grey5); padding: var(--dock_title_padding); text-align: center; } @@ -104,6 +114,10 @@ QDockWidget > QWidget { background: var(--bg_window); } +#transitionsFrame { + padding: var(--padding_xlarge); +} + SceneTree::item, SourceTreeItem { border-width: 0px; @@ -193,12 +207,20 @@ OBSBasicSettings QListWidget::item { padding: 4px; } +QPushButton:checked { + border-color: var(--primary); +} + QToolButton, QPushButton[toolButton="true"] { background-color: var(--bg_window); border-color: var(--bg_window); } +#stackedMixerArea QScrollArea { + background: var(--bg_base); +} + #stackedMixerArea QPushButton { min-width: var(--icon_base); padding: var(--padding_large) var(--padding_large); @@ -215,6 +237,33 @@ QPushButton[toolButton="true"] { border: none; } +#hMixerScrollArea VolControl { + padding: 0px 2px; + margin-bottom: 1px; +} + +#hMixerScrollArea QLabel { + margin: var(--padding_xlarge) 0px; +} + +#hMixerScrollArea #volMeterFrame { + margin-top: var(--spacing_large); +} + +#vMixerScrollArea VolControl { + padding: 0px var(--padding_xlarge) var(--spacing_large); + border-right: 1px solid var(--bg_window); +} + +#vMixerScrollArea QLabel { + font-size: var(--font_small); + margin: var(--padding_xlarge) 0px; +} + +#vMixerScrollArea #volLabel { + font-size: var(--font_base); +} + MuteCheckBox::indicator, MuteCheckBox::indicator:unchecked { background-color: var(--bg_base); @@ -255,3 +304,7 @@ VolumeMeter { qproperty-minorTickColor: rgb(122,121,122); /* light */ qproperty-meterThickness: 3; } + +OBSBasicStats { + background: var(--bg_window); +} diff --git a/UI/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui index ea0d5ae526e882..941eadcd2625de 100644 --- a/UI/forms/OBSBasicFilters.ui +++ b/UI/forms/OBSBasicFilters.ui @@ -128,6 +128,9 @@ addIconSmall + + true +
@@ -157,6 +160,9 @@ removeIconSmall + + true + @@ -186,6 +192,9 @@ upArrowIconSmall + + true + @@ -215,6 +224,9 @@ downArrowIconSmall + + true + @@ -350,6 +362,9 @@ addIconSmall + + true + @@ -379,6 +394,9 @@ removeIconSmall + + true + @@ -408,6 +426,9 @@ upArrowIconSmall + + true + @@ -437,6 +458,9 @@ downArrowIconSmall + + true + From 4830d6903e8c878ce0784a90658d4c197e5dc154 Mon Sep 17 00:00:00 2001 From: Vainock Date: Sun, 16 Jun 2024 13:13:33 +0200 Subject: [PATCH 0285/1073] UI: Fix capitalization of 'OBS' and 'RTMP' --- UI/data/locale/en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 54496d97220b9e..0361090684c31b 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -1566,10 +1566,10 @@ FailedToStartStream.MissingConfigURL="No config URL available for the current se FailedToStartStream.NoCustomRTMPURLInSettings="Custom RTMP URL not specified" FailedToStartStream.InvalidCustomConfig="Invalid custom config" FailedToStartStream.FailedToCreateMultitrackVideoService="Failed to create multitrack video service" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video rtmp output" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video RTMP output" FailedToStartStream.EncoderNotAvailable="NVENC not available.\n\nFailed to find encoder type '%1'" FailedToStartStream.FailedToCreateVideoEncoder="Failed to create video encoder '%1' (type: '%2')" -FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get obs video info while creating encoder '%1' (type: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get OBS video info while creating encoder '%1' (type: '%2')" FailedToStartStream.FailedToCreateAudioEncoder="Failed to create audio encoder" FailedToStartStream.NoRTMPURLInConfig="Config does not contain stream target RTMP(S) URL" FailedToStartStream.FallbackToDefault="Starting the stream using %1 failed; do you want to retry using single encode settings?" From 7d19add10bce26ef0951bfb811455c44acb3c2d1 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Tue, 18 Jun 2024 13:49:34 +0200 Subject: [PATCH 0286/1073] UI: Display dialog for multitrack video output audio channels mismatch --- UI/data/locale/en-US.ini | 3 + UI/multitrack-video-output.cpp | 133 +++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 5 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 0361090684c31b..b3303340cbe028 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -1585,3 +1585,6 @@ MultitrackVideo.IncompatibleSettings.Title="Incompatible Settings" MultitrackVideo.IncompatibleSettings.Text="%1 is not currently compatible with:\n\n%2\nTo continue streaming with %1, disable incompatible settings:\n\n%3\nand Start Streaming again." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Disable for this stream and Start Streaming" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Update Settings and Start Streaming" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 is not currently compatible with [Audio → General → Channels] set to '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → General → Channels] needs to be set to '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requires multiple different settings for [Audio → General → Channels]" diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index d9888a2c5827e6..4f773d003e7003 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -331,7 +332,8 @@ struct OBSOutputs { }; static OBSOutputs -SetupOBSOutput(obs_data_t *dump_stream_to_file_config, +SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name, + obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, std::shared_ptr &video_encoder_group, @@ -465,7 +467,8 @@ void MultitrackVideoOutput::PrepareStreaming( std::vector audio_encoders; std::shared_ptr video_encoder_group; - auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config, + auto outputs = SetupOBSOutput(parent, multitrack_video_name, + dump_stream_to_file_config, output_config, audio_encoders, video_encoder_group, audio_encoder_id, main_audio_mixer, vod_track_mixer); @@ -730,8 +733,46 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, obs_output_t *output, obs_output_t *recording_output, const char *audio_encoder_id, size_t main_audio_mixer, - std::optional vod_track_mixer) + std::optional vod_track_mixer, + std::vector &speaker_layouts, + speaker_layout ¤t_layout) { + speaker_layout speakers = SPEAKERS_UNKNOWN; + obs_audio_info oai = {}; + if (obs_get_audio_info(&oai)) + speakers = oai.speakers; + + current_layout = speakers; + + auto sanitize_audio_channels = [&](obs_encoder_t *encoder, + uint32_t channels) { + speaker_layout target_speakers = SPEAKERS_UNKNOWN; + for (size_t i = 0; i <= (size_t)SPEAKERS_7POINT1; i++) { + if (get_audio_channels((speaker_layout)i) != channels) + continue; + + target_speakers = (speaker_layout)i; + break; + } + if (target_speakers == SPEAKERS_UNKNOWN) { + blog(LOG_WARNING, + "MultitrackVideoOutput: Could not find " + "speaker layout for %" PRIu32 "channels " + "while configuring encoder '%s'", + channels, obs_encoder_get_name(encoder)); + return; + } + if (speakers != SPEAKERS_UNKNOWN && + (channels > get_audio_channels(speakers) || + speakers == target_speakers)) + return; + + auto it = std::find(std::begin(speaker_layouts), + std::end(speaker_layouts), target_speakers); + if (it == std::end(speaker_layouts)) + speaker_layouts.push_back(target_speakers); + }; + using encoder_configs_type = decltype(go_live_config.audio_configurations.live); DStr encoder_name_buffer; @@ -757,6 +798,10 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config, create_audio_encoder(encoder_name_buffer->array, audio_encoder_id, settings, mixer_idx); + + sanitize_audio_channels(audio_encoder, + configs[i].channels); + obs_output_set_audio_encoder(output, audio_encoder, output_encoder_index); if (recording_output) @@ -784,8 +829,80 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config, return; } +static const char *speaker_layout_to_string(speaker_layout layout) +{ + switch (layout) { + case SPEAKERS_MONO: + return "Mono"; + case SPEAKERS_2POINT1: + return "2.1"; + case SPEAKERS_4POINT0: + return "4.0"; + case SPEAKERS_4POINT1: + return "4.1"; + case SPEAKERS_5POINT1: + return "5.1"; + case SPEAKERS_7POINT1: + return "7.1"; + case SPEAKERS_UNKNOWN: + case SPEAKERS_STEREO: + return "Stereo"; + } + + return "Stereo"; +} + +static void handle_speaker_layout_issues( + QWidget *parent, const QString &multitrack_video_name, + const std::vector &requested_layouts, + speaker_layout layout) +{ + if (requested_layouts.empty()) + return; + + QString message; + if (requested_layouts.size() == 1) { + message = + QTStr("MultitrackVideo.IncompatibleSettings.AudioChannelsSingle") + .arg(QTStr(speaker_layout_to_string( + requested_layouts.front()))); + } else { + message = + QTStr("MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple") + .arg(multitrack_video_name); + } + + QMetaObject::invokeMethod( + parent, + [&] { + QMessageBox mb(parent); + mb.setIcon(QMessageBox::Critical); + mb.setWindowTitle(QTStr( + "MultitrackVideo.IncompatibleSettings.Title")); + mb.setText( + QTStr("MultitrackVideo.IncompatibleSettings.AudioChannels") + .arg(multitrack_video_name) + .arg(QTStr(speaker_layout_to_string( + layout))) + .arg(message)); + + mb.setStandardButtons( + QMessageBox::StandardButton::Cancel); + + mb.exec(); + }, + BlockingConnectionTypeFor(parent)); + + blog(LOG_INFO, + "MultitrackVideoOutput: Attempted to start stream with incompatible " + "audio channel setting. Action taken: cancel"); + + throw MultitrackVideoError::cancel(); +} + static OBSOutputs -SetupOBSOutput(obs_data_t *dump_stream_to_file_config, +SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name, + obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config, std::vector &audio_encoders, std::shared_ptr &video_encoder_group, @@ -803,9 +920,15 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config, recording_output)) return {nullptr, nullptr}; + std::vector requested_speaker_layouts; + speaker_layout current_layout = SPEAKERS_UNKNOWN; create_audio_encoders(go_live_config, audio_encoders, output, recording_output, audio_encoder_id, - main_audio_mixer, vod_track_mixer); + main_audio_mixer, vod_track_mixer, + requested_speaker_layouts, current_layout); + + handle_speaker_layout_issues(parent, multitrack_video_name, + requested_speaker_layouts, current_layout); return {std::move(output), std::move(recording_output)}; } From fb5bbc8575af8beeba1d471b8b77012a617ddbd0 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 19 Jun 2024 15:45:42 -0700 Subject: [PATCH 0287/1073] libobs: Set encoder initialized call closer to shutdown This is mainly code cleanup. --- libobs/obs-encoder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index c90786a0972c9e..b110eb7ceec16d 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -371,6 +371,8 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown) * up again */ if (shutdown) obs_encoder_shutdown(encoder); + encoder->initialized = false; + set_encoder_active(encoder, false); } @@ -810,7 +812,6 @@ static inline bool obs_encoder_stop_internal( if (last) { remove_connection(encoder, true); - encoder->initialized = false; if (encoder->destroy_on_stop) { pthread_mutex_unlock(&encoder->init_mutex); @@ -1334,7 +1335,6 @@ void full_stop(struct obs_encoder *encoder) pthread_mutex_unlock(&encoder->callbacks_mutex); remove_connection(encoder, false); - encoder->initialized = false; } } From 06291c720141ca9ea8c13eee87d297f1c0c01f8b Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 19 Jun 2024 16:13:29 -0700 Subject: [PATCH 0288/1073] libobs: Fix race when to-be-destroyed encoder group finishes stopping Fixes a crash when the following steps occur: - Encoder group created and then started with encoders, only the group owning encoder refs - Encoder group destroy called: group has `destroy_on_stop` set without actual destroy - Outputs holding encoders from stopping are stopped - `remove_connection()` function destroys encoder group, releasing encoders and causing the currently processed encoder to be destroyed early - parent scopes try to access destroyed encoder pointer and crash This change moves some logic around to improve the release/destruct order of `obs_encoder_stop()` to fix the race above. Note: Cases of encoder errors will not destruct the group if it has `destroy_on_stop` set, as the encoder is also not destroyed if it also is set to destroy on stop. This part of the code should be revisited at a later date and fixed up to prevent memory leaks. --- libobs/obs-encoder.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index b110eb7ceec16d..69a269a8ab5b55 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -353,15 +353,9 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown) if (encoder->encoder_group) { pthread_mutex_lock(&encoder->encoder_group->mutex); - if (--encoder->encoder_group->num_encoders_started == 0) { + if (--encoder->encoder_group->num_encoders_started == 0) encoder->encoder_group->start_timestamp = 0; - if (encoder->encoder_group->destroy_on_stop) - obs_encoder_group_actually_destroy( - encoder->encoder_group); - } - - if (encoder->encoder_group) - pthread_mutex_unlock(&encoder->encoder_group->mutex); + pthread_mutex_unlock(&encoder->encoder_group->mutex); } /* obs_encoder_shutdown locks init_mutex, so don't call it on encode @@ -792,7 +786,7 @@ void obs_encoder_start(obs_encoder_t *encoder, pthread_mutex_unlock(&encoder->init_mutex); } -static inline bool obs_encoder_stop_internal( +static inline void obs_encoder_stop_internal( obs_encoder_t *encoder, void (*new_packet)(void *param, struct encoder_packet *packet), void *param) @@ -800,6 +794,7 @@ static inline bool obs_encoder_stop_internal( bool last = false; size_t idx; + pthread_mutex_lock(&encoder->init_mutex); pthread_mutex_lock(&encoder->callbacks_mutex); idx = get_callback_idx(encoder, new_packet, param); @@ -812,15 +807,32 @@ static inline bool obs_encoder_stop_internal( if (last) { remove_connection(encoder, true); + pthread_mutex_unlock(&encoder->init_mutex); + + struct obs_encoder_group *group = encoder->encoder_group; - if (encoder->destroy_on_stop) { - pthread_mutex_unlock(&encoder->init_mutex); + if (encoder->destroy_on_stop) obs_encoder_actually_destroy(encoder); - return true; + + /* Destroying the group all the way back here prevents a race + * where destruction of the group can prematurely destroy the + * encoder within internal functions. This is the point where it + * is safe to destroy the group, even if the encoder is then + * also destroyed. */ + if (group) { + pthread_mutex_lock(&group->mutex); + if (group->destroy_on_stop && + group->num_encoders_started == 0) + obs_encoder_group_actually_destroy(group); + else + pthread_mutex_unlock(&group->mutex); } + + /* init_mutex already unlocked */ + return; } - return false; + pthread_mutex_unlock(&encoder->init_mutex); } void obs_encoder_stop(obs_encoder_t *encoder, @@ -835,10 +847,7 @@ void obs_encoder_stop(obs_encoder_t *encoder, if (!obs_ptr_valid(new_packet, "obs_encoder_stop")) return; - pthread_mutex_lock(&encoder->init_mutex); - destroyed = obs_encoder_stop_internal(encoder, new_packet, param); - if (!destroyed) - pthread_mutex_unlock(&encoder->init_mutex); + obs_encoder_stop_internal(encoder, new_packet, param); } const char *obs_encoder_get_codec(const obs_encoder_t *encoder) From 8892edde05f7d120f35f7006c2c9a6ccfa2f080b Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 19 Jun 2024 16:30:34 -0700 Subject: [PATCH 0289/1073] libobs: Merge `obs_encoder_stop()` and `..._stop_internal()` There is no longer any need for them to be separate functions. This is just code cleanup. --- libobs/obs-encoder.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 69a269a8ab5b55..908ef2993eff3c 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -786,14 +786,19 @@ void obs_encoder_start(obs_encoder_t *encoder, pthread_mutex_unlock(&encoder->init_mutex); } -static inline void obs_encoder_stop_internal( - obs_encoder_t *encoder, - void (*new_packet)(void *param, struct encoder_packet *packet), - void *param) +void obs_encoder_stop(obs_encoder_t *encoder, + void (*new_packet)(void *param, + struct encoder_packet *packet), + void *param) { bool last = false; size_t idx; + if (!obs_encoder_valid(encoder, "obs_encoder_stop")) + return; + if (!obs_ptr_valid(new_packet, "obs_encoder_stop")) + return; + pthread_mutex_lock(&encoder->init_mutex); pthread_mutex_lock(&encoder->callbacks_mutex); @@ -835,21 +840,6 @@ static inline void obs_encoder_stop_internal( pthread_mutex_unlock(&encoder->init_mutex); } -void obs_encoder_stop(obs_encoder_t *encoder, - void (*new_packet)(void *param, - struct encoder_packet *packet), - void *param) -{ - bool destroyed; - - if (!obs_encoder_valid(encoder, "obs_encoder_stop")) - return; - if (!obs_ptr_valid(new_packet, "obs_encoder_stop")) - return; - - obs_encoder_stop_internal(encoder, new_packet, param); -} - const char *obs_encoder_get_codec(const obs_encoder_t *encoder) { return obs_encoder_valid(encoder, "obs_encoder_get_codec") From 9d67bf2662158e7e652972576a4dd436a792d628 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 26 Jun 2024 17:53:40 -0400 Subject: [PATCH 0290/1073] Revert "plugins/win-dshow: Add CUDA decoder" This reverts commit ce4c99be4e52950a4bd49eb68d99ccdda2e87f96. This was causing infinitely looping log errors in systems with no CUDA-capable hardware when hardware decoding was enabled on video capture devices with custom config enabled. --- plugins/win-dshow/ffmpeg-decode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/win-dshow/ffmpeg-decode.c b/plugins/win-dshow/ffmpeg-decode.c index 20ad565903650f..e5ba5ea4fa9ebc 100644 --- a/plugins/win-dshow/ffmpeg-decode.c +++ b/plugins/win-dshow/ffmpeg-decode.c @@ -23,8 +23,10 @@ #endif enum AVHWDeviceType hw_priority[] = { - AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2, - AV_HWDEVICE_TYPE_QSV, AV_HWDEVICE_TYPE_NONE, + AV_HWDEVICE_TYPE_D3D11VA, + AV_HWDEVICE_TYPE_DXVA2, + AV_HWDEVICE_TYPE_QSV, + AV_HWDEVICE_TYPE_NONE, }; static bool has_hw_type(const AVCodec *c, enum AVHWDeviceType type) From 8c71b0b586a0b0a21fb6de9113c57de168d28668 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Tue, 2 Jul 2024 19:38:48 -0700 Subject: [PATCH 0291/1073] libobs: Fix plane heights for odd values --- libobs/media-io/video-frame.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c index 6da6d2a9e00a66..37aeea99f3bb79 100644 --- a/libobs/media-io/video-frame.c +++ b/libobs/media-io/video-frame.c @@ -133,14 +133,14 @@ void video_frame_get_plane_heights(uint32_t heights[MAX_AV_PLANES], case VIDEO_FORMAT_I420: /* three planes: full height, half height, half height */ case VIDEO_FORMAT_I010: heights[0] = height; - heights[1] = height / 2; - heights[2] = height / 2; + heights[1] = HALF(height); + heights[2] = HALF(height); break; case VIDEO_FORMAT_NV12: /* two planes: full height, half height */ case VIDEO_FORMAT_P010: heights[0] = height; - heights[1] = height / 2; + heights[1] = HALF(height); break; case VIDEO_FORMAT_Y800: /* one plane: full height */ @@ -168,8 +168,8 @@ void video_frame_get_plane_heights(uint32_t heights[MAX_AV_PLANES], case VIDEO_FORMAT_I40A: /* four planes: full height, half height, half height, full height */ heights[0] = height; - heights[1] = height / 2; - heights[2] = height / 2; + heights[1] = HALF(height); + heights[2] = HALF(height); heights[3] = height; break; From c6a100a9803f363e4f031843f12550c8a9a581ed Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 3 Jul 2024 18:11:44 +0200 Subject: [PATCH 0292/1073] obs-ffmpeg: Honor preferred format in native NVENC --- plugins/obs-ffmpeg/obs-nvenc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-nvenc.c b/plugins/obs-ffmpeg/obs-nvenc.c index 54436daeecbbc6..33dec0548be270 100644 --- a/plugins/obs-ffmpeg/obs-nvenc.c +++ b/plugins/obs-ffmpeg/obs-nvenc.c @@ -1377,7 +1377,12 @@ static bool init_encoder(struct nvenc_data *enc, enum codec_type codec, video_t *video = obs_encoder_video(enc->encoder); const struct video_output_info *voi = video_output_get_info(video); - enc->in_format = get_preferred_format(voi->format); + enum video_format pref_format = + obs_encoder_get_preferred_video_format(enc->encoder); + if (pref_format == VIDEO_FORMAT_NONE) + pref_format = voi->format; + + enc->in_format = get_preferred_format(pref_format); if (enc->in_format == VIDEO_FORMAT_I444 && !support_444) { NV_FAIL(obs_module_text("NVENC.444Unsupported")); From 877ddad270f192719f1794c726e8d50e72bd6fe4 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 20 Jun 2024 04:10:19 +0200 Subject: [PATCH 0293/1073] win-wasapi: Remove noisy and useless debug logging --- plugins/win-wasapi/win-wasapi.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/win-wasapi/win-wasapi.cpp b/plugins/win-wasapi/win-wasapi.cpp index 367152d1c5340a..d3cbae5dd18a7c 100644 --- a/plugins/win-wasapi/win-wasapi.cpp +++ b/plugins/win-wasapi/win-wasapi.cpp @@ -1109,16 +1109,7 @@ bool WASAPISource::ProcessCaptureData() sawBadTimestamp = true; } - if (flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) { - /* libobs should handle discontinuities fine. */ - blog(LOG_DEBUG, "[WASAPISource::ProcessCaptureData]" - " Discontinuity flag is set."); - } - if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { - blog(LOG_DEBUG, "[WASAPISource::ProcessCaptureData]" - " Silent flag is set."); - /* buffer size = frame size * number of frames * frame size = channels * sample size * sample size = 4 bytes (always float per InitFormat) */ From 5545674e6d4d9d0bbc58c33252bc7d0d00d3975b Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 7 Jul 2024 11:00:52 +0200 Subject: [PATCH 0294/1073] CI: Fix flatpak-builder-lint action - Fix for loops syntax - Manage jq empty output --- .github/actions/flatpak-builder-lint/action.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/flatpak-builder-lint/action.yaml b/.github/actions/flatpak-builder-lint/action.yaml index 2defa012435f8a..86cd7179337b36 100644 --- a/.github/actions/flatpak-builder-lint/action.yaml +++ b/.github/actions/flatpak-builder-lint/action.yaml @@ -51,15 +51,16 @@ runs: exit $exit_code fi - for ((i = 0 ; i < $(echo $ret | jq '.warnings | length') ; i++)); do + n_warnings=$(echo $ret | jq '.warnings | length') + for ((i = 0 ; i < n_warnings ; i++)); do warning=$(echo $ret | jq ".warnings[$i]") echo "::warning::$warning found in the Flatpak ${{ inputs.artifact }}" done n_errors=$(echo $ret | jq '.errors | length') - for ((i = 0 ; i < $n_errors ; i++)); do + for ((i = 0 ; i < n_errors ; i++)); do error=$(echo $ret | jq ".errors[$i]") echo "::error::$error found in the Flatpak ${{ inputs.artifact }}" done - [[ $n_errors == 0 ]] || exit 2 + [[ -z $n_errors || $n_errors == 0 ]] || exit 2 From b74e7ede0eb3c6c556e5a2563a5eadf55c27cd61 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Tue, 9 Jul 2024 13:25:49 +0200 Subject: [PATCH 0295/1073] UI: Move code out of unnamed lambdas This is in preparation of the next change, to hopefully minimize the resulting diff --- UI/window-basic-main-outputs.cpp | 315 ++++++++++++++----------------- UI/window-basic-main.cpp | 8 +- 2 files changed, 150 insertions(+), 173 deletions(-) diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 3473ac2fd50219..4ba3b996385e45 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1137,65 +1137,56 @@ FutureHolder SimpleOutput::SetupStreaming(obs_service_t *service) auto vod_track_mixer = IsVodTrackEnabled(service) ? std::optional{1} : std::nullopt; + auto handle_multitrack_video_result = [&](std::optional + multitrackVideoResult) { + if (multitrackVideoResult.has_value()) + return multitrackVideoResult.value(); + + /* XXX: this is messy and disgusting and should be refactored */ + if (outputType != type) { + streamDelayStarting.Disconnect(); + streamStopping.Disconnect(); + startStreaming.Disconnect(); + stopStreaming.Disconnect(); + + streamOutput = obs_output_create(type, "simple_stream", + nullptr, nullptr); + if (!streamOutput) { + blog(LOG_WARNING, + "Creation of stream output type '%s' " + "failed!", + type); + return false; + } + + streamDelayStarting.Connect( + obs_output_get_signal_handler(streamOutput), + "starting", OBSStreamStarting, this); + streamStopping.Connect( + obs_output_get_signal_handler(streamOutput), + "stopping", OBSStreamStopping, this); + + startStreaming.Connect( + obs_output_get_signal_handler(streamOutput), + "start", OBSStartStreaming, this); + stopStreaming.Connect( + obs_output_get_signal_handler(streamOutput), + "stop", OBSStopStreaming, this); + + outputType = type; + } + + obs_output_set_video_encoder(streamOutput, videoStreaming); + obs_output_set_audio_encoder(streamOutput, audioStreaming, 0); + obs_output_set_service(streamOutput, service); + return true; + }; + auto holder = SetupMultitrackVideo( service, GetSimpleAACEncoderForBitrate(audio_bitrate), 0, vod_track_mixer); - auto future = - PreventFutureDeadlock(holder.future) - .then(main, [&](std::optional - multitrackVideoResult) { - if (multitrackVideoResult.has_value()) - return multitrackVideoResult.value(); - - /* XXX: this is messy and disgusting and should be refactored */ - if (outputType != type) { - streamDelayStarting.Disconnect(); - streamStopping.Disconnect(); - startStreaming.Disconnect(); - stopStreaming.Disconnect(); - - streamOutput = obs_output_create( - type, "simple_stream", nullptr, - nullptr); - if (!streamOutput) { - blog(LOG_WARNING, - "Creation of stream output type '%s' " - "failed!", - type); - return false; - } - - streamDelayStarting.Connect( - obs_output_get_signal_handler( - streamOutput), - "starting", OBSStreamStarting, - this); - streamStopping.Connect( - obs_output_get_signal_handler( - streamOutput), - "stopping", OBSStreamStopping, - this); - - startStreaming.Connect( - obs_output_get_signal_handler( - streamOutput), - "start", OBSStartStreaming, - this); - stopStreaming.Connect( - obs_output_get_signal_handler( - streamOutput), - "stop", OBSStopStreaming, this); - - outputType = type; - } - - obs_output_set_video_encoder(streamOutput, - videoStreaming); - obs_output_set_audio_encoder(streamOutput, - audioStreaming, 0); - obs_output_set_service(streamOutput, service); - return true; - }); + auto future = PreventFutureDeadlock(holder.future) + .then(main, handle_multitrack_video_result); return {holder.cancelAll, future}; } @@ -2289,84 +2280,71 @@ FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) int streamTrackIndex = config_get_int(main->Config(), "AdvOut", "TrackIndex") - 1; - auto holder = - SetupMultitrackVideo(service, audio_encoder_id, - static_cast(streamTrackIndex), - VodTrackMixerIdx(service)); - auto future = - PreventFutureDeadlock(holder.future) - .then(main, [&](std::optional - multitrackVideoResult) { - if (multitrackVideoResult.has_value()) - return multitrackVideoResult.value(); - - /* XXX: this is messy and disgusting and should be refactored */ - if (outputType != type) { - streamDelayStarting.Disconnect(); - streamStopping.Disconnect(); - startStreaming.Disconnect(); - stopStreaming.Disconnect(); - - streamOutput = obs_output_create( - type, "adv_stream", nullptr, - nullptr); - if (!streamOutput) { - blog(LOG_WARNING, - "Creation of stream output type '%s' " - "failed!", - type); - return false; - } + auto handle_multitrack_video_result = [&](std::optional + multitrackVideoResult) { + if (multitrackVideoResult.has_value()) + return multitrackVideoResult.value(); + + /* XXX: this is messy and disgusting and should be refactored */ + if (outputType != type) { + streamDelayStarting.Disconnect(); + streamStopping.Disconnect(); + startStreaming.Disconnect(); + stopStreaming.Disconnect(); + + streamOutput = obs_output_create(type, "adv_stream", + nullptr, nullptr); + if (!streamOutput) { + blog(LOG_WARNING, + "Creation of stream output type '%s' " + "failed!", + type); + return false; + } - streamDelayStarting.Connect( - obs_output_get_signal_handler( - streamOutput), - "starting", OBSStreamStarting, - this); - streamStopping.Connect( - obs_output_get_signal_handler( - streamOutput), - "stopping", OBSStreamStopping, - this); - - startStreaming.Connect( - obs_output_get_signal_handler( - streamOutput), - "start", OBSStartStreaming, - this); - stopStreaming.Connect( - obs_output_get_signal_handler( - streamOutput), - "stop", OBSStopStreaming, this); - - outputType = type; - } + streamDelayStarting.Connect( + obs_output_get_signal_handler(streamOutput), + "starting", OBSStreamStarting, this); + streamStopping.Connect( + obs_output_get_signal_handler(streamOutput), + "stopping", OBSStreamStopping, this); + + startStreaming.Connect( + obs_output_get_signal_handler(streamOutput), + "start", OBSStartStreaming, this); + stopStreaming.Connect( + obs_output_get_signal_handler(streamOutput), + "stop", OBSStopStreaming, this); + + outputType = type; + } - obs_output_set_video_encoder(streamOutput, - videoStreaming); - obs_output_set_audio_encoder(streamOutput, - streamAudioEnc, 0); + obs_output_set_video_encoder(streamOutput, videoStreaming); + obs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0); - if (!is_multitrack_output) { + if (!is_multitrack_output) { + obs_output_set_audio_encoder(streamOutput, + streamAudioEnc, 0); + } else { + for (int i = 0; i < MAX_AUDIO_MIXES; i++) { + if ((multiTrackAudioMixes & (1 << i)) != 0) { obs_output_set_audio_encoder( - streamOutput, streamAudioEnc, - 0); - } else { - for (int i = 0; i < MAX_AUDIO_MIXES; - i++) { - if ((multiTrackAudioMixes & - (1 << i)) != 0) { - obs_output_set_audio_encoder( - streamOutput, - streamTrack[i], - idx); - idx++; - } - } + streamOutput, streamTrack[i], + idx); + idx++; } + } + } + + return true; + }; - return true; - }); + auto holder = + SetupMultitrackVideo(service, audio_encoder_id, + static_cast(streamTrackIndex), + VodTrackMixerIdx(service)); + auto future = PreventFutureDeadlock(holder.future) + .then(main, handle_multitrack_video_result); return {holder.cancelAll, future}; } @@ -2773,6 +2751,44 @@ FutureHolder> BasicOutputHandler::SetupMultitrackVideo( auto stream_dump_config = GenerateMultitrackVideoStreamDumpConfig(); + auto continue_on_main_thread = + [&, service = OBSService{service}]( + std::optional error) + -> std::optional { + if (error) { + OBSDataAutoRelease service_settings = + obs_service_get_settings(service); + auto multitrack_video_name = QTStr( + "Basic.Settings.Stream.MultitrackVideoLabel"); + if (obs_data_has_user_value(service_settings, + "multitrack_video_name")) { + multitrack_video_name = obs_data_get_string( + service_settings, + "multitrack_video_name"); + } + + multitrackVideoActive = false; + if (!error->ShowDialog(main, multitrack_video_name)) + return false; + return std::nullopt; + } + + multitrackVideoActive = true; + + auto signal_handler = multitrackVideo->StreamingSignalHandler(); + + streamDelayStarting.Connect(signal_handler, "starting", + OBSStreamStarting, this); + streamStopping.Connect(signal_handler, "stopping", + OBSStreamStopping, this); + + startStreaming.Connect(signal_handler, "start", + OBSStartStreaming, this); + stopStreaming.Connect(signal_handler, "stop", OBSStopStreaming, + this); + return true; + }; + auto firstFuture = CreateFuture().then( QThreadPool::globalInstance(), [=, multitrackVideo = multitrackVideo.get(), @@ -2795,48 +2811,7 @@ FutureHolder> BasicOutputHandler::SetupMultitrackVideo( return std::nullopt; }); - auto secondFuture = firstFuture.then( - main, - [&, service = OBSService{service}]( - std::optional error) - -> std::optional { - if (error) { - OBSDataAutoRelease service_settings = - obs_service_get_settings(service); - auto multitrack_video_name = QTStr( - "Basic.Settings.Stream.MultitrackVideoLabel"); - if (obs_data_has_user_value( - service_settings, - "multitrack_video_name")) { - multitrack_video_name = - obs_data_get_string( - service_settings, - "multitrack_video_name"); - } - - multitrackVideoActive = false; - if (!error->ShowDialog(main, - multitrack_video_name)) - return false; - return std::nullopt; - } - - multitrackVideoActive = true; - - auto signal_handler = - multitrackVideo->StreamingSignalHandler(); - - streamDelayStarting.Connect(signal_handler, "starting", - OBSStreamStarting, this); - streamStopping.Connect(signal_handler, "stopping", - OBSStreamStopping, this); - - startStreaming.Connect(signal_handler, "start", - OBSStartStreaming, this); - stopStreaming.Connect(signal_handler, "stop", - OBSStopStreaming, this); - return true; - }); + auto secondFuture = firstFuture.then(main, continue_on_main_thread); return {[=]() mutable { firstFuture.cancel(); }, PreventFutureDeadlock(secondFuture)}; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 8f5fc43c1b357b..36015b6131beb4 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -7109,8 +7109,7 @@ void OBSBasic::StartStreaming() sysTrayStream->setText("Basic.Main.PreparingStream"); } - auto holder = outputHandler->SetupStreaming(service); - auto future = holder.future.then(this, [&](bool setupStreamingResult) { + auto finish_stream_setup = [&](bool setupStreamingResult) { if (!setupStreamingResult) { DisplayStreamStartError(); return; @@ -7152,7 +7151,10 @@ void OBSBasic::StartStreaming() if (!autoStartBroadcast) OBSBasic::ShowYouTubeAutoStartWarning(); #endif - }); + }; + + auto holder = outputHandler->SetupStreaming(service); + auto future = holder.future.then(this, finish_stream_setup); startStreamingFuture = {holder.cancelAll, future}; } From 732766311275f776978f0a8458bda1f5a0874575 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Mon, 8 Jul 2024 14:07:10 +0200 Subject: [PATCH 0296/1073] UI: Remove `QFuture` usage `QFuture`s are broken on older Qt versions, even with the deadlock workaround (see ) --- UI/CMakeLists.txt | 2 - UI/goliveapi-network.hpp | 1 - UI/multitrack-video-output.cpp | 1 - UI/multitrack-video-output.hpp | 5 +- UI/qt-helpers.cpp | 10 --- UI/qt-helpers.hpp | 46 ----------- UI/window-basic-main-outputs.cpp | 129 ++++++++++++++++++++----------- UI/window-basic-main-outputs.hpp | 15 ++-- UI/window-basic-main.cpp | 26 +++---- UI/window-basic-main.hpp | 5 +- 10 files changed, 105 insertions(+), 135 deletions(-) delete mode 100644 UI/qt-helpers.cpp delete mode 100644 UI/qt-helpers.hpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 9a77da80f7f7b0..5b0c81c996429c 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -98,8 +98,6 @@ target_sources( multitrack-video-error.hpp multitrack-video-output.cpp multitrack-video-output.hpp - qt-helpers.cpp - qt-helpers.hpp system-info.hpp) if(OS_WINDOWS) diff --git a/UI/goliveapi-network.hpp b/UI/goliveapi-network.hpp index 2fd3def43c8cd2..e543640d97ff7e 100644 --- a/UI/goliveapi-network.hpp +++ b/UI/goliveapi-network.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include "models/multitrack-video.hpp" diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 4f773d003e7003..3a1f3430bb85ab 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -35,7 +35,6 @@ #include "goliveapi-postdata.hpp" #include "goliveapi-network.hpp" #include "multitrack-video-error.hpp" -#include "qt-helpers.hpp" #include "models/multitrack-video.hpp" Qt::ConnectionType BlockingConnectionTypeFor(QObject *object) diff --git a/UI/multitrack-video-output.hpp b/UI/multitrack-video-output.hpp index 83aa48e182ac43..71f590aca7c3bf 100644 --- a/UI/multitrack-video-output.hpp +++ b/UI/multitrack-video-output.hpp @@ -4,12 +4,11 @@ #include #include +#include #include #include -#include -#include -#include +#include #define NOMINMAX diff --git a/UI/qt-helpers.cpp b/UI/qt-helpers.cpp deleted file mode 100644 index ad76bd5a1bbd27..00000000000000 --- a/UI/qt-helpers.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "qt-helpers.hpp" - -QFuture CreateFuture() -{ - QPromise promise; - auto future = promise.future(); - promise.start(); - promise.finish(); - return future; -} diff --git a/UI/qt-helpers.hpp b/UI/qt-helpers.hpp deleted file mode 100644 index c0d7328c08215d..00000000000000 --- a/UI/qt-helpers.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include - -template struct FutureHolder { - std::function cancelAll; - QFuture future; -}; - -QFuture CreateFuture(); - -template inline QFuture PreventFutureDeadlock(QFuture future) -{ - /* - * QFutures deadlock if there are continuations on the same thread that - * need to wait for the previous continuation to finish, see - * https://github.com/qt/qtbase/commit/59e21a536f7f81625216dc7a621e7be59919da33 - * - * related bugs: - * https://bugreports.qt.io/browse/QTBUG-119406 - * https://bugreports.qt.io/browse/QTBUG-119103 - * https://bugreports.qt.io/browse/QTBUG-117918 - * https://bugreports.qt.io/browse/QTBUG-119579 - * https://bugreports.qt.io/browse/QTBUG-119810 - * @RytoEX's summary: - * QTBUG-119406 and QTBUG-119103 affect Qt 6.6.0 and are fixed in Qt 6.6.2 and 6.7.0+. - * QTBUG-119579 and QTBUG-119810 affect Qt 6.6.1 and are fixed in Qt 6.6.2 and 6.7.0+. - * QTBUG-117918 is the only strange one that seems to possibly affect all Qt 6.x versions - * until 6.6.2, but only in Debug builds. - * - * To fix this, move relevant QFutures to another thread before resuming - * on main thread for affected Qt versions - */ -#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) && \ - (QT_VERSION < QT_VERSION_CHECK(6, 6, 2)) - if (future.isFinished()) { - return future; - } - - return future.then(QtFuture::Launch::Async, [](T val) { return val; }); -#else - return future; -#endif -} diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 4ba3b996385e45..9e644e90acb8df 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include "qt-wrappers.hpp" #include "audio-encoders.hpp" #include "multitrack-video-error.hpp" @@ -183,6 +183,28 @@ static void OBSDeactivateVirtualCam(void *data, calldata_t * /* params */) /* ------------------------------------------------------------------------ */ +struct StartMultitrackVideoStreamingGuard { + StartMultitrackVideoStreamingGuard() + { + future = guard.get_future().share(); + }; + ~StartMultitrackVideoStreamingGuard() { guard.set_value(); } + + std::shared_future GetFuture() const { return future; } + + static std::shared_future MakeReadyFuture() + { + StartMultitrackVideoStreamingGuard guard; + return guard.GetFuture(); + } + +private: + std::promise guard; + std::shared_future future; +}; + +/* ------------------------------------------------------------------------ */ + static bool CreateSimpleAACEncoder(OBSEncoder &res, int bitrate, const char *name, size_t idx) { @@ -520,8 +542,9 @@ struct SimpleOutput : BasicOutputHandler { bool IsVodTrackEnabled(obs_service_t *service); void SetupVodTrack(obs_service_t *service); - virtual FutureHolder - SetupStreaming(obs_service_t *service) override; + virtual std::shared_future + SetupStreaming(obs_service_t *service, + SetupStreamingContinuation_t continuation) override; virtual bool StartStreaming(obs_service_t *service) override; virtual bool StartRecording() override; virtual bool StartReplayBuffer() override; @@ -1118,7 +1141,9 @@ const char *FindAudioEncoderFromCodec(const char *type) return nullptr; } -FutureHolder SimpleOutput::SetupStreaming(obs_service_t *service) +std::shared_future +SimpleOutput::SetupStreaming(obs_service_t *service, + SetupStreamingContinuation_t continuation) { if (!Active()) SetupOutputs(); @@ -1130,8 +1155,10 @@ FutureHolder SimpleOutput::SetupStreaming(obs_service_t *service) /* --------------------- */ const char *type = GetStreamOutputType(service); - if (!type) - return {[] {}, CreateFuture().then([] { return false; })}; + if (!type) { + continuation(false); + return StartMultitrackVideoStreamingGuard::MakeReadyFuture(); + } auto audio_bitrate = GetAudioBitrate(); auto vod_track_mixer = IsVodTrackEnabled(service) ? std::optional{1} @@ -1182,12 +1209,11 @@ FutureHolder SimpleOutput::SetupStreaming(obs_service_t *service) return true; }; - auto holder = SetupMultitrackVideo( + return SetupMultitrackVideo( service, GetSimpleAACEncoderForBitrate(audio_bitrate), 0, - vod_track_mixer); - auto future = PreventFutureDeadlock(holder.future) - .then(main, handle_multitrack_video_result); - return {holder.cancelAll, future}; + vod_track_mixer, [&, continuation](std::optional res) { + continuation(handle_multitrack_video_result(res)); + }); } static inline bool ServiceSupportsVodTrack(const char *service); @@ -1567,8 +1593,9 @@ struct AdvancedOutput : BasicOutputHandler { void SetupOutputs() override; int GetAudioBitrate(size_t i, const char *id) const; - virtual FutureHolder - SetupStreaming(obs_service_t *service) override; + virtual std::shared_future + SetupStreaming(obs_service_t *service, + SetupStreamingContinuation_t continuation) override; virtual bool StartStreaming(obs_service_t *service) override; virtual bool StartRecording() override; virtual bool StartReplayBuffer() override; @@ -2247,7 +2274,9 @@ inline void AdvancedOutput::SetupVodTrack(obs_service_t *service) clear_archive_encoder(streamOutput, ADV_ARCHIVE_NAME); } -FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) +std::shared_future +AdvancedOutput::SetupStreaming(obs_service_t *service, + SetupStreamingContinuation_t continuation) { int multiTrackAudioMixes = config_get_int(main->Config(), "AdvOut", "StreamMultiTrackAudioMixes"); @@ -2272,8 +2301,10 @@ FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) /* --------------------- */ const char *type = GetStreamOutputType(service); - if (!type) - return {[] {}, CreateFuture().then(main, [] { return false; })}; + if (!type) { + continuation(false); + return StartMultitrackVideoStreamingGuard::MakeReadyFuture(); + } const char *audio_encoder_id = config_get_string(main->Config(), "AdvOut", "AudioEncoder"); @@ -2339,13 +2370,13 @@ FutureHolder AdvancedOutput::SetupStreaming(obs_service_t *service) return true; }; - auto holder = - SetupMultitrackVideo(service, audio_encoder_id, - static_cast(streamTrackIndex), - VodTrackMixerIdx(service)); - auto future = PreventFutureDeadlock(holder.future) - .then(main, handle_multitrack_video_result); - return {holder.cancelAll, future}; + return SetupMultitrackVideo( + service, audio_encoder_id, + static_cast(streamTrackIndex), + VodTrackMixerIdx(service), + [&, continuation](std::optional res) { + continuation(handle_multitrack_video_result(res)); + }); } bool AdvancedOutput::StartStreaming(obs_service_t *service) @@ -2683,14 +2714,17 @@ std::string BasicOutputHandler::GetRecordingFilename( extern std::string DeserializeConfigText(const char *text); -FutureHolder> BasicOutputHandler::SetupMultitrackVideo( +std::shared_future BasicOutputHandler::SetupMultitrackVideo( obs_service_t *service, std::string audio_encoder_id, - size_t main_audio_mixer, std::optional vod_track_mixer) + size_t main_audio_mixer, std::optional vod_track_mixer, + std::function)> continuation) { - if (!multitrackVideo) - return {[] {}, CreateFuture().then([] { - return std::optional{std::nullopt}; - })}; + auto start_streaming_guard = + std::make_shared(); + if (!multitrackVideo) { + continuation(std::nullopt); + return start_streaming_guard->GetFuture(); + } multitrackVideoActive = false; @@ -2751,10 +2785,11 @@ FutureHolder> BasicOutputHandler::SetupMultitrackVideo( auto stream_dump_config = GenerateMultitrackVideoStreamDumpConfig(); - auto continue_on_main_thread = - [&, service = OBSService{service}]( - std::optional error) - -> std::optional { + auto continue_on_main_thread = [&, start_streaming_guard, + service = OBSService{service}, + continuation = std::move(continuation)]( + std::optional + error) { if (error) { OBSDataAutoRelease service_settings = obs_service_get_settings(service); @@ -2769,8 +2804,8 @@ FutureHolder> BasicOutputHandler::SetupMultitrackVideo( multitrackVideoActive = false; if (!error->ShowDialog(main, multitrack_video_name)) - return false; - return std::nullopt; + return continuation(false); + return continuation(std::nullopt); } multitrackVideoActive = true; @@ -2786,16 +2821,16 @@ FutureHolder> BasicOutputHandler::SetupMultitrackVideo( OBSStartStreaming, this); stopStreaming.Connect(signal_handler, "stop", OBSStopStreaming, this); - return true; + return continuation(true); }; - auto firstFuture = CreateFuture().then( - QThreadPool::globalInstance(), + QThreadPool::globalInstance()->start( [=, multitrackVideo = multitrackVideo.get(), service_name = std::string{service_name}, service = OBSService{service}, - stream_dump_config = std::move(stream_dump_config)]() - -> std::optional { + stream_dump_config = OBSData{stream_dump_config}, + start_streaming_guard = start_streaming_guard]() mutable { + std::optional error; try { multitrackVideo->PrepareStreaming( main, service_name.c_str(), service, @@ -2805,16 +2840,16 @@ FutureHolder> BasicOutputHandler::SetupMultitrackVideo( maximum_video_tracks, custom_config, stream_dump_config, main_audio_mixer, vod_track_mixer); - } catch (const MultitrackVideoError &error) { - return error; + } catch (const MultitrackVideoError &error_) { + error.emplace(error_); } - return std::nullopt; - }); - auto secondFuture = firstFuture.then(main, continue_on_main_thread); + QMetaObject::invokeMethod(main, [=] { + continue_on_main_thread(error); + }); + }); - return {[=]() mutable { firstFuture.cancel(); }, - PreventFutureDeadlock(secondFuture)}; + return start_streaming_guard->GetFuture(); } OBSDataAutoRelease BasicOutputHandler::GenerateMultitrackVideoStreamDumpConfig() diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp index abf40d66ff4d87..870f720a225ae3 100644 --- a/UI/window-basic-main-outputs.hpp +++ b/UI/window-basic-main-outputs.hpp @@ -1,15 +1,15 @@ #pragma once +#include #include #include -#include - -#include "qt-helpers.hpp" #include "multitrack-video-output.hpp" class OBSBasic; +using SetupStreamingContinuation_t = std::function; + struct BasicOutputHandler { OBSOutputAutoRelease fileOutput; OBSOutputAutoRelease streamOutput; @@ -63,7 +63,9 @@ struct BasicOutputHandler { virtual ~BasicOutputHandler(){}; - virtual FutureHolder SetupStreaming(obs_service_t *service) = 0; + virtual std::shared_future + SetupStreaming(obs_service_t *service, + SetupStreamingContinuation_t continuation) = 0; virtual bool StartStreaming(obs_service_t *service) = 0; virtual bool StartRecording() = 0; virtual bool StartReplayBuffer() { return false; } @@ -98,9 +100,10 @@ struct BasicOutputHandler { bool overwrite, const char *format, bool ffmpeg); - FutureHolder> SetupMultitrackVideo( + std::shared_future SetupMultitrackVideo( obs_service_t *service, std::string audio_encoder_id, - size_t main_audio_mixer, std::optional vod_track_mixer); + size_t main_audio_mixer, std::optional vod_track_mixer, + std::function)> continuation); OBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig(); }; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 36015b6131beb4..20e2d01a7b26f0 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2038,7 +2038,9 @@ void OBSBasic::ResetOutputs() bool advOut = astrcmpi(mode, "Advanced") == 0; if ((!outputHandler || !outputHandler->Active()) && - startStreamingFuture.future.isFinished()) { + (!setupStreamingGuard.valid() || + setupStreamingGuard.wait_for(std::chrono::seconds{0}) == + std::future_status::ready)) { outputHandler.reset(); outputHandler.reset(advOut ? CreateAdvancedOutputHandler(this) : CreateSimpleOutputHandler(this)); @@ -5170,18 +5172,11 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { - if (!startStreamingFuture.future.isFinished() && - !startStreamingFuture.future.isCanceled()) { - startStreamingFuture.future.onCanceled( - this, [basic = QPointer{this}] { - if (basic) - basic->close(); - }); - startStreamingFuture.cancelAll(); - event->ignore(); - return; - } else if (startStreamingFuture.future.isCanceled() && - !startStreamingFuture.future.isFinished()) { + /* Wait for multitrack video stream to start/finish processing in the background */ + if (setupStreamingGuard.valid() && + setupStreamingGuard.wait_for(std::chrono::seconds{0}) != + std::future_status::ready) { + QTimer::singleShot(1000, this, &OBSBasic::close); event->ignore(); return; } @@ -7153,9 +7148,8 @@ void OBSBasic::StartStreaming() #endif }; - auto holder = outputHandler->SetupStreaming(service); - auto future = holder.future.then(this, finish_stream_setup); - startStreamingFuture = {holder.cancelAll, future}; + setupStreamingGuard = + outputHandler->SetupStreaming(service, finish_stream_setup); } void OBSBasic::BroadcastButtonClicked() diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index f68075ba3013ee..eb55d2f051a2a3 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -23,10 +23,10 @@ #include #include #include -#include #include #include #include +#include #include "window-main.hpp" #include "window-basic-interaction.hpp" #include "window-basic-vcam.hpp" @@ -43,7 +43,6 @@ #include "auth-base.hpp" #include "log-viewer.hpp" #include "undo-stack-obs.hpp" -#include "qt-helpers.hpp" #include @@ -286,7 +285,7 @@ class OBSBasic : public OBSMainWindow { OBSService service; std::unique_ptr outputHandler; - FutureHolder startStreamingFuture; + std::shared_future setupStreamingGuard; bool streamingStopping = false; bool recordingStopping = false; bool replayBufferStopping = false; From da997d9648d747d16f5e1ae0813e01361a5cd94d Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Wed, 10 Jul 2024 14:53:00 -0400 Subject: [PATCH 0297/1073] UI: Add field growth policy to form layouts --- UI/forms/OBSBasicSettings.ui | 168 +++++++++++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 18 deletions(-) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 9d25ceef8c35ad..c58271dd156332 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -219,6 +219,9 @@ Basic.Settings.General + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -280,6 +283,9 @@ Basic.Settings.General.Updater + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -338,6 +344,9 @@ Basic.Settings.Output + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -430,6 +439,9 @@ false + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -524,6 +536,9 @@ Basic.Settings.General.Projectors + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -583,6 +598,9 @@ Basic.Settings.General.SysTray + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -641,6 +659,9 @@ StudioMode.Preview + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -704,6 +725,9 @@ Basic.Settings.General.Importers + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -739,6 +763,9 @@ Basic.TogglePreviewProgramMode + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -791,6 +818,9 @@ Basic.Settings.General.Multiview + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -955,6 +985,9 @@ false + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -1042,6 +1075,9 @@ + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -1142,6 +1178,9 @@ Basic.Settings.Stream.Destination + + QFormLayout::AllNonFixedFieldsGrow + 2 @@ -1262,7 +1301,7 @@ 0 0 987 - 809 + 791 @@ -1646,6 +1685,9 @@ + + QFormLayout::AllNonFixedFieldsGrow + @@ -2071,7 +2113,7 @@ 0 0 - 509 + 755 609 @@ -2100,6 +2142,9 @@ Basic.Settings.Output.Adv.Streaming + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2293,6 +2338,9 @@ Basic.Settings.Output.Adv.Recording + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2659,6 +2707,9 @@ true + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2847,8 +2898,8 @@ 0 0 - 424 - 175 + 766 + 592 @@ -2870,6 +2921,9 @@ Basic.Settings.Output.Adv.Streaming.Settings + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -2910,7 +2964,7 @@ - 1 + 0 @@ -3143,6 +3197,9 @@ + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -3247,8 +3304,8 @@ 0 0 - 509 - 371 + 766 + 558 @@ -3276,6 +3333,9 @@ Basic.Settings.Output.Adv.Recording.Settings + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -3885,8 +3945,8 @@ 0 0 - 625 - 467 + 766 + 558 @@ -4422,8 +4482,8 @@ 0 0 - 258 - 510 + 766 + 592 @@ -4454,6 +4514,9 @@ Basic.Settings.Output.Adv.Audio.Track1 + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4591,6 +4654,9 @@ Basic.Settings.Output.Adv.Audio.Track2 + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4728,6 +4794,9 @@ Basic.Settings.Output.Adv.Audio.Track3 + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -4865,6 +4934,9 @@ Basic.Settings.Output.Adv.Audio.Track4 + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5002,6 +5074,9 @@ Basic.Settings.Output.Adv.Audio.Track5 + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5139,6 +5214,9 @@ Basic.Settings.Output.Adv.Audio.Track6 + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5350,6 +5428,9 @@ + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5483,8 +5564,8 @@ 0 0 - 590 - 511 + 772 + 636 @@ -5521,6 +5602,9 @@ Basic.Settings.General + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5630,6 +5714,9 @@ Basic.Settings.Audio.Devices + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5759,6 +5846,9 @@ Basic.Settings.Audio.Meters + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5845,6 +5935,9 @@ Basic.Settings.Advanced + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5903,6 +5996,9 @@ Basic.Settings.Hotkeys + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -5986,6 +6082,9 @@ Basic.Settings.General + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -6127,6 +6226,9 @@ 0 + + 0 + @@ -6444,8 +6546,8 @@ 0 0 - 178 - 16 + 770 + 642 @@ -6464,6 +6566,9 @@ + + QFormLayout::AllNonFixedFieldsGrow + 0 @@ -6534,8 +6639,8 @@ 0 0 - 696 - 347 + 772 + 680 @@ -6571,6 +6676,9 @@ false + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7440,7 +7548,7 @@ 0 0 - 695 + 755 952 @@ -7478,6 +7586,9 @@ Basic.Settings.General + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7526,6 +7637,9 @@ Basic.Settings.Video + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7762,6 +7876,9 @@ Basic.Settings.Output.Adv.Recording + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7872,6 +7989,9 @@ Basic.Settings.Advanced.StreamDelay + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -7986,6 +8106,9 @@ Basic.Settings.Output.Reconnect + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -8089,6 +8212,9 @@ Basic.Settings.Advanced.Network + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -8177,6 +8303,9 @@ Basic.Main.Sources + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -8212,6 +8341,9 @@ Basic.Settings.Hotkeys + + QFormLayout::AllNonFixedFieldsGrow + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter From 3a884277ea7f19fe53395fb83ca4500f50e35b12 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 11 Jul 2024 18:37:52 +0200 Subject: [PATCH 0298/1073] UI: Fix `qt-helpers.{c,h}pp` not being removed from legacy.cmake Follow-up to https://github.com/obsproject/obs-studio/pull/10957 --- UI/cmake/legacy.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 709e9c7ab485bb..487c2a5c0996c4 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -295,8 +295,6 @@ target_sources( multitrack-video-error.hpp multitrack-video-output.cpp multitrack-video-output.hpp - qt-helpers.cpp - qt-helpers.hpp system-info.hpp) target_sources(obs PRIVATE importers/importers.cpp importers/importers.hpp importers/classic.cpp importers/sl.cpp From 1cb1864cc04e1036ea4066ee862f6ac88ea708d5 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Tue, 14 May 2024 12:02:38 +0200 Subject: [PATCH 0299/1073] libobs: Fix crash when mix is NULL --- libobs/obs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs.c b/libobs/obs.c index ea930cc6757b65..9b739c1f307dfe 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -878,13 +878,13 @@ static void obs_free_video(void) obs_free_video_mix(video); obs->video.mixes.array[i] = NULL; } + da_free(obs->video.mixes); if (num_views > 0) blog(LOG_WARNING, "Number of remaining views: %ld", num_views); pthread_mutex_unlock(&obs->video.mixes_mutex); pthread_mutex_destroy(&obs->video.mixes_mutex); pthread_mutex_init_value(&obs->video.mixes_mutex); - da_free(obs->video.mixes); for (size_t i = 0; i < obs->video.ready_encoder_groups.num; i++) { obs_weak_encoder_release( From e741a817e9fbb15aa710147555f13d89c1850c29 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 19 Dec 2023 02:47:45 +0100 Subject: [PATCH 0300/1073] rtmp-services: Remove FTL entries --- plugins/obs-outputs/ftl-sdk | 1 - plugins/rtmp-services/data/package.json | 4 ++-- .../data/schema/service-schema-v5.json | 4 +--- plugins/rtmp-services/data/services.json | 19 ------------------- plugins/rtmp-services/rtmp-custom.c | 4 ---- 5 files changed, 3 insertions(+), 29 deletions(-) delete mode 160000 plugins/obs-outputs/ftl-sdk diff --git a/plugins/obs-outputs/ftl-sdk b/plugins/obs-outputs/ftl-sdk deleted file mode 160000 index d0c8469f66806b..00000000000000 --- a/plugins/obs-outputs/ftl-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0c8469f66806b5ea738d607f7d2b000af8b1129 diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index bdd34a73ab07ad..4081f38ea651b5 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 256, + "version": 257, "files": [ { "name": "services.json", - "version": 256 + "version": 257 } ] } diff --git a/plugins/rtmp-services/data/schema/service-schema-v5.json b/plugins/rtmp-services/data/schema/service-schema-v5.json index 16e07848303b57..93237e089b47a9 100644 --- a/plugins/rtmp-services/data/schema/service-schema-v5.json +++ b/plugins/rtmp-services/data/schema/service-schema-v5.json @@ -23,7 +23,6 @@ "RTMP", "RTMPS", "HLS", - "FTL", "SRT", "RIST", "WHIP" @@ -124,7 +123,6 @@ "enum": [ "rtmp_output", "ffmpeg_hls_muxer", - "ftl_output", "ffmpeg_mpegts_muxer" ] }, @@ -235,7 +233,7 @@ }, { "$comment": "Require recommended output field if protocol field is not RTMP(S)", - "if": { "required": ["protocol"], "properties": { "protocol": { "pattern": "^(HLS|SRT|RIST|FTL|WHIP)$" } } }, + "if": { "required": ["protocol"], "properties": { "protocol": { "pattern": "^(HLS|SRT|RIST|WHIP)$" } } }, "then": { "properties": { "recommended": { "required": ["output"] } } } } ] diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 341de1a848ba37..8370b1fa90cbb3 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -1600,25 +1600,6 @@ "h264" ] }, - { - "name": "YouNow", - "common": false, - "protocol": "FTL", - "servers": [ - { - "name": "younow.com", - "url": "https://api.younow.com/php/api/broadcast/ingest?id=" - } - ], - "recommended": { - "keyint": 2, - "output": "ftl_output", - "max audio bitrate": 160, - "max video bitrate": 7000, - "profile": "main", - "bframes": 0 - } - }, { "name": "Steam", "common": false, diff --git a/plugins/rtmp-services/rtmp-custom.c b/plugins/rtmp-services/rtmp-custom.c index c44d881a265201..9c5b1c88db5731 100644 --- a/plugins/rtmp-services/rtmp-custom.c +++ b/plugins/rtmp-services/rtmp-custom.c @@ -111,7 +111,6 @@ static const char *rtmp_custom_password(void *data) } #define RTMPS_PREFIX "rtmps://" -#define FTL_PREFIX "ftl://" #define SRT_PREFIX "srt://" #define RIST_PREFIX "rist://" @@ -122,9 +121,6 @@ static const char *rtmp_custom_get_protocol(void *data) if (strncmp(service->server, RTMPS_PREFIX, strlen(RTMPS_PREFIX)) == 0) return "RTMPS"; - if (strncmp(service->server, FTL_PREFIX, strlen(FTL_PREFIX)) == 0) - return "FTL"; - if (strncmp(service->server, SRT_PREFIX, strlen(SRT_PREFIX)) == 0) return "SRT"; From 58264833c9bb4cc13780d9d8b059dc34eadd7f15 Mon Sep 17 00:00:00 2001 From: Rodney Date: Sun, 14 Jul 2024 06:08:51 +0200 Subject: [PATCH 0301/1073] rtmp-services: Remove YouNow specific code --- plugins/rtmp-services/CMakeLists.txt | 4 +- plugins/rtmp-services/cmake/legacy.cmake | 2 - plugins/rtmp-services/rtmp-common.c | 7 -- .../rtmp-services/service-specific/younow.c | 109 ------------------ .../rtmp-services/service-specific/younow.h | 3 - 5 files changed, 1 insertion(+), 124 deletions(-) delete mode 100644 plugins/rtmp-services/service-specific/younow.c delete mode 100644 plugins/rtmp-services/service-specific/younow.h diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index f2cf7555d258ee..29f621c4ef07d9 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -32,9 +32,7 @@ target_sources( service-specific/showroom.c service-specific/showroom.h service-specific/twitch.c - service-specific/twitch.h - service-specific/younow.c - service-specific/younow.h) + service-specific/twitch.h) target_compile_definitions(rtmp-services PRIVATE SERVICES_URL="${RTMP_SERVICES_URL}" $<$:ENABLE_SERVICE_UPDATES>) diff --git a/plugins/rtmp-services/cmake/legacy.cmake b/plugins/rtmp-services/cmake/legacy.cmake index 1215746fc7985c..2101c25b3d62ab 100644 --- a/plugins/rtmp-services/cmake/legacy.cmake +++ b/plugins/rtmp-services/cmake/legacy.cmake @@ -17,8 +17,6 @@ target_sources( rtmp-services PRIVATE service-specific/twitch.c service-specific/twitch.h - service-specific/younow.c - service-specific/younow.h service-specific/nimotv.c service-specific/nimotv.h service-specific/showroom.c diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 59899a8019b351..c409bca6e97b7c 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -6,7 +6,6 @@ #include "rtmp-format-ver.h" #include "service-specific/twitch.h" -#include "service-specific/younow.h" #include "service-specific/nimotv.h" #include "service-specific/showroom.h" #include "service-specific/dacast.h" @@ -826,12 +825,6 @@ static const char *rtmp_common_url(void *data) } } - if (service->service && strcmp(service->service, "YouNow") == 0) { - if (service->server && service->key) { - return younow_get_ingest(service->server, service->key); - } - } - if (service->service && strcmp(service->service, "Nimo TV") == 0) { if (service->server && strcmp(service->server, "auto") == 0) { return nimotv_get_ingest(service->key); diff --git a/plugins/rtmp-services/service-specific/younow.c b/plugins/rtmp-services/service-specific/younow.c deleted file mode 100644 index 2914dc480b4bdc..00000000000000 --- a/plugins/rtmp-services/service-specific/younow.c +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include - -#include -#include "util/base.h" -#include "younow.h" - -struct younow_mem_struct { - char *memory; - size_t size; -}; - -static char *current_ingest = NULL; - -static size_t younow_write_cb(void *contents, size_t size, size_t nmemb, - void *userp) -{ - size_t realsize = size * nmemb; - struct younow_mem_struct *mem = (struct younow_mem_struct *)userp; - - mem->memory = realloc(mem->memory, mem->size + realsize + 1); - if (mem->memory == NULL) { - blog(LOG_WARNING, "yyounow_write_cb: realloc returned NULL"); - return 0; - } - - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - - return realsize; -} - -const char *younow_get_ingest(const char *server, const char *key) -{ - CURL *curl_handle; - CURLcode res; - struct younow_mem_struct chunk; - struct dstr uri; - long response_code; - - // find the delimiter in stream key - const char *delim = strchr(key, '_'); - if (delim == NULL) { - blog(LOG_WARNING, - "younow_get_ingest: delimiter not found in stream key"); - return server; - } - - curl_handle = curl_easy_init(); - - chunk.memory = malloc(1); /* will be grown as needed by realloc */ - chunk.size = 0; /* no data at this point */ - - dstr_init(&uri); - dstr_copy(&uri, server); - dstr_ncat(&uri, key, delim - key); - - curl_easy_setopt(curl_handle, CURLOPT_URL, uri.array); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, true); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); - curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 3L); - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, younow_write_cb); - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); - curl_obs_set_revoke_setting(curl_handle); - - res = curl_easy_perform(curl_handle); - dstr_free(&uri); - - if (res != CURLE_OK) { - blog(LOG_WARNING, - "younow_get_ingest: curl_easy_perform() failed: %s", - curl_easy_strerror(res)); - curl_easy_cleanup(curl_handle); - free(chunk.memory); - return server; - } - - curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code); - if (response_code != 200) { - blog(LOG_WARNING, - "younow_get_ingest: curl_easy_perform() returned code: %ld", - response_code); - curl_easy_cleanup(curl_handle); - free(chunk.memory); - return server; - } - - curl_easy_cleanup(curl_handle); - - if (chunk.size == 0) { - blog(LOG_WARNING, - "younow_get_ingest: curl_easy_perform() returned empty response"); - free(chunk.memory); - return server; - } - - if (current_ingest) { - free(current_ingest); - current_ingest = NULL; - } - - current_ingest = strdup(chunk.memory); - free(chunk.memory); - blog(LOG_INFO, "younow_get_ingest: returning ingest: %s", - current_ingest); - return current_ingest; -} diff --git a/plugins/rtmp-services/service-specific/younow.h b/plugins/rtmp-services/service-specific/younow.h deleted file mode 100644 index 4f181daefcaf2b..00000000000000 --- a/plugins/rtmp-services/service-specific/younow.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -extern const char *younow_get_ingest(const char *server, const char *key); From 536812753b9bb0cbd162dfd50b840be11a0db4cd Mon Sep 17 00:00:00 2001 From: Rodney Date: Sun, 14 Jul 2024 06:07:29 +0200 Subject: [PATCH 0302/1073] CI: Remove YouNow exclude from service checker --- .github/scripts/utils.py/check-services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/utils.py/check-services.py b/.github/scripts/utils.py/check-services.py index 4638411c3307bc..401bb0261a5ac9 100644 --- a/.github/scripts/utils.py/check-services.py +++ b/.github/scripts/utils.py/check-services.py @@ -14,7 +14,7 @@ MINIMUM_PURGE_AGE = 9.75 * 24 * 60 * 60 # slightly less than 10 days TIMEOUT = 10 -SKIPPED_SERVICES = {"YouNow", "SHOWROOM", "Dacast"} +SKIPPED_SERVICES = {"SHOWROOM", "Dacast"} SERVICES_FILE = "plugins/rtmp-services/data/services.json" PACKAGE_FILE = "plugins/rtmp-services/data/package.json" CACHE_FILE = "other/timestamps.json" From b83df1b14186ae1473c36310e27c50cc31ddca8f Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 19 Dec 2023 02:50:19 +0100 Subject: [PATCH 0303/1073] obs-outputs: Remove FTL output --- plugins/obs-outputs/CMakeLists.txt | 2 - plugins/obs-outputs/cmake/ftl.cmake | 65 -- plugins/obs-outputs/cmake/legacy.cmake | 81 -- plugins/obs-outputs/data/locale/en-US.ini | 3 - plugins/obs-outputs/ftl-stream.c | 1167 --------------------- plugins/obs-outputs/obs-outputs.c | 8 +- 6 files changed, 1 insertion(+), 1325 deletions(-) delete mode 100644 plugins/obs-outputs/cmake/ftl.cmake delete mode 100644 plugins/obs-outputs/ftl-stream.c diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index 576911a0c399c7..f3fbb531cffd63 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -83,8 +83,6 @@ if(OS_WINDOWS) target_sources(obs-outputs PRIVATE obs-outputs.rc) endif() -include(cmake/ftl.cmake) - # cmake-format: off set_target_properties_obs(obs-outputs PROPERTIES FOLDER plugins/obs-outputs PREFIX "") # cmake-format: on diff --git a/plugins/obs-outputs/cmake/ftl.cmake b/plugins/obs-outputs/cmake/ftl.cmake deleted file mode 100644 index cdbeb948b63a8e..00000000000000 --- a/plugins/obs-outputs/cmake/ftl.cmake +++ /dev/null @@ -1,65 +0,0 @@ -find_package(CURL REQUIRED) -find_package(jansson REQUIRED) - -add_library(ftl-sdk OBJECT) -add_library(OBS::ftl-sdk ALIAS ftl-sdk) - -target_compile_definitions(ftl-sdk PUBLIC FTL_FOUND FTL_STATIC_COMPILE) - -target_link_libraries(ftl-sdk PRIVATE jansson::jansson CURL::libcurl) - -target_sources( - ftl-sdk - PRIVATE # cmake-format: sortable - $<$>:ftl-sdk/libftl/posix/socket.c> - $<$>:ftl-sdk/libftl/posix/threads.c> - $<$>:ftl-sdk/libftl/posix/threads.h> - $<$:ftl-sdk/libftl/win32/socket.c> - $<$:ftl-sdk/libftl/win32/threads.c> - $<$:ftl-sdk/libftl/win32/threads.h> - ftl-sdk/libftl/ftl-sdk.c - ftl-sdk/libftl/ftl_helpers.c - ftl-sdk/libftl/ftl_private.h - ftl-sdk/libftl/gettimeofday/gettimeofday.c - ftl-sdk/libftl/gettimeofday/gettimeofday.h - ftl-sdk/libftl/handshake.c - ftl-sdk/libftl/hmac/hmac.c - ftl-sdk/libftl/hmac/hmac.h - ftl-sdk/libftl/hmac/sha2.c - ftl-sdk/libftl/hmac/sha2.h - ftl-sdk/libftl/ingest.c - ftl-sdk/libftl/logging.c - ftl-sdk/libftl/media.c - PUBLIC ftl-sdk/libftl/ftl.h) - -target_compile_options( - ftl-sdk - PRIVATE $<$:-Wno-unused-parameter> - $<$:-Wno-unused-variable> - $<$:-Wno-sign-compare> - $<$:-Wno-pointer-sign> - $<$:-Wno-int-conversion> - $<$:-Wno-incompatible-function-pointer-types> - $<$:-Wno-implicit-int-conversion> - $<$:-Wno-shorten-64-to-32> - $<$:-Wno-macro-redefined> - $<$:-Wno-enum-conversion> - $<$:-Wno-extra> - $<$:-Wno-incompatible-pointer-types> - $<$:-Wno-builtin-macro-redefined>) - -target_include_directories( - ftl-sdk - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl" - "$<$:${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/win32>" - "$<$>:${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/posix>") - -if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10) - target_compile_options(ftl-sdk PRIVATE -Wno-error=enum-conversion -Wno-error=maybe-uninitialized) -endif() - -target_sources(obs-outputs PRIVATE ftl-stream.c) -target_link_libraries(obs-outputs PRIVATE ftl-sdk) -target_enable_feature(obs-outputs "FTL protocol support") - -set_target_properties(ftl-sdk PROPERTIES FOLDER plugins/obs-outputs POSITION_INDEPENDENT_CODE TRUE) diff --git a/plugins/obs-outputs/cmake/legacy.cmake b/plugins/obs-outputs/cmake/legacy.cmake index 12b474a1e412f9..af1baa028733ec 100644 --- a/plugins/obs-outputs/cmake/legacy.cmake +++ b/plugins/obs-outputs/cmake/legacy.cmake @@ -110,85 +110,4 @@ else() target_compile_definitions(obs-outputs PRIVATE NO_CRYPTO) endif() -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(FTL QUIET libftl) -endif() - -if(FTL_FOUND) - find_package(CURL REQUIRED) - obs_status(ENABLED "ftl outputs (system ftl-sdk)") - - target_sources(obs-outputs PRIVATE ftl-stream.c) - - target_include_directories(obs-outputs PRIVATE ${FTL_INCLUDE_DIRS}) - - target_link_libraries(obs-outputs PRIVATE ${FTL_LIBRARIES} CURL::libcurl) - - target_compile_features(obs-outputs PRIVATE c_std_11) - - target_compile_definitions(obs-outputs PRIVATE FTL_FOUND) - -elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/CMakeLists.txt") - find_package(CURL REQUIRED) - find_package(Jansson 2.5 REQUIRED) - obs_status(ENABLED "ftl ouputs (bundled ftl-sdk)") - - target_compile_definitions(obs-outputs PRIVATE FTL_STATIC_COMPILE) - - target_compile_features(obs-outputs PRIVATE c_std_11) - - target_link_libraries(obs-outputs PRIVATE Jansson::Jansson CURL::libcurl) - - target_sources( - obs-outputs - PRIVATE ftl-stream.c - ftl-sdk/libftl/ftl.h - ftl-sdk/libftl/ftl_private.h - ftl-sdk/libftl/hmac/hmac.c - ftl-sdk/libftl/hmac/hmac.h - ftl-sdk/libftl/hmac/sha2.c - ftl-sdk/libftl/hmac/sha2.h - ftl-sdk/libftl/ftl-sdk.c - ftl-sdk/libftl/handshake.c - ftl-sdk/libftl/ingest.c - ftl-sdk/libftl/ftl_helpers.c - ftl-sdk/libftl/media.c - ftl-sdk/libftl/gettimeofday/gettimeofday.c - ftl-sdk/libftl/logging.c) - - target_include_directories(obs-outputs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl) - - if(OS_WINDOWS) - target_sources( - obs-outputs PRIVATE ftl-sdk/libftl/gettimeofday/gettimeofday.c ftl-sdk/libftl/gettimeofday/gettimeofday.h - ftl-sdk/libftl/win32/socket.c ftl-sdk/libftl/win32/threads.c ftl-sdk/libftl/win32/threads.h) - - target_include_directories(obs-outputs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/win32) - elseif(OS_POSIX) - target_sources(obs-outputs PRIVATE ftl-sdk/libftl/posix/socket.c ftl-sdk/libftl/posix/threads.c - ftl-sdk/libftl/posix/threads.h) - - target_include_directories(obs-outputs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/posix) - endif() - - if(NOT MSVC) - target_compile_options( - obs-outputs - PRIVATE -Wno-error=extra - -Wno-error=sign-compare - -Wno-error=incompatible-pointer-types - -Wno-error=int-conversion - -Wno-error=unused-parameter - -Wno-error=deprecated-declarations - "$<$:-Wno-error=maybe-uninitialized>" - "$<$:-Wno-error=pointer-sign>") - if((NOT CMAKE_C_COMPILER_ID STREQUAL "GNU") OR CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "10") - target_compile_options(obs-outputs PRIVATE -Wno-error=enum-conversion) - endif() - endif() - - target_compile_definitions(obs-outputs PRIVATE FTL_FOUND) -endif() - setup_plugin_target(obs-outputs) diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index aabfd794526d0a..5eded159fa7a38 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -27,6 +27,3 @@ AddressNotAvailable="Address not available. You may have tried to bind to an inv SSLCertVerifyFailed="The RTMP server sent an invalid SSL certificate." InvalidParameter="Invalid connection parameters. Check that the streaming service address is correct." NoRoute="Error reaching host. Make sure that the interface you have bound can access the internet and that the streaming service supports the address family you selected (see Settings → Advanced)." - -FTLStream="FTL Stream" -FTLStream.PeakBitrate="Peak Bitrate" diff --git a/plugins/obs-outputs/ftl-stream.c b/plugins/obs-outputs/ftl-stream.c deleted file mode 100644 index ce628697f00028..00000000000000 --- a/plugins/obs-outputs/ftl-stream.c +++ /dev/null @@ -1,1167 +0,0 @@ -/****************************************************************************** - Copyright (C) 2017 by Quinn Damerell - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include "ftl.h" -#include "flv-mux.h" -#include "net-if.h" - -#ifdef _WIN32 -#include -#else -#include -#define INFINITE 0xFFFFFFFF -#endif - -#define do_log(level, format, ...) \ - blog(level, "[ftl stream: '%s'] " format, \ - obs_output_get_name(stream->output), ##__VA_ARGS__) - -#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) -#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) -#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) - -#define OPT_DROP_THRESHOLD "drop_threshold_ms" -#define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec" -#define OPT_BIND_IP "bind_ip" - -#define FTL_URL_PROTOCOL "ftl://" - -typedef struct _nalu_t { - int len; - int dts_usec; - int send_marker_bit; - uint8_t *data; -} nalu_t; - -typedef struct _frame_of_nalus_t { - nalu_t nalus[100]; - int total; - int complete_frame; -} frame_of_nalus_t; - -struct ftl_stream { - obs_output_t *output; - - pthread_mutex_t packets_mutex; - struct deque packets; - bool sent_headers; - int64_t frames_sent; - - volatile bool connecting; - pthread_t connect_thread; - pthread_t status_thread; - - volatile bool active; - volatile bool disconnected; - volatile bool encode_error; - pthread_t send_thread; - - int max_shutdown_time_sec; - - os_sem_t *send_sem; - os_event_t *stop_event; - uint64_t stop_ts; - uint64_t shutdown_timeout_ts; - - struct dstr path; - uint32_t channel_id; - struct dstr username, password; - struct dstr encoder_name; - struct dstr bind_ip; - - /* frame drop variables */ - int64_t drop_threshold_usec; - int64_t pframe_drop_threshold_usec; - int min_priority; - float congestion; - - int64_t last_dts_usec; - - uint64_t total_bytes_sent; - uint64_t dropped_frames; - uint64_t last_nack_count; - - ftl_handle_t ftl_handle; - ftl_ingest_params_t params; - int peak_kbps; - uint32_t scale_width, scale_height, width, height; - frame_of_nalus_t coded_pic_buffer; -}; - -static int init_connect(struct ftl_stream *stream); -static void *connect_thread(void *data); -static void *status_thread(void *data); -static int _ftl_error_to_obs_error(int status); - -static const char *ftl_stream_getname(void *unused) -{ - UNUSED_PARAMETER(unused); - return obs_module_text("FTLStream"); -} - -static inline size_t num_buffered_packets(struct ftl_stream *stream); - -static inline void free_packets(struct ftl_stream *stream) -{ - size_t num_packets; - - pthread_mutex_lock(&stream->packets_mutex); - - num_packets = num_buffered_packets(stream); - if (num_packets) - info("Freeing %d remaining packets", (int)num_packets); - - while (stream->packets.size) { - struct encoder_packet packet; - deque_pop_front(&stream->packets, &packet, sizeof(packet)); - obs_encoder_packet_release(&packet); - } - pthread_mutex_unlock(&stream->packets_mutex); -} - -static inline bool stopping(struct ftl_stream *stream) -{ - return os_event_try(stream->stop_event) != EAGAIN; -} - -static inline bool connecting(struct ftl_stream *stream) -{ - return os_atomic_load_bool(&stream->connecting); -} - -static inline bool active(struct ftl_stream *stream) -{ - return os_atomic_load_bool(&stream->active); -} - -static inline bool disconnected(struct ftl_stream *stream) -{ - return os_atomic_load_bool(&stream->disconnected); -} - -static void ftl_stream_destroy(void *data) -{ - struct ftl_stream *stream = data; - ftl_status_t status_code; - - info("ftl_stream_destroy"); - - if (stopping(stream) && !connecting(stream)) { - pthread_join(stream->send_thread, NULL); - - } else if (connecting(stream) || active(stream)) { - if (stream->connecting) { - info("wait for connect_thread to terminate"); - pthread_join(stream->status_thread, NULL); - pthread_join(stream->connect_thread, NULL); - info("wait for connect_thread to terminate: done"); - } - - stream->stop_ts = 0; - os_event_signal(stream->stop_event); - - if (active(stream)) { - os_sem_post(stream->send_sem); - obs_output_end_data_capture(stream->output); - pthread_join(stream->send_thread, NULL); - } - } - - info("ingest destroy"); - - status_code = ftl_ingest_destroy(&stream->ftl_handle); - if (status_code != FTL_SUCCESS) { - info("Failed to destroy from ingest %d", status_code); - } - - if (stream) { - free_packets(stream); - dstr_free(&stream->path); - dstr_free(&stream->username); - dstr_free(&stream->password); - dstr_free(&stream->encoder_name); - dstr_free(&stream->bind_ip); - os_event_destroy(stream->stop_event); - os_sem_destroy(stream->send_sem); - pthread_mutex_destroy(&stream->packets_mutex); - deque_free(&stream->packets); - bfree(stream); - } -} - -static void *ftl_stream_create(obs_data_t *settings, obs_output_t *output) -{ - struct ftl_stream *stream = bzalloc(sizeof(struct ftl_stream)); - info("ftl_stream_create"); - - stream->output = output; - pthread_mutex_init_value(&stream->packets_mutex); - - stream->peak_kbps = -1; - ftl_init(); - - if (pthread_mutex_init(&stream->packets_mutex, NULL) != 0) { - goto fail; - } - if (os_event_init(&stream->stop_event, OS_EVENT_TYPE_MANUAL) != 0) { - goto fail; - } - - stream->coded_pic_buffer.total = 0; - stream->coded_pic_buffer.complete_frame = 0; - - UNUSED_PARAMETER(settings); - return stream; - -fail: - return NULL; -} - -static void ftl_stream_stop(void *data, uint64_t ts) -{ - struct ftl_stream *stream = data; - info("ftl_stream_stop"); - - if (stopping(stream) && ts != 0) { - return; - } - - if (connecting(stream)) { - pthread_join(stream->status_thread, NULL); - pthread_join(stream->connect_thread, NULL); - } - - stream->stop_ts = ts / 1000ULL; - - if (ts) { - stream->shutdown_timeout_ts = - ts + - (uint64_t)stream->max_shutdown_time_sec * 1000000000ULL; - } - - if (active(stream)) { - os_event_signal(stream->stop_event); - if (stream->stop_ts == 0) - os_sem_post(stream->send_sem); - } else { - obs_output_signal_stop(stream->output, OBS_OUTPUT_SUCCESS); - } -} - -static inline bool get_next_packet(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - bool new_packet = false; - - pthread_mutex_lock(&stream->packets_mutex); - if (stream->packets.size) { - deque_pop_front(&stream->packets, packet, - sizeof(struct encoder_packet)); - new_packet = true; - } - pthread_mutex_unlock(&stream->packets_mutex); - - return new_packet; -} - -static int avc_get_video_frame(struct ftl_stream *stream, - struct encoder_packet *packet, bool is_header) -{ - int consumed = 0; - int len = (int)packet->size; - nalu_t *nalu; - - unsigned char *video_stream = packet->data; - - while ((size_t)consumed < packet->size) { - size_t total_max = sizeof(stream->coded_pic_buffer.nalus) / - sizeof(stream->coded_pic_buffer.nalus[0]); - - if ((size_t)stream->coded_pic_buffer.total >= total_max) { - warn("ERROR: cannot continue, nalu buffers are full"); - return -1; - } - - nalu = &stream->coded_pic_buffer - .nalus[stream->coded_pic_buffer.total]; - - if (is_header) { - if (consumed == 0) { - //first 6 bytes are some obs header with part - //of the sps - video_stream += 6; - consumed += 6; - } else { - //another spacer byte of 0x1 - video_stream += 1; - consumed += 1; - } - - len = video_stream[0] << 8 | video_stream[1]; - video_stream += 2; - consumed += 2; - } else { - len = video_stream[0] << 24 | video_stream[1] << 16 | - video_stream[2] << 8 | video_stream[3]; - - if ((size_t)len > (packet->size - (size_t)consumed)) { - warn("ERROR: got len of %d but packet only " - "has %d left", - len, (int)(packet->size - consumed)); - } - - consumed += 4; - video_stream += 4; - } - - consumed += len; - - uint8_t nalu_type = video_stream[0] & 0x1F; - uint8_t nri = (video_stream[0] >> 5) & 0x3; - - if ((nalu_type != 12 && nalu_type != 6 && nalu_type != 9) || - nri) { - nalu->data = video_stream; - nalu->len = len; - nalu->send_marker_bit = 0; - stream->coded_pic_buffer.total++; - } - - video_stream += len; - } - - if (!is_header) { - size_t idx = stream->coded_pic_buffer.total - 1; - stream->coded_pic_buffer.nalus[idx].send_marker_bit = 1; - } - - return 0; -} - -static int send_packet(struct ftl_stream *stream, struct encoder_packet *packet, - bool is_header) -{ - int bytes_sent = 0; - int ret = 0; - - if (packet->type == OBS_ENCODER_VIDEO) { - stream->coded_pic_buffer.total = 0; - avc_get_video_frame(stream, packet, is_header); - - int i; - for (i = 0; i < stream->coded_pic_buffer.total; i++) { - nalu_t *nalu = &stream->coded_pic_buffer.nalus[i]; - bytes_sent += ftl_ingest_send_media_dts( - &stream->ftl_handle, FTL_VIDEO_DATA, - packet->dts_usec, nalu->data, nalu->len, - nalu->send_marker_bit); - - if (nalu->send_marker_bit) { - stream->frames_sent++; - } - } - - } else if (packet->type == OBS_ENCODER_AUDIO) { - bytes_sent += ftl_ingest_send_media_dts( - &stream->ftl_handle, FTL_AUDIO_DATA, packet->dts_usec, - packet->data, (int)packet->size, 0); - } else { - warn("Got packet type %d", packet->type); - } - - if (is_header) { - bfree(packet->data); - } else { - obs_encoder_packet_release(packet); - } - - stream->total_bytes_sent += bytes_sent; - return ret; -} - -static void set_peak_bitrate(struct ftl_stream *stream) -{ - int speedtest_kbps = 15000; - int speedtest_duration = 1000; - speed_test_t results; - ftl_status_t status_code; - - status_code = ftl_ingest_speed_test_ex(&stream->ftl_handle, - speedtest_kbps, - speedtest_duration, &results); - - float percent_lost = 0; - - if (status_code == FTL_SUCCESS) { - percent_lost = (float)results.lost_pkts * 100.f / - (float)results.pkts_sent; - } else { - warn("Speed test failed with: %s", - ftl_status_code_to_string(status_code)); - } - - // Get what the user set the encoding bitrate to. - obs_encoder_t *video_encoder = - obs_output_get_video_encoder(stream->output); - obs_data_t *video_settings = obs_encoder_get_settings(video_encoder); - int user_desired_bitrate = - (int)obs_data_get_int(video_settings, "bitrate"); - obs_data_release(video_settings); - - // Report the results. - info("Speed test completed: User desired bitrate %d, Peak kbps %d, " - "initial rtt %d, " - "final rtt %d, %3.2f lost packets", - user_desired_bitrate, results.peak_kbps, results.starting_rtt, - results.ending_rtt, percent_lost); - - // We still want to set the peak to about 1.2x what the target bitrate is, - // even if the speed test reported it should be lower. If we don't, FTL - // will queue data on the client and start adding latency. If the internet - // connection really can't handle the bitrate the user will see either lost frame - // and recovered frame counts go up, which is reflect in the dropped_frames count. - stream->peak_kbps = stream->params.peak_kbps = - user_desired_bitrate * 12 / 10; - ftl_ingest_update_params(&stream->ftl_handle, &stream->params); -} - -static inline bool send_headers(struct ftl_stream *stream, int64_t dts_usec); - -static inline bool can_shutdown_stream(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - uint64_t cur_time = os_gettime_ns(); - bool timeout = cur_time >= stream->shutdown_timeout_ts; - - if (timeout) - info("Stream shutdown timeout reached (%d second(s))", - stream->max_shutdown_time_sec); - - return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts; -} - -static void *send_thread(void *data) -{ - struct ftl_stream *stream = data; - ftl_status_t status_code; - - os_set_thread_name("ftl-stream: send_thread"); - - while (os_sem_wait(stream->send_sem) == 0) { - struct encoder_packet packet; - - if (stopping(stream) && stream->stop_ts == 0) { - break; - } - - if (!get_next_packet(stream, &packet)) - continue; - - if (stopping(stream)) { - if (can_shutdown_stream(stream, &packet)) { - obs_encoder_packet_release(&packet); - break; - } - } - - /* sends sps/pps on every key frame as this is typically - * required for webrtc */ - if (packet.keyframe) { - if (!send_headers(stream, packet.dts_usec)) { - os_atomic_set_bool(&stream->disconnected, true); - break; - } - } - - if (send_packet(stream, &packet, false) < 0) { - os_atomic_set_bool(&stream->disconnected, true); - break; - } - } - - bool encode_error = os_atomic_load_bool(&stream->encode_error); - - if (disconnected(stream)) { - info("Disconnected from %s", stream->path.array); - } else if (encode_error) { - info("Encoder error, disconnecting"); - } else { - info("User stopped the stream"); - } - - if (!stopping(stream)) { - pthread_detach(stream->send_thread); - obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); - } else if (encode_error) { - obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR); - } else { - obs_output_end_data_capture(stream->output); - } - - info("ingest disconnect"); - - status_code = ftl_ingest_disconnect(&stream->ftl_handle); - if (status_code != FTL_SUCCESS) { - printf("Failed to disconnect from ingest %d", status_code); - } - - free_packets(stream); - os_event_reset(stream->stop_event); - os_atomic_set_bool(&stream->active, false); - stream->sent_headers = false; - return NULL; -} - -static bool send_video_header(struct ftl_stream *stream, int64_t dts_usec) -{ - obs_output_t *context = stream->output; - obs_encoder_t *vencoder = obs_output_get_video_encoder(context); - uint8_t *header; - size_t size; - - struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, - .timebase_den = 1, - .keyframe = true, - .dts_usec = dts_usec}; - - if (!obs_encoder_get_extra_data(vencoder, &header, &size)) - return false; - packet.size = obs_parse_avc_header(&packet.data, header, size); - return send_packet(stream, &packet, true) >= 0; -} - -static inline bool send_headers(struct ftl_stream *stream, int64_t dts_usec) -{ - stream->sent_headers = true; - - if (!send_video_header(stream, dts_usec)) - return false; - - return true; -} - -static inline bool reset_semaphore(struct ftl_stream *stream) -{ - os_sem_destroy(stream->send_sem); - return os_sem_init(&stream->send_sem, 0) == 0; -} - -#ifdef _WIN32 -#define socklen_t int -#endif - -static int init_send(struct ftl_stream *stream) -{ - int ret; - - reset_semaphore(stream); - - ret = pthread_create(&stream->send_thread, NULL, send_thread, stream); - if (ret != 0) { - warn("Failed to create send thread"); - return OBS_OUTPUT_ERROR; - } - - os_atomic_set_bool(&stream->active, true); - - obs_output_begin_data_capture(stream->output, 0); - - return OBS_OUTPUT_SUCCESS; -} - -static int try_connect(struct ftl_stream *stream) -{ - ftl_status_t status_code; - - if (dstr_is_empty(&stream->path)) { - warn("URL is empty"); - return OBS_OUTPUT_BAD_PATH; - } - - info("Connecting to FTL Ingest URL %s...", stream->path.array); - - stream->width = (int)obs_output_get_width(stream->output); - stream->height = (int)obs_output_get_height(stream->output); - - status_code = ftl_ingest_connect(&stream->ftl_handle); - if (status_code != FTL_SUCCESS) { - if (status_code == FTL_BAD_OR_INVALID_STREAM_KEY) { - blog(LOG_ERROR, "Invalid Key (%s)", - ftl_status_code_to_string(status_code)); - return OBS_OUTPUT_INVALID_STREAM; - } else { - warn("Ingest connect failed with: %s (%d)", - ftl_status_code_to_string(status_code), - status_code); - return _ftl_error_to_obs_error(status_code); - } - } - - info("Connection to %s successful", stream->path.array); - - // Always get the peak bitrate when we are starting. - set_peak_bitrate(stream); - - pthread_create(&stream->status_thread, NULL, status_thread, stream); - - return init_send(stream); -} - -static bool ftl_stream_start(void *data) -{ - struct ftl_stream *stream = data; - - info("ftl_stream_start"); - - // Mixer doesn't support bframes. So force them off. - obs_encoder_t *video_encoder = - obs_output_get_video_encoder(stream->output); - obs_data_t *video_settings = obs_encoder_get_settings(video_encoder); - obs_data_set_int(video_settings, "bf", 0); - obs_data_release(video_settings); - - if (!obs_output_can_begin_data_capture(stream->output, 0)) { - return false; - } - if (!obs_output_initialize_encoders(stream->output, 0)) { - return false; - } - - stream->frames_sent = 0; - os_atomic_set_bool(&stream->connecting, true); - - return pthread_create(&stream->connect_thread, NULL, connect_thread, - stream) == 0; -} - -static inline bool add_packet(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - deque_push_back(&stream->packets, packet, - sizeof(struct encoder_packet)); - return true; -} - -static inline size_t num_buffered_packets(struct ftl_stream *stream) -{ - return stream->packets.size / sizeof(struct encoder_packet); -} - -static void drop_frames(struct ftl_stream *stream, const char *name, - int highest_priority, bool pframes) -{ - UNUSED_PARAMETER(pframes); - - struct deque new_buf = {0}; - int num_frames_dropped = 0; - -#ifdef _DEBUG - int start_packets = (int)num_buffered_packets(stream); -#else - UNUSED_PARAMETER(name); -#endif - - deque_reserve(&new_buf, sizeof(struct encoder_packet) * 8); - - while (stream->packets.size) { - struct encoder_packet packet; - deque_pop_front(&stream->packets, &packet, sizeof(packet)); - - /* do not drop audio data or video keyframes */ - if (packet.type == OBS_ENCODER_AUDIO || - packet.drop_priority >= highest_priority) { - deque_push_back(&new_buf, &packet, sizeof(packet)); - - } else { - num_frames_dropped++; - obs_encoder_packet_release(&packet); - } - } - - deque_free(&stream->packets); - stream->packets = new_buf; - - if (stream->min_priority < highest_priority) - stream->min_priority = highest_priority; - if (!num_frames_dropped) - return; - - stream->dropped_frames += num_frames_dropped; -#ifdef _DEBUG - debug("Dropped %s, prev packet count: %d, new packet count: %d", name, - start_packets, (int)num_buffered_packets(stream)); -#endif -} - -static bool find_first_video_packet(struct ftl_stream *stream, - struct encoder_packet *first) -{ - size_t count = stream->packets.size / sizeof(*first); - - for (size_t i = 0; i < count; i++) { - struct encoder_packet *cur = - deque_data(&stream->packets, i * sizeof(*first)); - if (cur->type == OBS_ENCODER_VIDEO && !cur->keyframe) { - *first = *cur; - return true; - } - } - - return false; -} - -static void check_to_drop_frames(struct ftl_stream *stream, bool pframes) -{ - struct encoder_packet first; - int64_t buffer_duration_usec; - size_t num_packets = num_buffered_packets(stream); - const char *name = pframes ? "p-frames" : "b-frames"; - int priority = pframes ? OBS_NAL_PRIORITY_HIGHEST - : OBS_NAL_PRIORITY_HIGH; - int64_t drop_threshold = pframes ? stream->pframe_drop_threshold_usec - : stream->drop_threshold_usec; - - if (num_packets < 5) { - if (!pframes) - stream->congestion = 0.0f; - return; - } - - if (!find_first_video_packet(stream, &first)) - return; - - /* if the amount of time stored in the buffered packets waiting to be - * sent is higher than threshold, drop frames */ - buffer_duration_usec = stream->last_dts_usec - first.dts_usec; - - if (!pframes) { - stream->congestion = - (float)buffer_duration_usec / (float)drop_threshold; - } - - if (buffer_duration_usec > drop_threshold) { - debug("buffer_duration_usec: %" PRId64, buffer_duration_usec); - drop_frames(stream, name, priority, pframes); - } -} - -static bool add_video_packet(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - check_to_drop_frames(stream, false); - check_to_drop_frames(stream, true); - - /* if currently dropping frames, drop packets until it reaches the - * desired priority */ - if (packet->priority < stream->min_priority) { - stream->dropped_frames++; - return false; - } else { - stream->min_priority = 0; - } - - stream->last_dts_usec = packet->dts_usec; - return add_packet(stream, packet); -} - -static void ftl_stream_data(void *data, struct encoder_packet *packet) -{ - struct ftl_stream *stream = data; - - struct encoder_packet new_packet; - bool added_packet = false; - - if (disconnected(stream) || !active(stream)) - return; - - /* encoder failure */ - if (!packet) { - os_atomic_set_bool(&stream->encode_error, true); - os_sem_post(stream->send_sem); - return; - } - - if (packet->type == OBS_ENCODER_VIDEO) - obs_parse_avc_packet(&new_packet, packet); - else - obs_encoder_packet_ref(&new_packet, packet); - - pthread_mutex_lock(&stream->packets_mutex); - - if (!disconnected(stream)) { - added_packet = (packet->type == OBS_ENCODER_VIDEO) - ? add_video_packet(stream, &new_packet) - : add_packet(stream, &new_packet); - } - - pthread_mutex_unlock(&stream->packets_mutex); - - if (added_packet) - os_sem_post(stream->send_sem); - else - obs_encoder_packet_release(&new_packet); -} - -static void ftl_stream_defaults(obs_data_t *defaults) -{ - UNUSED_PARAMETER(defaults); -} - -static obs_properties_t *ftl_stream_properties(void *unused) -{ - UNUSED_PARAMETER(unused); - - obs_properties_t *props = obs_properties_create(); - obs_properties_add_int(props, "peak_bitrate_kbps", - obs_module_text("FTLStream.PeakBitrate"), 1000, - 10000, 500); - - return props; -} - -static uint64_t ftl_stream_total_bytes_sent(void *data) -{ - struct ftl_stream *stream = data; - - return stream->total_bytes_sent; -} - -static int ftl_stream_dropped_frames(void *data) -{ - struct ftl_stream *stream = data; - return (int)stream->dropped_frames; -} - -static float ftl_stream_congestion(void *data) -{ - struct ftl_stream *stream = data; - return stream->min_priority > 0 ? 1.0f : stream->congestion; -} - -enum ret_type { - RET_CONTINUE, - RET_BREAK, - RET_EXIT, -}; - -static enum ret_type ftl_event(struct ftl_stream *stream, - ftl_status_msg_t status) -{ - if (status.msg.event.type != FTL_STATUS_EVENT_TYPE_DISCONNECTED) - return RET_CONTINUE; - - info("Disconnected from ingest with reason: %s", - ftl_status_code_to_string(status.msg.event.error_code)); - - if (status.msg.event.reason == FTL_STATUS_EVENT_REASON_API_REQUEST) { - return RET_BREAK; - } - - //tell OBS and it will trigger a reconnection - blog(LOG_WARNING, "Reconnecting to Ingest"); - obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); - reset_semaphore(stream); - return RET_EXIT; -} - -static void *status_thread(void *data) -{ - struct ftl_stream *stream = data; - - ftl_status_msg_t status; - ftl_status_t status_code; - - while (!disconnected(stream)) { - status_code = ftl_ingest_get_status(&stream->ftl_handle, - &status, 1000); - - if (status_code == FTL_STATUS_TIMEOUT || - status_code == FTL_QUEUE_EMPTY) { - continue; - } else if (status_code == FTL_NOT_INITIALIZED) { - break; - } - - if (status.type == FTL_STATUS_EVENT) { - enum ret_type ret_type = ftl_event(stream, status); - if (ret_type == RET_EXIT) - return NULL; - else if (ret_type == RET_BREAK) - break; - - } else if (status.type == FTL_STATUS_LOG) { - blog(LOG_INFO, "[%d] %s", status.msg.log.log_level, - status.msg.log.string); - - } else if (status.type == FTL_STATUS_VIDEO_PACKETS) { - ftl_packet_stats_msg_t *p = &status.msg.pkt_stats; - - // Report nack requests as dropped frames - stream->dropped_frames += - p->nack_reqs - stream->last_nack_count; - stream->last_nack_count = p->nack_reqs; - - int log_level = p->nack_reqs > 0 ? LOG_INFO : LOG_DEBUG; - - blog(log_level, - "Avg packet send per second %3.1f, " - "total nack requests %d", - (float)p->sent * 1000.f / p->period, - (int)p->nack_reqs); - - } else if (status.type == FTL_STATUS_VIDEO_PACKETS_INSTANT) { - ftl_packet_stats_instant_msg_t *p = - &status.msg.ipkt_stats; - - int log_level = p->avg_rtt > 20 ? LOG_INFO : LOG_DEBUG; - - blog(log_level, - "avg transmit delay %dms " - "(min: %d, max: %d), " - "avg rtt %dms (min: %d, max: %d)", - p->avg_xmit_delay, p->min_xmit_delay, - p->max_xmit_delay, p->avg_rtt, p->min_rtt, - p->max_rtt); - - } else if (status.type == FTL_STATUS_VIDEO) { - ftl_video_frame_stats_msg_t *v = - &status.msg.video_stats; - - int log_level = v->queue_fullness > 0 ? LOG_INFO - : LOG_DEBUG; - - blog(log_level, - "Queue an average of %3.2f fps " - "(%3.1f kbps), " - "sent an average of %3.2f fps " - "(%3.1f kbps), " - "queue fullness %d, " - "max frame size %d", - (float)v->frames_queued * 1000.f / v->period, - (float)v->bytes_queued / v->period * 8, - (float)v->frames_sent * 1000.f / v->period, - (float)v->bytes_sent / v->period * 8, - v->queue_fullness, v->max_frame_size); - } else { - blog(LOG_DEBUG, - "Status: Got Status message of type " - "%d", - status.type); - } - } - - blog(LOG_DEBUG, "status_thread: Exited"); - pthread_detach(stream->status_thread); - return NULL; -} - -static void *connect_thread(void *data) -{ - struct ftl_stream *stream = data; - int ret; - - os_set_thread_name("ftl-stream: connect_thread"); - - blog(LOG_WARNING, "ftl-stream: connect thread"); - - ret = init_connect(stream); - if (ret != OBS_OUTPUT_SUCCESS) { - obs_output_signal_stop(stream->output, ret); - return NULL; - } - - ret = try_connect(stream); - if (ret != OBS_OUTPUT_SUCCESS) { - obs_output_signal_stop(stream->output, ret); - info("Connection to %s failed: %d", stream->path.array, ret); - } - - if (!stopping(stream)) - pthread_detach(stream->connect_thread); - - os_atomic_set_bool(&stream->connecting, false); - return NULL; -} - -static int init_connect(struct ftl_stream *stream) -{ - obs_service_t *service; - obs_data_t *settings; - const char *bind_ip, *key, *ingest_url; - ftl_status_t status_code; - - info("init_connect"); - - if (stopping(stream)) - pthread_join(stream->send_thread, NULL); - - free_packets(stream); - - service = obs_output_get_service(stream->output); - if (!service) { - return OBS_OUTPUT_ERROR; - } - - os_atomic_set_bool(&stream->disconnected, false); - os_atomic_set_bool(&stream->encode_error, false); - stream->total_bytes_sent = 0; - stream->dropped_frames = 0; - stream->min_priority = 0; - - settings = obs_output_get_settings(stream->output); - obs_encoder_t *video_encoder = - obs_output_get_video_encoder(stream->output); - obs_data_t *video_settings = obs_encoder_get_settings(video_encoder); - - ingest_url = obs_service_get_connect_info( - service, OBS_SERVICE_CONNECT_INFO_SERVER_URL); - if (strncmp(ingest_url, FTL_URL_PROTOCOL, strlen(FTL_URL_PROTOCOL)) == - 0) { - dstr_copy(&stream->path, ingest_url + strlen(FTL_URL_PROTOCOL)); - } else { - dstr_copy(&stream->path, ingest_url); - } - - key = obs_service_get_connect_info(service, - OBS_SERVICE_CONNECT_INFO_STREAM_KEY); - - int target_bitrate = (int)obs_data_get_int(video_settings, "bitrate"); - int peak_bitrate = (int)((float)target_bitrate * 1.1f); - - //minimum overshoot tolerance of 10% - if (peak_bitrate < target_bitrate) { - peak_bitrate = target_bitrate; - } - - stream->params.stream_key = (char *)key; - stream->params.video_codec = FTL_VIDEO_H264; - stream->params.audio_codec = FTL_AUDIO_OPUS; - stream->params.ingest_hostname = stream->path.array; - stream->params.vendor_name = "OBS Studio"; - stream->params.vendor_version = obs_get_version_string(); - stream->params.peak_kbps = stream->peak_kbps < 0 ? 0 - : stream->peak_kbps; - - //not required when using ftl_ingest_send_media_dts - stream->params.fps_num = 0; - stream->params.fps_den = 0; - - status_code = ftl_ingest_create(&stream->ftl_handle, &stream->params); - if (status_code != FTL_SUCCESS) { - if (status_code == FTL_BAD_OR_INVALID_STREAM_KEY) { - blog(LOG_ERROR, "Invalid Key (%s)", - ftl_status_code_to_string(status_code)); - return OBS_OUTPUT_INVALID_STREAM; - } else { - blog(LOG_ERROR, "Failed to create ingest handle (%s)", - ftl_status_code_to_string(status_code)); - return OBS_OUTPUT_ERROR; - } - } - - dstr_copy(&stream->username, - obs_service_get_connect_info( - service, OBS_SERVICE_CONNECT_INFO_USERNAME)); - dstr_copy(&stream->password, - obs_service_get_connect_info( - service, OBS_SERVICE_CONNECT_INFO_PASSWORD)); - dstr_depad(&stream->path); - - stream->drop_threshold_usec = - (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000; - stream->max_shutdown_time_sec = - (int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC); - - bind_ip = obs_data_get_string(settings, OPT_BIND_IP); - dstr_copy(&stream->bind_ip, bind_ip); - - obs_data_release(settings); - obs_data_release(video_settings); - return OBS_OUTPUT_SUCCESS; -} - -// Returns 0 on success -static int _ftl_error_to_obs_error(int status) -{ - /* Map FTL errors to OBS errors */ - - switch (status) { - case FTL_SUCCESS: - return OBS_OUTPUT_SUCCESS; - case FTL_SOCKET_NOT_CONNECTED: - case FTL_MALLOC_FAILURE: - case FTL_INTERNAL_ERROR: - case FTL_CONFIG_ERROR: - case FTL_NOT_ACTIVE_STREAM: - case FTL_NOT_CONNECTED: - case FTL_ALREADY_CONNECTED: - case FTL_STATUS_TIMEOUT: - case FTL_QUEUE_FULL: - case FTL_STATUS_WAITING_FOR_KEY_FRAME: - case FTL_QUEUE_EMPTY: - case FTL_NOT_INITIALIZED: - return OBS_OUTPUT_ERROR; - case FTL_BAD_REQUEST: - case FTL_DNS_FAILURE: - case FTL_CONNECT_ERROR: - case FTL_UNSUPPORTED_MEDIA_TYPE: - case FTL_OLD_VERSION: - case FTL_UNAUTHORIZED: - case FTL_AUDIO_SSRC_COLLISION: - case FTL_VIDEO_SSRC_COLLISION: - case FTL_STREAM_REJECTED: - case FTL_BAD_OR_INVALID_STREAM_KEY: - case FTL_CHANNEL_IN_USE: - case FTL_REGION_UNSUPPORTED: - case FTL_GAME_BLOCKED: - return OBS_OUTPUT_CONNECT_FAILED; - case FTL_NO_MEDIA_TIMEOUT: - return OBS_OUTPUT_DISCONNECTED; - case FTL_USER_DISCONNECT: - return OBS_OUTPUT_SUCCESS; - case FTL_UNKNOWN_ERROR_CODE: - default: - /* Unknown FTL error */ - return OBS_OUTPUT_ERROR; - } -} - -struct obs_output_info ftl_output_info = { - .id = "ftl_output", - .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE, - .protocols = "FTL", - .encoded_video_codecs = "h264", - .encoded_audio_codecs = "opus", - .get_name = ftl_stream_getname, - .create = ftl_stream_create, - .destroy = ftl_stream_destroy, - .start = ftl_stream_start, - .stop = ftl_stream_stop, - .encoded_packet = ftl_stream_data, - .get_defaults = ftl_stream_defaults, - .get_properties = ftl_stream_properties, - .get_total_bytes = ftl_stream_total_bytes_sent, - .get_congestion = ftl_stream_congestion, - .get_dropped_frames = ftl_stream_dropped_frames, -}; diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c index 23ffa7da6db8f7..dde358f3e96ee4 100644 --- a/plugins/obs-outputs/obs-outputs.c +++ b/plugins/obs-outputs/obs-outputs.c @@ -9,16 +9,13 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-outputs", "en-US") MODULE_EXPORT const char *obs_module_description(void) { - return "OBS core RTMP/FLV/null/FTL outputs"; + return "OBS core RTMP/FLV/null outputs"; } extern struct obs_output_info rtmp_output_info; extern struct obs_output_info null_output_info; extern struct obs_output_info flv_output_info; extern struct obs_output_info mp4_output_info; -#if defined(FTL_FOUND) -extern struct obs_output_info ftl_output_info; -#endif #if defined(_WIN32) && defined(MBEDTLS_THREADING_ALT) void mbed_mutex_init(mbedtls_threading_mutex_t *m) @@ -67,9 +64,6 @@ bool obs_module_load(void) obs_register_output(&null_output_info); obs_register_output(&flv_output_info); obs_register_output(&mp4_output_info); -#if defined(FTL_FOUND) - obs_register_output(&ftl_output_info); -#endif return true; } From e62c6008732bf481c68457330457624b238514a2 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 19 Dec 2023 02:48:42 +0100 Subject: [PATCH 0304/1073] .gitmodules: Remove ftl-sdk submodule --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4599972c5bda3a..7f21ac7eda1cf6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "plugins/obs-browser"] path = plugins/obs-browser url = https://github.com/obsproject/obs-browser.git -[submodule "plugins/obs-outputs/ftl-sdk"] - path = plugins/obs-outputs/ftl-sdk - url = https://github.com/Mixer/ftl-sdk.git [submodule "plugins/obs-websocket"] path = plugins/obs-websocket url = https://github.com/obsproject/obs-websocket.git From dfc1feac1a810580e1b451a5ea4c1b6cf7e977fd Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 19 Dec 2023 02:56:20 +0100 Subject: [PATCH 0305/1073] build-aux: Remove ftl-sdk exclusions from format script --- build-aux/.run-format.zsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh index e34e11e3768923..d0f76ee707d0e3 100755 --- a/build-aux/.run-format.zsh +++ b/build-aux/.run-format.zsh @@ -56,7 +56,7 @@ invoke_formatter() { if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps)/**/*.(c|cpp|h|hpp|m|mm)(.N)) - source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|obs-outputs/ftl-sdk|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|win-dshow/libdshowcapture)/*}) local -a format_args=(-style=file -fallback-style=none) if (( _loglevel > 2 )) format_args+=(--verbose) @@ -77,7 +77,7 @@ invoke_formatter() { if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|cmake)/**/(CMakeLists.txt|*.cmake)(.N)) - source_files=(${source_files:#*/(obs-outputs/ftl-sdk|jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) local -a format_args=() if (( _loglevel > 2 )) format_args+=(--log-level debug) From 1206870ceffcefa243b0b28cc37a4758f17a5fbd Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 19 Dec 2023 02:56:46 +0100 Subject: [PATCH 0306/1073] UI: Remove FTL support --- UI/window-basic-auto-config-test.cpp | 3 --- UI/window-basic-main-outputs.cpp | 1 - UI/window-basic-main.cpp | 5 ++--- UI/window-basic-settings-stream.cpp | 3 --- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index 90487578305234..d93a6a113cffb3 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -228,9 +228,6 @@ void AutoConfigTestPage::TestBandwidthThread() wiz->serviceName == "Restream.io - RTMP") { string_depad_key(key); key += "?test=true"; - } else if (wiz->serviceName == "Restream.io - FTL") { - string_depad_key(key); - key += "?test"; } obs_data_set_string(service_settings, "service", diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 9e644e90acb8df..eee1b352f68ccb 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -20,7 +20,6 @@ volatile bool recording_paused = false; volatile bool replaybuf_active = false; volatile bool virtualcam_active = false; -#define FTL_PROTOCOL "ftl" #define RTMP_PROTOCOL "rtmp" #define SRT_PROTOCOL "srt" #define RIST_PROTOCOL "rist" diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 20e2d01a7b26f0..3cbcd99af175ad 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1501,9 +1501,8 @@ bool OBSBasic::LoadService() if (!service) return false; - /* Enforce Opus on FTL if needed */ - if (strcmp(obs_service_get_protocol(service), "FTL") == 0 || - strcmp(obs_service_get_protocol(service), "WHIP") == 0) { + /* Enforce Opus on WHIP if needed */ + if (strcmp(obs_service_get_protocol(service), "WHIP") == 0) { const char *option = config_get_string( basicConfig, "SimpleOutput", "StreamAudioEncoder"); if (strcmp(option, "opus") != 0) diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 881e1d54247f53..3aa7bb57bb3d73 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -746,9 +746,6 @@ QString OBSBasicSettings::FindProtocol() server.startsWith("rtmps://")) return QString("RTMPS"); - if (server.startsWith("ftl://")) - return QString("FTL"); - if (server.startsWith("srt://")) return QString("SRT"); From bde68fed3297fe5b3d1421ee0f39fbeb129ae328 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Fri, 12 Jul 2024 21:47:25 -0400 Subject: [PATCH 0307/1073] UI: Fix source tree icon spacing --- UI/data/themes/Yami.obt | 22 ++++++++++++++-------- UI/source-tree.cpp | 2 ++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 015c006a79db75..6b1d566f4bfa3e 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -1532,13 +1532,16 @@ QGroupBox::indicator:unchecked:disabled { QCheckBox[lockCheckBox=true] { outline: none; background: transparent; + max-width: var(--icon_base); + max-height: var(--icon_base); + padding: var(--padding_base); + border: 1px solid transparent; + margin-left: var(--spacing_large); } QCheckBox[lockCheckBox=true]::indicator { width: var(--icon_base); height: var(--icon_base); - padding: 1px; - border: 1px solid transparent; border-radius: 4px; } @@ -1552,8 +1555,8 @@ QCheckBox[lockCheckBox=true]::indicator:unchecked:hover { image: url(:res/images/unlocked.svg); } -QCheckBox[lockCheckBox=true]::indicator:hover, -QCheckBox[lockCheckBox=true]::indicator:focus { +QCheckBox[lockCheckBox=true]:hover, +QCheckBox[lockCheckBox=true]:focus { border: 1px solid var(--border_highlight); } @@ -1562,13 +1565,16 @@ QCheckBox[lockCheckBox=true]::indicator:focus { QCheckBox[visibilityCheckBox=true] { outline: none; background: transparent; + max-width: var(--icon_base); + max-height: var(--icon_base); + padding: var(--padding_base); + border: 1px solid transparent; + margin-left: var(--spacing_large); } QCheckBox[visibilityCheckBox=true]::indicator { width: var(--icon_base); height: var(--icon_base); - padding: 1px; - border: 1px solid transparent; border-radius: 4px; } @@ -1582,8 +1588,8 @@ QCheckBox[visibilityCheckBox=true]::indicator:unchecked:hover { image: url(:res/images/invisible.svg); } -QCheckBox[visibilityCheckBox=true]::indicator:hover, -QCheckBox[visibilityCheckBox=true]::indicator:focus { +QCheckBox[visibilityCheckBox=true]:hover, +QCheckBox[visibilityCheckBox=true]:focus { border: 1px solid var(--border_highlight); } diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 14fb89deaea6c0..947b217ce04f66 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -77,6 +77,7 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) iconLabel->setPixmap(pixmap); iconLabel->setEnabled(sourceVisible); iconLabel->setStyleSheet("background: none"); + iconLabel->setProperty("TH_Source_Icon", true); } vis = new QCheckBox(); @@ -111,6 +112,7 @@ SourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) boxLayout = new QHBoxLayout(); boxLayout->setContentsMargins(0, 0, 0, 0); + boxLayout->setSpacing(0); if (iconLabel) { boxLayout->addWidget(iconLabel); boxLayout->addSpacing(2); From 0e84188f37556e866c1c3ab162b7887916ecc426 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Mon, 15 Jul 2024 13:57:36 -0400 Subject: [PATCH 0308/1073] UI: Fix read-only QTextEdit background color --- UI/data/themes/Yami.obt | 4 +++- UI/data/themes/Yami_Classic.ovt | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 6b1d566f4bfa3e..fb163b816b0afe 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -890,7 +890,9 @@ QPlainTextEdit:focus { border-color: var(--input_border_focus); } -QTextEdit:!editable { +QTextEdit:!editable, +QTextEdit:!editable:hover, +QTextEdit:!editable:focus { background-color: var(--input_bg); } diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index 05b79419ef7e1b..525dd2f6828b9a 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -171,6 +171,12 @@ QPlainTextEdit:hover { background-color: var(--input_bg_focus); } +QTextEdit:!editable, +QTextEdit:!editable:hover, +QTextEdit:!editable:focus { + background-color: var(--input_bg_focus); +} + QSpinBox, QDoubleSpinBox { background-color: var(--input_bg_focus); From ec5297b549aa2884287a1b7e986e0f2bf08ad143 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Mon, 15 Jul 2024 14:53:44 -0400 Subject: [PATCH 0309/1073] UI: Adjust sizing of Classic audio meter elements --- UI/data/themes/Yami.obt | 2 +- UI/data/themes/Yami_Classic.ovt | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index fb163b816b0afe..b97f65abe49508 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -129,7 +129,7 @@ --spinbox_button_height: calc(var(--input_height_half) - 1px); - --volume_slider: calc(calc(10px + var(--font_base_value)) / 4); + --volume_slider: calc(calc(4px + var(--font_base_value)) / 4); --volume_slider_box: calc(var(--volume_slider) * 4); --volume_slider_label: calc(var(--volume_slider_box) * 2); diff --git a/UI/data/themes/Yami_Classic.ovt b/UI/data/themes/Yami_Classic.ovt index 525dd2f6828b9a..045507a9db18c3 100644 --- a/UI/data/themes/Yami_Classic.ovt +++ b/UI/data/themes/Yami_Classic.ovt @@ -31,7 +31,7 @@ /* OS Fixes */ --os_mac_font_base_value: 11; - --font_small: calc(0.8pt * var(--font_base_value)); + --font_small: calc(0.75pt * var(--font_base_value)); --icon_base: calc(6px + var(--font_base_value)); @@ -298,6 +298,13 @@ MuteCheckBox::indicator:unchecked:hover { background-color: var(--bg_window); } +#stackedMixerArea QPushButton, +#stackedMixerArea QPushButton:!hover { + width: var(--icon_base_mixer); + height: var(--icon_base_mixer); + padding: var(--spacing_base); +} + VolumeMeter { qproperty-backgroundNominalColor: rgb(38,127,38); qproperty-backgroundWarningColor: rgb(127,127,38); From e7d6707fa7d9739fb57375f5f8620b204be14514 Mon Sep 17 00:00:00 2001 From: Translation Updater <> Date: Wed, 17 Jul 2024 09:34:12 +0000 Subject: [PATCH 0310/1073] Update translations from Crowdin --- AUTHORS | 96 +++++--- UI/data/locale/ar-SA.ini | 47 ++++ UI/data/locale/be-BY.ini | 7 +- UI/data/locale/ca-ES.ini | 11 +- UI/data/locale/cs-CZ.ini | 49 +++- UI/data/locale/de-DE.ini | 27 ++- UI/data/locale/eo-UY.ini | 11 + UI/data/locale/es-ES.ini | 17 +- UI/data/locale/eu-ES.ini | 47 ++++ UI/data/locale/fa-IR.ini | 16 ++ UI/data/locale/fi-FI.ini | 47 +++- UI/data/locale/fr-FR.ini | 40 ++++ UI/data/locale/he-IL.ini | 28 ++- UI/data/locale/hi-IN.ini | 47 ++++ UI/data/locale/hr-HR.ini | 2 + UI/data/locale/hu-HU.ini | 47 +++- UI/data/locale/id-ID.ini | 9 +- UI/data/locale/it-IT.ini | 214 ++++++++++-------- UI/data/locale/ja-JP.ini | 16 +- UI/data/locale/ka-GE.ini | 15 ++ UI/data/locale/ko-KR.ini | 47 +++- UI/data/locale/lv-LV.ini | 35 +-- UI/data/locale/ms-MY.ini | 1 + UI/data/locale/nl-NL.ini | 22 +- UI/data/locale/pl-PL.ini | 5 +- UI/data/locale/pt-BR.ini | 75 ++++-- UI/data/locale/pt-PT.ini | 69 ++++-- UI/data/locale/ro-RO.ini | 172 +++++++------- UI/data/locale/ru-RU.ini | 43 +++- UI/data/locale/sk-SK.ini | 47 ++++ UI/data/locale/sl-SI.ini | 9 + UI/data/locale/sr-SP.ini | 2 + UI/data/locale/sv-SE.ini | 35 ++- UI/data/locale/th-TH.ini | 1 + UI/data/locale/tr-TR.ini | 47 ++++ UI/data/locale/tt-RU.ini | 2 + UI/data/locale/ug-CN.ini | 2 + UI/data/locale/uk-UA.ini | 48 +++- UI/data/locale/vi-VN.ini | 41 ++-- UI/data/locale/zh-CN.ini | 5 +- UI/data/locale/zh-TW.ini | 59 ++++- .../aja-output-ui/data/locale/ro-RO.ini | 5 +- .../frontend-tools/data/locale/it-IT.ini | 14 +- .../frontend-tools/data/locale/ro-RO.ini | 2 +- .../frontend-tools/data/locale/sv-SE.ini | 2 +- .../frontend-tools/data/locale/vi-VN.ini | 2 +- plugins/aja/data/locale/it-IT.ini | 2 +- plugins/aja/data/locale/ro-RO.ini | 5 +- plugins/decklink/data/locale/ro-RO.ini | 2 +- plugins/decklink/data/locale/zh-TW.ini | 2 +- plugins/linux-alsa/data/locale/ro-RO.ini | 1 + plugins/linux-capture/data/locale/ar-SA.ini | 2 + plugins/linux-capture/data/locale/hi-IN.ini | 2 + plugins/linux-capture/data/locale/ka-GE.ini | 2 + plugins/linux-pipewire/data/locale/ar-SA.ini | 1 + plugins/linux-pipewire/data/locale/hi-IN.ini | 1 + plugins/linux-pipewire/data/locale/ro-RO.ini | 4 + plugins/linux-pipewire/data/locale/sl-SI.ini | 1 + .../linux-pulseaudio/data/locale/it-IT.ini | 4 +- plugins/mac-avcapture/data/locale/it-IT.ini | 2 +- plugins/mac-capture/data/locale/it-IT.ini | 2 +- .../mac-videotoolbox/data/locale/it-IT.ini | 4 +- .../src/obs-plugin/data/locale/it-IT.ini | 2 +- .../src/obs-plugin/data/locale/pt-BR.ini | 2 +- plugins/obs-browser | 2 +- plugins/obs-ffmpeg/data/locale/it-IT.ini | 8 +- plugins/obs-ffmpeg/data/locale/ro-RO.ini | 8 +- plugins/obs-filters/data/locale/it-IT.ini | 8 +- plugins/obs-filters/data/locale/ro-RO.ini | 2 +- plugins/obs-outputs/data/locale/ar-SA.ini | 4 + plugins/obs-outputs/data/locale/fr-FR.ini | 4 + plugins/obs-outputs/data/locale/hi-IN.ini | 4 + plugins/obs-outputs/data/locale/it-IT.ini | 4 +- plugins/obs-outputs/data/locale/ka-GE.ini | 2 + plugins/obs-outputs/data/locale/ro-RO.ini | 10 +- plugins/obs-outputs/data/locale/sk-SK.ini | 4 + plugins/obs-outputs/data/locale/sl-SI.ini | 4 + plugins/obs-outputs/data/locale/tr-TR.ini | 4 + plugins/obs-qsv11/data/locale/ro-RO.ini | 9 +- plugins/obs-transitions/data/locale/pt-PT.ini | 8 +- plugins/obs-vst/data/locale/sv-SE.ini | 6 +- plugins/obs-webrtc/data/locale/it-IT.ini | 4 +- plugins/obs-x264/data/locale/it-IT.ini | 2 +- plugins/rtmp-services/data/locale/ar-SA.ini | 2 + plugins/rtmp-services/data/locale/hi-IN.ini | 2 + plugins/rtmp-services/data/locale/ka-GE.ini | 1 + plugins/rtmp-services/data/locale/ko-KR.ini | 2 +- plugins/rtmp-services/data/locale/ro-RO.ini | 7 +- plugins/rtmp-services/data/locale/sk-SK.ini | 2 +- plugins/rtmp-services/data/locale/zh-TW.ini | 2 +- plugins/text-freetype2/data/locale/it-IT.ini | 6 +- plugins/vlc-video/data/locale/pt-PT.ini | 4 +- plugins/vlc-video/data/locale/sv-SE.ini | 4 +- plugins/win-capture/data/locale/it-IT.ini | 14 +- 94 files changed, 1377 insertions(+), 392 deletions(-) diff --git a/AUTHORS b/AUTHORS index 42331da4a9c99b..a127b8af0fce4c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,8 +8,8 @@ Contributors: PatTheMav R1CH cg2121 - Palana derrod + Palana Sebastian Beckmann Ryan Foster tytan652 @@ -22,8 +22,8 @@ Contributors: Rodney pkv Gol-D-Ace - BtbN tt2468 + BtbN Shaolin kc5nra Exeldro @@ -31,19 +31,19 @@ Contributors: Warchamp7 VodBox Zachary Lund + Ruwen Hahn SuslikV Paul Hindt - Ruwen Hahn Vainock CodeYan01 Reboot Martell Malone Ford Smith - columbarius Penwywern + columbarius + Ed Maste dodgepong 田七不甜 - Ed Maste HomeWorld Joel Bethke Alex Anderson @@ -53,14 +53,14 @@ Contributors: Tommy Vercetti shiina424 Roman Huts - Gale Service Checker + Gale Yuriy Chumak + jcm juvester mvji wangshaohui Florian Zwoch - jcm JohannMG craftwar sorayuki @@ -78,10 +78,10 @@ Contributors: Hector Martin Igor Bochkariov Luke Yelavich + Sean DuBois bin Chris Developer-Ecosystem-Engineering - Sean DuBois Stéphane Lepin Chip Bradford Ilya Melamed @@ -148,6 +148,7 @@ Contributors: obiwac rhutsAMD univrsal + Alex Luccisano Alexander Popov Artem Polishchuk Benjamin Klettbach @@ -199,6 +200,7 @@ Contributors: Alexander Schittler Andreas Reischuck Andrei Nistor + Andrew Francis Antti Tapaninen Arthus AuroraWright @@ -302,7 +304,6 @@ Contributors: Akihiko Koizuka Alcaros Alex Kosenko - Alex Luccisano Alexander Kozhevin Alexander Uhlmann Alexandre Biny @@ -395,6 +396,7 @@ Contributors: Eric Laberge Ethan Lee Evgeny Pavlov + Fabien Lavocat Faeez Kadiri Francisco Oltra Frank @@ -688,6 +690,7 @@ Translators: speedytechdev sadam2002 sadam4 (sadam2002sadam4) Vainock + Gol D. Ace (goldace) Albanian: Aredio Vani (aredio.vani) Albin Pllana (albinnpllanaa) @@ -700,6 +703,7 @@ Translators: Vainock VainockManager casttor + Gol D. Ace (goldace) Arabic: Tareq Almutairi (Tareq_s) ZILZAL @@ -735,6 +739,7 @@ Translators: Tensai Mustafa2018 FiberAhmed + Mohamed Saidi (ms0709037) Ndalabo Taema (hake_bsowq) MesterPerfect Fahad Alshaya (fashaya) @@ -769,6 +774,7 @@ Translators: Fabio Antonio Alanis Saushkina (fabio.alanis.saushkin) VainockManager mownd. (mownd0) + Vainock Armenian: ՀայՏղաՀՏ (Zoro02) HayTxa02 @@ -800,6 +806,7 @@ Translators: unknowndomain Башҡорт агенты (vint6908) VainockManager + Vainock Basque: Alexander Gabilondo (alexgabi) Xabier Aramendi (azpidatziak) @@ -890,6 +897,7 @@ Translators: unknowndomain Salif Mehmed (salifm) Warchamp7 + dodgepong Catalan: Jaime Muñoz Martín (jmmartin_5) Benet R. i Camps (BennyBeat) (BennyBeat) @@ -910,13 +918,14 @@ Translators: Pere O. (gotrunks) unknowndomain user (user1cat) + dodgepong Chinese Simplified: Bob Liu (Akagi201) fangzheng AlexGuo1998 wwj402_github - FrzMtrsprt (FrzHrzn) Andypsl8 + FrzMtrsprt (FrzHrzn) Steven (stevenlele) Origami Hexcolyte @@ -943,6 +952,7 @@ Translators: KyleBing 十月 (10jugatsu) cai_miao + 张非酋 (tiantianle1213) Opportunity (OpportunityLiu) lm902 MarsYoung @@ -993,7 +1003,7 @@ Translators: Chen99 cheriny FaZe Fakay (fazefakay) - xtexChooser + xtex pluwen cylin 赵杭灵 (h1679083640) @@ -1014,14 +1024,17 @@ Translators: 十月 (10jugatsu) Gol D. Ace (goldace) Vainock + 兔頭 (yeahdamn) VainockManager craftwar Thomas (thomassth) Jcys 菘菘 (SiongSng) Rice Hung (ricehung29) + SeasonChannel3322 (SeasonChannel32) xixiaofan (XXXF) 吳軒竹 (ggjason.tw) + hugoalh Jimmy_sheep Watson Tsai (ashaneba) Han-Jen Cheng (notexist) @@ -1033,6 +1046,7 @@ Translators: Meng Hao Li (GazCore) ak-47root Append Huang (append) + notlin4 X-RAY team film (961111ray) fangzheng zhtw @@ -1127,6 +1141,7 @@ Translators: Alex Smotra (smotraalex75) Daniel Aundal (aundal) VainockManager + dodgepong Dutch: Eric Bataille (ThoNohT) Harm van den Hoek (harm27) @@ -1176,6 +1191,7 @@ Translators: RyyzQ Mees (MeesJ) Roeland Kussé (roeland) + dodgepong English, United Kingdom: Andi Chandler (andibing) Issa1553 @@ -1216,6 +1232,7 @@ Translators: siamano Infinity ArcAngel (SirColdcrown) Marco Ciampa (ciampix) + Fitik David M. (Syndyo) Vainock VainockManager @@ -1266,7 +1283,6 @@ Translators: Kimmo Kujansuu (mrkujansuu) supersingularisogeny Jiri Grönroos (spammemoreplease) - Jarska Ricky Tigg (ricky.tigg) dodgepong Tero Keso (tero.keso) @@ -1288,7 +1304,6 @@ Translators: unknowndomain Juuso (juuso.keskikuru) Xupie - Operative Block (Suo) Juha Köpman (e0f) Ville Närhi (daimaah) ZapX5_ @@ -1306,9 +1321,9 @@ Translators: Skallr Tocram2 (tocram2) Benjamin Cambour (lesinfox) + Lucatacos Yberion Léo (leeo97one) - Lucatacos RisedSky Pikana BoboopTeam @@ -1350,10 +1365,10 @@ Translators: Keter LordFR (YorHaDen) Jérémy LITIQUE (JeremyLTE) christophe (TedM) + Remy Geko (Geko_Officiel) Wydady (Wyd1) ImDaBigBoss illusdidi - Remy Geko (Geko_Officiel) Mathieu Hautebas (matteyeux) 🌠 DarK | #Hello 🌠 (DarKTV_FR) Gabriel Dugny (Dugab) @@ -1370,11 +1385,13 @@ Translators: SkytAsul (skytasul) xav blacyre (xav35000) Alex Smotra (smotraalex75) + HugOmega Alexis Brandner (Alexinfos) Romain Gille (romgille) Camille Nury (kamsdu30) neesn dodgepong + Jean Dubois (jd-develop) BOSSFOX ! (BOSSFOX) Jean-Mathieu Jm Samson (sjm450666) Lexane Sirac (Exilexi) @@ -1411,15 +1428,17 @@ Translators: chaironeko aluaces VainockManager + dodgepong Georgian: georgianizator Levani Khutsishvili (Tricksteridze) Vainock Razmik Badalyan (badalyanrazmik) - Gol D. Ace (goldace) SimModer + Gol D. Ace (goldace) Temuri Doghonadze (temuri.doghonadze) unknowndomain + Sandro Kakabadze (SANDRO797) VainockManager German: Vainock @@ -1443,7 +1462,6 @@ Translators: cryxgio Jonas Otto (jottosmail) EinfachMerlin - Jeremy Jürgens (Jeremy3463) Holger Sinn (Holger-Sinn) WurstOnAir yojixe @@ -1582,6 +1600,7 @@ Translators: unknowndomain Arnav Kumar (arnavkumar) Saket Anand (saketanandjha) + Gol D. Ace (goldace) Hungarian: Gige Balázs Meskó (mesko.balazs) @@ -1645,13 +1664,13 @@ Translators: StarFang208 LordShadow95 Marocco2 + uman tiwi90 imcesca smart2128 Vincenzo Reale (vinx.reale) Sergio Beneduce (sbeneduce) Ruggero Tomaselli (ruggi99) - uman VainockManager Michele (ScrappyCocco) Albakham (albakham) @@ -1748,6 +1767,7 @@ Translators: Shingis Joldasbaev (joldasbaevkhan) Niyazbek Tolibaev (niyazbeksayipnazarovish) andyme + VainockManager Korean: ynetwork 영구땡 (wonkyupark) @@ -1758,6 +1778,7 @@ Translators: AlexKoala (alexkoala) 임세훈 (sh9351) vyteking + EP45 Hwanyong Lee (grbear) Vainock VainockManager @@ -1793,9 +1814,9 @@ Translators: bluestar8 swatzniker (dimitrisrtg324) Nesswit (rishubil) - EP45 김동현 (ehehguu) Bi0 D. (lefhi0731) + dodgepong Kurmanji (Kurdish): Cyax (Cyaxares) cehnemdark @@ -1810,14 +1831,15 @@ Translators: VainockManager Latvian: oskars - Andris Liepiņš (ahgpuc) + ahgpuc Arthur (ArthurLV) Imants Vancāns (Imants565) + Dejelnieks (dejelnieks212) Mops3005 VainockManager - Dejelnieks (dejelnieks212) Peridot Nation (PeridotNation) Markuss Strods (Marcuss_5001) + Vainock Lithuanian: Viljamas (Vegas1) Justas Vilimas (tyntas) @@ -1866,6 +1888,7 @@ Translators: Mbay Vainock VainockManager + Gol D. Ace (goldace) Norwegian Bokmal: Imre Eilertsen (DandelionSprout) Taesh (magnusmbratteng) @@ -1887,7 +1910,6 @@ Translators: Kenta Moe (Owndragoon) Sander Skjegstad (r530er) Gol D. Ace (goldace) - LBlend OsteHovel Lauren (loritsi) Legend27 @@ -1907,6 +1929,8 @@ Translators: unknowndomain Yngve Spjeld Landro (yslandro) VainockManager + Gol D. Ace (goldace) + Vainock Occitan: Diluns PinguiFabgeeklab @@ -1929,6 +1953,7 @@ Translators: Amin Mahmoudi (masterking32) Farshid Meidani (farshid_mi) mahdi ram (mhadi-ram) + rahimz ROkelbow Gol D. Ace (goldace) PRNUse @@ -2016,6 +2041,7 @@ Translators: Diogo Soares (DiogoSoares) Pedro Ricardo (Pedro270707) kayal15948 + andre_satorres ROkelbow unknowndomain Paulo Soares (psoares.gm) @@ -2047,6 +2073,7 @@ Translators: VALTER HITELMAN (vhitelman) Luiz Machado (qluizmachado) André Gama (ToeOficial) + andre_satorres Luciano Santos Gonçalves (luciano.ivec) Avellar (BetaTester) Maison da Silva (maisondasilva) @@ -2090,7 +2117,7 @@ Translators: João (fror) unknowndomain ThomasLawliet (thomaslawliet) - Gersonzao + Cirnos Ramon Gonzalez (ramon200000) Esdras Tarsis (esdrastarsis) Guilherme Cruz (gcrz) @@ -2112,6 +2139,7 @@ Translators: unknowndomain Sheikh Ahmed (sheikhahmed) VainockManager + Vainock Romanian: Cristian Silaghi (sno_w) Kele Ion Ion (krovyoll) @@ -2157,6 +2185,7 @@ Translators: Maxim Gribanov (MaximGribanov) dodgepong Vainock + Romka Almazniy (turbozver) Fitik Gol D. Ace (goldace) PanForPancakes @@ -2213,6 +2242,7 @@ Translators: unknowndomain zy9c 1337 (zy9c228) Илья Кузнецов (ilyavj) + xtemp09 allan walpy (AndreyLysenkov) Ninja Hacker3000 (ninjahacker3875) evilserge @@ -2252,6 +2282,7 @@ Translators: Alex Hall (Decidy) Alex Smotra (smotraalex75) unknowndomain + Gol D. Ace (goldace) Serbian (Cyrillic): nikolanikola medicmomcilo @@ -2265,8 +2296,10 @@ Translators: veles330 LittleGirl_WithPonyTail (alexs1320) unknowndomain + Daste dodgepong VainockManager + scienceangel Serbian (Latin): nikolanikola medicmomcilo @@ -2279,9 +2312,11 @@ Translators: unknowndomain dodgepong VainockManager + scienceangel Silesian: Psioczek Vainock + VainockManager Sinhala: නාමල් ජයසිංහ (nimnaya) HelaBasa Group (HelaBasa) @@ -2306,6 +2341,7 @@ Translators: unknowndomain HelloI'mUnderThe WaterPleaseHelpMe (BoBTHEbuilder) VainockManager + dodgepong Slovenian: Arnold Marko (atomicmind) Martin (miles) @@ -2336,8 +2372,8 @@ Translators: Pilar G. (TheMadnessLady) MarioMey Adolfo Jayme (fitojb) - Gol D. Ace (goldace) marcrisale + Gol D. Ace (goldace) Manuel Matías (manuel.matias) 716gjesu Alejandro Alzate Sanchez (alejandroalzate) @@ -2403,8 +2439,8 @@ Translators: Anton R (FirePhoenix) Sigge Stjärnholm (Kladdy) Laccy IEST (Laccy) - Bjorn Astrom (beeanyew) LinusW + Bjorn Astrom (beeanyew) 0x9fff00 Kristoffer Grundström (Umeaboy) Gustav Ekner (ekner) @@ -2425,11 +2461,13 @@ Translators: unknowndomain Axel Aminoff (axel.aminoff) chaironeko + Lennart Bonnevier (lennartbon) Jonas Svensson (jonassanojj99) Hannes Blåman (hannesblaman) pitedaniel TacticalKebab kronblom2 + dodgepong Tagalog: dandalion jermel @@ -2499,6 +2537,7 @@ Translators: Ryan Gleeson (Ryan_7149) วรภร หนูปลอด6.1 (woraporn.hnu) 123456789sssssssssssssssssssss + Nydeawia Laurent (aeph1edur) Turkish: monolifed Ali Kömesöğütlü (Mobile46) (byzlo685) @@ -2510,6 +2549,7 @@ Translators: Cemal Dursun (cmldrs) Kayhan (SwitchAlpha) Savas Tokmak (Laserist) + Burkicannjr Serhat Öktem (delidolu1adam) Vainock Umut kılıç (kilic190787) @@ -2547,6 +2587,7 @@ Translators: bitigchi Berk Kırıkçı (berkkrkc09) Zafer Yılmaz (yilmazzafer722) + myasincavdar Arat Ünal (AratreyizprogamerX) ownieyo Tarık Coşkun (tarikcoskun) @@ -2589,6 +2630,7 @@ Translators: chaironeko Huseyin Emre (emreemuzik) Selim Şumlu (maxcoder) + dodgepong Ukrainian: SuslikV Mykola Ronik (Mantikor) @@ -2602,8 +2644,10 @@ Translators: Lino Bico (bicolino34) VainockManager Dekanenko (dekanenko) + Dijerom Юрій (Devinit) ROkelbow + Oleh Hnat (aetrnm) FOR___REST (konstantinkostenko96) Свиридюк Іван (fl1per) Дмитро Маркевич (hotr1pak) @@ -2628,7 +2672,7 @@ Translators: Володимир Родич (vrodych) Mark Chorney (chorneymark2006) D_D - Oleh Hnat (aetrnm) + dodgepong Urdu (Pakistan): Abuzar (a6y3ap) Sheikh Ahmed (sheikhahmed) diff --git a/UI/data/locale/ar-SA.ini b/UI/data/locale/ar-SA.ini index bd32b5220d0a03..4821c460ef6095 100644 --- a/UI/data/locale/ar-SA.ini +++ b/UI/data/locale/ar-SA.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="قائمة مزج الصوت" SceneFilters="افتح فلاتر المشهد" List="قائمة" Grid="شبكة" +Automatic="تلقائي" PluginsFailedToLoad.Title="خطأ في تحميل الإضافات" PluginsFailedToLoad.Text="فشل تحميل إضافات OBS التالية:\n\n%1\nالرجاء تحديث أو إزالة هذه الإضافات." AlreadyRunning.Title="OBS قيد التشغيل بالفعل" @@ -193,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="تفضيل استخدام ا Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="استخدام الترميز بالهاردوير يلغي الحاجة الى معظم موارد المعالج, لكن قد يحتاج الى معدل بث أعلى للحفاظ على نفس مستوى الجودة." Basic.AutoConfig.StreamPage.StreamWarning.Title="تحذير يتعلق بالبث" Basic.AutoConfig.StreamPage.StreamWarning.Text="اختبار مستوى التدفق على وشك بدأ عرض مباشر لبيانات من فيديو عشوائي بدون صوت للقناة الخاصة بك. إذا أمكنك، يوصى بإيقاف حفظ فيديو للعرض المباشر مؤقتا وتعيين خصوصية العرض المباشر ل خاص إلى أن بكتمل الاختبار. هل تريد المتابعة؟" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="اختبار%1" Basic.AutoConfig.TestPage="النتيجة النهائية" Basic.AutoConfig.TestPage.SubTitle.Testing="يقوم البرنامج الآن بتنفيذ مجموعة من الإختبارات لتقدير الإعدادات الأكثر مثالية" Basic.AutoConfig.TestPage.SubTitle.Complete="انتهى الاختبار" @@ -211,6 +213,7 @@ Basic.AutoConfig.TestPage.Result.Header="قام البرنامج بتقدير ا Basic.AutoConfig.TestPage.Result.Footer="لاستخدام هذه الإعدادات اضغط على \"تطبيق الإعدادات\". لإعادة القيام بالإعدادات و المحاولة مرة أخرى، اضغط للخلف. للقيام بعمل الإعدادات يدوياً بنفسك، اضغط إلغاء و افتح الإعدادات." Basic.AutoConfig.Info="سيحدد معالج التهيئة التلقائية أفضل الإعدادات بناءً على مواصفات الكمبيوتر وسرعة الإنترنت." Basic.AutoConfig.RunAnytime="يمكن تشغيل هذا في أي وقت بالانتقال إلى قائمة الأدوات." +Basic.AutoConfig.TestPage.Result.StreamingResolution="دِقَّة البث (المحجم)" Basic.Stats="إحصائيات" Basic.Stats.CPUUsage="استخدام المعالج" Basic.Stats.HDDSpaceAvailable="مساحة القرص المتوفرة" @@ -562,6 +565,7 @@ Basic.Main.Scenes="المشاهد" Basic.Main.Sources="المصادر" Basic.Main.Source="مصدر" Basic.Main.Controls="التحكم" +Basic.Main.PreparingStream="التحضير..." Basic.Main.Connecting="يتصل..." Basic.Main.StartRecording="بدء التسجيل" Basic.Main.StartReplayBuffer="بدء التخزين المؤقت لإعادة العرض" @@ -573,6 +577,7 @@ Basic.Main.StopRecording="إيقاف التسجيل" Basic.Main.PauseRecording="إيقاف التسجيل مؤقتا" Basic.Main.UnpauseRecording="إكمال التسجيل" Basic.Main.SplitFile="تقسيم ملف التسجيل" +Basic.Main.AddChapterMarker="أضف علامة الفصل (Hybrid MP4 فقط)" Basic.Main.StoppingRecording="إيقاف التسجيل..." Basic.Main.StopReplayBuffer="إيقاف الخزن المؤقت لإعادة العرض" Basic.Main.StoppingReplayBuffer="يتم ايقاف الخزن المؤقت..." @@ -681,6 +686,7 @@ Basic.MainMenu.Help.About="&حول" Basic.Settings.ProgramRestart="يجب إعادة تشغيل البرنامج حتى تصبح الإعدادات نافذة المفعول." Basic.Settings.ConfirmTitle="تأكيد التغييرات" Basic.Settings.Confirm="لديك تغييرات غير محفوظة. هل تريد حفظها?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 يتحكم في بعض إعدادات البث الخاصة بك" Basic.Settings.General="عام" Basic.Settings.General.Language="اللغة" Basic.Settings.General.Updater="التحديثات" @@ -741,7 +747,13 @@ Basic.Settings.General.ChannelName.stable="مستقر" Basic.Settings.General.ChannelDescription.stable="أحدث إصدار مستقر" Basic.Settings.General.ChannelName.beta="بيتاس / ترك المرشحين" Basic.Settings.General.ChannelDescription.beta="إصدارات ما قبل الإصدار غير مستقرة" +Basic.Settings.Appearance="المظهر" +Basic.Settings.Appearance.General="الإعدادات العامة" +Basic.Settings.Appearance.General.Theme="القالب" +Basic.Settings.Appearance.General.Variant="النمط" +Basic.Settings.Appearance.General.NoVariant="لا توجد أنماط متوفرة" Basic.Settings.Stream="بث" +Basic.Settings.Stream.Destination="وجهة" Basic.Settings.Stream.Custom.UseAuthentication="استخدام المصادقة" Basic.Settings.Stream.Custom.Username="اسم المستخدم" Basic.Settings.Stream.Custom.Password="كلمة السر" @@ -763,10 +775,23 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="الحد الأقصى للف Basic.Settings.Stream.Recommended.MaxAudioBitrate="أقصى معدل للصوت: %1 كيلو بايت" Basic.Settings.Stream.Recommended.MaxResolution="أقصى دقة : %1" Basic.Settings.Stream.Recommended.MaxFPS="الحد الأقصى من FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="حدد خادما مخصصا..." +Basic.Settings.Stream.ServiceCustomServer="خادم مخصص" +Basic.Settings.Stream.EnableMultitrackVideo="تمكين %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="الحد الأقصى لعرض النطاق الترددي للبث" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="تلقائي" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="الحد الأقصى لمسارات الفيديو" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="تلقائي" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="قم بتمكين تفريغ البث إلى FLV يستخدم إعدادات ملف التسجيل البسيطة" +Basic.Settings.Stream.MultitrackVideoConfigOverride="تجاوز التكوين (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="تمكين تجاوز التكوين" +Basic.Settings.Stream.MultitrackVideoLabel="فيديو متعدد المسارات" +Basic.Settings.Stream.AdvancedOptions="خيارات متقدمة" Basic.Settings.Output="المخرج" Basic.Settings.Output.Format="صيغة التسجيل" Basic.Settings.Output.Format.MKV="فيديو ماتروسكا (.mkv)" Basic.Settings.Output.Format.MP4="MPEG-4 (mp4)" +Basic.Settings.Output.Format.hMP4="MP4 الهجين [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 مجزأ (.mp4)" Basic.Settings.Output.Format.fMOV="MOV مجزأ (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="MOV المجزأ يكتب التسجيل في أجزاء ولا يتطلب نفس الصيغة النهائية لملفات MOV التقليدية.\nيضمن هذا بقاء الملف قابلاً للتشغيل حتى في حالة مقاطعة الكتابة على القرص ، على سبيل المثال ، نتيجة الموت الزرقاء أو فقدان الطاقة.\n\nقد لا يكون هذا متوافقًا مع كافة المشغلات والمحررين. استخدم File → Remux Recordings لتحويل الملف إلى تنسيق أكثر توافقًا إذا لزم الأمر." @@ -1241,3 +1266,25 @@ YouTube.Errors.messageTextInvalid="نص الرسالة غير صالح." YouTube.Errors.rateLimitExceeded="أنت ترسل الرسائل بسرعة كبيرة." YouTube.DocksRemoval.Title="امسح قواعد متصفح YouTube القديمة" YouTube.DocksRemoval.Text="ستتم إزالة قواعد إرساء المتصفح هذه باعتبارها مهملة: \n\n%1\nاستخدم \"Docks / غرفة التحكم المباشر على YouTube\" بدلاً من ذلك." +ConfigDownload.WarningMessageTitle="تحذير" +FailedToStartStream.MissingConfigURL="لا يوجد عنوان URL للتكوين متاح للخدمة الحالية" +FailedToStartStream.NoCustomRTMPURLInSettings="لم يتم تحديد عنوان URL RTMP المخصص" +FailedToStartStream.InvalidCustomConfig="تكوين مخصص غير صالح" +FailedToStartStream.FailedToCreateMultitrackVideoService="أخفق إنشاء خدمة فيديو متعددة المسارات" +FailedToStartStream.EncoderNotAvailable="NVENC غير متوفر.\n\nفشل في العثور على نوع التشفير '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="فشل إنشاء برنامَج ترميز الفيديو '%1' (النوع: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="فشل في إنشاء برنامج ترميز الصوت" +FailedToStartStream.NoRTMPURLInConfig="لا يحتوي التكوين على عنوان URL RTMP(S) لهدف البث" +FailedToStartStream.FallbackToDefault="فشل بدء البث باستخدام %1؛ هل تريد إعادة المحاولة باستخدام إعدادات التشفير الفردية؟" +FailedToStartStream.ConfigRequestFailed="تعذر جلب التكوين من %1

خطأ HTTP: %2" +FailedToStartStream.WarningUnknownStatus="تم استلام قيمة حالة غير معروفة '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\N

\nهل تريد مواصلة البث بدون %1؟" +FailedToStartStream.WarningRetry="\N

\nهل تريد مواصلة البث؟" +FailedToStartStream.MissingEncoderConfigs="لم يتضمن تكوين الذهاب المباشر تكوينات التشفير" +FailedToStartStream.StatusMissingHTML="أعاد طلب الذهاب المباشر خطأ غير محدد" +FailedToStartStream.NoConfigSupplied="التكوين مفقود" +MultitrackVideo.Info="يقوم %1 تلقائيا بتحسين إعداداتك لترميز وإرسال صفات فيديو متعددة. سيؤدي تحديد هذا الخيار إلى إرسال 2٪ من المعلومات حول إعداد جهاز الكمبيوتر والبرامج." +MultitrackVideo.IncompatibleSettings.Title="إعدادات غير متوافقة" +MultitrackVideo.IncompatibleSettings.Text="%1 غير متوافق حاليا مع:\n\n%2\nلمواصلة البث مع %1، تعطيل الإعدادات غير المتوافقة:\n\n%3\nوبدء البث مرة أخرى." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="قم بتعطيل هذا البث وابدأ البث" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="قم بتحديث الإعدادات وابدأ البث" diff --git a/UI/data/locale/be-BY.ini b/UI/data/locale/be-BY.ini index 0175b2ba62e20e..f3ab2be52b02fb 100644 --- a/UI/data/locale/be-BY.ini +++ b/UI/data/locale/be-BY.ini @@ -581,7 +581,7 @@ Basic.Main.StopRecording="Спыніць запіс" Basic.Main.PauseRecording="Прыпыніць запіс" Basic.Main.UnpauseRecording="Працягнуць запіс" Basic.Main.SplitFile="Раздзяліць файл запісу" -Basic.Main.AddChapterMarker="Дадаць метку главы" +Basic.Main.AddChapterMarker="Дадаць метку главы (гібрыдны фармат MP4)" Basic.Main.StoppingRecording="Спыненне запісу…" Basic.Main.StopReplayBuffer="Спыніць буфер паўтору" Basic.Main.StoppingReplayBuffer="Спыненне буфера паўтору…" @@ -1258,7 +1258,7 @@ FailedToStartStream.MissingConfigURL="Для бягучага сэрвісу н FailedToStartStream.NoCustomRTMPURLInSettings="Уласны RTMP URL не зададзены" FailedToStartStream.InvalidCustomConfig="Памылковая ўласная канфігурацыя" FailedToStartStream.FailedToCreateMultitrackVideoService="Не атрымалася стварыць сэрвіс відэа з некалькімі відэадарожкамі" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="Не атрымалася стварыць RTMP-вывад відэа з некалькімі відэадарожкамі" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Не атрымалася стварыць RTMP-вывад відэа з некалькімі дарожкамі" FailedToStartStream.EncoderNotAvailable="NVENC недаступны.\n\nНе атрымалася знайсці кадавальнік тыпу «%1»" FailedToStartStream.FailedToCreateVideoEncoder="Не атрымалася стварыць кадавальнік відэа «%1» (тып: %2)" FailedToStartStream.FailedToGetOBSVideoInfo="Падчас стварэння кадавальніка «%1» (тып: %2) узнікла памылка атрымання звестак аб відэа" @@ -1277,3 +1277,6 @@ MultitrackVideo.IncompatibleSettings.Title="Несумяшчальныя нал MultitrackVideo.IncompatibleSettings.Text="У дадзены момант %1 не сумяшчаецца з наступнымі пунктамі:\n\n%2\nКаб працягнуць стрым з %1, адключыце несумяшчальныя налады:\n\n%3\nі зноў пачніце стрым." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Адключыць для гэтага стрыму" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Абнавіць налады і пачаць плынь" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 не сумяшчаецца з зададзенымі наладамі [Аўдыя → Агульныя → Каналы]: %2, %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="Патрабуецца змяніць наладу [Аўдыя → Агульныя → Каналы] на %1" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 патрабуе задаць наладу [Аўдыя → Агульныя → Каналы] інакш" diff --git a/UI/data/locale/ca-ES.ini b/UI/data/locale/ca-ES.ini index ce436ec4883b2b..6613f364adb65b 100644 --- a/UI/data/locale/ca-ES.ini +++ b/UI/data/locale/ca-ES.ini @@ -573,7 +573,7 @@ Basic.Main.StopRecording="Atura l'enregistrament" Basic.Main.PauseRecording="Pausa la gravació" Basic.Main.UnpauseRecording="Reprèn la gravació" Basic.Main.SplitFile="Divideix el fitxer enregistrat" -Basic.Main.AddChapterMarker="Afegeix un marcador de capítol" +Basic.Main.AddChapterMarker="Afegeix un marcador de capítols (només MP4 híbrid)" Basic.Main.StoppingRecording="Aturant l'enregistrament..." Basic.Main.StopReplayBuffer="Atura la reproducció de la memòria intermèdia" Basic.Main.StoppingReplayBuffer="S'està aturant la reproducció de la memòria intermèdia..." @@ -1255,10 +1255,10 @@ FailedToStartStream.MissingConfigURL="No hi ha cap URL de configuració disponib FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP personalitzada no especificada" FailedToStartStream.InvalidCustomConfig="Configuració personalitzada no vàlida" FailedToStartStream.FailedToCreateMultitrackVideoService="No s'ha pogut crear el servei de vídeo multipista" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="No s'ha pogut crear la sortida rtmp de vídeo multipista" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="No s'ha pogut crear la sortida RTMP de vídeo multipista" FailedToStartStream.EncoderNotAvailable="NVENC no disponible.\n\nNo s'ha pogut trobar el tipus de codificador «%1»" FailedToStartStream.FailedToCreateVideoEncoder="No s'ha pogut crear el codificador de vídeo «%1» (tipus: «%2»)" -FailedToStartStream.FailedToGetOBSVideoInfo="No s'ha pogut obtenir la informació del vídeo d'obs mentre es crea el codificador «%1» (tipus: «%2»)" +FailedToStartStream.FailedToGetOBSVideoInfo="No s'ha pogut obtenir la informació del vídeo d'OBS mentre es crea el codificador «%1» (tipus: «%2»)" FailedToStartStream.FailedToCreateAudioEncoder="No s'ha pogut crear el codificador d'àudio" FailedToStartStream.NoRTMPURLInConfig="La configuració no conté l'URL RTMP(S) de destinació del flux" FailedToStartStream.FallbackToDefault="No s'ha pogut iniciar la transmissió amb %1. Voleu tornar-ho a provar amb la configuració de codificació única?" @@ -1266,9 +1266,14 @@ FailedToStartStream.ConfigRequestFailed="No s'ha pogut obtenir la configuració FailedToStartStream.WarningUnknownStatus="S'ha rebut el valor d'estat desconegut «%1»" FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nVoleu continuar en directe sense %1?" FailedToStartStream.WarningRetry="\n

\nVoleu continuar en directe?" +FailedToStartStream.MissingEncoderConfigs="La configuració del directe no incloïa les configuracions del codificador" +FailedToStartStream.StatusMissingHTML="La sol·licitud del directe ha retornat un error no especificat" FailedToStartStream.NoConfigSupplied="Falta la configuració" MultitrackVideo.Info="%1 optimitza automàticament la vostra configuració per codificar i enviar múltiples qualitats de vídeo. Si seleccioneu aquesta opció, s'enviarà %2 informació sobre el vostre ordinador i la configuració del programari." MultitrackVideo.IncompatibleSettings.Title="Configuració incompatible" MultitrackVideo.IncompatibleSettings.Text="%1 actualment no és compatible amb:\n\n%2\nPer continuar en directe amb %1, desactiveu els paràmetres incompatibles:\n\n%3\ni torneu a començar la transmissió." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Desactiva per aquesta transmissió i comença el directe" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Actualitza la configuració i comença la transmissió" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 no és compatible actualment amb [Àudio → General → Canals] establert a «%2», %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Àudio → General → Canals] s'ha d'establir a «%1»" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requereix diversos paràmetres diferents per a [Àudio → General → Canals]" diff --git a/UI/data/locale/cs-CZ.ini b/UI/data/locale/cs-CZ.ini index 93b1da3f8c260b..d1585c96d14224 100644 --- a/UI/data/locale/cs-CZ.ini +++ b/UI/data/locale/cs-CZ.ini @@ -103,6 +103,7 @@ MixerToolbarMenu="Menu audio směšovače" SceneFilters="Otevřít filtry scény" List="Seznam" Grid="Mřížka" +Automatic="Automatické" PluginsFailedToLoad.Title="Chyba při načítání pluginu" PluginsFailedToLoad.Text="Následující OBS pluginy se nepodařilo načíst:\n\n%1\nAktualizujte nebo odstraňte tyto pluginy." AlreadyRunning.Title="OBS je již spuštěno" @@ -186,6 +187,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferovat hardwarové kódo Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardwarové kódování rapidně snižuje využití CPU, ale může vyžadovat použití vyššího bitratu k dosažení stejné kvality." Basic.AutoConfig.StreamPage.StreamWarning.Title="Varování o vysílání" Basic.AutoConfig.StreamPage.StreamWarning.Text="Test rychlosti spojení bude vysílat náhodná data obrazu bez zvuku na váš kanál. Pokud jste schopni, doporučujeme abyste dočasně vypnuli ukládání nahrávek vysílání a nastavili kanál na soukromý, dokud nebude tento test dokončen. Přejete si pokračovat?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Testovat %1" Basic.AutoConfig.TestPage="Konečné výsledky" Basic.AutoConfig.TestPage.SubTitle.Testing="Program právě provádí soubor testů pro výpočet ideálního nastavení" Basic.AutoConfig.TestPage.SubTitle.Complete="Testování dokončeno" @@ -204,6 +206,7 @@ Basic.AutoConfig.TestPage.Result.Header="Tento program určil, že tato přibli Basic.AutoConfig.TestPage.Result.Footer="Pro použití těchto nastavení klikněte na Použít. Pro změnu v průvodci klikněte na Zpět. Pokud si přejete změnit nastavení manuálně, klikněte na Zrušit a poté otevřete Nastavení." Basic.AutoConfig.Info="Průvodce nastavením zjistí nejlepší nastavení podle výkonu počítače a rychlosti připojení k internetu." Basic.AutoConfig.RunAnytime="Tento proces může být spuštěn kdykoliv pomocí položky v menu Nástroje." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Rozlišení (škálované) pro vysílání" Basic.Stats="Statistika" Basic.Stats.CPUUsage="Využití CPU" Basic.Stats.HDDSpaceAvailable="Dostupné místo na disku" @@ -558,6 +561,7 @@ Basic.Main.Scenes="Scény" Basic.Main.Sources="Zdroje" Basic.Main.Source="Zdroj" Basic.Main.Controls="Ovládací prvky" +Basic.Main.PreparingStream="Připravuje se ..." Basic.Main.Connecting="Připojování..." Basic.Main.StartRecording="Začít nahrávat" Basic.Main.StartReplayBuffer="Spustit záznam do paměti" @@ -569,7 +573,7 @@ Basic.Main.StopRecording="Zastavit nahrávání" Basic.Main.PauseRecording="Pozastavit nahrávání" Basic.Main.UnpauseRecording="Pokračovat v nahrávání" Basic.Main.SplitFile="Rozdělit soubor nahrávky" -Basic.Main.AddChapterMarker="Přidat značku kapitoly" +Basic.Main.AddChapterMarker="Přidat značku kapitoly (pouze pro hybridní MP4)" Basic.Main.StoppingRecording="Zastavuji nahrávání..." Basic.Main.StopReplayBuffer="Zastavit záznam do paměti" Basic.Main.StoppingReplayBuffer="Zastavuji záznam do paměti..." @@ -678,7 +682,8 @@ Basic.MainMenu.Help.About="O &aplikaci" Basic.Settings.ProgramRestart="Pro projevení nastavení je potřeba restartovat aplikaci." Basic.Settings.ConfirmTitle="Potvrzení změn" Basic.Settings.Confirm="Některé změny nejsou uložené. Chcete je uložit nyní ?" -Basic.Settings.General="Hlavní" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 ovládá některá nastavení vysílání" +Basic.Settings.General="Obecné" Basic.Settings.General.Language="Jazyk" Basic.Settings.General.Updater="Aktualizace" Basic.Settings.General.UpdateChannel="Kanál aktualizací" @@ -743,6 +748,7 @@ Basic.Settings.Appearance.General.Theme="Motiv" Basic.Settings.Appearance.General.Variant="Styl" Basic.Settings.Appearance.General.NoVariant="Nejsou dostupné žádné styly" Basic.Settings.Stream="Vysílání" +Basic.Settings.Stream.Destination="Cíl" Basic.Settings.Stream.Custom.UseAuthentication="Použít přihlášení" Basic.Settings.Stream.Custom.Username="Uživatelské jméno" Basic.Settings.Stream.Custom.Password="Heslo" @@ -764,6 +770,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximální bitrate videa: %1 Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximální bitrate zvuku: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximální rozlišení: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximální FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Použít vlastní server ..." +Basic.Settings.Stream.ServiceCustomServer="Vlastní server" +Basic.Settings.Stream.EnableMultitrackVideo="Povolit %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Max. propustnost pro vysílání" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Autom." +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Max. počet stop obrazu" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Autom." +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Povolit ukládání vysílání do FLV souboru (použije jednoduché nastavení pro nahrávání)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Přepis nastavení (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Povolit přepis nastaveníí" +Basic.Settings.Stream.MultitrackVideoLabel="Video s více stopami" +Basic.Settings.Stream.AdvancedOptions="Rozšířená nastavení" Basic.Settings.Output="Výstup" Basic.Settings.Output.Format="Formát nahrávání" Basic.Settings.Output.Format.hMP4="Hybridní MP4 [BETA] (.mp4)" @@ -1221,3 +1239,30 @@ YouTube.Errors.messageTextInvalid="Text zprávy je neplatný." YouTube.Errors.rateLimitExceeded="Posíláš zprávy příliš rychle." YouTube.DocksRemoval.Title="Vyčistit zastaralé YouTube doky prohlížeče" YouTube.DocksRemoval.Text="Tyto doky prohlížeče budou odstraněny jako zasataralé:\n\n%1\nPoužijte \"Doky/Ovládací panel YouTube Live\" místo toho." +ConfigDownload.WarningMessageTitle="Varování" +FailedToStartStream.MissingConfigURL="Není nastavena konfigurační URL adresa pro aktuální službu" +FailedToStartStream.NoCustomRTMPURLInSettings="URL adresa pro vlastní RTMP není nastavena" +FailedToStartStream.InvalidCustomConfig="Chybná vlastní konfigurace" +FailedToStartStream.FailedToCreateMultitrackVideoService="Chyba při vytváření služby pro video s více stopami" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Chyba při vytváření RTMP výstupu pro video s více stopami" +FailedToStartStream.EncoderNotAvailable="NVENC není dostupný.\n\nChyba při hledání kodéru typu '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Chyba při vytváření kodéru obrazu '%1' (typ: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Nezdařilo se získat OBS video info při vytváření kodéru '%1' (typ: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Chyba při vytváření kodéru zvuku" +FailedToStartStream.NoRTMPURLInConfig=" Nastavení neobsahuje adresy cílových RTMP(S) serverů" +FailedToStartStream.FallbackToDefault="Spuštění vysílání pomocí %1 selhalo; chcete se pokusit o spuštění vysílní za použití nastavení jednoho kodéru?" +FailedToStartStream.ConfigRequestFailed="Nepodařilo se načtení nastavení z %1

HTTP chyba: %2" +FailedToStartStream.WarningUnknownStatus="Přijata neznámá hodnota stavu '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nChcete pokračovat ve vysílání bez %1?" +FailedToStartStream.WarningRetry="\n

\nChcete pokračovat ve vysílání?" +FailedToStartStream.MissingEncoderConfigs="Nastavení pro vysílání neobsahovalo nastavení enkodéru" +FailedToStartStream.StatusMissingHTML="Požadavek o spuštění vysílání skončil neznámou chybou" +FailedToStartStream.NoConfigSupplied="Chybějící nastavení" +MultitrackVideo.Info="%1 automaticky optimalizuje nastavení pro enkódování a odesílání videí v několika kvalitách. Tato možnost odešle službě %2 informace o vašem počítači a nainstalovaném softwaru." +MultitrackVideo.IncompatibleSettings.Title="Nekompatibilní nastavení" +MultitrackVideo.IncompatibleSettings.Text="%1 není aktuálně kompatibilní s:\n\n%2\nPřo pokračování ve vysílání s %1, vypněte nekompatibilní nastavení:\n\n%3\na začněte vysílat znovu." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Zakázat pro toto vysílání a začít vysílat" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Upravit nastavení a začít vysílat" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 není aktuálně kompatibilní s [Zvuk → Obecné → Kanály] nastavenými na '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Zvuk → Obecné → Kanály] musejí být nastaveny na '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 vyžaduje více rozdílných nastavení pro [Zvuk → Obecné → Kanály]" diff --git a/UI/data/locale/de-DE.ini b/UI/data/locale/de-DE.ini index c25fed691b3a06..b198a876d9d148 100644 --- a/UI/data/locale/de-DE.ini +++ b/UI/data/locale/de-DE.ini @@ -109,7 +109,7 @@ Automatic="Automatisch" PluginsFailedToLoad.Title="Fehler beim Laden von Plugins" PluginsFailedToLoad.Text="Folgende OBS-Plugins konnten nicht geladen werden:\n\n%1\nBitte aktualisieren oder entfernen Sie sie." AlreadyRunning.Title="OBS wird bereits ausgeführt" -AlreadyRunning.Text="OBS wird bereits ausgeführt und sollte falls gewünscht vor dem Start einer weiteren Instanz (die möglicherweise im Infobereich ist) beendet werden." +AlreadyRunning.Text="OBS wird bereits ausgeführt und sollte gegebenenfalls vor dem Start einer weiteren Instanz (die sich möglicherweise im Infobereich befindet) beendet werden." AlreadyRunning.LaunchAnyway="Trotzdem starten" AutoSafeMode.Title="Abgesicherter Modus" AutoSafeMode.Text="OBS wurde während Ihrer letzten Sitzung nicht ordnungsgemäß beendet.\n\nMöchten Sie im abgesicherten Modus (mit deaktivierten Drittanbieter-Plugins, Skripten und WebSockets) starten?" @@ -221,7 +221,7 @@ Basic.Stats.Status.Reconnecting="Erneut verbinden" Basic.Stats.Status.Inactive="Inaktiv" Basic.Stats.Status.Active="Aktiv" Basic.Stats.DroppedFrames="Ausgelassene Frames (Netzwerk)" -Basic.Stats.MegabytesSent="Insgesamte Datenausgabe" +Basic.Stats.MegabytesSent="Gesamte Datenausgabe" Basic.Stats.DiskFullIn="Datenträger voll in ungefähr" Basic.Stats.ResetStats="Statistiken zurücksetzen" ResetUIWarning.Title="Möchten Sie die Benutzeroberfläche wirklich zurücksetzen?" @@ -562,7 +562,7 @@ Basic.Main.StopRecording="Aufnahme beenden" Basic.Main.PauseRecording="Aufnahme pausieren" Basic.Main.UnpauseRecording="Aufnahme fortsetzen" Basic.Main.SplitFile="Aufnehmende Datei teilen" -Basic.Main.AddChapterMarker="Kapitelmarkierung hinzufügen" +Basic.Main.AddChapterMarker="Kapitelmarkierung hinzufügen (bei hybrider MP4)" Basic.Main.StoppingRecording="Beende Aufnahme …" Basic.Main.StopReplayBuffer="Wiederholungspuffer beenden" Basic.Main.StoppingReplayBuffer="Beende Wiederholungspuffer …" @@ -760,8 +760,10 @@ Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximale Streaming Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatisch" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maximale Videospuren" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatisch" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Stream-Dump in FLV aktivieren (verwendet einfache Aufnahmedateieinstellungen)" Basic.Settings.Stream.MultitrackVideoConfigOverride="Konfiguration überschreiben (JSON)" Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Konfiguration überschreiben" +Basic.Settings.Stream.MultitrackVideoLabel="Mehrspur-Video" Basic.Settings.Stream.AdvancedOptions="Erweiterte Optionen" Basic.Settings.Output="Ausgabe" Basic.Settings.Output.Format="Aufnahmeformat" @@ -1209,15 +1211,23 @@ YouTube.Errors.messageTextInvalid="Die Nachricht ist ungültig." YouTube.Errors.rateLimitExceeded="Sie senden Nachrichten zu schnell." YouTube.DocksRemoval.Title="Veraltete YouTube-Browser-Docks bereinigen" YouTube.DocksRemoval.Text="Folgende Browser-Docks werden entfernt, da diese veraltet sind:\n\n%1\nVerwenden Sie stattdessen „Docks“ → „YouTube Live Control Room“." -ConfigDownload.WarningMessageTitle="Warnung" +ConfigDownload.WarningMessageTitle="Achtung" FailedToStartStream.MissingConfigURL="Keine Konfigurations-URL für die aktuelle Plattform verfügbar" FailedToStartStream.NoCustomRTMPURLInSettings="Benutzerdefinierte RTMP-URL nicht angegeben" FailedToStartStream.InvalidCustomConfig="Ungültige benutzerdefinierte Konfiguration" -FailedToStartStream.EncoderNotAvailable="NVENC nicht verfügbar.\n\nDer Encoder-Typ '%1' konnte nicht gefunden werden" -FailedToStartStream.FailedToCreateVideoEncoder="Erstellen des Videokodierers „%1“ (Typ: „%2“)" -FailedToStartStream.FailedToCreateAudioEncoder="Erstellen des Audiokodierers fehlgeschlagen" +FailedToStartStream.FailedToCreateMultitrackVideoService="Mehrspur-Videodienst konnte nicht erstellt werden." +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Mehrspur-Video-RTMP-Ausgabe konnte nicht erstellt werden." +FailedToStartStream.EncoderNotAvailable="NVENC nicht verfügbar.\n\nDer Kodierertyp „%1“ konnte nicht gefunden werden." +FailedToStartStream.FailedToCreateVideoEncoder="Der Videokodierer „%1“ (Typ: „%2“) konnte nicht erstellt werden." +FailedToStartStream.FailedToGetOBSVideoInfo="OBS-Videoinformationen konnten beim Erstellen des Encoders „%1“ (Typ: „%2“) nicht abgerufen werden." +FailedToStartStream.FailedToCreateAudioEncoder="Der Audiokodierer konnte nicht erstellt werden." +FailedToStartStream.NoRTMPURLInConfig="Die Konfiguration beinhaltet keine RTMP(S)-Stream Ziel-URL" +FailedToStartStream.FallbackToDefault="Das Starten des Streams mit %1 ist fehlgeschlagen. Möchten Sie es noch einmal mit den Einstellungen für die einfache Kodierung versuchen?" +FailedToStartStream.ConfigRequestFailed="Die Konfiguration konnte nicht von %1 abgefragt werden.

HTTP-Fehler: %2" +FailedToStartStream.WarningUnknownStatus="Unbekannten Statuswert „%1“ erhalten." FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nMöchten Sie ohne %1 weiter streamen?" FailedToStartStream.WarningRetry="\n

\nMöchten Sie weiter streamen?" +FailedToStartStream.MissingEncoderConfigs="Die Konfiguration um live gehen enthielt keine Kodiererkonfigurationen" FailedToStartStream.StatusMissingHTML="Die Anfrage live zu gehen gab einen unbestimmten Fehler zurück" FailedToStartStream.NoConfigSupplied="Fehlende Konfiguration" MultitrackVideo.Info="%1 optimiert automatisch Ihre Einstellungen, um mehrere Videoqualitäten zu kodieren und zu senden. %2 werden dann Informationen über die Konfiguration Ihres Computers und Ihrer Software mitgeteilt." @@ -1225,3 +1235,6 @@ MultitrackVideo.IncompatibleSettings.Title="Inkompatible Einstellungen" MultitrackVideo.IncompatibleSettings.Text="%1 ist inkompatibel mit:\n\n%2\nUm mit %1 weiter zu streamen, deaktivieren Sie inkompatible Einstellungen:\n\n%3\nund starten Sie den Stream erneut." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Für diesen Stream deaktivieren und Stream starten" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Einstellungen aktualisieren und Stream starten" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 ist zurzeit nicht mit auf „%2“ gesetzte [Audio → Allgemein → Kanäle] kompatibel. %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → Allgemein → Kanäle] muss auf „%1“ gesetzt werden." +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 benötigt mehrere verschiedene Einstellungen für [Audio → Allgemein → Kanäle]." diff --git a/UI/data/locale/eo-UY.ini b/UI/data/locale/eo-UY.ini index 8ef02bc065602a..17af685b6d236a 100644 --- a/UI/data/locale/eo-UY.ini +++ b/UI/data/locale/eo-UY.ini @@ -326,7 +326,10 @@ Basic.MainMenu.File.Exit="Eliri(&X)" Basic.MainMenu.Edit="&Editi" Basic.MainMenu.Edit.Undo="Malfari(&U)" Basic.MainMenu.Edit.Redo="&Refari" +Basic.MainMenu.Tools="&Iloj" Basic.Settings.General.Language="Lingvo" +Basic.Settings.Appearance.General.Theme="Etoso" +Basic.Settings.Appearance.General.Variant="Stilo" Basic.Settings.Stream.Custom.Username="Salutnomo" Basic.Settings.Stream.Custom.Password="Pasvorto" Basic.Settings.Stream.TTVAddon.None="Neniu" @@ -345,6 +348,11 @@ Basic.Settings.Output.Adv.Audio.Track5="Trako 5" Basic.Settings.Output.Adv.Audio.Track6="Trako 6" Basic.Settings.Output.Adv.Recording="Registrado" Basic.Settings.Output.Adv.Recording.Type="Speco" +FilenameFormatting.TT.mm="Minuto (00-59)" +FilenameFormatting.TT.ss="Sekundo (00-59)" +FilenameFormatting.TT.M="Minuto (00-59)" +FilenameFormatting.TT.S="Sekundo (00-59)" +FilenameFormatting.TT.Y="Jaro" Basic.Settings.Audio="Sona" Basic.Settings.Audio.MeterDecayRate.Fast="Rapida" Basic.Settings.Audio.MeterDecayRate.Slow="Malrapida (Speco II PPM)" @@ -354,3 +362,6 @@ Basic.AdvAudio.Name="Nomo" Basic.AdvAudio.AudioTracks="Trakoj" Basic.SystemTray.Show="Montri" Basic.SystemTray.Hide="Kaŝi" +About.Authors="Aŭtoroj" +About.License="Permesilo" +YouTube.Actions.Title="Titolo*" diff --git a/UI/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini index 419218afd2eaea..000d45b0cad6b2 100644 --- a/UI/data/locale/es-ES.ini +++ b/UI/data/locale/es-ES.ini @@ -246,12 +246,12 @@ Updater.RepairButUpdatesAvailable.Text="Comprobar la integridad de los archivos Updater.RepairConfirm.Title="Confirmar verificación de integridad" Updater.RepairConfirm.Text="Al iniciar la comprobación de integridad se escaneará la instalación de OBS para detectar archivos corruptos/modificados y descargarlos de nuevo. Esto puede llevar un tiempo.\n\n¿Desea continuar?" Updater.FailedToLaunch="No se pudo iniciar el actualizador" -QuickTransitions.SwapScenes="Intercambiar vista previa/Escenas del programa después de la transición" +QuickTransitions.SwapScenes="Intercambiar escenas de vista previa/programa después de la transición" QuickTransitions.SwapScenesTT="Intercambia la vista previa y las escenas del programa después de la transición (si la escena original del programa continúa existiendo).\nEsto no deshará ningún cambio que se haya realizado a la escena original del programa." QuickTransitions.DuplicateScene="Duplicar escena" QuickTransitions.DuplicateSceneTT="Al editar la misma escena, permite editar la transformación/visibilidad de las fuentes sin modificar la salida del programa.\nPara editar las propiedades de las fuentes sin modifcar la salida del programa, habilita 'Duplicar Fuentes'.\nCambiando este valor se restablecerá la escena actual de salida (si aún existe)." QuickTransitions.EditProperties="Duplicar fuentes" -QuickTransitions.EditPropertiesTT="Cuando se edita la misma escena, permite editar las propiedades de las fuentes sin modificar la salida del programa.\nEsto solo puede ser usado si 'Duplicar Escena' está habilitado.\nCiertas fuentes (como captura o multimedia) no soportan esto y no pueden ser editadas por separado.\nCambiando este valor se restablecera la escena actual (si aún existe).\n\nAdvertencia:Debido a que las fuentes serán duplicadas, esto puede requerir recursos adicionales de sistema o video." +QuickTransitions.EditPropertiesTT="Cuando se edita la misma escena, permite editar las propiedades de las fuentes sin modificar la salida del programa.\nEsto solo puede ser usado si 'Duplicar escena' está habilitado.\nCiertas fuentes (como fuentes de captura o multimedia) no soportan esto y no pueden ser editadas por separado.\nCambiar este valor restablecerá la escena actual (si aún existe).\n\nAdvertencia: Debido a que las fuentes serán duplicadas, esto puede requerir recursos adicionales del sistema o de vídeo." QuickTransitions.HotkeyName="Transición Rápida: %1" Basic.AddTransition="Añadir transición configurable" Basic.RemoveTransition="Quitar transición configurable" @@ -571,7 +571,7 @@ Basic.Main.StopRecording="Detener Grabación" Basic.Main.PauseRecording="Pausar Grabación" Basic.Main.UnpauseRecording="Reanudar Grabación" Basic.Main.SplitFile="Dividir archivo de grabación" -Basic.Main.AddChapterMarker="Agregar marcador de capítulo" +Basic.Main.AddChapterMarker="Agregar marcador de capítulo (solo MP4 híbrido)" Basic.Main.StoppingRecording="Deteniendo Grabación..." Basic.Main.StopReplayBuffer="Detener el Búfer de Repetición" Basic.Main.StoppingReplayBuffer="Deteniendo el Búfer de Repetición..." @@ -952,7 +952,7 @@ Basic.Settings.Audio.MeterDecayRate.Slow="Lenta (PPM de tipo II)" Basic.Settings.Audio.PeakMeterType="Tipo de medidor de pico" Basic.Settings.Audio.PeakMeterType.SamplePeak="Pico de muestra" Basic.Settings.Audio.PeakMeterType.TruePeak="True Peak (mayor uso de CPU)" -Basic.Settings.Audio.MultichannelWarning.Enabled="ADVERTENCIA: el audio de sonido envolvente está habilitado." +Basic.Settings.Audio.MultichannelWarning.Enabled="ADVERTENCIA: El audio de sonido envolvente está habilitado." Basic.Settings.Audio.MultichannelWarning="Si estás transmitiendo, comprueba si tu servicio de transmisión soporta tanto la ingesta como la reproducción de sonido envolvente. Por ejemplo, Facebook 360 Live soporta completamente el sonido envolvente; YouTube Live soporta la ingesta de audio 5.1 (y la reproducción en TV).\n\nLos filtros de audio de OBS son compatibles con el sonido envolvente, aunque no se garantiza la compatibilidad con plugins VST." Basic.Settings.Audio.MultichannelWarning.Title="¿Habilitar el audio de sonido envolvente?" Basic.Settings.Audio.MultichannelWarning.Confirm="¿Seguro que quiere habilitar el audio de sonido envolvente?" @@ -1100,7 +1100,7 @@ SceneItemHide="Ocultar '%1'" OutputWarnings.NoTracksSelected="Debe seleccionar al menos una pista" OutputWarnings.NoTracksSelectedOnExit.Title="Error de configuración de salida" OutputWarnings.NoTracksSelectedOnExit.Text="Todas las salidas han de tener al menos una pista de audio seleccionada." -OutputWarnings.MP4Recording="Advertencia: Las grabaciones guardadas en MP4/MOV serán irrecuperables si el archivo no puede finalizarse (e.g. como resultado de BSODs, cortes eléctricos, etc.). Si quieres grabar múltiples pistas de audio considera usar MKV y convierte la grabación a MP4/MOV después de finalizar (Archivo → Convertir Grabaciones)" +OutputWarnings.MP4Recording="Advertencia: Las grabaciones guardadas en MP4/MOV serán irrecuperables si el archivo no puede finalizarse (e.g. como resultado de BSODs, cortes eléctricos, etc.). Si quieres grabar múltiples pistas de audio considera usar MKV y convierte la grabación a MP4/MOV después de finalizar (Archivo → Convertir grabaciones)" OutputWarnings.CannotPause="Advertencia: Las grabaciones no se pueden pausar si el codificador de grabación se establece en \"(Usar codificador de transmisión)\"" OutputWarnings.CodecIncompatible="La selección de codificador de audio o video se restableció debido a una incompatibilidad. Seleccione un codificador compatible de la lista." CodecCompat.Incompatible="(Incompatible con %1)" @@ -1239,10 +1239,10 @@ FailedToStartStream.MissingConfigURL="No hay una URL de configuración disponibl FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP personalizada no especificada" FailedToStartStream.InvalidCustomConfig="Configuración personalizada no valida" FailedToStartStream.FailedToCreateMultitrackVideoService="Fallo al crear el servicio de vídeo multipista" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="Error al crear la salida rtmp de vídeo multipista" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Error al crear la salida RTMP de vídeo multipista" FailedToStartStream.EncoderNotAvailable="NVENC no disponible.\n\n No se puedo encontrar el tipo de codificador '%1'" FailedToStartStream.FailedToCreateVideoEncoder="No se pudo crear el codificador de vídeo '%1' (tipo: '%2')" -FailedToStartStream.FailedToGetOBSVideoInfo="No se puedo obtener la información de vídeo de obs mientras crea el codificador '%1' (tipo: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="No se pudo obtener la información de vídeo de OBS mientras se crea el codificador '%1' (tipo: '%2')" FailedToStartStream.FailedToCreateAudioEncoder="No se puedo crear el codificador de audio" FailedToStartStream.NoRTMPURLInConfig="La configuración no contiene la URL RTMP(S) de destino de la transmisión" FailedToStartStream.FallbackToDefault="No se puedo iniciar la transmisión usando %1; ¿quieres reintentarlo usando la configuración de codificación simple?" @@ -1258,3 +1258,6 @@ MultitrackVideo.IncompatibleSettings.Title="Ajustes incompatibles" MultitrackVideo.IncompatibleSettings.Text="%1 no es compatible actualmente con:\n\n%2\nPara continuar la transmisión con %1, desactiva los ajustes incompatibles:\n\n%3\ny empieza a transmitir de nuevo." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Deshabilitar para esta transmisión e iniciar transmisión" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Actualizar ajustes e iniciar transmisión" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 actualmente no es compatible con [Audio → General → Canales] establecidos a '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → General → Canales] necesita establecerse a '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requiere múltiples parámetros diferentes para [Audio → General → Canales]" diff --git a/UI/data/locale/eu-ES.ini b/UI/data/locale/eu-ES.ini index 6d2fee13220cdb..22d3824e3b8800 100644 --- a/UI/data/locale/eu-ES.ini +++ b/UI/data/locale/eu-ES.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="Audio nahasgailuaren menua" SceneFilters="Ireki eszena-iragazkiak" List="Zerrenda" Grid="Sareta" +Automatic="Automatikoa" PluginsFailedToLoad.Title="Plugin-a kargatzeko errorea" PluginsFailedToLoad.Text="OBS plugin hauek ezin izan dira kargatu:\n\n%1\nMesedez, eguneratu edo kendu plugin hauek." AlreadyRunning.Title="OBS dagoeneko martxan dago" @@ -193,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hobetsi hardware-kodeketa" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardware-kodeketa txikiagotzen du PUZaren erabilera, baina bit emari handiagoa eskatzen du kalitate maila bera lortzeko." Basic.AutoConfig.StreamPage.StreamWarning.Title="Transmisioaren alarma" Basic.AutoConfig.StreamPage.StreamWarning.Text="Banda zabaleraren testa zure kanalera audiorik gabeko ausazko bideo datuak transmitituko ditu. Posible baduzu, komeni da behin behinean transmisioen bideoen gordeketa ezgaitzea eta transmisio modua pribatua ezartzea testa bukatu arte. Jarraitu nahi duzu?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="%1. proba" Basic.AutoConfig.TestPage="Azken emaitzak" Basic.AutoConfig.TestPage.SubTitle.Testing="Programak une honetan test sorta bat exekutatzen ari da kalkulatzeko zein diren ezarpenik egokienak" Basic.AutoConfig.TestPage.SubTitle.Complete="Testa osatu da" @@ -211,6 +213,7 @@ Basic.AutoConfig.TestPage.Result.Header="Programaren arabera hauek dira zuretzak Basic.AutoConfig.TestPage.Result.Footer="Ezarpen hauek onartzeko, klika Aplikatu ezarpenak. Morroiaren bidez birkonfiguratzeko eta saiatzeko berriro klika Atzera. Ezarpenak eskuz zehazteko klika Utzi eta ireki Ezarpenak." Basic.AutoConfig.Info="Autokonfigurazio-laguntzaileak konfiguraziorik onena zehaztuko du, bere ordenagailuaren zehaztapenen eta Interneteko abiaduraren arabera." Basic.AutoConfig.RunAnytime="Hori edozein unetan exekutatu daiteke, Tresnak menura joatean." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Transmisio bereizmena (eskalatua)" Basic.Stats="Estatistikak" Basic.Stats.CPUUsage="PUZ erabilpena" Basic.Stats.HDDSpaceAvailable="Toki erabilgarria diskoan" @@ -565,6 +568,7 @@ Basic.Main.Scenes="Eszenak" Basic.Main.Sources="Iturburuak" Basic.Main.Source="Iturburua" Basic.Main.Controls="Kontrolak" +Basic.Main.PreparingStream="Prestatzen..." Basic.Main.Connecting="Konektatzen..." Basic.Main.StartRecording="Hasi grabazioa" Basic.Main.StartReplayBuffer="Abiatu erreprodukzio bufferra" @@ -576,6 +580,7 @@ Basic.Main.StopRecording="Gelditu grabazioa" Basic.Main.PauseRecording="Pausatu grabazioa" Basic.Main.UnpauseRecording="Amaitu grabazioaren pausa" Basic.Main.SplitFile="Zatitu grabaketa fitxategia" +Basic.Main.AddChapterMarker="Gehitu kapitulu-markatzailea (MP4 hibridoa soilik)" Basic.Main.StoppingRecording="Grabazioa gelditzen..." Basic.Main.StopReplayBuffer="Gelditu erreprodukzio bufferra" Basic.Main.StoppingReplayBuffer="Erreprodukzio bufferra gelditzen..." @@ -684,6 +689,7 @@ Basic.MainMenu.Help.About="Honi buruz (&A)" Basic.Settings.ProgramRestart="Programa berrabiarazi egin behar da ezarpen hauek eragina izateko." Basic.Settings.ConfirmTitle="Baieztatu aldaketak" Basic.Settings.Confirm="Gordegabeko aldaketak dituzu. Gorde aldaketak?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 zure transmisio-ezarpenetako batzuk kontrolatzen ari da" Basic.Settings.General="Orokorra" Basic.Settings.General.Language="Hizkuntza" Basic.Settings.General.Updater="Eguneraketak" @@ -750,6 +756,7 @@ Basic.Settings.Appearance.General.Theme="Gaia" Basic.Settings.Appearance.General.Variant="Estiloa" Basic.Settings.Appearance.General.NoVariant="Ez dago erabilgarri den estilorik." Basic.Settings.Stream="Transmisioa" +Basic.Settings.Stream.Destination="Helmuga" Basic.Settings.Stream.Custom.UseAuthentication="Erabili autentifikazioa" Basic.Settings.Stream.Custom.Username="Erabiltzaile izena" Basic.Settings.Stream.Custom.Password="Pasahitza" @@ -771,9 +778,22 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Bideoaren gehienezko bit-emar Basic.Settings.Stream.Recommended.MaxAudioBitrate="Gehienezko audio bit-tasa: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Gehienezko bereizmena: %1" Basic.Settings.Stream.Recommended.MaxFPS="Gehienezko FPM: %1" +Basic.Settings.Stream.SpecifyCustomServer="Zehaztu zerbitzari pertsonalizatua..." +Basic.Settings.Stream.ServiceCustomServer="Zerbitzari pertsonalizatua" +Basic.Settings.Stream.EnableMultitrackVideo="Gaitu %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Transmisioaren gehienezko banda-zabalera" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatikoa" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Gehienezko bideo-pistak" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatikoa" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Gaitu transmisio-iraulketa FLVra (grabaketa-fitxategien ezarpen sinpleak erabiltzen ditu)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Konfigurazioaren gainidaztea (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Gaitu konfigurazioaren gainidaztea" +Basic.Settings.Stream.MultitrackVideoLabel="Pista anitzeko bideoa" +Basic.Settings.Stream.AdvancedOptions="Ezarpen aurreratuak" Basic.Settings.Output="Irteera" Basic.Settings.Output.Format="Grabazio-formatua" Basic.Settings.Output.Format.MKV="Matroska Bideoa (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 hibridoa [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 Fragmentatuta (.mp4)" Basic.Settings.Output.Format.fMOV="MOV Fragmentatuta (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="MOV zatikatuak grabaketa zatika idazten du eta ez du MOV fitxategi tradizionalen amaiera bera behar.\nHori esker, fitxategia erreproduzigarria izaten jarraitzen du diskoan idaztea eten bada ere, adibidez, BSOD edo energia galtzearen ondorioz.\n\n Baliteke hau ez izatea erreproduzigailu eta editore guztiekin bateragarria. Erabili Fitxategia → Remux Grabaketak fitxategia formatu bateragarriago batera bihurtzeko, behar izanez gero." @@ -1241,3 +1261,30 @@ YouTube.Errors.messageTextInvalid="Mezuaren testua ez da baliozkoa." YouTube.Errors.rateLimitExceeded="Mezuak azkarregi bidaltzen ari zara." YouTube.DocksRemoval.Title="Garbitu Legacy YouTube arakatzailearen panelak" YouTube.DocksRemoval.Text="Arakatzailearen panel hauek zaharkituta daudelako kenduko dira:\n\n%1\nErabili \"Panelak/YouTube Live Control Room\" haren ordez." +ConfigDownload.WarningMessageTitle="Erne" +FailedToStartStream.MissingConfigURL="Ez dago konfigurazio URLrik uneko zerbitzurako" +FailedToStartStream.NoCustomRTMPURLInSettings="Ez da zehaztu RTMP URL pertsonalizatua" +FailedToStartStream.InvalidCustomConfig="Konfigurazio pertsonalizatua baliogabea" +FailedToStartStream.FailedToCreateMultitrackVideoService="Ezin izan da pista anitzeko bideo-zerbitzua sortu" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Ezin izan da pista anitzeko bideo RTMP irteera sortu" +FailedToStartStream.EncoderNotAvailable="NVENC ez dago erabilgarri.\n\nEzin izan da '%1' kodetzaile mota aurkitu" +FailedToStartStream.FailedToCreateVideoEncoder="Ezin izan da sortu '%1' bideo-kodetzailea (mota: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Ezin izan da lortu OBS bideoaren informazioa '%1' kodetzailea sortu bitartean (mota: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Ezin izan da sortu audio-kodetzailea" +FailedToStartStream.NoRTMPURLInConfig="Konfigurazioak ez du transmisioaren helburuko RTMP(S) URLrik" +FailedToStartStream.FallbackToDefault="Transmisioa %1 erabiliz abiarazteak huts egin du; Saiatu nahi al duzu berriro kodetze bakarreko ezarpenak erabiltzen?" +FailedToStartStream.ConfigRequestFailed="Ezin izan da %1-tik konfigurazioa eskuratu

HTTP errorea: %2" +FailedToStartStream.WarningUnknownStatus="'%1' egoera-balio ezezaguna jaso da" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n%1 gabe transmititzen jarraitu nahi duzu?" +FailedToStartStream.WarningRetry="\n

\nNahi duzu segitu transmititzen?" +FailedToStartStream.MissingEncoderConfigs="Go zuzeneko konfigurazioak ez zuen kodetzaileen konfiguraziorik barne hartzen" +FailedToStartStream.StatusMissingHTML="Go zuzeneko eskaerak zehaztu gabeko errore bat itzuli du" +FailedToStartStream.NoConfigSupplied="Konfigurazioa falta da" +MultitrackVideo.Info="%1 -ek automatikoki optimizatzen ditu zure ezarpenak hainbat bideo-kalitate kodetzeko eta bidaltzeko. Aukera hau hautatzen baduzu, zure ordenagailuaren eta softwarearen konfigurazioari buruzko %2 informazioa bidaliko da." +MultitrackVideo.IncompatibleSettings.Title="Ezarpen bateraezinak" +MultitrackVideo.IncompatibleSettings.Text="Une honetan %1 ez da bateragarria:\n\n%2\n%1-rekin transmisioa segitzeko, desgaitu bateraezinak diren ezarpenak:\n\n%3\neta Hasi berriro transmititzen." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Desgaitu transmisio honetarako eta hasi transmititzen" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Eguneratu ezarpenak eta hasi transmisioa" +MultitrackVideo.IncompatibleSettings.AudioChannels="Une honetan %1 ez da bateragarria [Audioa → Orokorra → Kanalak] '%2', %3 gisa ezarrita" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audioa → Orokorra → Kanalak] '%1' gisa ezarri behar da" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1-ek hainbat ezarpen desberdin behar ditu [Audioa → Orokorra → Kanalak]" diff --git a/UI/data/locale/fa-IR.ini b/UI/data/locale/fa-IR.ini index 55fb66244db981..8a8d30e1af6a6d 100644 --- a/UI/data/locale/fa-IR.ini +++ b/UI/data/locale/fa-IR.ini @@ -194,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="کد گذاری سخت اف Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="رمزگذاری سخت افزاری مصرف زیاد CPU را از بین می برد، اما ممکن است برای بدست آوردن همان کیفیت کیفیت به بیت ریت بیشتری نیاز داشته باشد." Basic.AutoConfig.StreamPage.StreamWarning.Title="هشدار جریان" Basic.AutoConfig.StreamPage.StreamWarning.Text="تست پهنای باند در حال پخش داده های ویدیویی تصادفی بدون صدا به کانال شما است. در صورت امکان ، توصیه می شود فیلم های ذخیره شده به طور موقت را خاموش کنید و پخش زنده را به صورت خصوصی تنظیم کنید تا پس از اتمام آزمون. ادامه می دهید?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="تست %1" Basic.AutoConfig.TestPage="نتایج نهایی" Basic.AutoConfig.TestPage.SubTitle.Testing="این برنامه اکنون مجموعه ای از تست ها را برای برآورد ایده آل ترین تنظیمات انجام می دهد" Basic.AutoConfig.TestPage.SubTitle.Complete="تست کامل شد" @@ -212,6 +213,7 @@ Basic.AutoConfig.TestPage.Result.Header="این برنامه تعیین کرده Basic.AutoConfig.TestPage.Result.Footer="برای استفاده از این تنظیمات، روی اعمال تنظیمات کلیک کنید. برای پیکربندی مجدد جادوگر و دوباره امتحان کردن، روی بازگشت کلیک کنید. برای پیکربندی دستی تنظیمات خود، روی لغو کلیک کنید و تنظیمات را باز کنید." Basic.AutoConfig.Info="جادوگر پیکربندی خودکار بهترین تنظیمات را براساس مشخصات رایانه و سرعت اینترنت شما تعیین می کند." Basic.AutoConfig.RunAnytime="این کار در هر زمان با رفتن به منوی ابزار قابل اجرا است." +Basic.AutoConfig.TestPage.Result.StreamingResolution="رزولوشن استریم کردن (مقیاس شده)" Basic.Stats="آمارها" Basic.Stats.CPUUsage="میزان استفاده CPU" Basic.Stats.HDDSpaceAvailable="فضای در دسترس دیسک" @@ -567,6 +569,7 @@ Basic.Main.Scenes="صحنه ها" Basic.Main.Sources="منابع" Basic.Main.Source="منبع" Basic.Main.Controls="کنترل ها" +Basic.Main.PreparingStream="در حال آماده‌سازی..." Basic.Main.Connecting="در حال اتصال..." Basic.Main.StartRecording="شروع ضبط" Basic.Main.StartReplayBuffer="شروع بافر پخش مجدد" @@ -752,6 +755,7 @@ Basic.Settings.Appearance.General.Theme="پوسته" Basic.Settings.Appearance.General.Variant="سبک" Basic.Settings.Appearance.General.NoVariant="سبکی در دسترس نیست" Basic.Settings.Stream="پخش" +Basic.Settings.Stream.Destination="مقصد" Basic.Settings.Stream.Custom.UseAuthentication="استفاده از تایید اعتبار" Basic.Settings.Stream.Custom.Username="نام کاربری" Basic.Settings.Stream.Custom.Password="رمزعبور" @@ -773,6 +777,15 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="حداکثر بیت ریت و Basic.Settings.Stream.Recommended.MaxAudioBitrate="حداکثر بیت ریت صدا: %1" Basic.Settings.Stream.Recommended.MaxResolution="حداکثر وضوح: %1" Basic.Settings.Stream.Recommended.MaxFPS="حداکثر فریم: %1" +Basic.Settings.Stream.SpecifyCustomServer="مشخص کردن سرور سفارشی..." +Basic.Settings.Stream.ServiceCustomServer="سرور سفارشی" +Basic.Settings.Stream.EnableMultitrackVideo="فعال کردن %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="حداکثر پنهای باند استریم کردن" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="خودکار" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="حداکثر قطعات ویدئویی" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="خودکار" +Basic.Settings.Stream.MultitrackVideoLabel="ویدئو چندلایه" +Basic.Settings.Stream.AdvancedOptions="تنظیمات پیشرفته" Basic.Settings.Output="خروجی" Basic.Settings.Output.Format="فرمت ضبط" Basic.Settings.Output.Format.MKV="فیلم ماتروسکا (.mkv)" @@ -1250,3 +1263,6 @@ YouTube.Errors.messageTextInvalid="پیام معتبر نیست." YouTube.Errors.rateLimitExceeded="شما خیلی سریع پیام می فرستید." YouTube.DocksRemoval.Title="پایگاه‌های مرورگر YouTube قدیمی را پاک کنید" YouTube.DocksRemoval.Text="این پایه‌های مرورگر به‌عنوان منسوخ شده حذف خواهند شد:\n\n%1\nبه‌جای آن از «Docks/YouTube Live Control Room» استفاده کنید." +ConfigDownload.WarningMessageTitle="اخطار" +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="غیرفعال کردن برای این استریم و شروع استریم" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="به‌روزرسانی تنظیمات و شروع استریم کردن" diff --git a/UI/data/locale/fi-FI.ini b/UI/data/locale/fi-FI.ini index 0a482b4a871e8d..d1f3ecc9129147 100644 --- a/UI/data/locale/fi-FI.ini +++ b/UI/data/locale/fi-FI.ini @@ -105,6 +105,7 @@ MixerToolbarMenu="Äänen Mikserivalikko" SceneFilters="Avaa näkymän suodattimet" List="Lista" Grid="Ruudukko" +Automatic="Automaattinen" PluginsFailedToLoad.Title="Liitännäisen latausvirhe" PluginsFailedToLoad.Text="Seuraavien OBS-liitännäisten lataaminen epäonnistui:\n\n%1\nPäivitä tai poista kyseiset liitänäiset." AlreadyRunning.Title="OBS on jo käynnissä" @@ -190,6 +191,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Suosi laitteistokoodausta" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Laitteistoenkoodaus poistaa suurimman osan CPU-käytöstä, mutta saattaa vaatia suuremman bittinopeuden saavuttaakseen saman laadun." Basic.AutoConfig.StreamPage.StreamWarning.Title="Lähetysvaroitus" Basic.AutoConfig.StreamPage.StreamWarning.Text="Kaistan testauksessa lähetetään satunnaista videodataa kanavallesi ilman ääntä. On suositeltavaa ottaa pois käytöstä lähetysten tallennus ja asettaa lähetys yksityiseksi, kunnes testi on suoritettu. Jatketaanko?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Testaa %1" Basic.AutoConfig.TestPage="Lopulliset tulokset" Basic.AutoConfig.TestPage.SubTitle.Testing="Ohjelma käynnistää nyt muutamia testejä ihanteellisten asetusten arvioimiseksi" Basic.AutoConfig.TestPage.SubTitle.Complete="Testaus valmis" @@ -208,6 +210,7 @@ Basic.AutoConfig.TestPage.Result.Header="Ohjelma on määrittänyt nämä asetuk Basic.AutoConfig.TestPage.Result.Footer="Ottaaksesi asetukset käyttöön, paina \"Ota käyttöön\". Määrittääksesi asetukset uudelleen, paina \"Edellinen\". Jos haluat määrittää asetukset itse, paina \"Peruuta\" ja avaa asetukset." Basic.AutoConfig.Info="Automaattinen määritystoiminto määrittää parhaat asetukset perustuen tietokoneesi laitteistoon ja internetin nopeuteen." Basic.AutoConfig.RunAnytime="Tämän voi suorittaa milloin vain Työkalut-valikosta." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Suoratoiston (skaalattu) resoluutio" Basic.Stats="Tilastot" Basic.Stats.CPUUsage="CPU-käyttö" Basic.Stats.HDDSpaceAvailable="Tallennustilaa vapaana" @@ -557,6 +560,7 @@ Basic.Main.Scenes="Näkymät" Basic.Main.Sources="Lähteet" Basic.Main.Source="Lähde" Basic.Main.Controls="Ohjaimet" +Basic.Main.PreparingStream="Valmistellaan..." Basic.Main.Connecting="Yhdistetään..." Basic.Main.StartRecording="Aloita tallennus" Basic.Main.StartReplayBuffer="Käynnistä toistopuskuri" @@ -568,7 +572,7 @@ Basic.Main.StopRecording="Pysäytä tallennus" Basic.Main.PauseRecording="Keskeytä tallennus" Basic.Main.UnpauseRecording="Jatka tallennusta" Basic.Main.SplitFile="Pilko tallennettu tiedosto" -Basic.Main.AddChapterMarker="Lisää kappale merkintä" +Basic.Main.AddChapterMarker="Lisää kappalemerkki (vain hybridi MP4)" Basic.Main.StoppingRecording="Pysäytetään tallennusta..." Basic.Main.StopReplayBuffer="Pysäytä toistopuskuri" Basic.Main.StoppingReplayBuffer="Pysäytetään toistopuskuria..." @@ -677,6 +681,7 @@ Basic.MainMenu.Help.About="Tietoj&a" Basic.Settings.ProgramRestart="Ohjelma on käynnistettävä uudelleen, jotta asetukset tulevat voimaan." Basic.Settings.ConfirmTitle="Vahvista muutokset" Basic.Settings.Confirm="Sinulla on tallentamattomia muutoksia. Tallennetaanko?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 hallitsee joitain suoratoistoasetuksia" Basic.Settings.General="Yleiset" Basic.Settings.General.Language="Kieli" Basic.Settings.General.Updater="Päivitykset" @@ -743,6 +748,7 @@ Basic.Settings.Appearance.General.Theme="Teema" Basic.Settings.Appearance.General.Variant="Tyyli" Basic.Settings.Appearance.General.NoVariant="Tyyliä ei ole saatavilla" Basic.Settings.Stream="Lähetys" +Basic.Settings.Stream.Destination="Kohde" Basic.Settings.Stream.Custom.UseAuthentication="Käytä todennusta" Basic.Settings.Stream.Custom.Username="Käyttäjätunnus" Basic.Settings.Stream.Custom.Password="Salasana" @@ -764,6 +770,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Kuvan maksimibittinopeus: %1 Basic.Settings.Stream.Recommended.MaxAudioBitrate="Äänen maksimibittinopeus: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maksimiresoluutio: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maksimi-FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Määritä omavalintainen palvelin..." +Basic.Settings.Stream.ServiceCustomServer="Omavalintainen palvelin" +Basic.Settings.Stream.EnableMultitrackVideo="Käytä %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maksimi suoratoiston kaistanleveys" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Autom." +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maksimi videoraidat" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Autom." +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Stream dump käyttöön FLV-tiedostoon (käyttää yksinkertaisia tallennuksen asetuksia)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Määrityksen ohitus (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Käytä määrityksen ohitusta" +Basic.Settings.Stream.MultitrackVideoLabel="Moniraitainen video" +Basic.Settings.Stream.AdvancedOptions="Lisäasetukset" Basic.Settings.Output="Ulostulo" Basic.Settings.Output.Format="Tallennusmuoto" Basic.Settings.Output.Format.fMP4="Hajanainen MP4 (.mp4)" @@ -1215,3 +1233,30 @@ YouTube.Errors.messageTextInvalid="Viestin teksti ei kelpaa." YouTube.Errors.rateLimitExceeded="Lähetät viestejä liian nopeasti." YouTube.DocksRemoval.Title="Tyhjennä vanhat YouTube selaimen telakat" YouTube.DocksRemoval.Text="Nämä selaimen telakoinnit poistetaan vanhentuneina:\n\n%1\nKäytä sen sijaan \"Docks/YouTube Live Control Room\"." +ConfigDownload.WarningMessageTitle="Varoitus" +FailedToStartStream.MissingConfigURL="Palvelulle ei ole saatavilla konfigurointi URL-osoitetta" +FailedToStartStream.NoCustomRTMPURLInSettings="Mukautettua RTMP URL-osoitetta ei ole määritetty" +FailedToStartStream.InvalidCustomConfig="Virheellinen mukautettu määritys" +FailedToStartStream.FailedToCreateMultitrackVideoService="Moniraitaisen videopalvelun luominen epäonnistui" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Moniraitaisen videon RTMP-ulostulon luominen epäonnistui" +FailedToStartStream.EncoderNotAvailable="NVENC ei ole saatavilla.\n\n\Kooderin tyyppiä \"%1\" ei löytynyt" +FailedToStartStream.FailedToCreateVideoEncoder="Videokooderin \"%1\" luominen epäonnistui (tyyppi: \"%2\")" +FailedToStartStream.FailedToGetOBSVideoInfo="OBS-videotietojen hakeminen epäonnistui luotaessa kooderia \"%1\" (tyyppi: \"%2\")" +FailedToStartStream.FailedToCreateAudioEncoder="Audiokooderin luominen epäonnistui" +FailedToStartStream.NoRTMPURLInConfig="Asetus ei sisällä suoratoiston kohdeosoitetta RTMP(S) URL" +FailedToStartStream.FallbackToDefault="Suoratoiston aloitus sovelluksella %1 epäonnistui; haluatko yrittää uudelleen käyttämällä koodaus asetusta?" +FailedToStartStream.ConfigRequestFailed="Asetusta ei voitu hakea %1

HTTP virhe: %2" +FailedToStartStream.WarningUnknownStatus="Vastaanotettu tilan tuntematon arvo \"%1\"" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nHaluatko jatkaa suoratoistoa ilman %1?" +FailedToStartStream.WarningRetry="\n

\nHaluatko jatkaa suoratoistoa?" +FailedToStartStream.MissingEncoderConfigs="Lähetyksen asetus ei sisältynyt kooderin määrityksiä" +FailedToStartStream.StatusMissingHTML="Lähetyksen aloituspyyntö palautti määrittelemättömän virheen" +FailedToStartStream.NoConfigSupplied="Asetus puuttuu" +MultitrackVideo.Info="%1 optimoi asetuksesi automaattisesti koodaukselle ja lähettää useita videolaatuja kohteeseen . Tämän valitseminen lähettää tietoja %2 tietokoneestasi ja ohjelmiston asetuksista." +MultitrackVideo.IncompatibleSettings.Title="Ei yhteensopivia asetuksia" +MultitrackVideo.IncompatibleSettings.Text="%1 ei ole yhteensopiva seuraavien kanssa:\n\n%2\nJos haluat jatkaa suoratoistoa sovelluksella %1, poista käytöstä yhteensopimattomat asetukset:\n\n%3\nja aloita uudelleen." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Poista nyt käytöstä ja aloita suoratoisto" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Päivitä asetukset ja aloita suoratoisto" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 ei ole yhteensopiva [Ääni → Yleiset → Kanavat] asetettu \"%2\", %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Ääni → Yleiset → Kanavat] on asetettava arvoon \"%1\"" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 vaatii useita eri asetuksia [Ääni → Yleiset → Kanavat]" diff --git a/UI/data/locale/fr-FR.ini b/UI/data/locale/fr-FR.ini index 4c247db192d65c..b649f1e6ea531e 100644 --- a/UI/data/locale/fr-FR.ini +++ b/UI/data/locale/fr-FR.ini @@ -105,6 +105,7 @@ MixerToolbarMenu="Menu du mixeur audio" SceneFilters="Ouvrir les filtres de la scène" List="Liste" Grid="Grille" +Automatic="Automatique" PluginsFailedToLoad.Title="Erreur de chargement du plugin" PluginsFailedToLoad.Text="Les plugins OBS suivants n'ont pas pu être chargés :\n\n%1\nVeuillez mettre à jour ou supprimer ces plugins." AlreadyRunning.Title="OBS est déjà en cours d'exécution" @@ -188,6 +189,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Préférer l’encodage mat Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="L'encodage matériel minimise l'utilisation du processeur (CPU), mais peut nécessiter un débit vidéo plus élevé pour obtenir une niveau de qualité équivalent." Basic.AutoConfig.StreamPage.StreamWarning.Title="Avertissement de diffusion" Basic.AutoConfig.StreamPage.StreamWarning.Text="Le test de bande passante va diffuser un signal vidéo aléatoire sans son sur votre chaîne. Si vous en avez la possibilité, il est recommandé de désactiver temporairement l'enregistrement des diffusions et de configurer le stream en privé jusqu'à ce que le test soit terminé. Souhaitez-vous continuer ?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Teste %1" Basic.AutoConfig.TestPage="Résultat final" Basic.AutoConfig.TestPage.SubTitle.Testing="Le programme exécute maintenant une série de tests pour estimer vos paramètres idéaux" Basic.AutoConfig.TestPage.SubTitle.Complete="Tests terminés" @@ -206,6 +208,7 @@ Basic.AutoConfig.TestPage.Result.Header="Le programme vous recommande ces param Basic.AutoConfig.TestPage.Result.Footer="Pour utiliser ces paramètres, cliquez sur « Appliquer les paramètres ». Pour reconfigurer l’Assistant et essayer de nouveau, cliquez sur « Précédent ». Pour configurer les paramètres vous-même, cliquez sur « Annuler » et rendez-vous dans les Paramètres." Basic.AutoConfig.Info="L'assistant de configuration automatique déterminera les meilleurs paramètres en fonction des spécifications de votre ordinateur et du débit internet." Basic.AutoConfig.RunAnytime="Cet assistant peut être lancé à tout moment en allant dans le menu Outils." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Résolution (Agrandie) du stream" Basic.Stats="Statistiques" Basic.Stats.CPUUsage="Utilisation du processeur" Basic.Stats.HDDSpaceAvailable="Espace disque disponible" @@ -547,6 +550,7 @@ Basic.Main.AddSourceHelp.Title="Impossible d'ajouter la source" Basic.Main.AddSourceHelp.Text="Vous devez avoir au moins une scène pour ajouter une source." Basic.Main.Scenes="Scènes" Basic.Main.Controls="Commandes" +Basic.Main.PreparingStream="Préparation..." Basic.Main.Connecting="Connexion en cours..." Basic.Main.StartRecording="Démarrer l'enregistrement" Basic.Main.StartReplayBuffer="Démarrer le tampon de relecture" @@ -558,6 +562,7 @@ Basic.Main.StopRecording="Arrêter l'enregistrement" Basic.Main.PauseRecording="Mettre en pause l'enregistrement" Basic.Main.UnpauseRecording="Reprendre l'enregistrement" Basic.Main.SplitFile="Fractionner le fichier cible" +Basic.Main.AddChapterMarker="Ajouter un marqueur de chapitre (Hybrid MP4 uniquement)" Basic.Main.StoppingRecording="Arrêt de l'enregistrement..." Basic.Main.StopReplayBuffer="Arrêter le tampon de relecture" Basic.Main.StoppingReplayBuffer="Arrêt du tampon de relecture..." @@ -664,6 +669,7 @@ Basic.MainMenu.Help.About="À propos (&A)" Basic.Settings.ProgramRestart="Le programme doit être redémarré pour que les paramètres prennent effet." Basic.Settings.ConfirmTitle="Valider les modifications" Basic.Settings.Confirm="Vous avez des modifications non enregistrées. Voulez-vous les enregistrer ?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 contrôle certains de vos paramètres de flux" Basic.Settings.General="Général" Basic.Settings.General.Language="Langue" Basic.Settings.General.Updater="Mises à jour" @@ -748,9 +754,20 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Débit vidéo maximal : %1 kb Basic.Settings.Stream.Recommended.MaxAudioBitrate="Débit audio maximal : %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Résolution maximale : %1" Basic.Settings.Stream.Recommended.MaxFPS="FPS Maximum : %1" +Basic.Settings.Stream.SpecifyCustomServer="Spécifier un serveur personnalisé..." +Basic.Settings.Stream.ServiceCustomServer="Serveur personnalisé" +Basic.Settings.Stream.EnableMultitrackVideo="Activer %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Bande passante de streaming maximale" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Nombre maximal de pistes vidéo" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Activer le vidage de flux au format FLV (utilise des paramètres de fichier d'enregistrement simples)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Remplacement de la configuration (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Activer le remplacement de la configuration" +Basic.Settings.Stream.MultitrackVideoLabel="Vidéo Multipiste" +Basic.Settings.Stream.AdvancedOptions="Options Avancées" Basic.Settings.Output="Sortie" Basic.Settings.Output.Format="Format d'enregistrement" Basic.Settings.Output.Format.MKV="Vidéo Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 hybride [BÊTA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 fragmenté (.mp4)" Basic.Settings.Output.Format.fMOV="MOV fragmenté (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Le MOV fragmenté enregistre l'enregistrement par morceaux et ne nécessite pas la même finalisation que les fichiers MOV traditionnels.\nCela garantit que le fichier reste lisible même si l'écriture sur le disque est interrompue, par exemple, suite à un BSOD ou d'une coupure de courant.\n\nCe format peut ne pas être compatible avec tous les lecteurs et éditeurs. Utilisez Fichier → Convertir des enregistrements pour convertir le fichier dans un format plus compatible si nécessaire." @@ -1212,3 +1229,26 @@ YouTube.Errors.messageTextInvalid="Le texte du message n'est pas valide." YouTube.Errors.rateLimitExceeded="Vous envoyez des messages trop rapidement." YouTube.DocksRemoval.Title="Effacer les anciens Docks Internet de YouTube" YouTube.DocksRemoval.Text="Ces Docks Internet seront enlevés car obsolètes :\n\n%1\nUtilisez les \"Docks/YouTube Live Control Room\" à la place." +ConfigDownload.WarningMessageTitle="Avertissement" +FailedToStartStream.MissingConfigURL="Aucune URL de configuration disponible pour le service actuel" +FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP personnalisée non spécifiée" +FailedToStartStream.InvalidCustomConfig="Configuration personnalisée invalide" +FailedToStartStream.FailedToCreateMultitrackVideoService="Échec de la création du service vidéo multipiste" +FailedToStartStream.EncoderNotAvailable="NVENC non disponible.\n\nÉchec de la recherche du type d'encodeur '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Échec de la création de l'encodeur vidéo '%1' (type : '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Échec de la création de l'encodeur audio" +FailedToStartStream.NoRTMPURLInConfig="La configuration ne contient pas l'URL RTMP(S) cible du flux" +FailedToStartStream.FallbackToDefault="Le démarrage du flux à l'aide de %1 a échoué ; Voulez-vous réessayer en utilisant les paramètres d'encodage unique ?" +FailedToStartStream.ConfigRequestFailed="Impossible de récupérer la configuration à partir de %1

Erreur HTTP : %2" +FailedToStartStream.WarningUnknownStatus="Valeur d'état inconnue reçue '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nVoulez-vous continuer la diffusion sans %1 ?" +FailedToStartStream.WarningRetry="\n

\nVoulez-vous continuer la diffusion ?" +FailedToStartStream.MissingEncoderConfigs="La configuration Passez en Direct n'incluait pas les configurations d'encodeur" +FailedToStartStream.StatusMissingHTML="La demande de Passez en Direct a renvoyé une erreur non spécifiée" +FailedToStartStream.NoConfigSupplied="Configuration manquante" +MultitrackVideo.Info="%1 optimise automatiquement vos paramètres pour encoder et envoyer plusieurs qualités vidéo. La sélection de cette option enverra %2 des informations sur votre ordinateur et la configuration de vos logiciels." +MultitrackVideo.IncompatibleSettings.Title="Paramètres Incompatibles" +MultitrackVideo.IncompatibleSettings.Text="%1 n'est actuellement pas compatible avec :\n\n%2\nPour continuer la diffusion avec %1, désactivez les paramètres incompatibles :\n\n%3\net redémarrez la diffusion." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Désactivez pour ce flux et démarrez la diffusion" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Mettre à jour les paramètres et démarrer la diffusion" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → Général → Canaux] doit être défini sur « %1 »" diff --git a/UI/data/locale/he-IL.ini b/UI/data/locale/he-IL.ini index d829069b958e07..29b75ebc2a0c31 100644 --- a/UI/data/locale/he-IL.ini +++ b/UI/data/locale/he-IL.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="תפריט מערבל שמע" SceneFilters="פתיחת מסנני סצנה" List="רשימה" Grid="טבלה" +Automatic="אוטומטי" PluginsFailedToLoad.Title="שגיאה בטעינת תוסף" PluginsFailedToLoad.Text="טעינת תוספי OBS הבאים נכשלה:\n\n%1\nנא לעדכן או להסיר תוספים אלה." AlreadyRunning.Title="OBS פועל כבר" @@ -213,6 +214,7 @@ Basic.AutoConfig.TestPage.Result.Header="התוכנית קבעה כי אלה ה Basic.AutoConfig.TestPage.Result.Footer="כדי להשתמש בהגדרות אלה, לחץ על החל הגדרות. כדי להגדיר מחדש את האשף ולנסות שוב, לחץ על אחורה. להגדיר ידנית את הגדרות, לחץ על ביטול ולאחר מכן פתח את הגדרות." Basic.AutoConfig.Info="אשף ההגדרה האוטומטית ימצא את ההגדרות הטובות ביותר בהתבסס על מפרט המחשב ומהירות האינטרנט שלך." Basic.AutoConfig.RunAnytime="אפשר להריץ אותו בכל זמן באמצעות תפריט הכלים." +Basic.AutoConfig.TestPage.Result.StreamingResolution="רזולוציית הזרמה (בגודל מתואם)" Basic.Stats="סטטיסטיקות" Basic.Stats.CPUUsage="שימוש במעבד" Basic.Stats.HDDSpaceAvailable="שטח דיסק פנוי" @@ -569,6 +571,7 @@ Basic.Main.Scenes="סצנות" Basic.Main.Sources="מקורות" Basic.Main.Source="מקור" Basic.Main.Controls="בקרה" +Basic.Main.PreparingStream="בהכנות…" Basic.Main.Connecting="מתבצעת התחברות…" Basic.Main.StartRecording="התחלת הקלטה" Basic.Main.StartReplayBuffer="התחל את אוגר ההילוך החוזר" @@ -580,7 +583,7 @@ Basic.Main.StopRecording="עצירת הקלטה" Basic.Main.PauseRecording="השהיית הקלטה" Basic.Main.UnpauseRecording="ביטול השהיית ההקלטה" Basic.Main.SplitFile="פיצול קובץ הקלטה" -Basic.Main.AddChapterMarker="הוספת סמן פרק" +Basic.Main.AddChapterMarker="הוספת סמן פרק (MP4 משולב בלבד)" Basic.Main.StoppingRecording="ההקלטה נעצרת…" Basic.Main.StopReplayBuffer="עצור את אוגר ההילוך החוזר" Basic.Main.StoppingReplayBuffer="עצירת אוגר ההילוך החוזר..." @@ -689,6 +692,7 @@ Basic.MainMenu.Help.About="על &אודות" Basic.Settings.ProgramRestart="יש להפעיל את התוכנית מחדש כדי שהגדרות האלה ייכנסו לתוקף." Basic.Settings.ConfirmTitle="אישור השינויים" Basic.Settings.Confirm="קיימים שינויים שלא נשמרו. לשמור את השינויים?" +Basic.Settings.MultitrackVideoDisabledSettings="לשירות %1 %2 יש שליטה בחלק מהגדרות ההזרמה שלך" Basic.Settings.General="כללי" Basic.Settings.General.Language="שפה" Basic.Settings.General.Updater="עדכונים" @@ -1267,7 +1271,29 @@ YouTube.Errors.rateLimitExceeded="שלחת הודעות מהר מדי." YouTube.DocksRemoval.Title="מחיקת מעגנים מיושנים של דפדפן YouTube" YouTube.DocksRemoval.Text="מעגני דפדפן אלה יוסרו כיוון שיצאו משימוש.\n\n%1\nאפשר להשתמש ב„מעגנים/חדר בקרה ל־YouTube חי” במקום." ConfigDownload.WarningMessageTitle="אזהרה" +FailedToStartStream.MissingConfigURL="אין כתובת הגדרות זמינה לשירות הנוכחי" +FailedToStartStream.NoCustomRTMPURLInSettings="לא צוינה כתובת RTMP מותאמת אישית" +FailedToStartStream.InvalidCustomConfig="הגדרות מותאמות שגויות" +FailedToStartStream.FailedToCreateMultitrackVideoService="יצירת שירות וידאו רב־רצועות נכשל" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="יצירת פלט וידאו RTMP רב־רצועות נכשל" +FailedToStartStream.EncoderNotAvailable="NVENC לא זמין.\n\איתור סוג מקודד ‚%1’ נכשל" +FailedToStartStream.FailedToCreateVideoEncoder="יצירת מקודד שמע ‚%1’ (סוג: ‚%2’) נכשלה" +FailedToStartStream.FailedToGetOBSVideoInfo="משיכת פרטי וידאו OBS בעת יצירת מקודד ‚%1’ (מסוג: ‚%2’) נכשלה" +FailedToStartStream.FailedToCreateAudioEncoder="יצירת מקודד השמע נכשלה" +FailedToStartStream.NoRTMPURLInConfig="ההגדרות לא מכילות כתובת יעד תזרים RTMP(S)" +FailedToStartStream.FallbackToDefault="התחלת ההזרמה באמצעות %1 נכשלה, לנסות שוב בעזרת הגדרות קידוד יחידניות?" +FailedToStartStream.ConfigRequestFailed="לא ניתן למשוך את ההגדרות מתוך %1

שגיאת HTTP‏: %2" +FailedToStartStream.WarningUnknownStatus="התקבל ערך מצב ‚%1’ לא מוכר" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nלהמשיך להזרים בלי %1?" +FailedToStartStream.WarningRetry="\n

\nלהמשיך להזרים?" +FailedToStartStream.MissingEncoderConfigs="הגדרות העלייה לאוויר לא כללו הגדרות מקודדים" +FailedToStartStream.StatusMissingHTML="בקשת עלייה לאוויר החזירה שגיאה לא ידועה" FailedToStartStream.NoConfigSupplied="הגדרות חסרות" +MultitrackVideo.Info="%1 ממטב את ההגדרות שלך אוטומטית כדי לקודד ולשלוח מגוון איכויות וידאו. בחירה באפשרות הזאת תשלח פרטים אל %2 על תצורת המחשב והתוכנה שלך." MultitrackVideo.IncompatibleSettings.Title="הגדרות לא תואמות" +MultitrackVideo.IncompatibleSettings.Text="%1 לא זמין כרגע עם:\n\n%2\כדי להמשיך להזרים עם %1, יש להשבית הגדרות לא נתמכות:\n\n%3\ולהתחיל להזרים שוב." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="להשבית לתזרים הזה ולהתחיל להזרים/לשדר" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="עדכון ההגדרות והתחלת הזרמה/שידור" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 כרגע לא בתאימות מול [שמע ← כללי ← ערוצים] יש להגדיר ל־‚%2’, %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="יש להגדיר את [שמע ← כללי ← ערוצים] לערך ‚%1’" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 דורש מגוון הגדרות שונות עבור [שמע ← כללי ← ערוצים]" diff --git a/UI/data/locale/hi-IN.ini b/UI/data/locale/hi-IN.ini index c5b81ba6867f8a..19192d80678d6c 100644 --- a/UI/data/locale/hi-IN.ini +++ b/UI/data/locale/hi-IN.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="ऑडियो मिक्सर मेनू" SceneFilters="दृश्य फ़िल्टर खोलें" List="सूची" Grid="ग्रिड" +Automatic="स्वचालित" PluginsFailedToLoad.Title="प्लगइन लोड त्रुटि" PluginsFailedToLoad.Text="निम्‍न OBS प्‍लग इन लोड होने में विफल :\n\n%1\nकृपया इन प्‍लग इन को अपडेट करें या निकालें." AlreadyRunning.Title="OBS चल रहा है" @@ -194,6 +195,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="हार्डवेयर Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="हार्डवेयर एन्कोडिंग अधिकांश CPU उपयोग को समाप्त करता है, लेकिन गुणवत्ता के समान स्तर को प्राप्त करने के लिए अधिक बिटरेट की आवश्यकता हो सकती है." Basic.AutoConfig.StreamPage.StreamWarning.Title="स्ट्रीम चेतावनी" Basic.AutoConfig.StreamPage.StreamWarning.Text="बैंडविड्थ परीक्षण आपके चैनल के लिए ऑडियो के बिना यादृच्छिक वीडियो डेटा को स्ट्रीम करने वाला है. यदि आप सक्षम हैं, तो स्ट्रीमिंग वीडियो को सहेजना अस्थायी रूप से बंद करना ठीक होगा और परीक्षण पूरा तक स्ट्रीम को निजी सेट करना अच्छा रहेगा. आगे बढ़ें?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="टेस्ट %1" Basic.AutoConfig.TestPage="अंतिम परिणाम" Basic.AutoConfig.TestPage.SubTitle.Testing="आदर्श सेटिंग्स का अनुमान लगाने के लिए कार्यक्रम अब परीक्षणों का एक सेट निष्पादित कर रहा है" Basic.AutoConfig.TestPage.SubTitle.Complete="परीक्षण पूर्ण" @@ -212,6 +214,7 @@ Basic.AutoConfig.TestPage.Result.Header="इस प्रोग्राम न Basic.AutoConfig.TestPage.Result.Footer="इन सेटिंग्स का उपयोग करने के लिए, सेटिंग्स लागू करें पर क्लिक करें. विज़ार्ड को पुन: कॉन्फ़िगर करने और पुन: प्रयास करने के लिए, पीछे जाएं पर क्लिक करें. सेटिंग्स को स्वयं कॉन्फ़िगर करने के लिए, रद्द करें पर क्लिक करें और सेटिंग्स खोलें." Basic.AutoConfig.Info="ऑटो-कॉन्फ़िगरेशन विज़ार्ड आपके कंप्यूटर विशेष विवरण और इंटरनेट की गति के आधार पर सर्वोत्तम सेटिंग्स निर्धारित करेगा." Basic.AutoConfig.RunAnytime="इसे कभी भी टूल्स मेनू में जाकर चला सकते हैं." +Basic.AutoConfig.TestPage.Result.StreamingResolution="स्ट्रीमिंग (स्केल्ड) रिज़ॉल्यूशन" Basic.Stats="आंकड़े" Basic.Stats.CPUUsage="सि पि यु का उपयोग" Basic.Stats.HDDSpaceAvailable="डिस्क में जगह उपलब्ध है" @@ -562,6 +565,7 @@ Basic.Main.Scenes="दृश्य" Basic.Main.Sources="स्रोत" Basic.Main.Source="स्रोत" Basic.Main.Controls="नियंत्रण" +Basic.Main.PreparingStream="तैयार कर रहे हैं..." Basic.Main.Connecting="कनेक्ट हो रहा है..." Basic.Main.StartRecording="रिकॉर्डिंग शुरू करें" Basic.Main.StartReplayBuffer="रीप्ले बफ़र आरंभ करें" @@ -573,6 +577,7 @@ Basic.Main.StopRecording="रिकॉर्डिंग बंद करें" Basic.Main.PauseRecording="रिकॉर्डिंग रोकें" Basic.Main.UnpauseRecording="रिकॉर्डिंग चलने दें" Basic.Main.SplitFile="रिकॉर्डिंग फ़ाइल विभाजित करें" +Basic.Main.AddChapterMarker="चैप्टर मार्कर जोड़ें (केवल हाइब्रिड MP4)" Basic.Main.StoppingRecording="रिकॉर्डिंग बंद कर रहे हैं" Basic.Main.StopReplayBuffer="रीप्ले बफ़र रोक दें" Basic.Main.StoppingReplayBuffer="रीप्ले बफ़र रोक रहे हैं" @@ -681,6 +686,7 @@ Basic.MainMenu.Help.About="परिचय (&A)" Basic.Settings.ProgramRestart="परिवर्तनों को लागू करने के लिए प्रोग्राम को रीस्टार्ट करना जरूरी है." Basic.Settings.ConfirmTitle="परिवर्तनों की पुष्टि करें" Basic.Settings.Confirm="आपने परिवर्तनों को अभी नहीं सहेजा है. क्या आप इन्हें सहेजना चाहते हैं?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 आपकी कुछ स्ट्रीम सेटिंग्स को नियंत्रित कर रहा है" Basic.Settings.General="सामान्य" Basic.Settings.General.Language="भाषा" Basic.Settings.General.Updater="अपडेट" @@ -747,6 +753,7 @@ Basic.Settings.Appearance.General.Theme="प्रदर्शनशैली" Basic.Settings.Appearance.General.Variant="शैली" Basic.Settings.Appearance.General.NoVariant="कोई शैलियाँ उपलब्ध नहीं हैं" Basic.Settings.Stream="स्ट्रीम" +Basic.Settings.Stream.Destination="गंतव्य" Basic.Settings.Stream.Custom.UseAuthentication="प्रमाणीकरण का प्रयोग करें" Basic.Settings.Stream.Custom.Username="उपयोगकर्ता का नाम" Basic.Settings.Stream.Custom.Password="पासवर्ड" @@ -768,9 +775,22 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="अधिकतम वीड Basic.Settings.Stream.Recommended.MaxAudioBitrate="अधिकतम ऑडियो बिट दर : %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="अधिकतम रिज़ोल्यूशन : %1" Basic.Settings.Stream.Recommended.MaxFPS="अधिकतम FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="कस्टम सर्वर निर्दिष्ट करें..." +Basic.Settings.Stream.ServiceCustomServer="कस्टम सर्वर" +Basic.Settings.Stream.EnableMultitrackVideo="%1 सक्षम करें" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="अधिकतम स्ट्रीमिंग बैंडविड्थ" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="स्वचालित" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="अधिकतम वीडियो ट्रैक" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="स्वचालित" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="स्ट्रीम डंप को FLV में सक्षम करें (सरल रिकॉर्डिंग फ़ाइल सेटिंग्स का उपयोग करता है)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="कॉन्फ़िग ओवरराइड (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="कॉन्फ़िग ओवरराइड सक्षम करें" +Basic.Settings.Stream.MultitrackVideoLabel="मल्टीट्रैक वीडियो" +Basic.Settings.Stream.AdvancedOptions="उन्नत विकल्प" Basic.Settings.Output="आउटपुट" Basic.Settings.Output.Format="रिकॉर्डिंग प्रारूप" Basic.Settings.Output.Format.MKV="मत्रोस्का वीडियो (.mkv)" +Basic.Settings.Output.Format.hMP4="हाइब्रिड MP4 [बीटा] (.mp4)" Basic.Settings.Output.Format.fMP4="खंडित MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="खंडित MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="खंडित MOV रिकॉर्डिंग को विखंडों में लिखता है और पारंपरिक MOV फ़ाइलों के समान इन्हेंअंतिम रूप देने की आवश्यकता नहीं होती है.\nयह सुनिश्चित करता है कि डिस्क पर लिखने में बाधा होने पर भी फ़ाइल चलने योग्य बनी रहे, उदाहरण के लिए, BSOD या पावर लॉस के परिणामस्वरूप.\n\nयह सभी प्लेअर्स और संपादकों के साथ संगत नहीं भी हो सकता है. यदि आवश्यक हो तो फ़ाइल को अधिक संगत प्रारूप में बदलने के लिए फ़ाइल → रीमक्स रिकॉर्डिंग का उपयोग करें." @@ -1241,3 +1261,30 @@ YouTube.Errors.messageTextInvalid="इस संदेश का आलेख YouTube.Errors.rateLimitExceeded="आप संदेश बहुत शीघ्रता से भेज रहे हैं." YouTube.DocksRemoval.Title="लीगेसी यूट्यूब ब्राउज़र डॉक्स साफ़ करें" YouTube.DocksRemoval.Text="इन ब्राउज़र डॉक को अप्रचलित मानकर हटा दिया जाएगा:\n\n%1\nइसके बजाय \"डॉक्स/यूट्यूब लाइव कंट्रोल रूम\" का उपयोग करें." +ConfigDownload.WarningMessageTitle="चेतावनी" +FailedToStartStream.MissingConfigURL="वर्तमान सेवा के लिए कोई कॉन्फिग URL उपलब्ध नहीं है" +FailedToStartStream.NoCustomRTMPURLInSettings="कस्टम RTMP URL निर्दिष्ट नहीं है" +FailedToStartStream.InvalidCustomConfig="अमान्य कस्टम कॉन्फ़िग" +FailedToStartStream.FailedToCreateMultitrackVideoService="मल्टीट्रैक वीडियो सेवा निर्माण में विफल" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="मल्टीट्रैक वीडियो RTMP आउटपुट बनाने में विफल" +FailedToStartStream.EncoderNotAvailable="NVENC उपलब्ध नहीं है.\n\nएनकोडर प्रकार '%1' ढूंढने में विफल" +FailedToStartStream.FailedToCreateVideoEncoder="वीडियो एनकोडर '%1' बनाने में विफल (प्रकार: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="एनकोडर '%1' बनाते समय OBS वीडियो जानकारी प्राप्त करने में विफल (प्रकार: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="ऑडियो एन्कोडर बनाने में विफल" +FailedToStartStream.NoRTMPURLInConfig="कॉन्फ़िगरेशन में स्ट्रीम लक्ष्य RTMP(S) URL शामिल नहीं है" +FailedToStartStream.FallbackToDefault="%1 का उपयोग करके स्ट्रीम प्रारंभ करना विफल रहा; क्या आप एकल एन्कोड सेटिंग्स का उपयोग करके पुनः प्रयास करना चाहते हैं?" +FailedToStartStream.ConfigRequestFailed="%1 से कॉन्फ़िगरेशन नहीं लाया जा सका

HTTP त्रुटि: %2" +FailedToStartStream.WarningUnknownStatus="अज्ञात स्थिति मान '%1' प्राप्त हुआ" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nक्या आप %1 के बिना स्ट्रीमिंग जारी रखना चाहते हैं?" +FailedToStartStream.WarningRetry="\n

\nक्या आप स्ट्रीमिंग जारी रखना चाहते हैं?" +FailedToStartStream.MissingEncoderConfigs="गो लाइव कॉन्फिग में एनकोडर कॉन्फिगरेशन शामिल नहीं था" +FailedToStartStream.StatusMissingHTML="गो लाइव अनुरोध ने एक अनिर्दिष्ट त्रुटि लौटा दी" +FailedToStartStream.NoConfigSupplied="अनुपलब्ध कॉन्फ़िग" +MultitrackVideo.Info="%1 एकाधिक वीडियो गुणवत्ता को एन्कोड करने और भेजने के लिए आपकी सेटिंग्स को स्वचालित रूप से अनुकूलित करता है. इस विकल्प को चुनने से आपके कंप्यूटर और सॉफ़्टवेयर सेटअप के बारे में %2 जानकारी भेजी जाएगी." +MultitrackVideo.IncompatibleSettings.Title="असंगत सेटिंग्स" +MultitrackVideo.IncompatibleSettings.Text="%1 वर्तमान में इसके साथ संगत नहीं है:\n\n%2\n%1 के साथ स्ट्रीमिंग जारी रखने के लिए, असंगत सेटिंग्स अक्षम करें:\n\n%3\nऔर दुबारा स्ट्रीमिंग शुरू करें." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="इस स्ट्रीम के लिए अक्षम करें और स्ट्रीमिंग प्रारंभ करें" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="सेटिंग्स अपडेट करें और स्ट्रीमिंग शुरू करें" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 वर्तमान में [ऑडियो → सामान्य → चैनल] के साथ संगत नहीं है '%2', %3 पर सेट किया गया" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[ऑडियो → सामान्य → चैनल] को '%1' पर सेट करने की आवश्यकता है" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 को [ऑडियो → सामान्य → चैनल] के लिए कई अलग-अलग सेटिंग्स की आवश्यकता है" diff --git a/UI/data/locale/hr-HR.ini b/UI/data/locale/hr-HR.ini index 2475a6643a60a6..33c9e08750e7a3 100644 --- a/UI/data/locale/hr-HR.ini +++ b/UI/data/locale/hr-HR.ini @@ -105,6 +105,7 @@ MixerToolbarMenu="Audio mikser jelovnik" SceneFilters="Otvorite Filtere Scene" List="Popis" Grid="Rešetka" +Automatic="Automatski" PluginsFailedToLoad.Title="Greška u učitavanju plugin" PluginsFailedToLoad.Text="Naredni OBS plugins nisu uspjelo utvarati:\n\n%1\nMolim vas ažuriranje ili sklanjajte ovi plugins." AlreadyRunning.Title="OBS je već pokrenut" @@ -487,6 +488,7 @@ Basic.Main.Connecting="Povezivanje..." Basic.Main.StartRecording="Počni snimanje" Basic.Main.StartStreaming="Počni streamanje" Basic.Main.StopRecording="Zaustavi snimanje" +Basic.Main.AddChapterMarker="Dodaj oynaku poglavlja (samo Hybrid MP4)" Basic.Main.StoppingRecording="Zaustavljanje snimanja..." Basic.Main.SetupBroadcast="Upravljaj emitiranjem" Basic.Main.StopStreaming="Zaustavi streamanje" diff --git a/UI/data/locale/hu-HU.ini b/UI/data/locale/hu-HU.ini index a5ba3800803eac..22de11249d2910 100644 --- a/UI/data/locale/hu-HU.ini +++ b/UI/data/locale/hu-HU.ini @@ -104,6 +104,7 @@ MixerToolbarMenu="Hangkeverő menü" SceneFilters="Jelenetszűrők megnyitása" List="Lista" Grid="Rács" +Automatic="Automatikus" PluginsFailedToLoad.Title="Hiba a bővítmény betöltése során" PluginsFailedToLoad.Text="A következő OBS bővítmények betöltése sikertelen:\n\n%1\nFrissítse vagy távolítsa el ezeket a bővítményeket." AlreadyRunning.Title="Az OBS már fut" @@ -190,6 +191,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Hardveres kódolás előnybe Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="A hardveres kódolás megszünteti a CPU erőforrás-használat tetemes részét, viszont sokkal több bitsebességre lehet szükség az azonos képminőség eléréséhez." Basic.AutoConfig.StreamPage.StreamWarning.Title="Stream figyelmeztetés" Basic.AutoConfig.StreamPage.StreamWarning.Text="A sávszélesség teszt véletlenszerűsített videoadatokat fog közvetíteni hang nélkül a csatornájára. Ha tudja, ideiglenesen kapcsolja ki a streamek rögzítését és állítsa a streamet privátra amíg a teszt befejeződik. Folytatja?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="%1 tesztelése" Basic.AutoConfig.TestPage="Végeredmény" Basic.AutoConfig.TestPage.SubTitle.Testing="A program most különböző tesztekkel megbecsüli a legideálisabb beállításokat" Basic.AutoConfig.TestPage.SubTitle.Complete="A teszt befejeződött" @@ -208,6 +210,7 @@ Basic.AutoConfig.TestPage.Result.Header="A program megállapította, hogy ezek a Basic.AutoConfig.TestPage.Result.Footer="A beállítások használatához kattintson a Beállítások alkalmazása gombra. A varázsló újrakonfigurálásához és az újrapróbálkozáshoz, kattintson a Vissza gombra. A manuális beállításokhoz kattintson a Mégse gombra és nyissa meg a Beállításokat." Basic.AutoConfig.Info="Az automatikus konfigurációs varázsló a számítógép műszaki adatait és az internet sebessége alapján meghatározza a legjobb beállításokat." Basic.AutoConfig.RunAnytime="Ezt bármikor futtathatod az eszközök menüjében." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Közvetítéshez használt (átméretezett) felbontás" Basic.Stats="Statisztika" Basic.Stats.CPUUsage="Processzorhasználat" Basic.Stats.HDDSpaceAvailable="Szabad lemezterület" @@ -560,6 +563,7 @@ Basic.Main.Scenes="Jelenetek" Basic.Main.Sources="Források" Basic.Main.Source="Forrás" Basic.Main.Controls="Vezérlés" +Basic.Main.PreparingStream="Előkészítés…" Basic.Main.Connecting="Kapcsolódás..." Basic.Main.StartRecording="Felvétel indítása" Basic.Main.StartReplayBuffer="Visszajátszási puffer indítása" @@ -571,7 +575,7 @@ Basic.Main.StopRecording="Felvétel leállítása" Basic.Main.PauseRecording="Felvétel szüneteltetése" Basic.Main.UnpauseRecording="Felvétel folytatása" Basic.Main.SplitFile="Felvétel fájljának felosztása" -Basic.Main.AddChapterMarker="Fejezetjelölő hozzáadása" +Basic.Main.AddChapterMarker="Fejezetjel hozzáadása (csak hibrid MP4 esetén)" Basic.Main.StoppingRecording="Felvétel leállítása..." Basic.Main.StopReplayBuffer="Visszajátszási puffer megállítása" Basic.Main.StoppingReplayBuffer="Visszajátszási puffer megállítása…" @@ -680,6 +684,7 @@ Basic.MainMenu.Help.About="&Névjegy" Basic.Settings.ProgramRestart="A beállítások érvénybe lépéséhez a program újraindítása szükséges." Basic.Settings.ConfirmTitle="Változtatások megerősítése" Basic.Settings.Confirm="Nem mentette a módosításokat. Menti a változtatásokat?" +Basic.Settings.MultitrackVideoDisabledSettings="Egyes közvetítési beállításokat a(z) %1 %2 vezérel" Basic.Settings.General="Általános" Basic.Settings.General.Language="Nyelv" Basic.Settings.General.Updater="Frissítések" @@ -746,6 +751,7 @@ Basic.Settings.Appearance.General.Theme="Téma" Basic.Settings.Appearance.General.Variant="Stílus" Basic.Settings.Appearance.General.NoVariant="Nincs elérhető stílus" Basic.Settings.Stream="Közvetítés" +Basic.Settings.Stream.Destination="Cél" Basic.Settings.Stream.Custom.UseAuthentication="Hitelesítés használata" Basic.Settings.Stream.Custom.Username="Felhasználónév" Basic.Settings.Stream.Custom.Password="Jelszó" @@ -767,6 +773,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Legnagyobb videó bitsebessé Basic.Settings.Stream.Recommended.MaxAudioBitrate="Legnagyobb hang bitsebesség: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Legnagyobb felbontás: %1" Basic.Settings.Stream.Recommended.MaxFPS="Legnagyobb FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Egyéni kiszolgáló megadása…" +Basic.Settings.Stream.ServiceCustomServer="Egyéni kiszolgáló" +Basic.Settings.Stream.EnableMultitrackVideo="%1 bekapcsolása" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Legnagyobb közvetítési sávszélesség" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatikus" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Legtöbb videósáv" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatikus" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Közvetítés FLV-be írásának bekapcsolása (egyszerű rögzítési fájl beállításainak használata)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Beállítások felülbírálása (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Beállítások felülbírálásának bekapcsolása" +Basic.Settings.Stream.MultitrackVideoLabel="Többsávos videó" +Basic.Settings.Stream.AdvancedOptions="Speciális beállítások" Basic.Settings.Output="Kimenet" Basic.Settings.Output.Format="Felvétel formátuma" Basic.Settings.Output.Format.hMP4="Hibrid MP4 [BÉTA] (.mp4)" @@ -1229,3 +1247,30 @@ YouTube.Errors.messageTextInvalid="Az üzenet szövege érvénytelen." YouTube.Errors.rateLimitExceeded="Túl gyorsan küld üzeneteket." YouTube.DocksRemoval.Title="Örökölt YouTube böngésződokkok törlése" YouTube.DocksRemoval.Text="Ezek a böngésződokkok el lesznek távolítva, mert elavultak:\n\n%1\nHelyette használja a „Dokkok/YouTube Élő irányítóközpont” lehetőséget." +ConfigDownload.WarningMessageTitle="Figyelmeztetés" +FailedToStartStream.MissingConfigURL="Nem érhető el beállítási webcím a jelenlegi szolgáltatáshoz" +FailedToStartStream.NoCustomRTMPURLInSettings="Nincs megadva egyéni RTMP webcím" +FailedToStartStream.InvalidCustomConfig="Érvénytelen egyéni beállítások" +FailedToStartStream.FailedToCreateMultitrackVideoService="A többsávos videószolgáltatás létrehozása sikertelen" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="A többsávos RTMP videókimenet létrehozása sikertelen" +FailedToStartStream.EncoderNotAvailable="Az NVENC nem érhető el.\n\nA(z) „%1” típusú kódoló nem található." +FailedToStartStream.FailedToCreateVideoEncoder="A(z) „%1” (típus: „%2”) kódoló létrehozása sikertelen" +FailedToStartStream.FailedToGetOBSVideoInfo="Az OBS videóinformációk lekérése sikertelen a(z) „%1” kódoló (típus: „%2”) létrehozása során" +FailedToStartStream.FailedToCreateAudioEncoder="A hangkódoló létrehozása sikertelen" +FailedToStartStream.NoRTMPURLInConfig="A beállítás nem tartalmazza az RTMP(S) célwebcímet" +FailedToStartStream.FallbackToDefault="A közvetítés indítása a(z) %1 használatával sikertelen; megpróbálja újra egy kódolási beállítás használatával?" +FailedToStartStream.ConfigRequestFailed="A beállítások nem kérhetőek le innen: %1

HTTP hiba: %2" +FailedToStartStream.WarningUnknownStatus="Ismeretlen állapotérték kapva: „%1”" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nFolytatja a közvetítést enélkül: %1?" +FailedToStartStream.WarningRetry="\n

\nBiztos, hogy folytatja a közvetítést?" +FailedToStartStream.MissingEncoderConfigs="Az élő adás beállítása nem tartalmazta a kódolókonfigurációkat" +FailedToStartStream.StatusMissingHTML="Az élő adási kérés nem meghatározott hibával tért vissza" +FailedToStartStream.NoConfigSupplied="Hiányzó konfiguráció" +MultitrackVideo.Info="Az %1 automatikusan optimalizálja a beállításait, hogy többféle minőségű videót kódoljon és küldjön. A beallítás választása információkat küld a(z) %2 számára a számítógépéről és szoftverbeállításairól." +MultitrackVideo.IncompatibleSettings.Title="Nem kompatibilis beállítások" +MultitrackVideo.IncompatibleSettings.Text="Az %1 jelenleg nem kompatibilis ezzel:\n\n%2\nHogy folytassa a közvetítést az %1 használatával, kapcsolja ki a nem kompatibilis beállításokat:\n\n%3\nés kezdje el újra az adást." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Kikapcsolás ennél az adásnál, és a közvetítés indítása" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Beállítások frissítése és a közvetítés indítása" +MultitrackVideo.IncompatibleSettings.AudioChannels="A(z) %1 jelenleg nem kompatibilis a [Hang → Általános → Csatornák] ezen értékével: „%2”, %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="A [Hang → Általános → Csatornák] értékének ennek kell lennie: „%1”" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="A(z) %1 több különböző beállítást igényel ennél: [Hang → Általános → Csatornák]" diff --git a/UI/data/locale/id-ID.ini b/UI/data/locale/id-ID.ini index 78debfcfb1c6c7..d5a7d85f3e8fec 100644 --- a/UI/data/locale/id-ID.ini +++ b/UI/data/locale/id-ID.ini @@ -562,7 +562,7 @@ Basic.Main.StopRecording="Berhenti Merekam" Basic.Main.PauseRecording="Jeda Rekaman" Basic.Main.UnpauseRecording="Lanjut Merekam" Basic.Main.SplitFile="Pisahkan Berkas Rekaman" -Basic.Main.AddChapterMarker="Tambah Penanda Bab" +Basic.Main.AddChapterMarker="Tambahkan Penanda Bab (Hanya Hybrid MP4)" Basic.Main.StoppingRecording="Menghentikan Perekaman..." Basic.Main.StopReplayBuffer="Hentikan Buffer Replay" Basic.Main.StoppingReplayBuffer="Menghentikan Buffer Replay..." @@ -1218,10 +1218,10 @@ FailedToStartStream.MissingConfigURL="Tidak ada URL konfigurasi yang tersedia un FailedToStartStream.NoCustomRTMPURLInSettings="Kustomisasi URL RTMP tidak ditentukan" FailedToStartStream.InvalidCustomConfig="Penyesuaian konfigurasi tidak sah" FailedToStartStream.FailedToCreateMultitrackVideoService="Gagal untuk membuat layanan video multitrack" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="Gagal untuk membuat video multitrack ber-output rtmp" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Gagal untuk membuat video multitrack ber-output RTMP" FailedToStartStream.EncoderNotAvailable="NVENC tidak tersedia.\n\nGagal untuk mencari tipe pengkodean '%1'" FailedToStartStream.FailedToCreateVideoEncoder="Gagal untuk membuat pengkodean video '%1' (tipe: '%2')" -FailedToStartStream.FailedToGetOBSVideoInfo="Gagal untuk mendapatkan info video obs ketika membuat pengkodean '%1' (tipe: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Gagal untuk mendapatkan info video OBS ketika membuat pengkodean '%1' (type: '%2')" FailedToStartStream.FailedToCreateAudioEncoder="Gagal untuk membuat pengkodean audio" FailedToStartStream.NoRTMPURLInConfig="Konfigurasi tidak mengandung target URL streaming RTMP(S) " FailedToStartStream.FallbackToDefault="Memulai streaming menggunakan %1 gagal; Anda ingin mencoba lagi menggunakan pengaturan mengkodekan tunggal?" @@ -1237,3 +1237,6 @@ MultitrackVideo.IncompatibleSettings.Title="Pengaturan Tidak Cocok" MultitrackVideo.IncompatibleSettings.Text="%1 saat ini tidak cocok dengan:\n\n%2\nUntuk melanjutkan streaming dengan %1, nonaktifkan pengaturan yang tidak cocok:\n\n%3\ndan Mulai Streaming lagi." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Nonaktifkan untuk streaming ini dan Mulai Streaming" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Perbarui Pengaturan dan Mulai Streaming" +MultitrackVideo.IncompatibleSettings.AudioChannels="Saat ini %1 tidak kompatibel dengan set [Audio → Umum → Saluran] ke '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → Umum → Saluran] perlu diatur ke set '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 membutuhkan berbagai pengaturan yang berbeda untuk [Audio → Umum → Saluran]" diff --git a/UI/data/locale/it-IT.ini b/UI/data/locale/it-IT.ini index 2dfea23a9c1347..b5c612e5948f64 100644 --- a/UI/data/locale/it-IT.ini +++ b/UI/data/locale/it-IT.ini @@ -22,15 +22,15 @@ Mixer="Mixer audio" Browse="Sfoglia" DroppedFrames="Fotogrammi persi %1 (%2%)" StudioProgramProjector="Proiettore a schermo intero (programma)" -PreviewProjector="Proiettore Schermo intero (Anteprima)" -SceneProjector="Proiettore Schermo intero (Scena)" +PreviewProjector="Proiettore a schermo intero (anteprima)" +SceneProjector="Proiettore a schermo intero (scena)" SourceProjector="Proiettore a schermo intero (fonte)" StudioProgramWindow="Proiettore in finestra (programma)" -PreviewWindow="Proiettore in finestra (Anteprima)" -SceneWindow="Proiettore in finestra (Scena)" +PreviewWindow="Proiettore in finestra (anteprima)" +SceneWindow="Proiettore in finestra (scena)" SourceWindow="Proiettore in finestra (fonte)" -MultiviewProjector="Vista multipla (Schermo intero)" -MultiviewWindowed="Vista multipla (Finestra)" +MultiviewProjector="Vista multipla (schermo intero)" +MultiviewWindowed="Vista multipla (finestra)" ResizeProjectorWindowToContent="Adatta la finestra al contenuto" Clear="Rimuovi" Revert="Ripristina" @@ -68,7 +68,7 @@ Next="Avanti" Back="Indietro" Defaults="Predefinite" RestoreDefaults="Predefiniti" -HideMixer="Nascondi nel Mixer" +HideMixer="Nascondi nel mixer" TransitionOverride="Sovrascrivi transizione" ShowTransition="Visualizza transizione" HideTransition="Transizione quando invisibile" @@ -82,7 +82,7 @@ VerticalLayout="Layout verticale" Group="Gruppo" DoNotShowAgain="Non mostrare più" Default="(predefinito)" -Calculating="Calcolando..." +Calculating="Calcolo in corso..." Fullscreen="Schermo intero" Windowed="In finestra" RefreshBrowser="Aggiorna" @@ -110,11 +110,11 @@ PluginsFailedToLoad.Text="Non sono stati caricati i seguenti plugin OBS :\n\n%1\ AlreadyRunning.Title="OBS è già in esecuzione" AlreadyRunning.Text="OBS è già in esecuzione! A meno che non si intendeva effettuare questa operazione, chiudere tutte le istanze esistenti di OBS prima di provare a eseguirne una nuova. Se avete OBS impostato per minimizzarsi nell'area di notifica, si prega di controllare per vedere se è ancora in esecuzione." AlreadyRunning.LaunchAnyway="Avvia comunque" -AutoSafeMode.Title="Modalità provvisoria" -AutoSafeMode.Text="OBS durante l'ultima sessione non si è chiuso correttamente.\n\nVuoi avviarlo in modalità provvisoria (verranno disattivati plugin di terze parti, script e websocket)?" -AutoSafeMode.LaunchSafe="Esegui in modalità provvisoria" +AutoSafeMode.Title="Modalità sicura" +AutoSafeMode.Text="OBS durante l'ultima sessione non si è chiuso correttamente.\n\nVorresti avviarlo in modalità sicura (verranno disattivati plugin di terze parti, script e websocket)?" +AutoSafeMode.LaunchSafe="Esegui in Modalità sicura" AutoSafeMode.LaunchNormal="Esegui normalmente" -SafeMode.Restart="Vuoi riavviare OBS in modalità provvisoria (verranno disattivati plugin di terze parti, script, e websocket)?" +SafeMode.Restart="Vuoi riavviare OBS in Modalità sicura (verranno disattivati plugin di terze parti, script, e websocket)?" SafeMode.RestartNormal="Vuoi riavviare OBS in modalità normale?" ChromeOS.Title="Piattaforma non supportata" ChromeOS.Text="OBS sembra essere in esecuzione all'interno di un contenitore ChromeOS. Questa piattaforma non è supportata." @@ -127,7 +127,7 @@ ExtraBrowsers.Info="Aggiungi pannelli dando loro un nome e un URL, quindi clicca ExtraBrowsers.DockName="Nome del Pannello" Auth.Authing.Title="Accesso in corso..." Auth.Authing.Text="Accesso in corso con %1, attendi.." -Auth.AuthFailure.Title="Autenticazione fallita" +Auth.AuthFailure.Title="Autenticazione non riuscita" Auth.AuthFailure.Text="Impossibile eseguire l'accesso con %1:\n\n%2: %3" Auth.InvalidScope.Title="Autenticazione richiesta" Auth.InvalidScope.Text="I requisiti di autenticazione per %1 sono cambiati. Alcune funzionalità potrebbero non essere disponibili." @@ -150,14 +150,14 @@ BandwidthTest.Region="Regione" BandwidthTest.Region.US="Stati Uniti" BandwidthTest.Region.EU="Europa" BandwidthTest.Region.Other="Altro" -Basic.AutoConfig="Configurazione Automatica guidata" +Basic.AutoConfig="Configurazione automatica guidata" Basic.AutoConfig.ApplySettings="Applica impostazioni" Basic.AutoConfig.StartPage="Informazioni sull'utilizzo" Basic.AutoConfig.StartPage.SubTitle="Specifica per cosa desideri utilizzare il programma" Basic.AutoConfig.StartPage.PrioritizeStreaming="Ottimizza per le dirette, le registrazioni sono meno importanti" Basic.AutoConfig.StartPage.PrioritizeRecording="Ottimizza solo per le registrazioni, non faccio dirette" Basic.AutoConfig.StartPage.PrioritizeVirtualCam="Userò solo la fotocamera virtuale" -Basic.AutoConfig.VideoPage="Impostazioni Video" +Basic.AutoConfig.VideoPage="Impostazioni video" Basic.AutoConfig.VideoPage.SubTitle="Specifica le impostazioni video che vorresti utilizzare" Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent="Attuale (%1x%2)" Basic.AutoConfig.VideoPage.BaseResolution.Display="Schermo %1 (%2×%3)" @@ -168,7 +168,7 @@ Basic.AutoConfig.VideoPage.CanvasExplanation="Nota: la risoluzione (di base) del Basic.AutoConfig.StreamPage="Informazioni Diretta" Basic.AutoConfig.StreamPage.SubTitle="Per favore inserisci le informazioni per le dirette" Basic.AutoConfig.StreamPage.ConnectAccount="Connetti account (consigliato)" -Basic.AutoConfig.StreamPage.DisconnectAccount="Disconnetti Account" +Basic.AutoConfig.StreamPage.DisconnectAccount="Disconnetti account" Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title="Vuoi disconnettere l'account?" Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text="Questa modifica verrà applicata immediatamente. Sei sicuro di voler disconnettere il tuo account?" Basic.AutoConfig.StreamPage.GetStreamKey="Ottieni Chiave Stream" @@ -180,8 +180,8 @@ Basic.AutoConfig.StreamPage.Service.ShowAll="Mostra tutti..." Basic.AutoConfig.StreamPage.Service.Custom="Personalizzato..." Basic.AutoConfig.StreamPage.StreamKey="Chiave Stream" Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: inserisci la frase segreta di cifratura.\nRTMP: inserisci la chiave fornita dal servizio.\nSRT: inserisci lo streamid se il servizio ne usa una." -Basic.AutoConfig.StreamPage.EncoderKey="Chiave Codificatore" -Basic.AutoConfig.StreamPage.BearerToken="Tohen Bearer" +Basic.AutoConfig.StreamPage.EncoderKey="Chiave del codificatore" +Basic.AutoConfig.StreamPage.BearerToken="Portatore del token" Basic.AutoConfig.StreamPage.ConnectedAccount="Account collegato" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Calcola la velocità in bit tramite un test di larghezza di banda (richiede alcuni minuti)" Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferisci la codifica hardware" @@ -195,14 +195,14 @@ Basic.AutoConfig.TestPage.SubTitle.Complete="Test completato" Basic.AutoConfig.TestPage.TestingBandwidth="Esecuzione del test della larghezza di banda in corso, potrebbe richiedere alcuni minuti..." Basic.AutoConfig.TestPage.TestingBandwidth.NoOutput="Nessun output trovato per il protocollo di questo servizio" Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Connessione a: %1..." -Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Impossibile connettersi a qualunque server, per favore verifica la tua connessione internet e riprova." +Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Impossibile connettersi a qualunque server, verifica la tua connessione internet e riprova." Basic.AutoConfig.TestPage.TestingBandwidth.Server="Test della larghezza di banda per: %1" Basic.AutoConfig.TestPage.TestingStreamEncoder="Test della codifica delle dirette, può richiedere circa un minuto..." Basic.AutoConfig.TestPage.TestingRecordingEncoder="Test della codifica di registrazione, può richiedere circa un minuto..." Basic.AutoConfig.TestPage.TestingRes.Fail="Impossibile avviare la codifica" Basic.AutoConfig.TestPage.TestingRes.Resolution="Test della risoluzione %1×%2 a %3 FPS..." Basic.AutoConfig.TestPage.Result.StreamingEncoder="Codifica per le dirette" -Basic.AutoConfig.TestPage.Result.RecordingEncoder="Codifica per le registrazioni" +Basic.AutoConfig.TestPage.Result.RecordingEncoder="Codificatore per la registrazione" Basic.AutoConfig.TestPage.Result.Header="Il programma ha rilevato che queste impostazioni sono le più adatte per questo dispositivo:" Basic.AutoConfig.TestPage.Result.Footer="Per utilizzare queste impostazioni, fai clic su «Applica le impostazioni». Per riprovare a configurare la procedura guidata, fai clic su «Indietro». Per configurare manualmente le impostazioni, fai clic su «Annulla» e apri le «Impostazioni»." Basic.AutoConfig.Info="La configurazione guidata automatica determinerà le migliori impostazioni in base alle specifiche del computer e alla velocità di Internet." @@ -218,7 +218,7 @@ Basic.Stats.MissedFrames="Fotogrammi persi (lag del rendering)" Basic.Stats.Output.Stream="Diretta" Basic.Stats.Output.Recording="Registrazione" Basic.Stats.Status="Stato" -Basic.Stats.Status.Recording="Registrazione in corso" +Basic.Stats.Status.Recording="Registrazione" Basic.Stats.Status.Live="IN DIRETTA" Basic.Stats.Status.Reconnecting="Riconnessione in corso" Basic.Stats.Status.Inactive="Inattiva" @@ -229,7 +229,7 @@ Basic.Stats.Bitrate="Velocità in bit" Basic.Stats.DiskFullIn="Disco pieno in (appross.)" Basic.Stats.ResetStats="Ripristina le statistiche" ResetUIWarning.Title="Sei sicuro di voler ripristinare l'interfaccia utente?" -ResetUIWarning.Text="Ripristinndoa l'interfaccia utente verranno nascosti i pannelli aggiuntivi.\nSe vuoi che siano nuovamente visibili dovrai visualizzare questi pannelli dal menu 'Pannelli' \n\nSei sicuro di voler ripristinare l'interfaccia utente?" +ResetUIWarning.Text="La reimpostazione dell'interfaccia utente nasconderà i pannelli aggiuntivi.\nSe vuoi che siano nuovamente visibili dovrai mostrare questi pannelli dal menu 'Pannelli'.\n\nSei sicuro di voler ripristinare l'interfaccia utente?" Updater.Title="Nuovo aggiornamento disponibile" Updater.Text="È disponibile un nuovo aggiornamento:" Updater.UpdateNow="Aggiorna ora" @@ -289,7 +289,7 @@ Undo.Balance.Change="Bilanciamento audio su '%1'" Undo.SyncOffset.Change="Cambio sincronizzazione audio su '%1'" Undo.MonitoringType.Change="Cambia monitoraggio audio su '%1'" Undo.Mixers.Change="Cambia mixer audio su '%1'" -Undo.ForceMono.On="Abilita Forza Mono su '%1'" +Undo.ForceMono.On="Abilita Forza mono su '%1'" Undo.ForceMono.Off="Disabilita Mono forzato su '%1'" Undo.Properties="Cambio proprietà su '%1'" Undo.Scene.Duplicate="Duplica la scena '%1'" @@ -307,7 +307,7 @@ Undo.PasteSourceRef="Incolla riferimenti fonte in '%1'" Undo.GroupItems="Raggruppa elementi in '%1'" TransitionNameDlg.Text="Inserisci il nome della transizione" TransitionNameDlg.Title="Nome della transizione" -TitleBar.SafeMode="MODALITA' PROVVISORIA" +TitleBar.SafeMode="MODALITÀ SICURA" TitleBar.PortableMode="Modalità portatile" TitleBar.Profile="Profilo" TitleBar.Scenes="Scene" @@ -331,7 +331,7 @@ ConfirmRemove.TextMultiple="Sei sicuro di voler rimuovere %1 elementi?" ConfirmReset.Title="Ripristina proprietà" ConfirmReset.Text="Sei sicuro di voler ripristinare i valori delle proprietà ai loro valori predefiniti?" Output.StartStreamFailed="Impossibile avviare la diretta" -Output.StartRecordingFailed="Impossibile avviare la registrazione" +Output.StartRecordingFailed="Avvio della registrazione non riuscito" Output.StartReplayFailed="Impossibile avviare il buffer di replay" Output.StartVirtualCamFailed="Impossibile avviare la videocamera virtuale" Output.StartFailedGeneric="Impossibile creare il file di uscita. Controlla il log per i dettagli.\n\nNota: se utilizzi la codifica NVENC o AMD, assicurati che i driver video siano aggiornati all'ultima versione." @@ -346,15 +346,15 @@ Output.ConnectFail.Error="Si è verificato un errore non previsto durante la con Output.ConnectFail.Disconnected="Disconnesso dal server." Output.StreamEncodeError.Title="Errore di codifica" Output.StreamEncodeError.Msg="Si è verificato un errore di codifica durante lo streaming." -Output.StreamEncodeError.Msg.LastError="Durante lo streaming si è verificato un errore dell'encoder :

%1" -Output.RecordFail.Title="Impossibile avviare la registrazione" -Output.RecordFail.Unsupported="Il formato del file di uscita non è supportato o non supporta più di una traccia audio. Per favore, controlla le tue impostazioni e riprova." +Output.StreamEncodeError.Msg.LastError="Durante lo streaming si è verificato un errore del codificatore:

%1" +Output.RecordFail.Title="Avvio della registrazione non riuscito" +Output.RecordFail.Unsupported="Il formato del file di uscita non è supportato o non supporta più di una traccia audio. Controlla le tue impostazioni e riprova." Output.RecordNoSpace.Title="Spazio su disco insufficiente" -Output.RecordNoSpace.Msg="Non c'è abbastanza spazio su disco per continuare la registrazione." +Output.RecordNoSpace.Msg="Non c'è abbastanza spazio sul disco per continuare la registrazione." Output.RecordError.Title="Errore di registrazione" Output.RecordError.Msg="Si è verificato un errore non specificato durante la registrazione." Output.RecordError.EncodeErrorMsg="Si è verificato un errore di codifica durante la registrazione." -Output.RecordError.EncodeErrorMsg.LastError="Durante la registrazione si è verificato un errore dell'encoder :

%1" +Output.RecordError.EncodeErrorMsg.LastError="Si è verificato un errore del codificatore durante la registrazione:

%1" Output.BadPath.Title="Percorso del file non valido" Output.BadPath.Text="Impossibile aprire il percorso di registrazione configurato. Controlla il percorso di registrazione in Impostazioni → Output → Recording." Output.NoBroadcast.Title="Nessuna trasmissione configurata" @@ -367,7 +367,7 @@ LogReturnDialog.Description.Crash="Il tuo rapporto sui crash è stato caricato. LogReturnDialog.CopyURL="Copia l'URL" LogReturnDialog.AnalyzeURL="Analizza" LogReturnDialog.ErrorUploadingLog="Errore nel caricamento del file di log" -Remux.SourceFile="Registrazioni di OBS" +Remux.SourceFile="Registrazione di OBS" Remux.TargetFile="File di destinazione" Remux.Remux="Converti" Remux.Stop="Interrompi la conversione" @@ -383,7 +383,7 @@ Remux.FileExistsTitle="Il file di destinazione è già presente" Remux.FileExists="I seguenti file di destinazione sono già presenti. Vuoi sostituirli?" Remux.ExitUnfinishedTitle="Conversione in corso" Remux.ExitUnfinished="La conversione non è finita, interromperla ora potrebbe rendere il file di destinazione inutilizzabile.\nSei sicuro di voler interrompere la conversione?" -Remux.HelpText="Trascina i file da convertire in questa finestra, oppure seleziona una casella «Registrazioni di OBS» vuota per sfogliare i file." +Remux.HelpText="Trascina i file in questa finestra per convertirli, oppure seleziona una cella «Registrazione di OBS» vuota per cercare i file." Remux.NoFilesAddedTitle="Nessun remuxing per i file aggiunti" Remux.NoFilesAdded="Nessun file è stato aggiunto nel remux. Scelgi una cartella contenente uno o più file video." MissingFiles="File non trovati" @@ -411,7 +411,7 @@ MacPermissions.Description.OpenDialog="È possibile riaprire questa finestra di MacPermissions.AccessGranted="Accesso concesso" MacPermissions.RequestAccess="Richiesta accesso" MacPermissions.OpenPreferences="Apri impostazioni %1" -MacPermissions.Item.ScreenRecording="Registrazione schermo" +MacPermissions.Item.ScreenRecording="Registrazione dello schermo" MacPermissions.Item.ScreenRecording.Details="OBS richiede questo autorizzazione per poter catturare lo schermo." MacPermissions.Item.Camera="Fotocamera" MacPermissions.Item.Camera.Details="Questa autorizzazione è necessaria per catturare contenuti da una webcam o da una scheda di cattura." @@ -445,7 +445,7 @@ BlendingMode.Normal="Normale" BlendingMode.Additive="Aggiungi" BlendingMode.Subtract="Sottrai" BlendingMode.Screen="Schermo" -BlendingMode.Multiply="Moltiplicare" +BlendingMode.Multiply="Moltiplica" BlendingMode.Lighten="Illuminato" BlendingMode.Darken="Scuro" Deinterlacing="Deinterlacciamento" @@ -457,7 +457,7 @@ VolControl.SliderMuted="Barra del volume per «%1»: (attualmente muto)" VolControl.Mute="Silenzia «%1'»" VolControl.Properties="Proprietà di «%1»" VolControl.UnassignedWarning.Title="Sorgente audio non assegnata" -VolControl.UnassignedWarning.Text="\"%1\" non è assegnato a nessuna traccia audio e non sarà udibile negli stream o nelle registrazioni.\n\nPer assegnare una sorgente audio ad una traccia, apri 'Proprietà audio avanzate' tramite il menu di scelta rapida o usa il pulsante a forma di ingranaggio nella barra strumenti del mixer." +VolControl.UnassignedWarning.Text="\"%1\" non è assegnato ad alcuna traccia audio e non sarà udibile nelle trasmissioni o nelle registrazioni.\n\nPer assegnare una sorgente audio a una traccia, apri 'Proprietà avanzate dell'audio' tramite il menu dal tasto destro o il pulsante ingranaggio nella \"barra degli strumenti\" del mixer." Basic.Main.AddSceneDlg.Title="Aggiunta di una scena" Basic.Main.AddSceneDlg.Text="Inserisci il nome della scena" Basic.Main.DefaultSceneName.Text="Scena %1" @@ -509,8 +509,8 @@ Basic.StatusBar.DelayStoppingIn="Ritardo (interruzione dopo %1 sec)" Basic.StatusBar.DelayStartingStoppingIn="Ritardo (interruzione dopo %1 sec, avvio dopo %2 sec)" Basic.StatusBar.RecordingSavedTo="Registrazione salvata in '%1'" Basic.StatusBar.ReplayBufferSavedTo="Buffer di replay salvato in '%1'" -Basic.StatusBar.ScreenshotSavedTo="Screenshot salvato in '%1'" -Basic.StatusBar.AutoRemuxedTo="Registrazione automaticamente trasformata a '%1'" +Basic.StatusBar.ScreenshotSavedTo="Schermata salvata in '%1'" +Basic.StatusBar.AutoRemuxedTo="Registrazione convertita automaticamente in '%1'" Basic.Filters="Filtri" Basic.Filters.AsyncFilters="Filtri audio/video" Basic.Filters.AudioFilters="Filtri audio" @@ -534,10 +534,10 @@ Basic.TransformWindow.BoundsWidth="Larghezza Casella di Delimitazione" Basic.TransformWindow.BoundsHeight="Altezza Casella di Delimitazione" Basic.TransformWindow.CropToBounds="Ritaglia nel riquadro di delimitazione" Basic.TransformWindow.Crop="Ritaglia" -Basic.TransformWindow.CropLeft="Ritaglia A Sinistra" -Basic.TransformWindow.CropRight="Ritaglia A Destra" -Basic.TransformWindow.CropTop="Ritaglia In Alto" -Basic.TransformWindow.CropBottom="Ritaglia In Basso" +Basic.TransformWindow.CropLeft="Ritaglia a sinistra" +Basic.TransformWindow.CropRight="Ritaglia a destra" +Basic.TransformWindow.CropTop="Ritaglia in alto" +Basic.TransformWindow.CropBottom="Ritaglia in basso" Basic.TransformWindow.Alignment.TopLeft="In alto a sinistra" Basic.TransformWindow.Alignment.TopCenter="In alto al centro" Basic.TransformWindow.Alignment.TopRight="In alto a destra" @@ -567,20 +567,21 @@ Basic.Main.Connecting="Connessione in corso..." Basic.Main.StartRecording="Avvia la registrazione" Basic.Main.StartReplayBuffer="Avvia il buffer di replay" Basic.Main.SaveReplay="Salva il replay" -Basic.Main.StartStreaming="Avvia la diretta" +Basic.Main.StartStreaming="Avvia la trasmissione" Basic.Main.StartBroadcast="Vai in diretta" Basic.Main.StartVirtualCam="Avvia la fotocamera virtuale" Basic.Main.StopRecording="Termina la registrazione" Basic.Main.PauseRecording="Sospendi la registrazione" Basic.Main.UnpauseRecording="Riprendi la registrazione" -Basic.Main.SplitFile="Dividi file registrazione" -Basic.Main.AddChapterMarker="Aggiungi marcatore del capitolo" -Basic.Main.StoppingRecording="Terminazione della registrazione in corso..." +Basic.Main.SplitFile="Dividi il file registrazione" +Basic.Main.AddChapterMarker="Aggiungi il marcatore del capitolo (solamente MP4 ibrido)" +Basic.Main.StoppingRecording="Arresto della registrazione..." Basic.Main.StopReplayBuffer="Termina il buffer di replay" Basic.Main.StoppingReplayBuffer="Terminazione del buffer di replay in corso..." Basic.Main.SetupBroadcast="Gestisci trasmissione" Basic.Main.StopStreaming="Termina la diretta" Basic.Main.StopBroadcast="Ferma trasmissione" +Basic.Main.AutoStopEnabled="(Stop automatico)" Basic.Main.StoppingStreaming="Terminazione della diretta in corso..." Basic.Main.ForceStopStreaming="Termina la diretta (annulla ritardo)" Basic.Main.ShowContextBar="Mostra la barra degli strumenti delle fonti" @@ -600,10 +601,10 @@ Basic.VCam.OutputSelection.NoSelection="Nessuna selezione per questa tipologia d Basic.VCam.RestartWarning="Per applicare la modifica sarà riavviata la camera virtuale " Basic.MainMenu.File.Export="&Esporta" Basic.MainMenu.File.Import="&Importa" -Basic.MainMenu.File.ShowRecordings="Visualizza ®istrazioni" +Basic.MainMenu.File.ShowRecordings="Mostra le ®istrazioni" Basic.MainMenu.File.Remux="&Converti le registrazioni" Basic.MainMenu.File.Settings="Impo&stazioni" -Basic.MainMenu.File.ShowSettingsFolder="Visualizza la cartella delle impostazioni" +Basic.MainMenu.File.ShowSettingsFolder="Mostra la cartella delle impostazioni" Basic.MainMenu.File.ShowProfileFolder="Mostra la cartella dei profili" Basic.MainMenu.File.ShowMissingFiles="Controlla i file mancanti" Basic.MainMenu.File.Exit="&Esci" @@ -638,7 +639,7 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Sposta in &fondo" Basic.MainMenu.Edit.AdvAudio="Proprietà &audio avanzate" Basic.MainMenu.View="&Visualizza" Basic.MainMenu.View.Toolbars="Barre degli s&trumenti" -Basic.MainMenu.View.ListboxToolbars="Pannelli barre strumenti" +Basic.MainMenu.View.ListboxToolbars="Barre degli strumenti dei pannelli" Basic.MainMenu.View.ContextBar="Barra degli strumenti delle fonti" Basic.MainMenu.View.SourceIcons="&Icone fonti" Basic.MainMenu.View.StatusBar="Barra di &stato" @@ -672,7 +673,7 @@ Basic.MainMenu.Help.Logs.ViewCurrentLog="&Visualizza il file di log attuale" Basic.MainMenu.Help.ReleaseNotes="Note di rilascio" Basic.MainMenu.Help.CheckForUpdates="Controlla gli aggiornamenti" Basic.MainMenu.Help.Repair="Controlla integrità file" -Basic.MainMenu.Help.RestartSafeMode="Riavvia in modalità provvisoria" +Basic.MainMenu.Help.RestartSafeMode="Riavvia in modalità sicura" Basic.MainMenu.Help.RestartNormal="Riavvia in modalità normale" Basic.MainMenu.Help.CrashLogs="&Registro degli arresti anomali" Basic.MainMenu.Help.CrashLogs.ShowLogs="Vi&sualizza i registri dei crash" @@ -700,9 +701,9 @@ Basic.Settings.General.Projectors="Proiettori" Basic.Settings.General.HideProjectorCursor="Nascondi il cursore sopra i proiettori" Basic.Settings.General.ProjectorAlwaysOnTop="I proiettori devono essere sempre in primo piano?" Basic.Settings.General.Snapping="Aggancio delle fonti" -Basic.Settings.General.ScreenSnapping="Aggancia le fonti quandi sono vicine ai bordi dello schermo" -Basic.Settings.General.CenterSnapping="Aggancia le fonti quandi sono vicine al centro orizzontale e verticale" -Basic.Settings.General.SourceSnapping="Aggancia le fonti quandi sono vicine ad altre fonti" +Basic.Settings.General.ScreenSnapping="Aggancia le fonti quando sono vicine ai bordi dello schermo" +Basic.Settings.General.CenterSnapping="Aggancia le fonti quando sono vicine al centro orizzontale e verticale" +Basic.Settings.General.SourceSnapping="Aggancia le fonti quando sono vicine ad altre fonti" Basic.Settings.General.SnapDistance="Sensibilità dell'aggancio" Basic.Settings.General.SpacingHelpers="Visualizza guide di allineamento pixel" Basic.Settings.General.RecordWhenStreaming="Registra automaticamente quando sei in diretta" @@ -723,11 +724,11 @@ Basic.Settings.General.AutomaticCollectionSearch="Cerca luoghi conosciuti per le Basic.Settings.General.SwitchOnDoubleClick="Esegui la transizione quando fai doppio clic sulla scena" Basic.Settings.General.StudioPortraitLayout="Attiva il layout verticale" Basic.Settings.General.TogglePreviewProgramLabels="Mostra le etichette di anteprima/programma" -Basic.Settings.General.Multiview="Visualizzazione multipla" +Basic.Settings.General.Multiview="Vista multipla" Basic.Settings.General.Multiview.MouseSwitch="Fai clic per passare da una scena all'altra" Basic.Settings.General.Multiview.DrawSourceNames="Visualizza i nomi delle scene" Basic.Settings.General.Multiview.DrawSafeAreas="Evidenzia le aree sicure (EBU R 95)" -Basic.Settings.General.MultiviewLayout="Disposizione della visualizzazione multipla" +Basic.Settings.General.MultiviewLayout="Disposizione della vista multipla" Basic.Settings.General.MultiviewLayout.Horizontal.Top="Orizzontale, in alto (8 scene)" Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Orizzontale, in basso (8 scene)" Basic.Settings.General.MultiviewLayout.Vertical.Left="Verticale, a sinistra (8 scene)" @@ -758,13 +759,13 @@ Basic.Settings.Stream.TTVAddon="Plugins per la chat di Twitch" Basic.Settings.Stream.TTVAddon.None="Nessuno" Basic.Settings.Stream.TTVAddon.Both="BetterTTV e FrankerFaceZ" Basic.Settings.Stream.MissingSettingAlert="Configurazione flusso mancante" -Basic.Settings.Stream.StreamSettingsWarning="Apri Impostazioni" +Basic.Settings.Stream.StreamSettingsWarning="Apri le impostazioni" Basic.Settings.Stream.MissingUrlAndApiKey="URL e Codice delle dirette sono mancanti.\n\nApri le impostazioni per inserire URL e codice delle dirette nella scheda 'stream'." Basic.Settings.Stream.MissingUrl="URL dello stream mancante.\n\nApri le impostazione per inserire l'URL nella scheda \"Stream\"." Basic.Settings.Stream.MissingStreamKey="Chiave stream mancante.\n\nApri le impostazioni per inserire la chiave stream nella scheda \"Stream\"." Basic.Settings.Stream.IgnoreRecommended="Ignora opzioni raccomandate di streaming" Basic.Settings.Stream.IgnoreRecommended.Warn.Title="Ignora le impostazioni raccomandate" -Basic.Settings.Stream.IgnoreRecommended.Warn.Text="Attenzione: ignorare le limitazioni del servizio può degradare la qualità o impedire completamente lo streaming.\n\nContinuare?" +Basic.Settings.Stream.IgnoreRecommended.Warn.Text="Attenzione: ignorare le limitazioni del servizio può degradare la qualità della trasmissione o impedirla completamente.\n\nContinuare?" Basic.Settings.Stream.Recommended.MaxVideoBitrate="Bitrate video massimo: %1 kbps" Basic.Settings.Stream.Recommended.MaxAudioBitrate="Bitrate audio massimo: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Risoluzione massima: %1" @@ -776,6 +777,11 @@ Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Larghezza di banda Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automatica" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Numero massimo di traccie video" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automatico" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Abilita la scrittura della diretta su un file FLV (utilizza impostazioni di registrazione semplici)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Sovrascrivi la configurazione (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Abilita la sovrascrittura della configurazione" +Basic.Settings.Stream.MultitrackVideoLabel="Video multi traccia" +Basic.Settings.Stream.AdvancedOptions="Opzioni avanzate" Basic.Settings.Output="Uscita" Basic.Settings.Output.Format="Formato di registrazione" Basic.Settings.Output.Format.MKV="Video Matroska (.mkv)" @@ -783,7 +789,7 @@ Basic.Settings.Output.Format.hMP4="MP4 ibrido [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 frammentato (.mp4)" Basic.Settings.Output.Format.fMOV="MOV frammentato (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Un file MOV frammentato scrive la registrazione in pezzi e non richiede la stessa finalizzazione dei file MOV tradizionali.\nQuesto assicura che il file rimanga riproducibile anche se la scrittura su disco viene interrotta, ad esempio, a causa di un BSOD o di una mancanza alimentazione.\n\nIl file frammentato porebbe non essere compatibile con tutti i programmi di riproduzione/modifica.\nSe necessario usa 'File' → 'Remux registrazioni' per convertire il file in un formato più compatibile." -Basic.Settings.Output.Format.TT.fragmented_mp4="Un file MP4 frammentato scrive la registrazione in pezzi e non richiede la stessa finalizzazione dei file MP4 tradizionali.\nQuesto assicura che il file rimanga riproducibile anche se la scrittura su disco viene interrotta, ad esempio, a causa di un BSOD o di una mancanza alimentazione.\n\nIl file frammentato potrebbe non essere compatibile con tutti i programmi di riproduzione/modifica.\nSe necessario usa 'File' → 'Remux registrazioni' per convertire il file in un formato più compatibile." +Basic.Settings.Output.Format.TT.fragmented_mp4="Un file MP4 frammentato scrive la registrazione in pezzi e non richiede la stessa finalizzazione dei file MP4 tradizionali.\nQuesto assicura che il file rimanga riproducibile anche se la scrittura su disco viene interrotta, per esempio, a causa di un BSOD o di una mancanza di alimentazione.\n\nIl file frammentato potrebbe non essere compatibile con tutti i programmi di riproduzione/modifica.\nSe necessario usa 'File' → 'Remux registrazioni' per convertire il file in un formato più compatibile." Basic.Settings.Output.Encoder.Video="Encoder video" Basic.Settings.Output.Encoder.Audio="Encoder audio" Basic.Settings.Output.SelectDirectory="Seleziona la cartella di registrazione" @@ -802,8 +808,8 @@ Basic.Settings.Output.ReplayBuffer.EstimateTooLarge="Attenzione: l'uso stimato d Basic.Settings.Output.ReplayBuffer.EstimateUnknown="Impossibile stimare la memoria utilizzata. Imposta un limite massimo di memoria." Basic.Settings.Output.ReplayBuffer.Prefix="Prefisso del nome dei file del buffer di replay" Basic.Settings.Output.ReplayBuffer.Suffix="Suffisso" -Basic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg="Il buffer di riproduzione non può essere usato quando il tipo di registrazione è impostato su Destinazione personalizzata (FFmpeg)." -Basic.Settings.Output.Simple.SavePath="Percorso di registrazione" +Basic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg="Il buffer di riproduzione non può essere usato quando il tipo di registrazione è impostato su Uscita personalizzata (FFmpeg)." +Basic.Settings.Output.Simple.SavePath="Percorso della registrazione" Basic.Settings.Output.Simple.RecordingQuality="Qualità della registrazione" Basic.Settings.Output.Simple.RecordingQuality.Stream="La stessa della diretta" Basic.Settings.Output.Simple.RecordingQuality.Small="Alta qualità, dimensioni dei file medie" @@ -812,13 +818,13 @@ Basic.Settings.Output.Simple.RecordingQuality.Lossless="Senza perdita di qualit Basic.Settings.Output.Simple.Warn.VideoBitrate="Attenzione: il bitrate video in streaming sarà impostato a %1, che è il limite superiore per il servizio di streaming corrente." Basic.Settings.Output.Simple.Warn.AudioBitrate="Attenzione: il bitrate audio in streaming sarà impostato a %1, che è il limite superiore per il servizio di streaming corrente." Basic.Settings.Output.Simple.Warn.CannotPause="Attenzione: Le registrazioni non possono essere sospese se la qualità di registrazione è impostata su \"La stessa della diretta\"." -Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Attenzione: il formato di registrazione attualmente selezionato non è compatibile con l'encoder per lo streaming selezionati." -Basic.Settings.Output.Simple.Warn.Encoder="Attenzione: registrare con una codifica software a una qualità diversa dalla diretta richiederà un utilizzo aggiuntivo della CPU se esegui dirette e registri allo stesso tempo." +Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Attenzione: il formato di registrazione attualmente selezionato è incompatibile con il/i codificatore/i selezionato/i per la diretta." +Basic.Settings.Output.Simple.Warn.Encoder="Attenzione: registrare con un codificatore software a una qualità diversa dalla diretta richiederà un utilizzo aggiuntivo della CPU se esegui dirette e registri allo stesso tempo." Basic.Settings.Output.Simple.Warn.Lossless="Attenzione: la qualità senza perdita di dati genera file di dimensioni enormi! La qualità senza perdite può occupare fino a 7 gigabyte di spazio sul disco al minuto a risoluzioni e framerate elevati. La modalità senza perdita dati non è consigliata per registrazioni lunghe, a meno che non si disponga di una quantità di spazio su disco molto grande. Il buffer di riproduzione non è disponibile quando si utilizza la qualità senza perdita dati." -Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sei sicuro di volere utilizzare la qualità lossless?" -Basic.Settings.Output.Simple.Warn.Lossless.Title="Avviso sulla qualità lossless!" +Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sei sicuro di volere utilizzare la qualità senza perdita?" +Basic.Settings.Output.Simple.Warn.Lossless.Title="Avviso sulla qualità senza perdita!" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264, preset con basso utilizzo della CPU, aumenta le dimensioni dei file)" -Basic.Settings.Output.Simple.Codec.AAC.Default="AAC (Standaard)" +Basic.Settings.Output.Simple.Codec.AAC.Default="AAC (Predefinito)" Basic.Settings.Output.Simple.TwitchVodTrack="Incrocia la traccia VOD (usa traccia 2)" Basic.Settings.Output.Simple.RecAudioTrack="Traccia audio" Basic.Settings.Output.Warn.EnforceResolutionFPS.Title="Risoluzione/framerate incompatibili" @@ -834,9 +840,9 @@ Basic.Settings.Output.RetryDelay="Ritardo dei tentativi" Basic.Settings.Output.MaxRetries="Tentativi massimi" Basic.Settings.Output.Advanced="Abilita impostazioni personalizzate encoder (avanzate)" Basic.Settings.Output.EncoderPreset="Preset della codifica" -Basic.Settings.Output.EncoderPreset.ultrafast="%1 (basso uso CPU, qualità minima)" -Basic.Settings.Output.EncoderPreset.veryfast="%1 (predefinito) (uso medio CPU, qualità standard)" -Basic.Settings.Output.EncoderPreset.fast="%1 (uso CPU elevato, alta qualità)" +Basic.Settings.Output.EncoderPreset.ultrafast="%1 (utilizzo della CPU basso, qualità minima)" +Basic.Settings.Output.EncoderPreset.veryfast="%1 (predefinito) (utilizzo della CPU medio, qualità standard)" +Basic.Settings.Output.EncoderPreset.fast="%1 (utilizzo della CPU elevato, alta qualità)" Basic.Settings.Output.CustomEncoderSettings="Parametri della codifica personalizzati" Basic.Settings.Output.CustomMuxerSettings="Parametri del muxer personalizzati" Basic.Settings.Output.NoSpaceFileName="Genera il nome dei file senza spazi" @@ -844,7 +850,7 @@ Basic.Settings.Output.Adv.Rescale="Riscala l'uscita" Basic.Settings.Output.Adv.Rescale.Disabled="Disattivato" Basic.Settings.Output.Adv.AudioTrack="Traccia audio" Basic.Settings.Output.Adv.Streaming="Diretta" -Basic.Settings.Output.Adv.Streaming.Settings="Impostazioni streaming" +Basic.Settings.Output.Adv.Streaming.Settings="Impostazioni della trasmissione" Basic.Settings.Output.Adv.Audio.Track1="Traccia 1" Basic.Settings.Output.Adv.Audio.Track2="Traccia 2" Basic.Settings.Output.Adv.Audio.Track3="Traccia 3" @@ -852,9 +858,9 @@ Basic.Settings.Output.Adv.Audio.Track4="Traccia 4" Basic.Settings.Output.Adv.Audio.Track5="Traccia 5" Basic.Settings.Output.Adv.Audio.Track6="Traccia 6" Basic.Settings.Output.Adv.TwitchVodTrack="Traccia VOD Twitch" -Basic.Settings.Output.Adv.Encoder="Impostazioni encoder" -Basic.Settings.Output.Adv.Recording="Registrazioni" -Basic.Settings.Output.Adv.Recording.Settings="Impostazioni registrazione" +Basic.Settings.Output.Adv.Encoder="Impostazioni del codificatore" +Basic.Settings.Output.Adv.Recording="Registrazione" +Basic.Settings.Output.Adv.Recording.Settings="Impostazioni della registrazione" Basic.Settings.Output.Adv.Recording.RecType="Tipo di registrazione" Basic.Settings.Output.Adv.Recording.Type="Tipo" Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Uscita personalizzata (FFmpeg)" @@ -895,17 +901,17 @@ Screenshot.Scene="Screenshot (scena)" Screenshot.Source="Schermata (fonte)" FilenameFormatting.TT.CCYY="Anno, 4 cifre" FilenameFormatting.TT.YY="Anno, ultime due cifre (00-99)" -FilenameFormatting.TT.MM="Mese come numero decimale (01-12)" -FilenameFormatting.TT.DD="Giorno del mese, con riempimento con zero (01-31)" +FilenameFormatting.TT.MM="Mese come un numero decimale (01-12)" +FilenameFormatting.TT.DD="Giorno del mese, riempito con zeri (01-31)" FilenameFormatting.TT.hh="Ora in formato 24h (00-23)" FilenameFormatting.TT.mm="Minuti (00-59)" FilenameFormatting.TT.ss="Secondi (00-59)" FilenameFormatting.TT.Percent="Un segno %" -FilenameFormatting.TT.a="Nome abbreviato giorno della settimana" -FilenameFormatting.TT.A="Nome completo giorno della settimana" +FilenameFormatting.TT.a="Nome abbreviato del giorno della settimana" +FilenameFormatting.TT.A="Nome completo del giorno della settimana" FilenameFormatting.TT.b="Nome abbreviato del mese" FilenameFormatting.TT.B="Nome del mese completo" -FilenameFormatting.TT.d="Giorno dle mese, riemptimento zero (01-31)" +FilenameFormatting.TT.d="Giorno del mese, riempito con zeri (01-31)" FilenameFormatting.TT.H="Ora in formato 24h (00-23)" FilenameFormatting.TT.I="Ora in formato 12h (01-12)" FilenameFormatting.TT.m="Mese come numero decimale (01-12)" @@ -916,9 +922,9 @@ FilenameFormatting.TT.S="Secondi (00-59)" FilenameFormatting.TT.y="Anno, ultime due cifre (00-99)" FilenameFormatting.TT.Y="Anno" FilenameFormatting.TT.z="ISO 8601 offset dall'ora UTC nel fuso orario" -FilenameFormatting.TT.Z="Nome fuso orario o abbreviazione" +FilenameFormatting.TT.Z="Nome del fuso orario o abbreviazione" FilenameFormatting.TT.FPS="Fotogrammi al secondo" -FilenameFormatting.TT.CRES="Risoluzione di base (canvas" +FilenameFormatting.TT.CRES="Risoluzione di base (riquadro)" FilenameFormatting.TT.ORES="Risoluzione in uscita (scalato)" FilenameFormatting.TT.VF="Formato video" Basic.Settings.Video.Adapter="Adattatore video" @@ -993,8 +999,8 @@ Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Superiore al normal Basic.Settings.Advanced.General.ProcessPriority.Normal="Normale" Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Inferiore al normale" Basic.Settings.Advanced.General.ProcessPriority.Idle="Bassa" -Basic.Settings.Advanced.FormatWarning="Attenzione: formati di colore diversi da NV12/P010 sono principalmente destinati alla registrazione, e non sono raccomandati durante lo streaming. A causa della conversione del formato colore lo streaming potrebbe comportare un maggiore uso della CPU." -Basic.Settings.Advanced.FormatWarningPreciseSdr="Avviso: i formati ad alta precisione sono più comunemente usati con gli spazi colore HDR." +Basic.Settings.Advanced.FormatWarning="Attenzione: i formati di colore diversi da NV12/P010 sono principalmente destinati alla registrazione e non sono raccomandati durante la trasmissione. A causa della conversione del formato colore la trasmissione potrebbe comportare un maggiore utilizzo della CPU." +Basic.Settings.Advanced.FormatWarningPreciseSdr="Avviso: i formati ad alta precisione sono più comunemente usati con gli spazi dei colori HDR." Basic.Settings.Advanced.FormatWarning2100="Attenzione: Rec. 2100 dovrebbe usare un formato con più precisione." Basic.Settings.Advanced.Video.ColorFormat="Formato colore" Basic.Settings.Advanced.Video.ColorFormat.NV12="NV12 (8bit, 4.2:0, 2 piani)" @@ -1023,7 +1029,7 @@ Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilizzo della memoria stimato: Basic.Settings.Advanced.Network="Rete" Basic.Settings.Advanced.Network.Disabled="Il protocollo di streaming attualmente selezionato non supporta la modifica delle impostazioni di rete." Basic.Settings.Advanced.Network.BindToIP="Associa all'indirizzo IP" -Basic.Settings.Advanced.Network.IPFamily="Famiglia IP" +Basic.Settings.Advanced.Network.IPFamily="Famiglia dell'IP" Basic.Settings.Advanced.Network.EnableNewSocketLoop="Abilita ottimizzazioni di rete" Basic.Settings.Advanced.Network.EnableLowLatencyMode="Abilita pacing TCP" Basic.Settings.Advanced.Network.TCPPacing.Tooltip="Tentativi di rendere l'output RTMP compatibile ad altre applicazioni sensibili alla latenza sulla rete regolando il tasso di trasmissione.\nPuò aumentare il rischio di fotogrammi persi su connessioni instabili." @@ -1034,7 +1040,7 @@ Basic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus="Disattiva le scorciato Basic.Settings.Advanced.AutoRemux="Remuxa automaticamente in %1" Basic.Settings.Advanced.AutoRemux.MP4="(registra in mkv)" Basic.AdvAudio="Proprietà audio avanzate" -Basic.AdvAudio.ActiveOnly="Solo Fonti Attive" +Basic.AdvAudio.ActiveOnly="Solo fonti attive" Basic.AdvAudio.Name="Nome" Basic.AdvAudio.VolumeSource="Volume per «%1»" Basic.AdvAudio.MonoSource="Mix in mono per «%1»" @@ -1102,10 +1108,10 @@ OutputWarnings.CodecIncompatible="L'encoder audio o video è stata reimpostato a CodecCompat.Incompatible="(non compatibile con %1)" CodecCompat.CodecPlaceholder="Seleziona encoder..." CodecCompat.ContainerPlaceholder="Seleziona formato..." -CodecCompat.CodecMissingOnExit.Title="Nessun encoder selezionato" -CodecCompat.CodecMissingOnExit.Text="Deve essere impostata almeno una codifica video o audio non è impostata.\nAssicuati di selezionare una codifica per la registrazione e per lo streaming." +CodecCompat.CodecMissingOnExit.Title="Nessun codificatore selezionato" +CodecCompat.CodecMissingOnExit.Text="Almeno un codificatore per video o audio non è impostato. Assicurarti di selezionare i codificatori sia per la registrazione che per la diretta." CodecCompat.ContainerMissingOnExit.Title="Nessun formato selezionato" -CodecCompat.ContainerMissingOnExit.Text="Nessun formato di registrazione è stato selezionato. Seleziona un formato di registrazione che sia compatibile con la codifica diretta selezionata." +CodecCompat.ContainerMissingOnExit.Text="Nessun formato di registrazione è stato selezionato. Seleziona un formato di registrazione che sia compatibile con il codificatore per la diretta selezionato." FinalScene.Title="Eliminazione della scena" FinalScene.Text="Deve esserci almeno una scena." NoSources.Title="Nessuna fonte" @@ -1169,7 +1175,7 @@ YouTube.Actions.Thumbnail="Anteprima" YouTube.Actions.Thumbnail.SelectFile="Seleziona file..." YouTube.Actions.Thumbnail.NoFileSelected="Nessun file selezionato" YouTube.Actions.Thumbnail.ClearFile="Pulisci" -YouTube.Actions.MadeForKids="Questo vdieo è adatto per bambini?*" +YouTube.Actions.MadeForKids="Questo video è adatto per bambini?*" YouTube.Actions.MadeForKids.Yes="Sì, è adatto per bambini" YouTube.Actions.MadeForKids.No="No, non è adatto per bambini" YouTube.Actions.AdditionalSettings="Impostazioni aggiuntive" @@ -1179,7 +1185,7 @@ YouTube.Actions.Latency.Low="Bassa" YouTube.Actions.Latency.UltraLow="Ultra bassa" YouTube.Actions.EnableAutoStart="Abilita avvio automatico" YouTube.Actions.EnableAutoStop="Abilita stop automatico" -YouTube.Actions.AutoStartStop.TT="Indica se questa trasmissione pianificata deve iniziare automaticamente" +YouTube.Actions.AutoStartStop.TT="Indica se questa trasmissione pianificata dovrebbe iniziare automaticamente" YouTube.Actions.EnableDVR="Abilita DVR" YouTube.Actions.360Video="Video 360" YouTube.Actions.ScheduleForLater="Pianificato più tardi" @@ -1192,7 +1198,7 @@ YouTube.Actions.Create_Schedule="Pianifica trasmissione" YouTube.Actions.Create_Schedule_Ready="Pianifica e seleziona trasmissione" YouTube.Actions.Dashboard="Apri YouTube Studio" YouTube.Actions.Error.Title="Errore creazione trasmissione in diretta" -YouTube.Actions.Error.Text="Errore accesso YouTube '%1'.
Descrizione dettagliata errore disponibile qui: https://developers.google.com/youtube/v3/live/docs/errors" +YouTube.Actions.Error.Text="Errore d'accesso a YouTube '%1'.
Una descrizione dettagliata dell'errore è disponibile qui: https://developers.google.com/youtube/v3/live/docs/errors" YouTube.Actions.Error.General="Errore di accesso a YouTube.\nControlla la connessione di rete o l'accesso al server YouTube." YouTube.Actions.Error.NoBroadcastCreated="Errore creazione trasmissione '%1'.
Descrizione dettagliata dell'errore disponibile qui: https://developers.google.com/youtube/v3/live/docs/errors" YouTube.Actions.Error.NoStreamCreated="Nessun flusso creato.\nRi-collegati all'account." @@ -1205,7 +1211,8 @@ YouTube.Actions.Error.BroadcastTransitionFailed="La transizione della trasmissio YouTube.Actions.Error.BroadcastTestStarting="La trasmissione sta per passare alla fase di prova, questo può richiedere un po' di tempo. Riprova tra 10-30 secondi." YouTube.Actions.EventsLoading="Caricamento elenco degli eventi..." YouTube.Actions.EventCreated.Title="Evento creato" -YouTube.Actions.EventCreated.Text="Creazione evento completata." +YouTube.Actions.EventCreated.Text="Evento creato con successo." +YouTube.Actions.Stream="Trasmissione" YouTube.Actions.Stream.ScheduledFor="Pianificato per %1" YouTube.Actions.Stream.Resume="Riprendi lo stream interrotto" YouTube.Actions.Stream.YTStudio="Creato automaticamente da YouTube Studio" @@ -1221,7 +1228,7 @@ YouTube.Chat.Error.Text="Il messaggio non può essere inviato: %1" YouTube.Errors.liveStreamingNotEnabled="La trasmissione in diretta non è abilitata sul canale YouTube selezionato.

Vedi youtube.com/features per ulteriori informazioni." YouTube.Errors.livePermissionBlocked="La trasmissione in diretta non è disponibile sul canale YouTube selezionato.
Nota che potrebbero essere necessarie fino a 24 ore perché la trasmissione in diretta diventi disponibile dopo averlo abilitato nelle impostazioni del canale.

Vedi youtube.com/features per i dettagli." YouTube.Errors.errorExecutingTransition="Transizione fallita a causa di un errore di backend. Riprova tra pochi secondi." -YouTube.Errors.errorStreamInactive="YouTube non sta ricevendo dati per il tuo flusso. Controlla la tua configurazione e riprova." +YouTube.Errors.errorStreamInactive="YouTube non sta ricevendo dati per la tua trasmissione. Controlla la tua configurazione e riprova." YouTube.Errors.invalidTransition="Il tentativo di transizione non è valido. Questo potrebbe essere dovuto al fatto che lo stream non ha terminato una transizione precedente. Si prega di attendere alcuni secondi e riprovare." YouTube.Errors.liveChatDisabled="In questa trasmissione la chat live è disabilitata." YouTube.Errors.liveChatEnded="La trasmissione in diretta è terminata." @@ -1229,3 +1236,30 @@ YouTube.Errors.messageTextInvalid="Il testo del messaggio non è valido." YouTube.Errors.rateLimitExceeded="Stai inviando messaggi troppo velocemente." YouTube.DocksRemoval.Title="Rimuovi i panelli YouTube legacy del browser" YouTube.DocksRemoval.Text="Questi pannelli del browser verranno rimossi perché obsoleti:\n\n%1\nUsa invece \"Pannello/Controllo regia YouTube Live\"." +ConfigDownload.WarningMessageTitle="Attenzione" +FailedToStartStream.MissingConfigURL="Nessun URL di configurazione disponibile per il servizio corrente" +FailedToStartStream.NoCustomRTMPURLInSettings="L'URL RTMP personalizzato non è specificato" +FailedToStartStream.InvalidCustomConfig="Configurazione personalizzata non valida" +FailedToStartStream.FailedToCreateMultitrackVideoService="Creazione servizio video multitraccia fallito" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Non sono riuscito a creare l'output RTMP del video multi traccia" +FailedToStartStream.EncoderNotAvailable="NVENC non disponibile.\n\nFallita la ricerca del tipo di codificatore '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Creazione del codificatore video '%1' (tipo: '%2') non riuscita" +FailedToStartStream.FailedToGetOBSVideoInfo="Errore nell'ottenere le informazioni sul video di OBS durante la creazione del codificatore '%1' (tipo: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Creazione del codificatore audio non riuscita" +FailedToStartStream.NoRTMPURLInConfig="La configurazione non contiene l'URL RTMP(S) della destinazione della trasmissione" +FailedToStartStream.FallbackToDefault="L'avvio della trasmissione usando %1 è fallito; vuoi riprovare usando le impostazioni con la codifica singola?" +FailedToStartStream.ConfigRequestFailed="Non è stato possibile recuperare le configurazioni da %1

errore HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Ricevuto il valore di stato sconosciuto '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nVuoi continuare la diretta senza %1?" +FailedToStartStream.WarningRetry="\n

\nVuoi continuare la diretta?" +FailedToStartStream.MissingEncoderConfigs="La configurazione di \"Vai in diretta\" non includeva le configurazioni del codificatore" +FailedToStartStream.StatusMissingHTML="La richiesta di \"Vai in diretta\" ha restituito un errore non specificato" +FailedToStartStream.NoConfigSupplied="Configurazione mancante" +MultitrackVideo.Info="%1 ottimizza automaticamente le tue impostazioni per codificare e inviare molteplici qualità di video. Selezionando questa opzione saranno inviate a %2 informazioni sul tuo computer e sulle impostazioni del software." +MultitrackVideo.IncompatibleSettings.Title="Impostazioni incompatibili" +MultitrackVideo.IncompatibleSettings.Text="%1 al momento non è compatibile con:\n\n%2\nPer continuare la trasmissione con %1, disabilita le impostazioni incompatibili:\n\n%3\ne Avvia la diretta di nuovo." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Disabilita per questa diretta e inizia la trasmissione" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Aggiorna le impostazioni e inizia la trasmissione" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 non è attualmente compatibile con [Audio → Generale → Canali] impostato su '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → Generale → Canali] deve essere impostato su '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 richiede più configurazioni differenti per [Audio → Generale → Canali]" diff --git a/UI/data/locale/ja-JP.ini b/UI/data/locale/ja-JP.ini index b6d06811753382..a827116a6667a8 100644 --- a/UI/data/locale/ja-JP.ini +++ b/UI/data/locale/ja-JP.ini @@ -193,7 +193,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="ハードウェアエンコ Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="ハードウェアエンコードはCPU使用率がほとんどなくなりますが、同レベルの品質を得るためにはより多くのビットレートが必要になります。" Basic.AutoConfig.StreamPage.StreamWarning.Title="配信の警告" Basic.AutoConfig.StreamPage.StreamWarning.Text="帯域幅のテストはランダム化された音声なしの映像データを自分のチャンネルに配信しようとしています。可能ならば、一時的に配信の映像保存をオフにしてテストが完了するまで配信を非公開に設定することをおすすめします。 続行しますか?" -Basic.AutoConfig.StreamPage.UseMultitrackVideo="テスト %1" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="%1 をテスト" Basic.AutoConfig.TestPage="最終結果" Basic.AutoConfig.TestPage.SubTitle.Testing="プログラムは最も理想的な設定を推定するための一連のテストを現在実行中です" Basic.AutoConfig.TestPage.SubTitle.Complete="テスト完了" @@ -580,7 +580,7 @@ Basic.Main.StopRecording="録画終了" Basic.Main.PauseRecording="録画一時停止" Basic.Main.UnpauseRecording="録画再開" Basic.Main.SplitFile="録画ファイルの分割" -Basic.Main.AddChapterMarker="チャプターマーカーを追加" +Basic.Main.AddChapterMarker="チャプターマーカーを追加 (Hybrid MP4のみ)" Basic.Main.StoppingRecording="録画停止処理中..." Basic.Main.StopReplayBuffer="リプレイバッファ停止" Basic.Main.StoppingReplayBuffer="リプレイバッファ停止処理中..." @@ -756,7 +756,7 @@ Basic.Settings.Appearance.General.Theme="テーマ" Basic.Settings.Appearance.General.Variant="スタイル" Basic.Settings.Appearance.General.NoVariant="利用可能なスタイルはありません" Basic.Settings.Stream="配信" -Basic.Settings.Stream.Destination="伝送先" +Basic.Settings.Stream.Destination="宛先" Basic.Settings.Stream.Custom.UseAuthentication="認証を使用する" Basic.Settings.Stream.Custom.Username="ユーザー名" Basic.Settings.Stream.Custom.Password="パスワード" @@ -785,7 +785,8 @@ Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="最大配信帯域 Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="自動" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="最大ビデオトラック数" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="自動" -Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="FLVへのストリームダンプを有効にする (基本録画ファイル設定を使用)"\nBasic.Settings.Stream.MultitrackVideoConfigOverride="\n設定を上書き (JSON)" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="FLVへのストリームダンプを有効にする (基本録画ファイル設定を使用)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="設定を上書き (JSON)" Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="設定の上書きを有効にする" Basic.Settings.Stream.MultitrackVideoLabel="マルチトラックビデオ" Basic.Settings.Stream.AdvancedOptions="詳細オプション" @@ -1261,14 +1262,14 @@ FailedToStartStream.MissingConfigURL="現在のサービスで利用可能な設 FailedToStartStream.NoCustomRTMPURLInSettings="カスタムRTMP URLが指定されていません" FailedToStartStream.InvalidCustomConfig="無効なカスタム設定" FailedToStartStream.FailedToCreateMultitrackVideoService="マルチトラックビデオサービスの作成に失敗しました" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="マルチトラックビデオrtmp出力の作成に失敗しました" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="マルチトラックビデオRTMP出力の作成に失敗しました" FailedToStartStream.EncoderNotAvailable="NVENCが利用できません。\n\nエンコーダタイプ '%1' が見つかりません" FailedToStartStream.FailedToCreateVideoEncoder="映像エンコーダ '%1' (タイプ: '%2') の作成に失敗しました" FailedToStartStream.FailedToGetOBSVideoInfo="エンコーダ '%1' (タイプ: '%2') の作成中にOBSの映像情報を取得できませんでした" FailedToStartStream.FailedToCreateAudioEncoder="音声エンコーダの作成に失敗しました" FailedToStartStream.NoRTMPURLInConfig="設定に配信ターゲットRTMP(S)のURLが含まれていません" FailedToStartStream.FallbackToDefault="%1 を使用した配信の開始に失敗しました。シングルエンコード設定を使用して再試行しますか?" -FailedToStartStream.ConfigRequestFailed="%1 から設定を取得できませんでした

HTTP エラー: %2" +FailedToStartStream.ConfigRequestFailed="%1 から設定を取得できませんでした

HTTP エラー: %2" FailedToStartStream.WarningUnknownStatus="不明なステータス値 '%1' を受信しました" FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n%1 を使用せずに配信を続行しますか?" FailedToStartStream.WarningRetry="\n

\n配信を続行しますか?" @@ -1280,3 +1281,6 @@ MultitrackVideo.IncompatibleSettings.Title="互換性のない設定" MultitrackVideo.IncompatibleSettings.Text="%1 は現在以下と互換性がありません:\n\n%2\n%1 で配信を続行するには、互換性のない設定を無効にして:\n\n%3\n再度配信を開始してください。" MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="この配信では無効にして配信を開始する" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="設定を更新して配信を開始する" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 は現在 [音声 → 一般 → チャンネル] を '%2' に設定した場合、互換性がありません %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[音声 → 一般 → チャンネル] を '%1' に設定する必要があります" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 は [音声 → 一般 → チャンネル] に複数の異なる設定が必要です" diff --git a/UI/data/locale/ka-GE.ini b/UI/data/locale/ka-GE.ini index c789a2c9bc1ee2..9fa84867e3b22c 100644 --- a/UI/data/locale/ka-GE.ini +++ b/UI/data/locale/ka-GE.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="ხმის შემრევის მენიუ" SceneFilters="სცენის ფილტრების გახსნა" List="სია" Grid="ცხრილი" +Automatic="ავტომატური" PluginsFailedToLoad.Title="მოდულის ჩატვირთვის შეცდომ" PluginsFailedToLoad.Text="OBS-ის მოცემული მოდულები ვერ ჩაიტვირთა:\n\n%1\nგთხოვთ, განაახლოთ ან მოაცილოთ აღნიშნული მოდულები." AlreadyRunning.Title="OBS უკვე გაშვებულია" @@ -194,6 +195,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="აპარატურუ Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="აპარატურული დაშიფვრა შეამცირებს პროცესორზე დატვირთვას, თუმცა შესაძლოა მეტი ბიტური სიხშირე დასჭირდეს, იმავე ხარისხის მისაღწევად." Basic.AutoConfig.StreamPage.StreamWarning.Title="გაფრთხილება ნაკადის გაშვებისას" Basic.AutoConfig.StreamPage.StreamWarning.Text="ქსელის გამტარუნარიანობის შემოწმება გულისხმობს, თქვენს არხზე შემთხვევითი ვიდეოფაილების ხმის გაშერე ნაკადად გაშვებას. სასურველია, თუ დროებით გათიშავთ ნაკადის შენახვის შესაძლებლობას და შემოწმების დასრულებამდე ამ ნაკადს გადაიყვანთ პირად რეჟიმში. გსურთ, განაგრძოთ?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="ტესტი %1" Basic.AutoConfig.TestPage="საბოლოო შედეგები" Basic.AutoConfig.TestPage.SubTitle.Testing="პროგრამა ახლა უშვებს სხვადასხვა სახის შემოწმებებს, სასურველი პარამეტრების დადგენის მიზნით" Basic.AutoConfig.TestPage.SubTitle.Complete="შემოწმება დასრულებულია" @@ -563,6 +565,7 @@ Basic.Main.Scenes="სცენები" Basic.Main.Sources="წყაროები" Basic.Main.Source="წყარო" Basic.Main.Controls="სამართავი" +Basic.Main.PreparingStream="მომზადება" Basic.Main.Connecting="უკავშირდება..." Basic.Main.StartRecording="ჩაწერის დაწყება" Basic.Main.StartReplayBuffer="გადახვევის ჩართვა" @@ -742,7 +745,13 @@ Basic.Settings.General.ChannelName.stable="მდგრადი" Basic.Settings.General.ChannelDescription.stable="უახლესი მდგრადი გამოშვება" Basic.Settings.General.ChannelName.beta="საცდელი / გამოსაშვებად მომზადებული" Basic.Settings.General.ChannelDescription.beta="სავარაუდოდ არამდგრადი გამოშვებამდელი ვერსიები" +Basic.Settings.Appearance="ვიზუალი" +Basic.Settings.Appearance.General="ზოგადი" +Basic.Settings.Appearance.General.Theme="თემა" +Basic.Settings.Appearance.General.Variant="სტილი" +Basic.Settings.Appearance.General.NoVariant="სტილები არაა" Basic.Settings.Stream="ნაკადი" +Basic.Settings.Stream.Destination="დანიშნულება" Basic.Settings.Stream.Custom.UseAuthentication="ანგარიშზე შესვლით" Basic.Settings.Stream.Custom.Username="მომხმარებლის სახელი" Basic.Settings.Stream.Custom.Password="პაროლი" @@ -764,6 +773,11 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="ვიდეოს უმა Basic.Settings.Stream.Recommended.MaxAudioBitrate="ხმის უმაღლესი ბიტური სიხშირე: %1 კბწმ" Basic.Settings.Stream.Recommended.MaxResolution="გაფართოება არაუმეტეს: %1" Basic.Settings.Stream.Recommended.MaxFPS="FPS არაუმეტეს: %1" +Basic.Settings.Stream.EnableMultitrackVideo="ჩართვა %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="მაქსიმალური სტრიმინგის გამტარობა" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="ავტომატური" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="მაქსიმალური ვიდეო გზა" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="ავტომატური" Basic.Settings.Output="გამოტანა" Basic.Settings.Output.Format="ჩაწერის ფორმატი" Basic.Settings.Output.Format.MKV="Matroska-ვიდეო (.mkv)" @@ -1226,3 +1240,4 @@ YouTube.Errors.messageTextInvalid="ნაწერი გაუმართა YouTube.Errors.rateLimitExceeded="ზედმეტად სწრაფად აგზავნით." YouTube.DocksRemoval.Title="YouTube-ბრაუზერის მოძველებული ნაწილების მოცილება" YouTube.DocksRemoval.Text="ბრაუზერის ეს ნაწილები მოცილდება, როგორც მოძველებული:\n\n%1\nსანაცვლოდ იხილეთ „იერსახის ნაწილები/YouTube Live Control Room“." +ConfigDownload.WarningMessageTitle="გაფრთხილება" diff --git a/UI/data/locale/ko-KR.ini b/UI/data/locale/ko-KR.ini index a5d344e8adf6d6..46a2f81595266f 100644 --- a/UI/data/locale/ko-KR.ini +++ b/UI/data/locale/ko-KR.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="오디오 믹서 메뉴" SceneFilters="장면 필터 열기" List="목록" Grid="격자" +Automatic="자동" PluginsFailedToLoad.Title="플러그인 로딩 오류" PluginsFailedToLoad.Text="다음 플러그인을 로딩하는데 실패했습니다:\n\n%1\n이 플러그인을 업데이트 하거나 삭제하십시오." AlreadyRunning.Title="OBS가 이미 실행 중입니다" @@ -194,6 +195,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="하드웨어 인코딩 선 Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="하드웨어 인코딩은 CPU 사용량을 대폭 줄일 수 있지만, 소프트웨어 인코딩과 동등한 품질을 달성하려면 더 많은 비트 전송률이 필요합니다." Basic.AutoConfig.StreamPage.StreamWarning.Title="방송 경고" Basic.AutoConfig.StreamPage.StreamWarning.Text="대역폭 검사 작업은 무작위 영상 데이터를 사용자의 방송 채널로 송출합니다. 이 검사가 끝날 때까지 방송 서비스에서 저장 기능을 잠시 끄거나 방송을 비공개로 전환하는 것을 추천합니다. 계속할까요?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="%1 테스트" Basic.AutoConfig.TestPage="최종 결과" Basic.AutoConfig.TestPage.SubTitle.Testing="이 프로그램은 가장 이상적인 설정을 찾기 위해서 몇 가지 검사를 실행합니다." Basic.AutoConfig.TestPage.SubTitle.Complete="검사 완료" @@ -212,6 +214,7 @@ Basic.AutoConfig.TestPage.Result.Header="프로그램에서 선택한 가장 이 Basic.AutoConfig.TestPage.Result.Footer="이 설정을 사용하려면 설정 적용을 누르세요. 구성 마법사를 다시 시작하려면 뒤로 가기를 누르세요. 수동으로 설정하고 싶다면 취소를 누른 다음 설정 창을 확인하세요." Basic.AutoConfig.Info="자동 설정 마법사는 컴퓨터의 사양과 인터넷 속도에 따라 최적의 설정을 선택할 것입니다." Basic.AutoConfig.RunAnytime="이것은 아무 때나 도구 메뉴에서 실행할 수 있습니다." +Basic.AutoConfig.TestPage.Result.StreamingResolution="스트리밍 (조정된) 해상도" Basic.Stats="통계" Basic.Stats.CPUUsage="CPU 이용률" Basic.Stats.HDDSpaceAvailable="사용 가능 저장 공간" @@ -565,6 +568,7 @@ Basic.Main.Scenes="장면 목록" Basic.Main.Sources="소스 목록" Basic.Main.Source="소스" Basic.Main.Controls="제어" +Basic.Main.PreparingStream="준비 중..." Basic.Main.Connecting="연결 중..." Basic.Main.StartRecording="녹화 시작" Basic.Main.StartReplayBuffer="리플레이 버퍼 시작" @@ -576,7 +580,7 @@ Basic.Main.StopRecording="녹화 중단" Basic.Main.PauseRecording="녹화 일시정지" Basic.Main.UnpauseRecording="녹화 재개" Basic.Main.SplitFile="녹화 파일 분할" -Basic.Main.AddChapterMarker="챕터 마커 추가" +Basic.Main.AddChapterMarker="챕터 마커 추가 (하이브리드 MP4 전용)" Basic.Main.StoppingRecording="녹화를 중단합니다...." Basic.Main.StopReplayBuffer="리플레이 버퍼 중단" Basic.Main.StoppingReplayBuffer="리플레이 버퍼 중단중..." @@ -685,6 +689,7 @@ Basic.MainMenu.Help.About="정보(&A)" Basic.Settings.ProgramRestart="설정을 적용하려면 프로그램을 다시 시작해야 합니다." Basic.Settings.ConfirmTitle="변경사항 확인" Basic.Settings.Confirm="저장하지 않은 설정이 있습니다. 저장하시겠습니까?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 이(가) 일부 스트림 설정을 제어하고 있음" Basic.Settings.General="일반" Basic.Settings.General.Language="언어" Basic.Settings.General.Updater="업데이트" @@ -751,6 +756,7 @@ Basic.Settings.Appearance.General.Theme="테마" Basic.Settings.Appearance.General.Variant="스타일" Basic.Settings.Appearance.General.NoVariant="사용 가능한 스타일 없음" Basic.Settings.Stream="방송" +Basic.Settings.Stream.Destination="대상" Basic.Settings.Stream.Custom.UseAuthentication="인증 기능 사용" Basic.Settings.Stream.Custom.Username="사용자 이름" Basic.Settings.Stream.Custom.Password="비밀번호" @@ -772,6 +778,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="최대 비디오 비트레이 Basic.Settings.Stream.Recommended.MaxAudioBitrate="최대 오디오 비트레이트: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="최대 해상도: %1" Basic.Settings.Stream.Recommended.MaxFPS="최대 FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="사용자 정의 서버 지정..." +Basic.Settings.Stream.ServiceCustomServer="사용자 지정 서버" +Basic.Settings.Stream.EnableMultitrackVideo="%1 활성화" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="최대 스트리밍 대역폭" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="자동" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="최대 비디오 트랙 수" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="자동" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="FLV로 스트리밍 덤프 활성화 (단순 녹화 파일 설정 사용)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="구성 재정의 (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="구성 재정의 활성화" +Basic.Settings.Stream.MultitrackVideoLabel="멀티트랙 비디오" +Basic.Settings.Stream.AdvancedOptions="고급 옵션" Basic.Settings.Output="출력" Basic.Settings.Output.Format="녹화 형식" Basic.Settings.Output.Format.MKV="Matroska 비디오 (.mkv)" @@ -1236,3 +1254,30 @@ YouTube.Errors.messageTextInvalid="메시지 내용이 유효하지 않습니다 YouTube.Errors.rateLimitExceeded="메시지를 너무 빨리 보내고 있습니다." YouTube.DocksRemoval.Title="레거시 YouTube 브라우저 독 제거" YouTube.DocksRemoval.Text="다음 브라우저 독은 지원 중단으로 제거됩니다:\n\n%1\n대신 '독/YouTube 라이브 관제실'을 사용하세요." +ConfigDownload.WarningMessageTitle="경고" +FailedToStartStream.MissingConfigURL="현재 서비스에 사용 가능한 구성 URL 없음" +FailedToStartStream.NoCustomRTMPURLInSettings="사용자 지정 RTMP 주소가 지정되지 않음" +FailedToStartStream.InvalidCustomConfig="유효하지 않은 사용자 지정 구성" +FailedToStartStream.FailedToCreateMultitrackVideoService="멀티트랙 비디오 서비스 생성에 실패했습니다" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="멀티트랙 비디오 RTMP 출력 생성에 실패했습니다" +FailedToStartStream.EncoderNotAvailable="NVENC를 사용할 수 없습니다.\n\n인코더 유형 '%1'을(를) 찾을 수 없습니다" +FailedToStartStream.FailedToCreateVideoEncoder="비디오 인코더 '%1' 생성에 실패했습니다 (유형: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="인코더 '%1' 생성 중 OBS 비디오 정보를 불러오는 데 실패했습니다 (유형: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="오디오 인코더 생성에 실패했습니다" +FailedToStartStream.NoRTMPURLInConfig="구성에 스트림 대상 RTMP(S) 주소가 없습니다" +FailedToStartStream.FallbackToDefault="%1 을(를) 사용하여 스트림을 시작하지 못했습니다. 단일 인코딩 설정을 사용하여 다시 시도하겠습니까?" +FailedToStartStream.ConfigRequestFailed="%1에서 구성을 가져오지 못했습니다

HTTP 오류: %2" +FailedToStartStream.WarningUnknownStatus="알 수 없는 상태 값 '%1'을(를) 받았습니다" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n%1 없이 스트리밍을 계속하시겠습니까?" +FailedToStartStream.WarningRetry="\n

\n스트리밍을 계속하시겠습니까?" +FailedToStartStream.MissingEncoderConfigs="방송 시작 구성에 인코더 설정이 포함되지 않았습니다" +FailedToStartStream.StatusMissingHTML="방송 시작 요청에서 알 수 없는 오류를 반환했습니다" +FailedToStartStream.NoConfigSupplied="구성을 찾을 수 없습니다" +MultitrackVideo.Info="%1 (은)는 설정을 자동으로 최적화하여 여러 비디오 품질을 인코딩 및 전송합니다. 이 옵션을 선택하면 컴퓨터 사양 및 소프트웨어 설정에 대한 정보가 %2 (으)로 전송됩니다." +MultitrackVideo.IncompatibleSettings.Title="호환되지 않는 설정" +MultitrackVideo.IncompatibleSettings.Text="%1 은(는) 현재 다음과 호환되지 않습니다:\n\n%2\n%1 (으)로 스트리밍을 계속하시려면, 다음 호환되지 않는 설정을 비활성화하세요:\n\n%3\n그리고 스트리밍을 다시 시작하세요." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="이번 스트림에서만 비활성화하고 스트리밍 시작" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="설정 업데이트 및 스트리밍 시작" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1은(는) [오디오 → 일반 → 채널]에 설정된 '%2', %3와(과) 호환하지 않습니다" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[오디오 → 일반 → 채널]을 '%1'(으)로 설정해야 합니다." +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1은(는) [오디오 → 일반 → 채널]에 대한 여러 다른 설정이 필요합니다." diff --git a/UI/data/locale/lv-LV.ini b/UI/data/locale/lv-LV.ini index 3e67926dff7ea5..7ac1bdca401209 100644 --- a/UI/data/locale/lv-LV.ini +++ b/UI/data/locale/lv-LV.ini @@ -88,31 +88,32 @@ Fullscreen="Pilnekrānā" Windowed="Logā" RefreshBrowser="Atsvaidzināt" AspectRatio="Proporcija %1:%2" -LockVolume="Bloķēt volume?" +LockVolume="Bloķēt skaļumu" LogViewer="Žurnālu skatītājs" -ShowOnStartup="Rādīt palaižot" +ShowOnStartup="Rādīt palaišanas laikā" OpenFile="Atvērt failu" -AddSource="Pievienot Avotu" -RemoveScene="Dzēst Izvēlēto Ainu" +AddSource="Pievienot avotu" +RemoveScene="Dzēst izvēlēto ainu" RemoveSource="Dzēst izvēlēto(-ās) ainu(-as)" -MoveSceneUp="Pārvietot Ainu uz Augšu" -MoveSceneDown="Pārvietot Ainu uz Leju" -MoveSourceUp="Pārvietot Avotu(-us) uz Augšu" -MoveSourceDown="Pārvietot Avotu(-us) uz Leju" -SourceProperties="Atvērt Satura Īpašības" -SourceFilters="Atvērt Satura Filtrus" -MixerToolbarMenu="Audio Miksera Galvenā Izvēlne" -SceneFilters="Atvērt Ainas Filtrus" +MoveSceneUp="Pārvietot ainu uz augšu" +MoveSceneDown="Pārvietot ainu uz leju" +MoveSourceUp="Pārvietot avotu(-us) uz augšu" +MoveSourceDown="Pārvietot avotu(-us) uz leju" +SourceProperties="Atvērt satura rekvizīti" +SourceFilters="Atvērt satura filtrus" +MixerToolbarMenu="Skaļuma pulta izvēlne" +SceneFilters="Atvērt ainas filtrus" List="Saraksts" Grid="Režģis" -PluginsFailedToLoad.Title="Spraudņu Ielādes Kļūda" -PluginsFailedToLoad.Text="Sekojošie OBS spraudņi nevarēja tikt ielādēti:\n\n%1\nLūdzu veiciet atjauninājumu vai dzēsiet šos spraudņus." -AlreadyRunning.Title="OBS jau darbojas (ir palaists)" +Automatic="Automātiski" +PluginsFailedToLoad.Title="Spraudņu ielādes kļūda" +PluginsFailedToLoad.Text="Sekojošie OBS spraudņi neizdevās ielādēt:\n\n%1\nLūdzu, atjauniniet vai noņemiet šos spraudņus." +AlreadyRunning.Title="OBS jau tiek palaists" AlreadyRunning.Text="OBS jau darbojas! Ja nevēlaties vairākas paralēli darbojošās OBS kopijas, lūdzu, aiztaisiet/izslēdziet pašreiz darbojošās. Ja jums iestatīts, ka OBS tiek minimizēts sistēmas teknē, lūdzu, pārbaudiet, vai tas tur joprojām darbojas." -AlreadyRunning.LaunchAnyway="Palaist vēl vienu" +AlreadyRunning.LaunchAnyway="Palaist jebkurā gadījumā" AutoSafeMode.Title="Drošais režīms" AutoSafeMode.Text="OBS neizslēdzās pareizi pagājušās sesijas laikā.\n\nVai vēlies sākt Drošajā Režīmā (trešo pušu spraudņi, scripti, un WebSockets atspējoti)?" -AutoSafeMode.LaunchSafe="Palaist Drošajā Režīmā" +AutoSafeMode.LaunchSafe="Palaist drošajā režīmā" AutoSafeMode.LaunchNormal="Palaist Normāli" SafeMode.Restart="Vai vēlies restartēt OBS Drošajā Režīmā (trešo pušu spraudņi, skripti un WebSockets atspējoti)?" SafeMode.RestartNormal="Vai vēlies restartēt OBS Normālajā Režīmā?" diff --git a/UI/data/locale/ms-MY.ini b/UI/data/locale/ms-MY.ini index 95886ac1b784f5..c64f1e6888211d 100644 --- a/UI/data/locale/ms-MY.ini +++ b/UI/data/locale/ms-MY.ini @@ -560,6 +560,7 @@ Basic.Main.StopRecording="Henti Rakaman" Basic.Main.PauseRecording="Jeda Rakaman" Basic.Main.UnpauseRecording="Nyahjeda Rakaman" Basic.Main.SplitFile="Pisah Fail Rakaman" +Basic.Main.AddChapterMarker="Tambah Penanda Bab (MP4 Hibrid sahaja)" Basic.Main.StoppingRecording="Menghentikan Rakaman..." Basic.Main.StopReplayBuffer="Henti Penimbal Main Semula" Basic.Main.StoppingReplayBuffer="Menghentikan Penimbal Main Semula..." diff --git a/UI/data/locale/nl-NL.ini b/UI/data/locale/nl-NL.ini index d4a1ba6f784d2f..dd910bda67f139 100644 --- a/UI/data/locale/nl-NL.ini +++ b/UI/data/locale/nl-NL.ini @@ -553,7 +553,7 @@ Basic.Main.StopRecording="Opname stoppen" Basic.Main.PauseRecording="Pauzeer de opname" Basic.Main.UnpauseRecording="Vervolg de opname" Basic.Main.SplitFile="Opnamebestand splitsen" -Basic.Main.AddChapterMarker="Hoofdstuk markering toevoegen" +Basic.Main.AddChapterMarker="Voeg hoofdstuk markering toe (Alleen hybride MP4)" Basic.Main.StoppingRecording="De opname stoppen..." Basic.Main.StopReplayBuffer="Replaybuffer stoppen" Basic.Main.StoppingReplayBuffer="De replaybuffer aan het stoppen..." @@ -719,6 +719,7 @@ Basic.Settings.Appearance="Uiterlijk" Basic.Settings.Appearance.General="Algemeen" Basic.Settings.Appearance.General.Theme="Thema" Basic.Settings.Appearance.General.Variant="Stijl" +Basic.Settings.Appearance.General.NoVariant="Geen styles beschikbaar" Basic.Settings.Stream.Destination="Bestemming" Basic.Settings.Stream.Custom.UseAuthentication="Gebruik authenticatie" Basic.Settings.Stream.Custom.Username="Gebruikersnaam" @@ -1158,7 +1159,26 @@ ConfigDownload.WarningMessageTitle="Waarschuwing" FailedToStartStream.MissingConfigURL="Geen configuratie url bescchikbaar voor de huidige service" FailedToStartStream.NoCustomRTMPURLInSettings="Aangepaste RTMP url niet gespecificeerd" FailedToStartStream.InvalidCustomConfig="Incorrecte aangepaste configuratie" +FailedToStartStream.FailedToCreateMultitrackVideoService="Aanmaken van multitrack video service mislukt" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Aanmaken van multitrack video RTMP output mislukt" +FailedToStartStream.EncoderNotAvailable="NVENC niet beschikbaar.\n\nEncoder type '%1' niet gevonden" +FailedToStartStream.FailedToCreateVideoEncoder="Aanmaken video encoder '%1' (type: '%2') mislukt" +FailedToStartStream.FailedToGetOBSVideoInfo="Opvragen OBS video informatie mislukt tijdens aanmaken van encoder '%1' (type: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Audio encoder aanmaken mislukt" +FailedToStartStream.NoRTMPURLInConfig="Configuratie bevat niet de stream doel RTMP(S) url" +FailedToStartStream.FallbackToDefault="Het starten van de stream met %1 is mislkt; wil je opnieuw proberen met een encoder instellingen?" +FailedToStartStream.ConfigRequestFailed="Kon geen configuratie ophalen van %1

HTTP fout: %2" +FailedToStartStream.WarningUnknownStatus="Onbekende status waarde '%1' ontvangen" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nWil je doorgaan met streamen zonder %1?" +FailedToStartStream.WarningRetry="\n

\nWil je doorgaan met streamen?" +FailedToStartStream.MissingEncoderConfigs="De live gaan configuratie bevat geen encoder configuraties" +FailedToStartStream.StatusMissingHTML="Live gaan verzoek gaf een niet-gespecificeerde fout terug" FailedToStartStream.NoConfigSupplied="Ontbrekende configuratie" +MultitrackVideo.Info="%1 optimaliseert automatisch jouw instellingen voor encoden en versturen van meerdere video kwaliteiten. Selecteren van deze optie stuurt informatie over jouw computer en drivers naar %2." MultitrackVideo.IncompatibleSettings.Title="Incompatible instellingen" +MultitrackVideo.IncompatibleSettings.Text="%1 is op dit moment niet compatible met:\n\n%2\nOm door te gaan met streamen met %1, disable de niet compatible instellingen:\n\n%3\nen start streamen opnieuw." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Uitschakelen voor deze stream en start streamen" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Update instellingen en start streamen" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 is op dit moment niet compatible met [Audio → Algemeen → kanalen] ingesteld op '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → Algemeen → kanalen] moet ingesteld staan op '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 vereist meerdere andere instellingen voor [Audio → Algemeen → kanalen] " diff --git a/UI/data/locale/pl-PL.ini b/UI/data/locale/pl-PL.ini index 896b13403d9aa8..b78960cc760416 100644 --- a/UI/data/locale/pl-PL.ini +++ b/UI/data/locale/pl-PL.ini @@ -572,7 +572,7 @@ Basic.Main.StopRecording="Zatrzymaj nagrywanie" Basic.Main.PauseRecording="Pauzuj nagrywanie" Basic.Main.UnpauseRecording="Wznów nagrywanie" Basic.Main.SplitFile="Podziel plik nagrywania" -Basic.Main.AddChapterMarker="Dodaj Marker Rozdziału" +Basic.Main.AddChapterMarker="Dodaj znacznik rozdziału (tylko hybrydowy MP4)" Basic.Main.StoppingRecording="Zatrzymywanie nagrywania..." Basic.Main.StopReplayBuffer="Zatrzymaj nagrywanie powtórek" Basic.Main.StoppingReplayBuffer="Zatrzymywanie nagrywania powtórek..." @@ -1256,3 +1256,6 @@ MultitrackVideo.IncompatibleSettings.Title="Niezgodne ustawienia" MultitrackVideo.IncompatibleSettings.Text="%1 nie jest kompatybilny z:\n\n%2\nWyłącz nieprawidłowe opcje w ustawieniach, aby kontynuować korzystanie z %1:\n\n%3\ni uruchom transmisję jeszcze raz." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Wyłącz dla tego streamu i uruchom transmisję" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Aktualizuj ustawienia i uruchom transmisję" +MultitrackVideo.IncompatibleSettings.AudioChannels="Brak kompatybilności %1 z ustawieniem [Dźwięk → Główne → Kanały] na '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Dźwięk → Główne → Kanały] powinno być ustawione na '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 wymaga wielu różnych ustawień w [Dźwięk → Główne → Kanały]" diff --git a/UI/data/locale/pt-BR.ini b/UI/data/locale/pt-BR.ini index 7ccd5394baa8be..be9d9f12aa5d07 100644 --- a/UI/data/locale/pt-BR.ini +++ b/UI/data/locale/pt-BR.ini @@ -106,6 +106,7 @@ MixerToolbarMenu="Menu de mixagem" SceneFilters="Abrir filtros de cena" List="Lista" Grid="Grade" +Automatic="Automático" PluginsFailedToLoad.Title="Erro ao carregar o plugin" PluginsFailedToLoad.Text="Não foi possível carregar os seguintes plugins do OBS:\n\n%1\nPor favor, atualize ou remova estes plugins." AlreadyRunning.Title="OBS já está em execução" @@ -191,6 +192,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferir codificação por h Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Codificação por hardware elimina boa parte do uso de CPU, mas pode exigir uma taxa de bits maior para obter o mesmo nível de qualidade." Basic.AutoConfig.StreamPage.StreamWarning.Title="Aviso de transmissão" Basic.AutoConfig.StreamPage.StreamWarning.Text="O teste de largura de banda está prestes a transmitir imagens aleatórias e sem áudio para o seu canal. Se for possível, é recomendável desativar temporariamente a gravação da transmissão e definir a transmissão como privada até que o teste seja concluído. Continuar?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Teste %1" Basic.AutoConfig.TestPage="Resultados finais" Basic.AutoConfig.TestPage.SubTitle.Testing="O programa está executando um conjunto de testes para estimar as configurações ideais" Basic.AutoConfig.TestPage.SubTitle.Complete="Teste completo" @@ -209,6 +211,7 @@ Basic.AutoConfig.TestPage.Result.Header="O programa recomenda as seguintes confi Basic.AutoConfig.TestPage.Result.Footer="Para usar essas configurações, clique em Aplicar configurações. Para reconfigurar o assistente e tentar novamente, clique em Voltar. Para definir manualmente, clique em Cancelar e abra as Configurações." Basic.AutoConfig.Info="O assistente de configuração irá determinar as melhores configurações baseadas nas especificações do seu computador e na velocidade da internet." Basic.AutoConfig.RunAnytime="Isto pode ser executado a qualquer momento no menu Ferramentas." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Resolução de Transmissão (Escalonada)" Basic.Stats="Estatísticas" Basic.Stats.CPUUsage="Uso de CPU" Basic.Stats.HDDSpaceAvailable="Espaço em disco disponível" @@ -556,6 +559,7 @@ Basic.Main.Scenes="Cenas" Basic.Main.Sources="Fontes" Basic.Main.Source="Fonte" Basic.Main.Controls="Controles" +Basic.Main.PreparingStream="Preparando..." Basic.Main.Connecting="Conectando..." Basic.Main.StartRecording="Iniciar gravação" Basic.Main.StartReplayBuffer="Iniciar buffer de repetição" @@ -567,7 +571,7 @@ Basic.Main.StopRecording="Interromper gravação" Basic.Main.PauseRecording="Pausar gravação" Basic.Main.UnpauseRecording="Continuar gravação" Basic.Main.SplitFile="Cortar Arquivo de Gravação" -Basic.Main.AddChapterMarker="Adicionar marcador de capítulo" +Basic.Main.AddChapterMarker="Adicionar Marcador de Capítulo (Somente em MP4 Híbrido)" Basic.Main.StoppingRecording="Interrompendo gravação..." Basic.Main.StopReplayBuffer="Interromper buffer de repetição" Basic.Main.StoppingReplayBuffer="Interrompendo buffer de repetição..." @@ -676,6 +680,7 @@ Basic.MainMenu.Help.About="Sobre (&A)" Basic.Settings.ProgramRestart="Reinicie o programa para aplicar as configurações." Basic.Settings.ConfirmTitle="Confirmar alterações" Basic.Settings.Confirm="Há alterações não salvas. Deseja salvar agora?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 está controlando algumas das configurações da sua transmissão" Basic.Settings.General="Geral" Basic.Settings.General.Language="Idioma" Basic.Settings.General.Updater="Atualizações" @@ -730,7 +735,7 @@ Basic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top="Horizontal, acima Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Horizontal, acima (24 cenas)" Basic.Settings.General.MultiviewLayout.4Scene="Cenas apenas (4 cenas)" Basic.Settings.General.MultiviewLayout.9Scene="Cenas apenas (9 cenas)" -Basic.Settings.General.MultiviewLayout.16Scene="Cenas apenas (16 cenas)" +Basic.Settings.General.MultiviewLayout.16Scene="Apenas cenas (16 cenas)" Basic.Settings.General.MultiviewLayout.25Scene="Cenas apenas (25 cenas)" Basic.Settings.General.ChannelName.stable="Estável" Basic.Settings.General.ChannelDescription.stable="Último lançamento estável" @@ -742,6 +747,7 @@ Basic.Settings.Appearance.General.Theme="Tema" Basic.Settings.Appearance.General.Variant="Estilo" Basic.Settings.Appearance.General.NoVariant="Nenhum estilo disponível" Basic.Settings.Stream="Transmissão" +Basic.Settings.Stream.Destination="Destino" Basic.Settings.Stream.Custom.UseAuthentication="Utilizar autenticação" Basic.Settings.Stream.Custom.Username="Nome de usuário" Basic.Settings.Stream.Custom.Password="Senha" @@ -763,6 +769,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Taxa de bits máxima de víde Basic.Settings.Stream.Recommended.MaxAudioBitrate="Taxa de bits máxima de áudio: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Resolução máxima: %1" Basic.Settings.Stream.Recommended.MaxFPS="Taxa de quadros máxima: %1" +Basic.Settings.Stream.SpecifyCustomServer="Especifique o Servidor Personalizado..." +Basic.Settings.Stream.ServiceCustomServer="Servidor Personalizado" +Basic.Settings.Stream.EnableMultitrackVideo="Habilitar %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Largura de Banda Máxima de Transmissão" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automático" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Máximo de Trilhas de Vídeo" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automático" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Habilitar descarga de transmissão para FLV (usa configurações simples de arquivo de gravação)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Substituição de configuração (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Habilitar Substituição de Configuração" +Basic.Settings.Stream.MultitrackVideoLabel="Vídeo Multifaixa" +Basic.Settings.Stream.AdvancedOptions="Opções Avançadas" Basic.Settings.Output="Saída" Basic.Settings.Output.Format="Formato de gravação" Basic.Settings.Output.Format.hMP4="MP4 híbrido [BETA] (.mp4)" @@ -839,7 +857,7 @@ Basic.Settings.Output.Adv.Audio.Track4="Faixa 4" Basic.Settings.Output.Adv.Audio.Track5="Faixa 5" Basic.Settings.Output.Adv.Audio.Track6="Faixa 6" Basic.Settings.Output.Adv.TwitchVodTrack="Faixa de VOD da Twitch" -Basic.Settings.Output.Adv.Encoder="Configurações de Encoder" +Basic.Settings.Output.Adv.Encoder="Configurações do Codificador" Basic.Settings.Output.Adv.Recording="Gravação" Basic.Settings.Output.Adv.Recording.Settings="Configurações de Gravação" Basic.Settings.Output.Adv.Recording.RecType="Tipo de gravação" @@ -957,16 +975,16 @@ Basic.Settings.Audio.EnablePushToTalk="Ativar pressionar-para-falar" Basic.Settings.Audio.PushToTalkDelay="Atraso do pressionar-para-falar" Basic.Settings.Audio.UnknownAudioDevice="[Dispositivo desconectado ou indisponível]" Basic.Settings.Audio.Disabled="Desativado" -Basic.Settings.Audio.LowLatencyBufferingMode="Modo de buffer de áudio de baixa latência (para saídas Decklink/NDI)" -Basic.Settings.Audio.LowLatencyBufferingWarning.Enabled="AVISO: buffer de áudio de baixa latência está ativado." +Basic.Settings.Audio.LowLatencyBufferingMode="Modo de Buffer de Áudio de Baixa Latência (Para Saídas Decklink/NDI)" +Basic.Settings.Audio.LowLatencyBufferingWarning.Enabled="ATENÇÃO: O buffer de áudio de baixa latência está habilitado." Basic.Settings.Audio.LowLatencyBufferingWarning="Modo de buffer de áudio de baixa latência pode causar falhas no áudio ou parar a reprodução de algumas fontes." -Basic.Settings.Audio.LowLatencyBufferingWarning.Title="Ativar modo de buffer de baixa latência de áudio?" -Basic.Settings.Audio.LowLatencyBufferingWarning.Confirm="Tem certeza que deseja ativar o modo de buffer de áudio de baixa latência?" +Basic.Settings.Audio.LowLatencyBufferingWarning.Title="Habilitar o modo de buffer de áudio de baixa latência?" +Basic.Settings.Audio.LowLatencyBufferingWarning.Confirm="Tem certeza de que deseja habilitar o modo de buffer de áudio de baixa latência?" Basic.Settings.Accessibility="Acessibilidade" Basic.Settings.Accessibility.ColorOverrides="Usar cores diferentes" -Basic.Settings.Accessibility.ColorOverrides.SelectRed="Borda de Origem (Seleção)" -Basic.Settings.Accessibility.ColorOverrides.SelectGreen="Borda de Origem (Corte)" -Basic.Settings.Accessibility.ColorOverrides.SelectBlue="Borda de Origem (Sobre)" +Basic.Settings.Accessibility.ColorOverrides.SelectRed="Borda da Fonte (Seleção)" +Basic.Settings.Accessibility.ColorOverrides.SelectGreen="Borda da Fonte (Recorte)" +Basic.Settings.Accessibility.ColorOverrides.SelectBlue="Borda da Fonte (Ao Passar o Mouse)" Basic.Settings.Accessibility.ColorOverrides.MixerGreen="Faixa de volume do Mixer (-60 a -20dB)" Basic.Settings.Accessibility.ColorOverrides.MixerYellow="Faixa de volume do Mixer (-20 a -9dB)" Basic.Settings.Accessibility.ColorOverrides.MixerRed="Faixa de volume do Mixer (-9 a 0dB)" @@ -975,18 +993,18 @@ Basic.Settings.Accessibility.ColorOverrides.MixerYellowActive="Faixa de volume d Basic.Settings.Accessibility.ColorOverrides.MixerRedActive="Faixa de volume do Mixer (-9 a 0dB) (ativo)" Basic.Settings.Accessibility.ColorOverrides.Preset="Predefinição de Cores" Basic.Settings.Accessibility.ColorOverrides.Preset.Default="Padrão" -Basic.Settings.Accessibility.ColorOverrides.Preset.Custom="Customizado" +Basic.Settings.Accessibility.ColorOverrides.Preset.Custom="Personalizado" Basic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1="Alternativa para Daltônicos" Basic.Settings.Advanced="Avançado" -Basic.Settings.Advanced.General.ConfirmOnExit="Mostrar aviso de saída ativa ao sair" +Basic.Settings.Advanced.General.ConfirmOnExit="Mostrar aviso de saídas ativas ao sair" Basic.Settings.Advanced.General.ProcessPriority="Prioridade do processo" Basic.Settings.Advanced.General.ProcessPriority.High="Alta" Basic.Settings.Advanced.General.ProcessPriority.AboveNormal="Acima do normal" Basic.Settings.Advanced.General.ProcessPriority.BelowNormal="Abaixo do normal" Basic.Settings.Advanced.General.ProcessPriority.Idle="Inativa" -Basic.Settings.Advanced.FormatWarning="Aviso: Os formatos de cores diferentes de NV12/P010 destinam-se principalmente à gravação e não são recomendados durante a transmissão. O streaming pode aumentar o uso da CPU devido à conversão do formato de cor." +Basic.Settings.Advanced.FormatWarning="Aviso: Os formatos de cores diferentes de NV12/P010 destinam-se principalmente a gravação e não são recomendados durante a transmissão. A transmissão pode aumentar o uso da CPU devido à conversão do formato de cor." Basic.Settings.Advanced.FormatWarningPreciseSdr="Aviso: formatos de alta precisão são mais comumente usados com espaços de cores HDR." -Basic.Settings.Advanced.FormatWarning2100="Aviso: Rec. 2100 deve usar um formato com mais precisão." +Basic.Settings.Advanced.FormatWarning2100="Aviso: Rec. 2100 deveria usar um formato com mais precisão." Basic.Settings.Advanced.Video.ColorFormat="Formato de cor" Basic.Settings.Advanced.Video.ColorFormat.NV12="NV12 (8 bits, 4:2:0, 2 planos)" Basic.Settings.Advanced.Video.ColorFormat.I420="I420 (8 bits, 4:2:0, 3 planos)" @@ -1010,7 +1028,7 @@ Basic.Settings.Advanced.StreamDelay.Duration="Duração" Basic.Settings.Advanced.StreamDelay.Preserve="Preservar o ponto de corte ao reconectar (aumenta o atraso)" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Uso de memória estimado: %1 MB" Basic.Settings.Advanced.Network="Rede" -Basic.Settings.Advanced.Network.Disabled="O protocolo de streaming atualmente selecionado não suporta alterações nas configurações de rede." +Basic.Settings.Advanced.Network.Disabled="O protocolo de transmissão atualmente selecionado não suporta alterações nas configurações de rede." Basic.Settings.Advanced.Network.BindToIP="Ligar pelo IP" Basic.Settings.Advanced.Network.IPFamily="Família IP" Basic.Settings.Advanced.Network.EnableNewSocketLoop="Ativar otimizações de rede" @@ -1209,3 +1227,30 @@ YouTube.Errors.messageTextInvalid="O texto da mensagem não é válido." YouTube.Errors.rateLimitExceeded="Você está enviando mensagens muito rapidamente." YouTube.DocksRemoval.Title="Limpar Docas YouTube Legadas" YouTube.DocksRemoval.Text="Estas docas de Navegador serão removidas por descontinuidade:\n\n%1\nUse \"Docas/YouTube Live Sala de Controle\" ao invés." +ConfigDownload.WarningMessageTitle="Aviso" +FailedToStartStream.MissingConfigURL="Nenhum URL de configuração disponível para o serviço atual" +FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP personalizada não especificada" +FailedToStartStream.InvalidCustomConfig="Configuração personalizada inválida" +FailedToStartStream.FailedToCreateMultitrackVideoService="Falha ao criar serviço de vídeo multifaixa" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Falha ao criar saída RTMP de vídeo multifaixa" +FailedToStartStream.EncoderNotAvailable="NVENC não disponível.\n\nFalha em encontrar o tipo de codificador '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Falha ao criar o codificador de vídeo '%1' (tipo: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Falha ao obter informações do vídeo do OBS durante criação do codificador '%1' (tipo: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Falha ao criar codificador de áudio" +FailedToStartStream.NoRTMPURLInConfig="A configuração não contém URL RTMP(S) de destino da transmissão" +FailedToStartStream.FallbackToDefault="Falha ao iniciar a transmissão usando %1; deseja tentar novamente usando configurações de codificação única?" +FailedToStartStream.ConfigRequestFailed="Não foi possível buscar a configuração de %1

Erro HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Valor de status desconhecido recebido '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nVocê deseja continuar transmitindo sem %1?" +FailedToStartStream.WarningRetry="\n

\nDeseja continuar transmitindo?" +FailedToStartStream.MissingEncoderConfigs="Configuração de transmissão ao vivo não incluiu configurações do codificador" +FailedToStartStream.StatusMissingHTML="Solicitação de transmissão ao vivo retornou um erro não especificado" +FailedToStartStream.NoConfigSupplied="Configuração ausente" +MultitrackVideo.Info="%1 otimiza automaticamente suas configurações para codificar e enviar video em múltiplas qualidades. Selecionar esta opção enviará %2 informações sobre a configuração do seu computador e software." +MultitrackVideo.IncompatibleSettings.Title="Configurações Incompatíveis" +MultitrackVideo.IncompatibleSettings.Text="%1 ainda não é compatível com:\n%2\nPara continuar transmitindo com %1, desabilite as configurações incompatíveis:\n%3\ne inicie a transmissão novamente." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Desative para esta transmissão e Inicie a transmissão" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Atualize as Configurações e Inicie a Transmissão" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 não atualmente compatível com [Áudio → Geral → Canais] definidos para '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Áudio → Geral → Canais] precisa ser definido para '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requer multiplos definições diferentes para [Áudio → Geral → Canais]" diff --git a/UI/data/locale/pt-PT.ini b/UI/data/locale/pt-PT.ini index 129875184794de..76bfb6b5d746f0 100644 --- a/UI/data/locale/pt-PT.ini +++ b/UI/data/locale/pt-PT.ini @@ -81,7 +81,7 @@ StudioMode.Program="Programa" StudioMode.PreviewSceneName="Antevisão: %1" StudioMode.ProgramSceneName="Programa: %1" ShowInMultiview="Mostrar em multivisualização" -VerticalLayout="Disposição vertical" +VerticalLayout="Esquema vertical" Group="Grupo" DoNotShowAgain="Não voltar a mostrar" Default="(predefinição)" @@ -212,6 +212,7 @@ Basic.AutoConfig.TestPage.Result.Header="O programa determinou que estas são as Basic.AutoConfig.TestPage.Result.Footer="Para utilizar estas definições, clique em Aplicar definições. Para reconfigurar o assistente e tentar novamente, clique em Anterior. Para configurar manualmente as definições, clique em Cancelar e abra as definições." Basic.AutoConfig.Info="O assistente de configuração automática vai determinar as melhores definições baseadas nas especificações do computador e na velocidade da Internet." Basic.AutoConfig.RunAnytime="Isto pode ser executado a qualquer momento através do menu Ferramentas." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Resolução de transmissão (Escalonada)" Basic.Stats="Estatísticas" Basic.Stats.CPUUsage="Utilização de CPU" Basic.Stats.HDDSpaceAvailable="Espaço em disco disponível" @@ -352,7 +353,7 @@ Output.StreamEncodeError.Title="Erro de codificação" Output.StreamEncodeError.Msg="Ocorreu um erro de codificação ao transmitir." Output.StreamEncodeError.Msg.LastError="Ocorreu um erro de codificação ao transmitir:

%1" Output.RecordFail.Title="Falha ao iniciar a gravação" -Output.RecordFail.Unsupported="O formato de saída ou não é suportado ou não suporta mais do que uma faixa de áudio. Por favor, verifique as suas definições e tente novamente." +Output.RecordFail.Unsupported="O formato de saída não é suportado ou não suporta mais do que uma pista de áudio. Por favor, verifique as suas definições e tente novamente." Output.RecordNoSpace.Title="Espaço em disco insuficiente" Output.RecordNoSpace.Msg="Não há espaço em disco suficiente para continuar a gravação." Output.RecordError.Title="Erro de gravação" @@ -460,7 +461,7 @@ VolControl.SliderMuted="Barra de volume para '%1': (atualmente silenciado)" VolControl.Mute="Silenciar '%1'" VolControl.Properties="Propriedades de '%1'" VolControl.UnassignedWarning.Title="Fonte de áudio não atribuído" -VolControl.UnassignedWarning.Text="\"%1\" não está atribuído a nenhuma faixa de áudio e não será audível em transmissões ou gravações.\n\nPara atribuir uma fonte de áudio a uma faixa, abra as propriedades de áudio avançadas, clicando com o botão direito do rato ou no botão da roda dentada na barra de ferramentas da doca do misturador." +VolControl.UnassignedWarning.Text="\"%1\" não está atribuído a nenhuma pista de áudio e não será audível em transmissões ou gravações.\n\nPara atribuir uma fonte de áudio a uma pista, abra as propriedades de áudio avançadas clicando com o botão direito do rato ou no botão da roda dentada na barra de ferramentas da doca do misturador." Basic.Main.AddSceneDlg.Title="Adicionar cena" Basic.Main.AddSceneDlg.Text="Por favor, insira o nome da cena" Basic.Main.DefaultSceneName.Text="Cena %1" @@ -577,7 +578,7 @@ Basic.Main.StopRecording="Parar gravação" Basic.Main.PauseRecording="Pausar gravação" Basic.Main.UnpauseRecording="Retomar gravação" Basic.Main.SplitFile="Dividir arquivo de gravação" -Basic.Main.AddChapterMarker="Adicionar marcador de capítulo" +Basic.Main.AddChapterMarker="Adicionar marcador de capítulo (apenas em MP4 híbrido)" Basic.Main.StoppingRecording="A parar gravação..." Basic.Main.StopReplayBuffer="Parar memória de repetição" Basic.Main.StoppingReplayBuffer="A parar memória de repetição..." @@ -686,6 +687,7 @@ Basic.MainMenu.Help.About="&Acerca" Basic.Settings.ProgramRestart="O programa necessita de ser reiniciado para estas alterações terem efeito." Basic.Settings.ConfirmTitle="Confirmar alterações" Basic.Settings.Confirm="Tem alterações não guardadas. Deseja guardá-las?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 está a controlar algumas das configurações da sua transmissão" Basic.Settings.General="Geral" Basic.Settings.General.Language="Idioma" Basic.Settings.General.Updater="Atualizações" @@ -725,13 +727,13 @@ Basic.Settings.General.OverflowSelectionHidden="Mostrar excesso mesmo quando a f Basic.Settings.General.Importers="Importadores" Basic.Settings.General.AutomaticCollectionSearch="Procurar coleções de cena em locais conhecidos ao importar" Basic.Settings.General.SwitchOnDoubleClick="Transição para cena ao fazer duplo clique" -Basic.Settings.General.StudioPortraitLayout="Ativar disposição horizontal/vertical" +Basic.Settings.General.StudioPortraitLayout="Ativar esquema horizontal/vertical" Basic.Settings.General.TogglePreviewProgramLabels="Mostrar etiquetas de antevisão/programa" Basic.Settings.General.Multiview="Multivisualização" Basic.Settings.General.Multiview.MouseSwitch="Clique para alternar entre cenas" Basic.Settings.General.Multiview.DrawSourceNames="Mostrar nome das cenas" Basic.Settings.General.Multiview.DrawSafeAreas="Desenhar áreas seguras (EBU R 95)" -Basic.Settings.General.MultiviewLayout="Disposição de multivisualização" +Basic.Settings.General.MultiviewLayout="Esquema de multivisualização" Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, topo (8 cenas)" Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, fundo (8 cenas)" Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, esquerda (8 cenas)" @@ -777,8 +779,14 @@ Basic.Settings.Stream.Recommended.MaxFPS="Máximo FPS:%1" Basic.Settings.Stream.SpecifyCustomServer="Especificar o servidor personalizado" Basic.Settings.Stream.ServiceCustomServer="Servidor personalizado" Basic.Settings.Stream.EnableMultitrackVideo="Ativar %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Largura de banda máxima na transmissão" Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automático" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Máximo de pistas vídeo" Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automático" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Ativar descarga de transmissão para FLV (usa configurações simples de ficheiro de gravação)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Sobreposição de configuração (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Ativar sobreposição de configuração" +Basic.Settings.Stream.MultitrackVideoLabel="Video multipista" Basic.Settings.Stream.AdvancedOptions="Opções avançadas" Basic.Settings.Output="Saída" Basic.Settings.Output.Format="Formato de gravação" @@ -847,16 +855,16 @@ Basic.Settings.Output.CustomMuxerSettings="Definições personalizadas do conver Basic.Settings.Output.NoSpaceFileName="Gerar nome do ficheiro sem espaço" Basic.Settings.Output.Adv.Rescale="Redimensionar saída" Basic.Settings.Output.Adv.Rescale.Disabled="Desativado" -Basic.Settings.Output.Adv.AudioTrack="Faixa de áudio" +Basic.Settings.Output.Adv.AudioTrack="Pista de áudio" Basic.Settings.Output.Adv.Streaming="Transmissão" Basic.Settings.Output.Adv.Streaming.Settings="Configurações de transmissão" -Basic.Settings.Output.Adv.Audio.Track1="Faixa 1" -Basic.Settings.Output.Adv.Audio.Track2="Faixa 2" -Basic.Settings.Output.Adv.Audio.Track3="Faixa 3" -Basic.Settings.Output.Adv.Audio.Track4="Faixa 4" -Basic.Settings.Output.Adv.Audio.Track5="Faixa 5" -Basic.Settings.Output.Adv.Audio.Track6="Faixa 6" -Basic.Settings.Output.Adv.TwitchVodTrack="Faixa Twitch VOD" +Basic.Settings.Output.Adv.Audio.Track1="Pista 1" +Basic.Settings.Output.Adv.Audio.Track2="Pista 2" +Basic.Settings.Output.Adv.Audio.Track3="Pista 3" +Basic.Settings.Output.Adv.Audio.Track4="Pista 4" +Basic.Settings.Output.Adv.Audio.Track5="Pista 5" +Basic.Settings.Output.Adv.Audio.Track6="Pista 6" +Basic.Settings.Output.Adv.TwitchVodTrack="Pista Twitch VOD" Basic.Settings.Output.Adv.Encoder="Configurações do codificador" Basic.Settings.Output.Adv.Recording="Gravação" Basic.Settings.Output.Adv.Recording.Settings="Configurações de gravação" @@ -1053,7 +1061,7 @@ Basic.AdvAudio.Monitoring.None="Monitor desligado" Basic.AdvAudio.Monitoring.MonitorOnly="Só monitor (silenciar saída)" Basic.AdvAudio.Monitoring.Both="Monitor e saída" Basic.AdvAudio.MonitoringSource="Monitorização de áudio para '%1'" -Basic.AdvAudio.AudioTracks="Faixas" +Basic.AdvAudio.AudioTracks="Pistas" Basic.Settings.Hotkeys="Atalhos" Basic.Settings.Hotkeys.Pair="Combinações de teclas partilhadas com '%1' atuam como comutadores" Basic.Settings.Hotkeys.Filter="Filtro" @@ -1090,10 +1098,10 @@ Push-to-mute="Premir-para-silenciar" Push-to-talk="Premir-para-falar" SceneItemShow="Mostrar '%1'" SceneItemHide="Ocultar '%1'" -OutputWarnings.NoTracksSelected="Tem de selecionar pelo menos uma faixa" +OutputWarnings.NoTracksSelected="Tem de selecionar pelo menos uma pista" OutputWarnings.NoTracksSelectedOnExit.Title="Erro nas configurações de saída" -OutputWarnings.NoTracksSelectedOnExit.Text="Todas as saídas de áudio devem ter pelo menos uma faixa de som selecionada." -OutputWarnings.MP4Recording="Aviso: gravações em MP4/MOV serão irrecuperáveis se o ficheiro não puder ser finalizado (e.g., como resultado de um BSOD, perdas de energia, etc.). Se quiser gravar múltiplas faixas de áudio, considere usar MKV e converter a gravação para MP4/MOV, depois de ficar concluída (Ficheiro → Converter gravações)" +OutputWarnings.NoTracksSelectedOnExit.Text="Todas as saídas devem ter pelo menos uma pista de som selecionada." +OutputWarnings.MP4Recording="Aviso: gravações em MP4/MOV serão irrecuperáveis se o ficheiro não puder ser finalizado (e.g., como resultado de um BSOD, perdas de energia, etc.). Se quiser gravar múltiplas pistas de áudio, considere usar MKV e converter a gravação para MP4/MOV depois de ficar concluída (Ficheiro → Converter gravações)" OutputWarnings.CannotPause="Aviso: as gravações não podem ser paradas se o codificador estiver definido como \"(usar codificador da transmissão)\"" OutputWarnings.CodecIncompatible="A seleção do codificador de áudio ou vídeo foi redefinida devido a incompatibilidades. Selecione, na lista, um codificador compatível." CodecCompat.Incompatible="(Incompatível com %1)" @@ -1228,4 +1236,29 @@ YouTube.Errors.rateLimitExceeded="Está a enviar mensagens rápido demais!" YouTube.DocksRemoval.Title="Limpar docas de navegador legadas do YouTube" YouTube.DocksRemoval.Text="Estas docas de navegador serão removidas por serem obsoletas:\n\n%1\nEm vez disto, use \"Docas/Sala de controle ao vivo do YouTube\"." ConfigDownload.WarningMessageTitle="Aviso" +FailedToStartStream.MissingConfigURL="Nenhum URL de configuração disponível para o serviço atual" +FailedToStartStream.NoCustomRTMPURLInSettings="URL de RTMP personalizado não especificada" +FailedToStartStream.InvalidCustomConfig="Configuração personalizada inválida" +FailedToStartStream.FailedToCreateMultitrackVideoService="Falha ao criar um serviço de vídeo multipista" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Falha ao criar uma saída RTMP de vídeo multipista" +FailedToStartStream.EncoderNotAvailable="NVENC não disponível.\n\nFalha em encontrar o tipo de codificador '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Falha ao criar o codificador de vídeo '%1' (tipo: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Falha ao obter informações do vídeo OBS enquanto se criava o codificador '%1' (tipo: '%2')" FailedToStartStream.FailedToCreateAudioEncoder="Falha ao criar o codificador áudio" +FailedToStartStream.NoRTMPURLInConfig="A configuração não contém URL de RTMP de destino da transmissão" +FailedToStartStream.FallbackToDefault="Falha ao iniciar a transmissão usando %1. Deseja tentar novamente usando as configurações de codificação única?" +FailedToStartStream.ConfigRequestFailed="Não foi possível obter a configuração de %1

Erro HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Recebido um valor de estado desconhecido: '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nDeseja continuar a transmitir mesmo sem %1?" +FailedToStartStream.WarningRetry="\n

\nDeseja continuar a transmitir?" +FailedToStartStream.MissingEncoderConfigs="A configuração de transmissão ao vivo não incluiu as configurações do codificador" +FailedToStartStream.StatusMissingHTML="Solicitação de transmissão ao vivo devolveu um erro não especificado" +FailedToStartStream.NoConfigSupplied="Configuração em falta" +MultitrackVideo.Info="%1 otimiza automaticamente as suas configurações para codificar e enviar vídeo em múltiplas qualidades. Selecionar esta opção enviará %2 informações sobre a configuração do seu computador e software." +MultitrackVideo.IncompatibleSettings.Title="Configurações incompatíveis" +MultitrackVideo.IncompatibleSettings.Text="%1 ainda não é compatível com:\n\n%2:\nPara continuar a transmitir com %1, desative as configurações incompatíveis::\n\n%3:\ne inicie a transmissão novamente." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Desativar para esta transmissão e iniciar transmissão" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Atualizar configurações e iniciar transmissão" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 não é atualmente compatível com [Áudio → Geral → Canais] definido para '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Áudio → Geral → Canais] deve ser definido para '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 necessita de várias configurações diferentes para [Áudio → Geral → Canais]" diff --git a/UI/data/locale/ro-RO.ini b/UI/data/locale/ro-RO.ini index 3e228e62288458..2fe38b5905d120 100644 --- a/UI/data/locale/ro-RO.ini +++ b/UI/data/locale/ro-RO.ini @@ -1,5 +1,4 @@ Language="Română" -OK="Ok" Apply="Aplică" Cancel="Anulează" Close="Închide" @@ -106,7 +105,7 @@ Grid="Grilă" PluginsFailedToLoad.Title="Eroare la încărcarea pluginului" PluginsFailedToLoad.Text="Următoarele pluginuri OBS nu au putut fi încărcate:\n\n%1\nTe rugăm să actualizezi sau să elimini aceste pluginuri." AlreadyRunning.Title="OBS rulează deja" -AlreadyRunning.Text="OBS rulează deja! Dacă nu intenționezi să faci acest lucru, te rugăm să închizi toate instanțele OBS existente înainte de a încerca să rulezi o nouă instanță. Dacă ai OBS-ul setat să se minimizeze în bara de sistem, te rugam să verifici dacă rulează acolo." +AlreadyRunning.Text="OBS rulează deja! Cu excepția cazului în care intenționezi să faci acest lucru, te rugăm să închizi orice instanță OBS înainte de a încerca să rulezi o instanță nouă. Dacă ai OBS-ul setat pentru a fi minimizat în bara de sistem, te rugăm să verifici dacă încă rulează acolo." AlreadyRunning.LaunchAnyway="Lansează oricum" AutoSafeMode.Title="Mod sigur" AutoSafeMode.Text="OBS nu s-a închis în mod corespunzător în timpul ultimei tale sesiuni.\n\nDorești să pornești în modul sigur (pluginurile terțe, scripturile și WebSocketurile dezactivate)?" @@ -134,10 +133,10 @@ Auth.LoadingChannel.Text="Se încarcă informațiile canalului pentru %1, te rug Auth.LoadingChannel.Error="Nu s-au putut obține informații despre canal." Auth.ChannelFailure.Title="Încărcarea canalului a eșuat" Auth.ChannelFailure.Text="Încărcarea informațiilor canalului pentru %1 a eșuat\n\n%2: %3" -Auth.StreamInfo="Informații privind transmisiunea" +Auth.StreamInfo="Informații privind streamul" TwitchAuth.Stats="Statistici Twitch" TwitchAuth.Feed="Flux de activitate Twitch" -TwitchAuth.TwoFactorFail.Title="Nu se poate interoga cheia de transmisiune" +TwitchAuth.TwoFactorFail.Title="Nu se poate interoga cheia de stream" TwitchAuth.TwoFactorFail.Text="OBS nu a putut să se conecteze la contul tău Twitch. Te rugăm să te asiguri că autentificarea cu doi factori este configurată în setările de securitate din Twitch, deoarece acest lucru este necesar pentru a transmite." RestreamAuth.Channels="Canale Restream" Copy.Filters="Copiază filtrele" @@ -162,21 +161,21 @@ Basic.AutoConfig.VideoPage.BaseResolution.Display="Displayul %1 (%2x%3)" Basic.AutoConfig.VideoPage.FPS.UseCurrent="Folosește valoarea actuală (%1)" Basic.AutoConfig.VideoPage.FPS.PreferHighFPS="Fie 60 sau 30, însă prefer 60 când este posibil" Basic.AutoConfig.VideoPage.FPS.PreferHighRes="Fie 60 sau 30, însă prefer rezoluție înaltă" -Basic.AutoConfig.VideoPage.CanvasExplanation="Notă: Rezoluția (de bază) a planșei nu este neapărat aceeași cu rezoluția cu care vei transmite sau înregistra. Rezoluția efectivă de transmisiune/înregistrare poate fi sub-scalată de la rezoluția planșei pentru a reduce utilizarea de resurse sau cerințele privind rata de biți." -Basic.AutoConfig.StreamPage="Informații privind transmisiunea" -Basic.AutoConfig.StreamPage.SubTitle="Te rugăm să introduci informațiile pentru transmisiune" +Basic.AutoConfig.VideoPage.CanvasExplanation="Notă: Rezoluția (de bază) a planșei nu este neapărat aceeași cu rezoluția cu care vei face stream sau înregistra. Rezoluția efectivă de stream/înregistrare poate fi sub-scalată de la rezoluția planșei pentru a reduce utilizarea de resurse sau cerințele privind rata de biți." +Basic.AutoConfig.StreamPage="Informații privind streamul" +Basic.AutoConfig.StreamPage.SubTitle="Te rugăm să introduci informațiile pentru stream" Basic.AutoConfig.StreamPage.ConnectAccount="Conectează un cont (recomandat)" Basic.AutoConfig.StreamPage.DisconnectAccount="Deconectează contul" Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title="Deconectezi contul?" Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text="Această modificare se va aplica imediat. Sigur vrei să deconectezi contul?" -Basic.AutoConfig.StreamPage.GetStreamKey="Obține cheia de transmisiune" +Basic.AutoConfig.StreamPage.GetStreamKey="Obține cheia de stream" Basic.AutoConfig.StreamPage.MoreInfo="Mai multe informații" -Basic.AutoConfig.StreamPage.UseStreamKey="Folosește o cheie de transmisiune" -Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced="Folosește o cheie de transmisiune (avansat)" +Basic.AutoConfig.StreamPage.UseStreamKey="Folosește o cheie de stream" +Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced="Folosește o cheie de stream (avansat)" Basic.AutoConfig.StreamPage.Service="Serviciu" Basic.AutoConfig.StreamPage.Service.ShowAll="Afișează toate..." Basic.AutoConfig.StreamPage.Service.Custom="Personalizat..." -Basic.AutoConfig.StreamPage.StreamKey="Cheie de transmisiune" +Basic.AutoConfig.StreamPage.StreamKey="Cheie de stream" Basic.AutoConfig.StreamPage.StreamKey.ToolTip="RIST: introduceți fraza de criptare.\nRTMP: introduceți cheia furnizată de serviciu.\nSRT: introduceți streamid-ul, dacă serviciul utilizează unul." Basic.AutoConfig.StreamPage.EncoderKey="Cheie de codificare" Basic.AutoConfig.StreamPage.BearerToken="Token Bearer" @@ -184,7 +183,7 @@ Basic.AutoConfig.StreamPage.ConnectedAccount="Cont conectat" Basic.AutoConfig.StreamPage.PerformBandwidthTest="Estimează rata de biți cu o testare a lățimii de bandă (poate dura câteva minute)" Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferă codificarea hardware" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Codificarea hardware elimină cea mai mare parte a utilizării CPU, însă poate necesita o rată de biți mai mare pentru a obține același nivel de calitate." -Basic.AutoConfig.StreamPage.StreamWarning.Title="Avertisment privind transmisiunea" +Basic.AutoConfig.StreamPage.StreamWarning.Title="Avertisment privind streamul" Basic.AutoConfig.StreamPage.StreamWarning.Text="Testarea lățimii de bandă este pe cale să transmită date video aleatorii fără audio către canalul tău. Dacă ai posibilitatea, este recomandat să oprești temporar salvarea videoclipurilor transmisiunilor și să setezi transmisiunea pe privat până la încheierea testării. Continui?" Basic.AutoConfig.TestPage="Rezultate finale" Basic.AutoConfig.TestPage.SubTitle.Testing="Programul execută acum o serie de teste pentru estimarea celor mai ideale setări" @@ -198,7 +197,7 @@ Basic.AutoConfig.TestPage.TestingStreamEncoder="Se testează codificatorul de tr Basic.AutoConfig.TestPage.TestingRecordingEncoder="Se testează codificatorul de înregistrare, acest lucru poate dura un minut..." Basic.AutoConfig.TestPage.TestingRes.Fail="Eșec la pornirea codificatorului" Basic.AutoConfig.TestPage.TestingRes.Resolution="Se testează %1x%2, %3 FPS..." -Basic.AutoConfig.TestPage.Result.StreamingEncoder="Codificator de transmisiune" +Basic.AutoConfig.TestPage.Result.StreamingEncoder="Codificator de streaming" Basic.AutoConfig.TestPage.Result.RecordingEncoder="Codificator de înregistrare" Basic.AutoConfig.TestPage.Result.Header="Programul a determinat că aceste setări sunt cele mai ideale:" Basic.AutoConfig.TestPage.Result.Footer="Pentru a folosi aceste setări, dă clic pe Aplică setările. Pentru a reconfigura și a încerca din nou, dă clic pe Înapoi. Pentru a configura manual setările, apasă Anulează și deschide Setările." @@ -246,7 +245,7 @@ QuickTransitions.DuplicateSceneTT="La editarea aceleiași scene, permite editare QuickTransitions.EditProperties="Duplică scenele" QuickTransitions.EditPropertiesTT="La editarea aceleiași scene, permite editarea proprietăților surselor fără a modifica outputul programului.\nAceasta poate fi folosită numai dacă „Duplică scena” este activată.\nAnumite surse (cum ar fi sursele de captură sau media) nu suportă acest lucru și nu pot fi editate separat.\nModificarea acestei valori va reseta scena actuală a programului (dacă mai există).\n\nAvertisment: Deoarece sursele vor fi duplicate, acest lucru poate necesita resurse suplimentare de sistem sau video." QuickTransitions.HotkeyName="Tranziție rapidă: %1" -Basic.AddTransition="Adaugă tranziție configurabilă" +Basic.AddTransition="Adaugă o tranziție configurabilă" Basic.RemoveTransition="Elimină tranziția configurabilă" Basic.TransitionProperties="Proprietăți pentru tranziție" Basic.SceneTransitions="Tranziții pentru scene" @@ -340,8 +339,8 @@ Output.ConnectFail.HdrDisabled="Ieșirea HDR este oprită pentru această ieșir Output.ConnectFail.Error="A apărut o eroare neașteptată la încercarea de conectare la server. Mai multe informații în fișierul jurnal." Output.ConnectFail.Disconnected="Deconectat de la server." Output.StreamEncodeError.Title="Eroare privind codificarea" -Output.StreamEncodeError.Msg="A apărut o eroare de codificare în timpul transmisiunii." -Output.StreamEncodeError.Msg.LastError="Codificatorul a eșuat in timpul stream-ului

%1" +Output.StreamEncodeError.Msg="A apărut o eroare de codificare în timpul streamingului." +Output.StreamEncodeError.Msg.LastError="S-a produs o eroare cu codificatorul în timpul streamingului:

%1" Output.RecordFail.Title="Eșec la pornirea înregistrării" Output.RecordFail.Unsupported="Formatul de output este fie nesuportat, fie nu suportă mai mult de o pistă audio. Te rugăm să verifici setările şi să încerci din nou." Output.RecordNoSpace.Title="Spațiu insuficient pe disc" @@ -352,10 +351,10 @@ Output.RecordError.EncodeErrorMsg="A apărut o eroare de codificare în timpul Output.RecordError.EncodeErrorMsg.LastError="Codificatorul a eșuat in timpul înregistrării:

%1" Output.BadPath.Title="Calea fișierului greșită" Output.BadPath.Text="Calea configurată de înregistrare nu a putut fi deschisă. Verificați calea de înregistrare din meniul Setări → Ieșire → Înregistrare." -Output.NoBroadcast.Title="Nicio transmisiune configurată" -Output.NoBroadcast.Text="Trebuie să configurezi o transmisiune înainte de a putea porni streamingul." -Output.BroadcastStartFailed="Eșec la pornirea transmisiunii" -Output.BroadcastStopFailed="Eșec la oprirea transmisiunii" +Output.NoBroadcast.Title="Nicio difuzare configurată" +Output.NoBroadcast.Text="Trebuie să configurezi o difuzare înainte de a putea porni streamingul." +Output.BroadcastStartFailed="Eșec la pornirea difuzării" +Output.BroadcastStopFailed="Eșec la oprirea difuzării" LogReturnDialog="Jurnal încărcat cu succes" LogReturnDialog.Description="Fișierul jurnal a fost încărcat. Acum poți distribui adresa URL în scopuri de depanare sau de asistență." LogReturnDialog.Description.Crash="Raportul de defecțiune a fost încărcat. Acum poți distribui adresa URL în scopuri de depanare sau de asistență." @@ -426,7 +425,7 @@ Basic.Main.PreviewConextMenu.Enable="Activează previzualizarea" Basic.Main.Preview.Disable="Dezactivează previzualizarea" ScaleFiltering="Filtrare scalară" ScaleFiltering.Point="Punct" -ScaleFiltering.Bilinear="Bilineară" +ScaleFiltering.Bilinear="Biliniară" ScaleFiltering.Bicubic="Bicubică" ScaleFiltering.Area="Zonă" BlendingMethod="Metodă de amestec" @@ -488,7 +487,7 @@ Basic.PropertiesWindow.EditEditableListEntry="Editează intrarea de la '%1'" Basic.PropertiesView.FPS.Simple="Valori FPS simple" Basic.PropertiesView.FPS.Rational="Valori FPS raționale" Basic.PropertiesView.FPS.ValidFPSRanges="Intervale FPS valide:" -Basic.PropertiesView.UrlButton.Text="Deschideți acest link in browser-ul dvs. implicit?" +Basic.PropertiesView.UrlButton.Text="Deschizi acest link în browserul web implicit?" Basic.PropertiesView.UrlButton.OpenUrl="Deschide URL-ul" Basic.InteractionWindow="Interacționează cu '%1'" Basic.StatusBar.Reconnecting="Deconectat, se reconectează în %2 secundă(e) (încercarea %1)" @@ -501,7 +500,7 @@ Basic.StatusBar.DelayStartingStoppingIn="Întârziere (se oprește în %1 sec, p Basic.StatusBar.RecordingSavedTo="Înregistrare salvată în '%1'" Basic.StatusBar.ReplayBufferSavedTo="Buffer de reluare salvat în '%1'" Basic.StatusBar.ScreenshotSavedTo="Captură de ecran salvată în '%1'" -Basic.StatusBar.AutoRemuxedTo="Înregistrare remuxată automat la '%1'" +Basic.StatusBar.AutoRemuxedTo="Înregistrare remuxată automat în '%1'" Basic.Filters="Filtre" Basic.Filters.AsyncFilters="Filtre audio/video" Basic.Filters.AudioFilters="Filtre audio" @@ -552,6 +551,7 @@ Basic.Main.Scenes="Scene" Basic.Main.Sources="Surse" Basic.Main.Source="Sursă" Basic.Main.Controls="Comenzi" +Basic.Main.PreparingStream="Se pregătește..." Basic.Main.Connecting="Se conectează..." Basic.Main.StartRecording="Pornește înregistrarea" Basic.Main.StartReplayBuffer="Pornește bufferul de reluări" @@ -562,13 +562,14 @@ Basic.Main.StartVirtualCam="Pornește camera virtuală" Basic.Main.StopRecording="Oprește înregistrarea" Basic.Main.PauseRecording="Pune pe pauză înregistrarea" Basic.Main.UnpauseRecording="Scoate de pe pauză înregistrarea" -Basic.Main.SplitFile="Divizează fișierul de înregistrare" +Basic.Main.SplitFile="Separă fișierul înregistrării" +Basic.Main.AddChapterMarker="Adaugă un marcator de capitol (numai MP4 hibrid)" Basic.Main.StoppingRecording="Se oprește înregistrarea..." Basic.Main.StopReplayBuffer="Oprește bufferul de reluări" Basic.Main.StoppingReplayBuffer="Se oprește bufferul de reluări..." -Basic.Main.SetupBroadcast="Gestionează transmisiunea" +Basic.Main.SetupBroadcast="Gestionează difuzarea" Basic.Main.StopStreaming="Oprește streamingul" -Basic.Main.StopBroadcast="Încheie transmisiunea" +Basic.Main.StopBroadcast="Încheie difuzarea" Basic.Main.AutoStopEnabled="(Oprire automată)" Basic.Main.StoppingStreaming="Se oprește streamul..." Basic.Main.ForceStopStreaming="Oprește streamingul (renunță la întârziere)" @@ -584,7 +585,7 @@ Basic.Main.VirtualCamConfig="Configurare cameră virtuală" Basic.VCam.VirtualCamera="Cameră virtuală" Basic.VCam.OutputType="Tipul outputului" Basic.VCam.OutputSelection="Selectarea outputului" -Basic.VCam.OutputType.Program="Program (Implicit)" +Basic.VCam.OutputType.Program="Program (implicit)" Basic.VCam.RestartWarning="Camera virtuală va fi repornită pentru a aplica această modificare" Basic.MainMenu.File="&Fișier" Basic.MainMenu.File.Export="&Exportă" @@ -692,9 +693,9 @@ Basic.Settings.General.CenterSnapping="Aliniază sursele la centrul vertical și Basic.Settings.General.SourceSnapping="Aliniază sursele la alte surse" Basic.Settings.General.SnapDistance="Sensibilitatea alinierii" Basic.Settings.General.SpacingHelpers="Afișează ghidurile de aliniere a pixelilor" -Basic.Settings.General.RecordWhenStreaming="Înregistrează automat atunci când se face transmisiune" +Basic.Settings.General.RecordWhenStreaming="Înregistrează automat atunci când se face streaming" Basic.Settings.General.KeepRecordingWhenStreamStops="Continuă înregistrarea când streamul se oprește" -Basic.Settings.General.ReplayBufferWhileStreaming="Pornește automat bufferul de reluări atunci când se face transmisiune" +Basic.Settings.General.ReplayBufferWhileStreaming="Pornește automat bufferul de reluări atunci când se face streaming" Basic.Settings.General.KeepReplayBufferStreamStops="Păstrează activ bufferul de reluări când streamul se oprește" Basic.Settings.General.SysTray="Bară de sistem" Basic.Settings.General.SysTrayWhenStarted="Minimizează în bara de sistem la pornire" @@ -719,7 +720,7 @@ Basic.Settings.General.MultiviewLayout.Horizontal.Top="Orizontal, sus (8 scene)" Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Orizontal, jos (8 scene)" Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, stânga (8 scene)" Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, dreapta (8 scene)" -Basic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top="Orizontal, Sus (18 Scene)" +Basic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top="Orizontal, sus (18 scene)" Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top="Orizontal, sus (24 de scene)" Basic.Settings.General.MultiviewLayout.4Scene="Doar scene (4 scene)" Basic.Settings.General.MultiviewLayout.9Scene="Doar scene (9 scene)" @@ -729,7 +730,11 @@ Basic.Settings.General.ChannelName.stable="Stabil" Basic.Settings.General.ChannelDescription.stable="Ultima versiune stabilă" Basic.Settings.General.ChannelName.beta="Versiuni beta / release candidate" Basic.Settings.General.ChannelDescription.beta="Versiuni de pre-lansare potențial instabile" -Basic.Settings.Stream="Transmisiune" +Basic.Settings.Appearance="Aspect" +Basic.Settings.Appearance.General.Theme="Temă" +Basic.Settings.Appearance.General.Variant="Stil" +Basic.Settings.Appearance.General.NoVariant="Niciun stil disponibil" +Basic.Settings.Stream.Destination="Destinație" Basic.Settings.Stream.Custom.UseAuthentication="Folosește autentificarea" Basic.Settings.Stream.Custom.Username="Nume de utilizator" Basic.Settings.Stream.Custom.Password="Parolă" @@ -739,7 +744,7 @@ Basic.Settings.Stream.BandwidthTestMode="Activează modul de testare a lățimii Basic.Settings.Stream.TTVAddon="Suplimente pentru chatul Twitch" Basic.Settings.Stream.TTVAddon.None="Niciunul" Basic.Settings.Stream.TTVAddon.Both="BetterTTV și FrankerFaceZ" -Basic.Settings.Stream.MissingSettingAlert="Lipsește configurarea pentru transmisiune" +Basic.Settings.Stream.MissingSettingAlert="Lipsește configurarea pentru stream" Basic.Settings.Stream.StreamSettingsWarning="Deschide Setările" Basic.Settings.Stream.MissingUrlAndApiKey="URL-ul și cheia de transmisiune lipsesc.\n\nDeschide setările pentru a introduce URL-ul și cheia de transmisiune în fila Transmisiune." Basic.Settings.Stream.MissingUrl="URL-ul de transmisiune lipsește.\n\nDeschide setările pentru a introduce URL-ul în fila Transmisiune." @@ -751,11 +756,16 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Rată de biți video maximă: Basic.Settings.Stream.Recommended.MaxAudioBitrate="Rată de biți audio maximă: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Rezoluție maximă: %1" Basic.Settings.Stream.Recommended.MaxFPS="FPS maxim: %1" +Basic.Settings.Stream.SpecifyCustomServer="Specifică un server personalizat..." +Basic.Settings.Stream.ServiceCustomServer="Server personalizat" +Basic.Settings.Stream.AdvancedOptions="Opțiuni avansate" Basic.Settings.Output.Format="Format de înregistrare" +Basic.Settings.Output.Format.MKV="Video Matroska (.mkv)" +Basic.Settings.Output.Format.hMP4="MP4 hibrid [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="MP4 fragmentat (.mp4)" Basic.Settings.Output.Format.fMOV="MOV fragmentat (.mov)" -Basic.Settings.Output.Format.TT.fragmented_mov="MP4/MOV fragmentat scrie înregistrarea în bucăți și nu necesită aceeași finalizare ca și fișierele MP4/MOV tradiționale.\nAcest lucru asigură faptul că fișierul rămâne reproductibil chiar dacă scrierea pe disc este întreruptă, de exemplu, ca urmare a unui BSOD sau a unei pierderi de curent.\n\nEste posibil ca acest lucru să nu fie compatibil cu toate player-ele și programele de editare. Folosiți Fișier → Remuxează înregistrări pentru a converti fișierul într-un format mai compatibil, dacă este necesar." -Basic.Settings.Output.Format.TT.fragmented_mp4="MP4/MOV fragmentat scrie înregistrarea în bucăți și nu necesită aceeași finalizare ca și fișierele MP4/MOV tradiționale.\nAcest lucru asigură faptul că fișierul rămâne reproductibil chiar dacă scrierea pe disc este întreruptă, de exemplu, ca urmare a unui BSOD sau a unei pierderi de curent.\n\nEste posibil ca acest lucru să nu fie compatibil cu toate player-ele și programele de editare. Folosiți Fișier → Remuxează înregistrări pentru a converti fișierul într-un format mai compatibil, dacă este necesar." +Basic.Settings.Output.Format.TT.fragmented_mov="MOV fragmentat scrie înregistrarea în bucăți și nu necesită aceeași finalizare ca și fișierele MOV tradiționale.\nAcest lucru asigură faptul că fișierul rămâne reproductibil chiar dacă scrierea pe disc este întreruptă, de exemplu, ca urmare a unui BSOD sau a unei pierderi de curent.\n\nEste posibil ca acest lucru să nu fie compatibil cu toate playerele și programele de editare. Folosește Fișier → Remuxează înregistrări pentru a converti fișierul într-un format mai compatibil, dacă este necesar." +Basic.Settings.Output.Format.TT.fragmented_mp4="MP4 fragmentat scrie înregistrarea în bucăți și nu necesită aceeași finalizare ca și fișierele MP4 tradiționale.\nAcest lucru asigură faptul că fișierul rămâne reproductibil chiar dacă scrierea pe disc este întreruptă, de exemplu, ca urmare a unui BSOD sau a unei pierderi de curent.\n\nEste posibil ca acest lucru să nu fie compatibil cu toate playerele și programele de editare. Folosește Fișier → Remuxează înregistrări pentru a converti fișierul într-un format mai compatibil, dacă este necesar." Basic.Settings.Output.Encoder.Video="Codificator video" Basic.Settings.Output.Encoder.Audio="Codificator audio" Basic.Settings.Output.SelectDirectory="Selectează directorul de înregistrări" @@ -784,8 +794,8 @@ Basic.Settings.Output.Simple.RecordingQuality.Lossless="Calitate fără pierderi Basic.Settings.Output.Simple.Warn.VideoBitrate="Avertisment: Rata de biți pentru transmisiunea video va fi setată la %1, care este limita superioară pentru serviciul actual de transmisiune." Basic.Settings.Output.Simple.Warn.AudioBitrate="Avertisment: Rata de biți pentru transmisiunea audio va fi setată la %1, care este limita superioară pentru serviciul actual de transmisiune." Basic.Settings.Output.Simple.Warn.CannotPause="Avertisment: Înregistrările nu pot fi puse pe pauză dacă calitatea înregistrării este setată pe „La fel cu cea a transmisiunii”." -Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Avertisment: formatul de înregistrare selectat în prezent este incompatibil cu encoderul de streaming selectat." -Basic.Settings.Output.Simple.Warn.Encoder="Avertisment: Înregistrarea cu un codificator software la o calitate diferită de cea a transmisiunii va necesita o utilizare CPU crescută dacă transmiți şi înregistrezi în același timp." +Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Avertisment: formatul de înregistrare selectat în prezent este incompatibil cu codificatorul de streaming selectat." +Basic.Settings.Output.Simple.Warn.Encoder="Avertisment: Înregistrarea cu un codificator software la o calitate diferită de cea pentru stream va necesita o utilizare CPU extra dacă faci streaming și înregistrezi în același timp." Basic.Settings.Output.Simple.Warn.Lossless="Avertisment: Calitatea fără pierderi generează dimensiuni extrem de mari de fișiere! Calitatea fără pierderi poate folosi până la 7GB spațiu de disc per minut la frecvențe de cadre și rezoluții ridicate. Această calitate nu este recomandată pentru înregistrări lungi decât dacă ai o cantitate foarte mare de spațiu disponibil pe disc. Bufferul de reluări nu este disponibil atunci când se utilizează calitatea fără pierderi." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Sigur vrei să folosești calitatea fără pierderi?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Avertizare privind calitatea fără pierderi!" @@ -797,8 +807,8 @@ Basic.Settings.Output.Warn.EnforceResolutionFPS.Title="Rezoluție/Frecvența cad Basic.Settings.Output.Warn.EnforceResolutionFPS.Msg="Acest serviciu de transmisiune nu suportă rezoluția actuală a outputului și/sau frecvența cadrelor. Acestea vor fi schimbate la cea mai apropiată valoare compatibilă:\n\n%1\n\nVrei să continui?" Basic.Settings.Output.Warn.EnforceResolutionFPS.Resolution="Rezoluție: %1" Basic.Settings.Output.Warn.ServiceCodecCompatibility.Title="Codificator incompatibil" -Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg="Serviciul de transmisie în flux \"%1\" nu acceptă codificatorul \"%2\". Codificatorul va fi schimbat în \"%3\".\n\nDoriți să continuați?" -Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="Serviciul de transmisie în flux \"%1\" nu acceptă codificatorul \"%2\" și \"%3\". Codificatorul va fi schimbat în \"%4\"și \"%5\".\n\nDoriți să continuați?" +Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg="Serviciul de streaming „%1” nu suportă codificatorul „%2”. Codificatorul va fi schimbat în „%3”.\n\nVrei să continui?" +Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="Serviciul de streaming „%1” nu suportă codificatoarele „%2” și „%3”. Aceste codificatoare vor fi schimbate în „%4” și „%5”.\n\nVrei să continui?" Basic.Settings.Output.VideoBitrate="Rată de biți video" Basic.Settings.Output.AudioBitrate="Rată de biți audio" Basic.Settings.Output.Reconnect="Reconectare automată" @@ -815,8 +825,7 @@ Basic.Settings.Output.NoSpaceFileName="Generează nume de fișiere fără spați Basic.Settings.Output.Adv.Rescale="Rescalează outputul" Basic.Settings.Output.Adv.Rescale.Disabled="Dezactivat" Basic.Settings.Output.Adv.AudioTrack="Pistă audio" -Basic.Settings.Output.Adv.Streaming="Transmisiune" -Basic.Settings.Output.Adv.Streaming.Settings="Setări de transmisie în flux" +Basic.Settings.Output.Adv.Streaming.Settings="Setări pentru streaming" Basic.Settings.Output.Adv.Audio.Track1="Pistă 1" Basic.Settings.Output.Adv.Audio.Track2="Pistă 2" Basic.Settings.Output.Adv.Audio.Track3="Pistă 3" @@ -830,7 +839,7 @@ Basic.Settings.Output.Adv.Recording.Settings="Setări de înregistrare" Basic.Settings.Output.Adv.Recording.RecType="Tip de înregistrare" Basic.Settings.Output.Adv.Recording.Type="Tip" Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Output personalizat (FFmpeg)" -Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Folosește codificatorul de transmisiune)" +Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Folosește codificatorul de stream)" Basic.Settings.Output.Adv.Recording.Filename="Formatarea numelui pentru fișiere" Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Suprascrie în cazul în care fișierul există" Basic.Settings.Output.Adv.FFmpeg.Type="Tipul outpului FFmpeg" @@ -853,12 +862,12 @@ Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Setările muxerului (dacă exist Basic.Settings.Output.Adv.FFmpeg.GOPSize="Interval de cadre cheie (cadre)" Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Afișează toate codecurile (chiar dacă sunt potențial incompatibile)" Basic.Settings.Output.Adv.FFmpeg.Settings="Setări FFmpeg" -Basic.Settings.Output.EnableSplitFile="Divizarea automată a fișierelor" -Basic.Settings.Output.SplitFile.TypeTime="Divizat în funcție de timp" -Basic.Settings.Output.SplitFile.TypeSize="Divizat după mărime" -Basic.Settings.Output.SplitFile.TypeManual="Se divizează numai manual" -Basic.Settings.Output.SplitFile.Time="Timp divizat" -Basic.Settings.Output.SplitFile.Size="Mărime divizată" +Basic.Settings.Output.EnableSplitFile="Separarea automată a fișierelor" +Basic.Settings.Output.SplitFile.TypeTime="Separă după durată de timp" +Basic.Settings.Output.SplitFile.TypeSize="Separă după dimensiune" +Basic.Settings.Output.SplitFile.TypeManual="Separă numai manual" +Basic.Settings.Output.SplitFile.Time="Durata pentru separare" +Basic.Settings.Output.SplitFile.Size="Dimensiunea pentru separare" Screenshot="Realizează o captură de ecran a outputului" Screenshot.SourceHotkey="Realizează o captură de ecran a sursei selectate" Screenshot.StudioProgram="Realizează o captură de ecran (Program)" @@ -903,7 +912,7 @@ Basic.Settings.Video.Numerator="Numărător" Basic.Settings.Video.Denominator="Numitor" Basic.Settings.Video.InvalidResolution="Valoare nevalidă pentru rezoluție. Trebuie să fie [lățime]x[înălțime] (de ex., 1920x1080)" Basic.Settings.Video.CurrentlyActive="Outputul video este în prezent activ. Te rugăm să oprești orice output pentru a schimba setările video." -Basic.Settings.Video.DownscaleFilter.Bilinear="Biliniar (Cel mai rapid, dar neclar în cazul scalării)" +Basic.Settings.Video.DownscaleFilter.Bilinear="Biliniar (cel mai rapid, dar neclar la scalare)" Basic.Settings.Video.DownscaleFilter.Bicubic="Bicubic (Scalare ascuțită, 16 mostre)" Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Scalare ascuțită, 36 de mostre)" Basic.Settings.Video.DownscaleFilter.Area="Zonă (Sumă ponderată, 4/6/9 mostre)" @@ -982,12 +991,12 @@ Basic.Settings.Advanced.Video.HdrNominalPeakLevel="Nivel de vârf nominal HDR" Basic.Settings.Advanced.Audio.MonitoringDevice="Dispozitiv de monitorizare" Basic.Settings.Advanced.Audio.MonitoringDevice.Default="Implicit" Basic.Settings.Advanced.Audio.DisableAudioDucking="Dezactivează efectul audio de ducking din Windows" -Basic.Settings.Advanced.StreamDelay="Întârziere pentru transmisiune" +Basic.Settings.Advanced.StreamDelay="Întârziere pentru stream" Basic.Settings.Advanced.StreamDelay.Duration="Durată" Basic.Settings.Advanced.StreamDelay.Preserve="Păstrează punctul de tăiere (crește întârzierea) la reconectare" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilizare estimată a memoriei: %1 MB" Basic.Settings.Advanced.Network="Rețea" -Basic.Settings.Advanced.Network.Disabled="Protocolul de transmisie în flux selectat în prezent nu acceptă modificarea setărilor de rețea." +Basic.Settings.Advanced.Network.Disabled="Protocolul de streaming selectat în prezent nu suportă modificarea setărilor de rețea." Basic.Settings.Advanced.Network.BindToIP="Leagă de IP" Basic.Settings.Advanced.Network.IPFamily="Familie IP" Basic.Settings.Advanced.Network.EnableNewSocketLoop="Activează optimizările pentru rețea" @@ -997,7 +1006,7 @@ Basic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior="Comportamentul focalizării Basic.Settings.Advanced.Hotkeys.NeverDisableHotkeys="Nu dezactiva niciodată tastele rapide" Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="Dezactivează tastele rapide când fereastra principală este focalizată" Basic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus="Dezactivează tastele rapide când fereastra principală nu este focalizată" -Basic.Settings.Advanced.AutoRemux="Remux automat la %1" +Basic.Settings.Advanced.AutoRemux="Remuxează automat în %1" Basic.Settings.Advanced.AutoRemux.MP4="(înregistrează ca mkv)" Basic.AdvAudio="Proprietăți audio avansate" Basic.AdvAudio.ActiveOnly="Numai surse active" @@ -1020,7 +1029,7 @@ Basic.Settings.Hotkeys.Pair="Combinațiile de taste partajate cu '%1' acționeaz Basic.Settings.Hotkeys.Filter="Filtrează" Basic.Settings.Hotkeys.FilterByHotkey="Filtrează după tasta rapidă" Basic.Settings.Hotkeys.DuplicateWarning="Acest hotkey este folosit de una sau mai multe acțiuni, click pentru a afișa conflictele" -Basic.Settings.Hotkeys.PleaseWait="Se încarcă tastele rapide, așteptați..." +Basic.Settings.Hotkeys.PleaseWait="Se încarcă tastele rapide, te rugăm să aștepți..." Basic.Hotkeys.SelectScene="Comută la scenă" Basic.SystemTray.Show="Afișează" Basic.SystemTray.Hide="Ascunde" @@ -1053,26 +1062,26 @@ SceneItemShow="Afișează „%1”" SceneItemHide="Ascunde „%1”" OutputWarnings.NoTracksSelected="Trebuie să selectezi cel puțin o pistă" OutputWarnings.MP4Recording="Avertisment: Înregistrările salvate în MP4/MOV nu vor putea fi recuperate dacă fișierul nu poate fi finalizat (de ex., ca urmare a BSOD-urilor, întreruperilor de alimentare etc.). Dacă vrei să înregistrezi mai multe piste audio, ia în considerare folosirea MKV și remuxează înregistrarea în MP4/MOV după terminare (Fișier → Remuxează înregistrări)" -OutputWarnings.CannotPause="Avertisment: Înregistrările nu pot fi puse pe pauză dacă codificatorul de înregistrare este setat pe „(Folosește codificatorul de transmisiune)”" +OutputWarnings.CannotPause="Avertisment: Înregistrările nu pot fi puse pe pauză dacă codificatorul de înregistrare este setat pe „(Folosește codificatorul de stream)”" OutputWarnings.CodecIncompatible="Selecția codificatorului audio sau video a fost resetată din cauza unei incompatibilități. Vă rugăm să selectați un codificator compatibil din listă." CodecCompat.Incompatible="(Incompatibil cu %1)" CodecCompat.CodecPlaceholder="Selectați Codificatorul..." CodecCompat.ContainerPlaceholder="Selectați Formatul..." CodecCompat.CodecMissingOnExit.Title="Nu este selectat niciun codificator" -CodecCompat.CodecMissingOnExit.Text="Cel puțin un codificator video sau audio nu este setat. Asigurați-vă că selectați codificatoare atât pentru înregistrare, cât și pentru transmisiune." +CodecCompat.CodecMissingOnExit.Text="Cel puțin un codificator video sau audio nu este setat. Te rugăm să te asiguri că selectezi codificatoare atât pentru înregistrare, cât și pentru streaming." CodecCompat.ContainerMissingOnExit.Title="Nu este selectat niciun format" -CodecCompat.ContainerMissingOnExit.Text="Nu a fost selectat niciun format de înregistrare. Selectați un format de înregistrare care este compatibil cu codificatorul de transmisiune selectat." +CodecCompat.ContainerMissingOnExit.Text="Nu a fost selectat niciun format de înregistrare. Te rugăm să selectezi un format de înregistrare care este compatibil cu codificatorul de stream selectat." FinalScene.Title="Șterge scena" FinalScene.Text="Trebuie să existe cel puțin o scenă." NoSources.Title="Nicio sursă" NoSources.Text="Se pare că nu ai adăugat încă nicio sursă video, așa că se va afișa doar un ecran negru. Sigur vrei să faci asta?" -NoSources.Text.AddSource="Poți adăuga surse dând clic pe pictograma + din caseta Surse în fereastra principală, în orice moment." +NoSources.Text.AddSource="Poți adăuga oricând surse dând clic pe pictograma + de sub caseta Surse din fereastra principală." NoSources.Label="Nu ai nicio sursă. Dă clic pe\nbutonul + de mai jos sau\nclic dreapta aici pentru a adăuga una." ChangeBG="Setează culoarea" CustomColor="Culoare personalizată" BrowserSource.EnableHardwareAcceleration="Activează accelerarea hardware pentru Browser Source" About="Despre" -About.Info="OBS Studio este un program gratuit și open-source de înregistrare video și transmisiune live." +About.Info="OBS Studio este un program gratuit și open-source pentru înregistrare video și streaming live." About.Donate="Contribuie" About.GetInvolved="Implică-te" About.Authors="Autori" @@ -1111,11 +1120,11 @@ YouTube.Auth.NoChannels="Nici un canal(e) disponibil(e) în contul selectat" YouTube.Auth.WaitingAuth.Title="Autorizare pentru utilizatorul YouTube" YouTube.Auth.WaitingAuth.Text="Te rugăm să completezi autorizarea în browserul extern.
Dacă browserul extern nu se deschide, urmează acest link și completează autorizarea:
%1" YouTube.AuthError.Text="Eșuare obținere informații despre canal: %1" -YouTube.Actions.WindowTitle="Configurarea transmisiunii YouTube - Canal: %1" -YouTube.Actions.CreateNewEvent="Creează o nouă transmisiune" -YouTube.Actions.ChooseEvent="Selectează o transmisiune existentă" +YouTube.Actions.WindowTitle="Configurarea difuzării pe YouTube - Canal: %1" +YouTube.Actions.CreateNewEvent="Creează o nouă difuzare" +YouTube.Actions.ChooseEvent="Selectează o difuzare existentă" YouTube.Actions.Title="Titlu*" -YouTube.Actions.MyBroadcast="Transmisiunea mea" +YouTube.Actions.MyBroadcast="Difuzarea mea" YouTube.Actions.Description="Descriere" YouTube.Actions.Privacy="Confidențialitate*" YouTube.Actions.Privacy.Private="Privat" @@ -1135,54 +1144,55 @@ YouTube.Actions.Latency.Low="Scăzută" YouTube.Actions.Latency.UltraLow="Ultrascăzută" YouTube.Actions.EnableAutoStart="Activează pornirea automată" YouTube.Actions.EnableAutoStop="Activează oprirea automată" -YouTube.Actions.AutoStartStop.TT="Indică dacă această transmisiune programată ar trebui să pornească automat" +YouTube.Actions.AutoStartStop.TT="Indică dacă această difuzare programată ar trebui să pornească automat" YouTube.Actions.EnableDVR="Activează DVR" YouTube.Actions.360Video="Videoclip la 360°" YouTube.Actions.ScheduleForLater="Programează pentru mai târziu" YouTube.Actions.RememberSettings="Ține minte aceste setări" -YouTube.Actions.Create_Ready="Creează transmisiunea" -YouTube.Actions.Create_GoLive="Creează transmisiunea și pornește streamingul" -YouTube.Actions.Choose_Ready="Selectează o transmisiune" -YouTube.Actions.Choose_GoLive="Selectează transmisiunea și pornește streamingul" -YouTube.Actions.Create_Schedule="Programează transmisiunea" -YouTube.Actions.Create_Schedule_Ready="Programează și selectează transmisiunea" +YouTube.Actions.Create_Ready="Creează difuzarea" +YouTube.Actions.Create_GoLive="Creează difuzarea și pornește streamingul" +YouTube.Actions.Choose_Ready="Selectează o difuzare" +YouTube.Actions.Choose_GoLive="Selectează difuzarea și pornește streamingul" +YouTube.Actions.Create_Schedule="Programează difuzarea" +YouTube.Actions.Create_Schedule_Ready="Programează și selectează difuzarea" YouTube.Actions.Dashboard="Deschide YouTube Studio" -YouTube.Actions.Error.Title="Eroare la crearea transmisiunii live" +YouTube.Actions.Error.Title="Eroare la crearea difuzării live" YouTube.Actions.Error.Text="Eroare acces YouTube '%1'.
Descrierea detaliată a erorii poate fi găsită la https://developers.google.com/youtube/v3/live/docs/errors" YouTube.Actions.Error.General="Eroare de acces YouTube. Verificați conexiunea la rețea sau accesul la serverul YouTube." -YouTube.Actions.Error.NoBroadcastCreated="Eroare creare transmisiune \"%1\".
Descrierea detaliată a erorii poate fi găsită la https://developers.google.com/youtube/v3/live/docs/errors" -YouTube.Actions.Error.NoStreamCreated="Nici un flux creat. Vă rugăm să vă reconectați contul." +YouTube.Actions.Error.NoBroadcastCreated="Eroare la crearea difuzării '%1'.
Descrierea detaliată a erorii poate fi găsită la https://developers.google.com/youtube/v3/live/docs/errors" +YouTube.Actions.Error.NoStreamCreated="Niciun stream creat. Te rugăm să reconectezi contul." YouTube.Actions.Error.YouTubeApi="Eroare API YouTube. Consultați fișierul jurnal pentru mai multe informații." -YouTube.Actions.Error.BroadcastNotFound="Transmisiunea selectată nu a fost găsită." +YouTube.Actions.Error.BroadcastNotFound="Difuzarea selectată nu a fost găsită." YouTube.Actions.Error.FileMissing="Fișierul selectat nu există." YouTube.Actions.Error.FileOpeningFailed="Deschiderea fișierului selectat nu a reușit." YouTube.Actions.Error.FileTooLarge="Fișierul selectat este prea mare (Limita: 2MiB)." -YouTube.Actions.Error.BroadcastTransitionFailed="Transitionarea emisiunii a eșuat: %1

Dacă eroarea persistă deschide transmisiunea în YouTube Studio și încearcă manual." -YouTube.Actions.Error.BroadcastTestStarting="Transmisia trece la etapa de testare, acest lucru poate dura ceva timp. Vă rugăm să încercați din nou în 10-30 de secunde." +YouTube.Actions.Error.BroadcastTransitionFailed="Tranziția spre difuzare a eșuat: %1

Dacă această eroare persistă, deschide difuzarea în YouTube Studio și încearcă manual." +YouTube.Actions.Error.BroadcastTestStarting="Difuzarea face tranziția la etapa de testare, acest lucru poate dura ceva timp. Te rugăm să încerci din nou în 10-30 de secunde." YouTube.Actions.EventsLoading="Încarcare listă de evenimente..." YouTube.Actions.EventCreated.Title="Eveniment creat" YouTube.Actions.EventCreated.Text="Eveniment creat cu succes." -YouTube.Actions.Stream="Transmisiune" YouTube.Actions.Stream.ScheduledFor="Programat(ă) pentru %1" YouTube.Actions.Stream.Resume="Reluare flux întrerupt" YouTube.Actions.Stream.YTStudio="Creat(ă) automat de YouTube Studio" -YouTube.Actions.Notify.CreatingBroadcast="Se creează o nouă transmisiune live, te rugăm să aștepți..." +YouTube.Actions.Notify.CreatingBroadcast="Se creează o nouă difuzare live, te rugăm să aștepți..." YouTube.Actions.AutoStartStreamingWarning.Title="Pornire manuală necesară" -YouTube.Actions.AutoStartStreamingWarning="Pornirea automată este dezactivată pentru acest eveniment, dă click pe „Intră live” pentru a porni transmisiunea." +YouTube.Actions.AutoStartStreamingWarning="Pornirea automată este dezactivată pentru acest eveniment, dă clic pe „Intră live” pentru a porni difuzarea." YouTube.Actions.AutoStopStreamingWarning="Nu te vei putea reconecta.
Streamul tău se va opri și nu vei mai fi live." YouTube.Chat.Input.Send="Trimite" YouTube.Chat.Input.Placeholder="Introdu mesajul aici..." YouTube.Chat.Input.Sending="Se trimite..." YouTube.Chat.Error.Title="Eroare la trimiterea mesajului" YouTube.Chat.Error.Text="Mesajul nu a putut fi trimis: %1" -YouTube.Errors.liveStreamingNotEnabled="Transmisiunea în direct nu este activată pe canalul YouTube selectat.

Veziyoutube.com/features pentru mai multe informații." -YouTube.Errors.livePermissionBlocked="Transmisiunea în direct nu este disponibilă pe canalul dvs. YouTube selectat
Vă rugăm să rețineți că poate dura până la 24 de ore pentru a deveni disponibil după activarea acesteia în setările canalului.

Vezi youtube.com/features pentru detalii." +YouTube.Errors.liveStreamingNotEnabled="Streamingul live nu este activat pe canalul de YouTube selectat.

Veziyoutube.com/features pentru mai multe informații." +YouTube.Errors.livePermissionBlocked="Streamingul live este indisponibil pe canalul de YouTube selectat.
Te rugăm să reții că poate dura până la 24 de ore pentru a deveni disponibil după activarea acesteia în setările canalului tău.

Vezi youtube.com/features pentru detalii." YouTube.Errors.errorExecutingTransition="Tranziția a eșuat din cauza unei erori din fundal. Încercați din nou în câteva secunde." YouTube.Errors.errorStreamInactive="YouTube nu primește date pentru stream-ul tău. Verificați configurația și încercați din nou." -YouTube.Errors.invalidTransition="Încercarea de tranziție nu a fost validă. Acest lucru se poate datora faptului că fluxul nu a încheiat o tranziție anterioară. Aşteptați câteva secunde, apoi încercați din nou." +YouTube.Errors.invalidTransition="Încercarea de tranziție a fost nevalidă. Acest lucru se poate datora faptului că streamul nu a încheiat o tranziție anterioară. Te rugăm să aștepți câteva secunde și să încerci din nou." YouTube.Errors.liveChatDisabled="Discuția în direct este dezactivată pe acest stream." -YouTube.Errors.liveChatEnded="Transmisiunea în direct s-a încheiat." +YouTube.Errors.liveChatEnded="Streamul live s-a încheiat." YouTube.Errors.messageTextInvalid="Conținutul mesajului nu este valid." YouTube.Errors.rateLimitExceeded="Trimiteți mesaje prea repede." YouTube.DocksRemoval.Title="Șterge vechile andocări de browser ale YouTube" YouTube.DocksRemoval.Text="Aceste andocări de browser vor fi eliminate ca fiind depreciate:\n\n%1\nFolosește „Andocări/YouTube Live Control Room” în schimb." +ConfigDownload.WarningMessageTitle="Avertisment" +MultitrackVideo.IncompatibleSettings.Title="Setări incompatibile" diff --git a/UI/data/locale/ru-RU.ini b/UI/data/locale/ru-RU.ini index 2c91002c62f49f..90e5b26e7d4530 100644 --- a/UI/data/locale/ru-RU.ini +++ b/UI/data/locale/ru-RU.ini @@ -213,6 +213,7 @@ Basic.AutoConfig.TestPage.Result.Header="Программа определила Basic.AutoConfig.TestPage.Result.Footer="Для использования этих настроек нажмите «Применить настройки». Для повторной настройки с помощью мастера нажмите «Назад». Чтобы задать настройки самостоятельно, нажмите «Отмена» и откройте «Настройки»." Basic.AutoConfig.Info="Мастер автонастройки определит оптимальные параметры на основе особенностей вашего компьютера и скорости Интернета." Basic.AutoConfig.RunAnytime="Его можно запустить снова из меню «Сервис»." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Разрешение трансляции (масштабированное)" Basic.Stats="Статистика" Basic.Stats.CPUUsage="Использование ЦП" Basic.Stats.HDDSpaceAvailable="Доступно места на диске" @@ -569,6 +570,7 @@ Basic.Main.Scenes="Сцены" Basic.Main.Sources="Источники" Basic.Main.Source="Источник" Basic.Main.Controls="Управление" +Basic.Main.PreparingStream="Подготовка..." Basic.Main.Connecting="Соединение..." Basic.Main.StartRecording="Начать запись" Basic.Main.StartReplayBuffer="Запустить повтор" @@ -580,7 +582,7 @@ Basic.Main.StopRecording="Остановить запись" Basic.Main.PauseRecording="Приостановить запись" Basic.Main.UnpauseRecording="Возобновить запись" Basic.Main.SplitFile="Разделить файл записи" -Basic.Main.AddChapterMarker="Добавить метку главы" +Basic.Main.AddChapterMarker="Добавить разделительный маркер (только гибридный MP4)" Basic.Main.StoppingRecording="Остановка записи..." Basic.Main.StopReplayBuffer="Остановить повтор" Basic.Main.StoppingReplayBuffer="Остановка повтора..." @@ -689,6 +691,7 @@ Basic.MainMenu.Help.About="О приложении (&A)" Basic.Settings.ProgramRestart="Чтобы эти настройки вступили в силу, требуется перезапустить программу." Basic.Settings.ConfirmTitle="Подтверждение изменений" Basic.Settings.Confirm="У вас есть несохранённые изменения. Сохранить их?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 контролирует некоторые настройки Вашей трансляции" Basic.Settings.General="Общие" Basic.Settings.General.Language="Язык" Basic.Settings.General.Updater="Обновления" @@ -777,7 +780,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Максимальный би Basic.Settings.Stream.Recommended.MaxAudioBitrate="Максимальный битрейт аудио: %1 кбит/с" Basic.Settings.Stream.Recommended.MaxResolution="Максимальное разрешение: %1" Basic.Settings.Stream.Recommended.MaxFPS="Максимум частоты кадров: %1" +Basic.Settings.Stream.SpecifyCustomServer="Укажите пользовательский сервер..." +Basic.Settings.Stream.ServiceCustomServer="Пользовательский сервер" Basic.Settings.Stream.EnableMultitrackVideo="Включить %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Максимальная пропускная способность трансляции" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Авто" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Максимум видеодорожек" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Авто" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Включить дамп трансляции в FLV (использует простые настройки записи)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Перезаписывать файл конфигурации (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Включить перезапись конфигурации" +Basic.Settings.Stream.MultitrackVideoLabel="Многодорожечное видео" +Basic.Settings.Stream.AdvancedOptions="Расширенные настройки" Basic.Settings.Output="Вывод" Basic.Settings.Output.Format="Формат записи" Basic.Settings.Output.Format.MKV="Видеоформат «Матрёшка» (.mkv)" @@ -1247,3 +1261,30 @@ YouTube.Errors.messageTextInvalid="Недопустимый текст сооб YouTube.Errors.rateLimitExceeded="Вы отправляете сообщения слишком быстро." YouTube.DocksRemoval.Title="Убрать устаревшие доки браузера YouTube" YouTube.DocksRemoval.Text="Эти доки браузера будут удалены как устаревшие:\n\n%1\nИспользуйте вместо них «Доки / Комнату управления эфиром YouTube»." +ConfigDownload.WarningMessageTitle="Внимание" +FailedToStartStream.MissingConfigURL="Для текущей службы нет доступного URL-адреса" +FailedToStartStream.NoCustomRTMPURLInSettings="Пользовательский URL-адрес RTMP не указан" +FailedToStartStream.InvalidCustomConfig="Неверная пользовательская конфигурация" +FailedToStartStream.FailedToCreateMultitrackVideoService="Не удалось создать сервис многодорожечного видео" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Не удалось создать многодорожечный RTMP-вывод видео" +FailedToStartStream.EncoderNotAvailable="NVENC недоступен.\n\nНе удалось найти тип кодировщика «%1»" +FailedToStartStream.FailedToCreateVideoEncoder="Не удалось создать кодировщик видео «%1» (тип: «%2»)" +FailedToStartStream.FailedToGetOBSVideoInfo="Не удалось получить сведения о видео OBS во время создания кодировщика «%1» (тип: «%2»)" +FailedToStartStream.FailedToCreateAudioEncoder="Не удалось создать кодировщик звука" +FailedToStartStream.NoRTMPURLInConfig="В конфигурации отсутствует URL-адрес целевого потока RTMP(S) " +FailedToStartStream.FallbackToDefault="Не удалось начать трансляцию используя %1; хотите ли Вы попробовать снова используя одиночные настройки кодирования?" +FailedToStartStream.ConfigRequestFailed="Не удалось получить конфигурацию с %1

Ошибка HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Получено неизвестное значение состояния «%1»" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nХотите продолжить трансляцию без %1?" +FailedToStartStream.WarningRetry="\n

\nХотите ли Вы продолжить трансляцию?" +FailedToStartStream.MissingEncoderConfigs="В конфигурации трансляции отсутствуют настройки кодировщика" +FailedToStartStream.StatusMissingHTML="Запрос о начале трансляции привел к неизвестной ошибке" +FailedToStartStream.NoConfigSupplied="Отсутствует конфигурация" +MultitrackVideo.Info="%1 автоматически оптимизирует Ваши настройки для кодирования и отправки видео разного качества. Выбор этого параметра приведет к отправке на %2 информации о конфигурации Вашего компьютера и настройках программы." +MultitrackVideo.IncompatibleSettings.Title="Несовместимые настройки" +MultitrackVideo.IncompatibleSettings.Text="%1 на данный момент не совместим с:\n\n%2\nЧтобы продолжить вести трансляцию с %1, отключите несовместимые настройки:\n\n%3\nи начните трансляцию снова." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Отключить для этого потока и начать трансляцию" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Обновить настройки и начать трансляцию" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 сейчас не совместимо с [Аудио → Общие → Каналы] равным «%2», %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Аудио → Общие → Каналы] надо настроить на «%1»" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 требует несколько разных настроек для [Аудио → Общие → Каналы]" diff --git a/UI/data/locale/sk-SK.ini b/UI/data/locale/sk-SK.ini index b6a61dd8ec3e84..ee619d6ba412a1 100644 --- a/UI/data/locale/sk-SK.ini +++ b/UI/data/locale/sk-SK.ini @@ -103,6 +103,7 @@ MixerToolbarMenu="Menu zvukového zmiešavača" SceneFilters="Otvoriť filtre scény" List="Zoznam" Grid="Mriežka" +Automatic="Automatický" PluginsFailedToLoad.Title="Chyba pri načítaní pluginu" PluginsFailedToLoad.Text="Nasledujúce OBS pluginy sa nepodarilo načítať:\n\n%1\nProsím aktualizujte alebo odstráňte tieto pluginy." AlreadyRunning.Title="OBS je už spustený" @@ -187,6 +188,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Preferovať hardvérové enk Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hardvérové enkódovanie eliminuje väčšinu využitia CPU, ale na dosiahnutie rovnakej kvality videa môže vyžadovať väčší bitrate." Basic.AutoConfig.StreamPage.StreamWarning.Title="Upozornenie o streame" Basic.AutoConfig.StreamPage.StreamWarning.Text="Test rýchlosti pripojenia vysiela obraz bez zvuku s náhodnými dátami. Odporúčame dočasne zakázať záznam vysielaní a nastaviť živý prenos ako súkromný, kým sa celý test nedokončí. Pokračovať?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Testovať %1" Basic.AutoConfig.TestPage="Konečné výsledky" Basic.AutoConfig.TestPage.SubTitle.Testing="Program práve vykonáva sériu testov pre odhad optimálneho nastavenia" Basic.AutoConfig.TestPage.SubTitle.Complete="Testovanie dokončené" @@ -205,6 +207,7 @@ Basic.AutoConfig.TestPage.Result.Header="Program zistil, že nasledujúce nastav Basic.AutoConfig.TestPage.Result.Footer="Pre použitie týchto nastavení kliknite na Použiť. Pre úpravu nastavení sprievodcu kliknite na Späť. Ak si želáte nastaviť program manuálne, kliknite na Zrušiť a následne na tlačidlo Nastavenia." Basic.AutoConfig.Info="Sprievodca automatickou konfiguráciou určí najlepšie nastavenia na základe špecifikácií vášho počítača a rýchlosti internetu." Basic.AutoConfig.RunAnytime="Túto funkciu môžete spustiť kedykoľvek v ponuke Nástroje." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Streamovacie (škálované) rozlíšenie" Basic.Stats="Štatistiky" Basic.Stats.CPUUsage="Využitie CPU" Basic.Stats.HDDSpaceAvailable="Dostupné miesto na disku" @@ -557,6 +560,7 @@ Basic.Main.Scenes="Scény" Basic.Main.Sources="Zdroje" Basic.Main.Source="Zdroj" Basic.Main.Controls="Ovládacie prvky" +Basic.Main.PreparingStream="Pripravuje sa..." Basic.Main.Connecting="Pripájanie..." Basic.Main.StartRecording="Spustiť nahrávanie" Basic.Main.StartReplayBuffer="Spustiť záznam do pamäte" @@ -568,6 +572,7 @@ Basic.Main.StopRecording="Ukončiť nahrávanie" Basic.Main.PauseRecording="Pozastaviť nahrávanie" Basic.Main.UnpauseRecording="Pokračovať v nahrávaní" Basic.Main.SplitFile="Rozdeliť nahrávací súbor" +Basic.Main.AddChapterMarker="Pridať značku kapitoly (iba pre Hybridný MP4)" Basic.Main.StoppingRecording="Zastavenie nahrávania..." Basic.Main.StopReplayBuffer="Zastaviť záznam do pamäte" Basic.Main.StoppingReplayBuffer="Zastavujem záznam do pamäte..." @@ -676,6 +681,7 @@ Basic.MainMenu.Help.About="O &aplikácii" Basic.Settings.ProgramRestart="Tieto nastavenia sa prejavia až po reštarte programu." Basic.Settings.ConfirmTitle="Potvrdenie zmien" Basic.Settings.Confirm="Máte neuložené zmeny. Chcete uložiť zmeny?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 ovláda niektoré z vašich streamovacích nastavení" Basic.Settings.General="Všeobecné" Basic.Settings.General.Language="Jazyk" Basic.Settings.General.Updater="Aktualizácie" @@ -740,6 +746,7 @@ Basic.Settings.Appearance.General="Všeobecné" Basic.Settings.Appearance.General.Theme="Téma" Basic.Settings.Appearance.General.Variant="Štýl" Basic.Settings.Appearance.General.NoVariant="Žiadne dostupné štýly" +Basic.Settings.Stream.Destination="Destinácia" Basic.Settings.Stream.Custom.UseAuthentication="Použiť overenie" Basic.Settings.Stream.Custom.Username="Užívateľské meno" Basic.Settings.Stream.Custom.Password="Heslo" @@ -761,8 +768,21 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximálny Video Bitrate: %1 Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximálny Bitrate zvuku: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximálne rozlíšenie: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximálne FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Špecifikovať vlastný server…" +Basic.Settings.Stream.ServiceCustomServer="Vlastný server" +Basic.Settings.Stream.EnableMultitrackVideo="Zapnúť %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximálna šírka pásma streamovania" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Automaticky" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maximálny počet video stôp" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Automaticky" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Zapnúť výpis streamu do FLV (používa jednoduché nahrávacie nastavenia súboru)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Prepísanie konfigurácie (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Zapnúť prepísanie konfigurácie" +Basic.Settings.Stream.MultitrackVideoLabel="Viacstopé video" +Basic.Settings.Stream.AdvancedOptions="Pokročilé možnosti" Basic.Settings.Output="Výstup" Basic.Settings.Output.Format="Formát nahrávania" +Basic.Settings.Output.Format.hMP4="Hybridný MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Fragmentovaný MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Fragmentovaný MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Fragmentovaný MOV zapisuje nahrávku v kusoch a nevyžaduje rovnakú finalizáciu ako tradičné MOV súbory.\nToto zabezpečí, že súbor ostane prehrávateľný aj keď je zapisovanie na disk prerušené, napríklad pri BSOD alebo strate napájania.\n\nToto však nemusí byť kompatibilné so všetkými prehrávačmi a editormi. Použite Súbor -> Previesť nahrávky pre konvertovanie súboru do viac kompatibilného formátu ak je to potrebné." @@ -1214,3 +1234,30 @@ YouTube.Errors.messageTextInvalid="Text správy je neplatný." YouTube.Errors.rateLimitExceeded="Posielate správy príliš rýchlo!" YouTube.DocksRemoval.Title="Odstrániť pôvodné doky YouTube prehliadača" YouTube.DocksRemoval.Text="Tieto doky prehliadača budú odstránené ako zastaralé:\n\n%1\nPoužite \"Doky/YouTube Live Control Room\" namiesto toho." +ConfigDownload.WarningMessageTitle="Upozornenie" +FailedToStartStream.MissingConfigURL="Žiadna dostupná konfiguračná URL adresa pre aktuálnu službu" +FailedToStartStream.NoCustomRTMPURLInSettings="Vlastná RTMP URL adresa nebola špecifikovaná" +FailedToStartStream.InvalidCustomConfig="Neplatná vlastná konfigurácia" +FailedToStartStream.FailedToCreateMultitrackVideoService="Zlyhalo vytvorenie viacstopovej video služby" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Zlyhalo vytvorenie viacstopového RTMP video výstupu" +FailedToStartStream.EncoderNotAvailable="NVENC nedostupný.\n\nNebolo možné nájsť enkodér typu '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Zlyhalo vytvorenie video enkodéra '%1' (typ: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Zlyhalo získanie OBS video informácií počas vytvárania enkodéra '%1' (typ: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Zlyhalo vytvorenie audio enkodéra" +FailedToStartStream.NoRTMPURLInConfig="Konfigurácia neobsahuje cieľovú streamovaciu RTMP(S) URL adresu" +FailedToStartStream.FallbackToDefault="Spustenie streamu s použitím %1 zlyhalo; chcete to skúsiť znova s použitím nastavení jedného kódovania?" +FailedToStartStream.ConfigRequestFailed="Nebolo možné získať konfiguráciu z %1

HTTP chyba: %2" +FailedToStartStream.WarningUnknownStatus="Prijatá neznáma hodnota stavu '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nChcete pokračovať v streamovaní bez %1?" +FailedToStartStream.WarningRetry="\n

\nChcete pokračovať v streamovaní?" +FailedToStartStream.MissingEncoderConfigs="Konfigurácia spustenia streamovania neobsahovala konfigurácie enkodéra" +FailedToStartStream.StatusMissingHTML="Požiadavka pre začatie vysielania vrátila nešpecifikovanú chybu" +FailedToStartStream.NoConfigSupplied="Chýbajúca konfigurácia" +MultitrackVideo.Info="%1 automaticky optimalizuje vaše nastavenia pre enkódovanie a odosielanie viacerých kvalít videa. Vybratím tejto možnosti pošle %2 informácie o vašom počítači a nastavenie softvéru." +MultitrackVideo.IncompatibleSettings.Title="Nekompatibilné nastavenia" +MultitrackVideo.IncompatibleSettings.Text="%1 nie je momentálne kompatibilný s:\n\n%2\nPre pokračovanie streamovania s %1, vypnite nekompatibilné nastavenia:\n\n%3\na spustite streamovanie znova." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Vypnúť pre tento stream a začať streamovanie" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Aktualizovať nastavenia a začať streamovanie" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 nie je momentálne kompatibilný s [Zvuk → Všeobecné → Kanály] nastavené na '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Zvuk → Všeobecné → Kanály] je potrebné nastaviť na '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 vyžaduje viacero rôznych nastavení pre [Zvuk → Všeobecné → Kanály]" diff --git a/UI/data/locale/sl-SI.ini b/UI/data/locale/sl-SI.ini index 84194ccb18aaf5..3c13d4c80f7c0a 100644 --- a/UI/data/locale/sl-SI.ini +++ b/UI/data/locale/sl-SI.ini @@ -104,6 +104,7 @@ MixerToolbarMenu="Meni mešalnika zvoka" SceneFilters="Odpti filtre prizora" List="Seznam" Grid="Mreža" +Automatic="Samodejno" PluginsFailedToLoad.Title="Napaka pri nalaganju vstavka" PluginsFailedToLoad.Text="Naslednji vstavki OBS se niso uspešno naložili.\n\n%1\nTe vstavke posodobite ali odstranite." AlreadyRunning.Title="OBS se že izvaja" @@ -562,6 +563,7 @@ Basic.Main.Scenes="Prizori" Basic.Main.Sources="Viri" Basic.Main.Source="Vir" Basic.Main.Controls="Nadzorne tipke" +Basic.Main.PreparingStream="V pripravi ..." Basic.Main.Connecting="Povezovanje …" Basic.Main.StartRecording="Začni snemati" Basic.Main.StartReplayBuffer="Zaženi medpomnilnik za ponovitev" @@ -747,6 +749,7 @@ Basic.Settings.Appearance.General.Theme="Tema" Basic.Settings.Appearance.General.Variant="Slog" Basic.Settings.Appearance.General.NoVariant="Slogi niso na voljo" Basic.Settings.Stream="Pretok" +Basic.Settings.Stream.Destination="Cilj" Basic.Settings.Stream.Custom.UseAuthentication="Uporabi overitev" Basic.Settings.Stream.Custom.Username="Uporabniško ime" Basic.Settings.Stream.Custom.Password="Geslo" @@ -768,6 +771,11 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Največja bitna hitrost videa Basic.Settings.Stream.Recommended.MaxAudioBitrate="Največja bitna hitrost zvoka: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Največja ločljivost: %1" Basic.Settings.Stream.Recommended.MaxFPS="Največje število sl./s: %1" +Basic.Settings.Stream.ServiceCustomServer="Strežnik po meri" +Basic.Settings.Stream.EnableMultitrackVideo="Omogoči %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Samodejno" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Samodejno" +Basic.Settings.Stream.AdvancedOptions="Napredne možnosti" Basic.Settings.Output="Izhod" Basic.Settings.Output.Format="Format posnetkov" Basic.Settings.Output.Format.MKV="Video Matroska (.mkv)" @@ -1237,3 +1245,4 @@ YouTube.Errors.messageTextInvalid="Besedilo sporočila ni veljavno." YouTube.Errors.rateLimitExceeded="Sporočila pošiljate prehitro!" YouTube.DocksRemoval.Title="Počisti opuščene zložene zavihke brskalnika YouTube" YouTube.DocksRemoval.Text="Ti zloženi zavihki brskalnika bodo odstranjeni kot opuščeni:\n\n%1\nNamesto tega uporabite »Zavihki/režija YouTube«." +ConfigDownload.WarningMessageTitle="Opozorilo" diff --git a/UI/data/locale/sr-SP.ini b/UI/data/locale/sr-SP.ini index 9f27982642ec1c..2ca7a8e859c7af 100644 --- a/UI/data/locale/sr-SP.ini +++ b/UI/data/locale/sr-SP.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="Мени за aудио миксовање" SceneFilters="Отвори сценске филтере" List="Списак" Grid="Мрежни приказ" +Automatic="Аутоматски" PluginsFailedToLoad.Title="Грешка приликом учитавања додатне компоненте" PluginsFailedToLoad.Text="Следеће OBS додатне компоненте нису успеле да се учитају:\n\n%1\nМолимо Вас ажурирајте их или их уклоните." AlreadyRunning.Title="OBS је већ покренут" @@ -193,6 +194,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Са акцентом на Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Хардверско кодирање спречава велику употребу процесора, али може захтевати већу брзину протока како би се одржао исти ниво квалитета." Basic.AutoConfig.StreamPage.StreamWarning.Title="Упозорење о стримовању" Basic.AutoConfig.StreamPage.StreamWarning.Text="Тест пропусног опсега стримује насумичне видео податке без звука на Вашем каналу. Препоручено је да привремено искључите чување видео снимака стримова и да подесите стрим као приватни док се тест не заврши, ако је то могуће. Желите ли да наставите?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Тестирај %1" Basic.AutoConfig.TestPage="Коначни резултати" Basic.AutoConfig.TestPage.SubTitle.Testing="Програм тренутно извршава сет тестова како би проценио оптимална подешавања" Basic.AutoConfig.TestPage.SubTitle.Complete="Тестирање је завршено" diff --git a/UI/data/locale/sv-SE.ini b/UI/data/locale/sv-SE.ini index ef59b8ae08cbc8..c0913c0a9ac4e4 100644 --- a/UI/data/locale/sv-SE.ini +++ b/UI/data/locale/sv-SE.ini @@ -188,6 +188,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Föredra hårdvarukodning" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Hårdvarukodning eliminerar den mesta processoranvändningen, men kan kräva mer bithastighet för att uppnå samma kvalitetsnivå." Basic.AutoConfig.StreamPage.StreamWarning.Title="Strömvarning" Basic.AutoConfig.StreamPage.StreamWarning.Text="Bandbreddstestet kommer att strömma slumpad videodata utan ljud till din kanal. Om det fungerar, är det rekommenderat det att tillfälligt inaktivera att spara videor av strömmar och ändra strömmen till privat tills testet är färdigt. Fortsätta?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Testa %1" Basic.AutoConfig.TestPage="Slutgiltiga resultat" Basic.AutoConfig.TestPage.SubTitle.Testing="Programmet utför nu en grupp tester för att uppskatta de mest idealiska inställningarna" Basic.AutoConfig.TestPage.SubTitle.Complete="Testet slutfördes" @@ -554,7 +555,7 @@ Basic.Main.Scenes="Scener" Basic.Main.Sources="Källor" Basic.Main.Source="Källa" Basic.Main.Controls="Kontroller" -Basic.Main.PreparingStream="Förbereder ..." +Basic.Main.PreparingStream="Förbereder..." Basic.Main.Connecting="Ansluter..." Basic.Main.StartRecording="Börja spela in" Basic.Main.StartReplayBuffer="Starta reprisbuffert" @@ -566,7 +567,7 @@ Basic.Main.StopRecording="Sluta spela in" Basic.Main.PauseRecording="Pausa inspelning" Basic.Main.UnpauseRecording="Återuppta inspelning" Basic.Main.SplitFile="Dela upp inspelningsfil" -Basic.Main.AddChapterMarker="Lägg till kapitelmarkör" +Basic.Main.AddChapterMarker="Lägg till kapitelmarkör (endast Hybrid MP4)" Basic.Main.StoppingRecording="Slutar spela in..." Basic.Main.StopReplayBuffer="Stoppa reprisbuffert" Basic.Main.StoppingReplayBuffer="Stoppar reprisbuffert..." @@ -763,7 +764,7 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Maximal videobithastighet: %1 Basic.Settings.Stream.Recommended.MaxAudioBitrate="Maximal ljudbithastighet: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Maximal upplösning: %1" Basic.Settings.Stream.Recommended.MaxFPS="Maximal bildfrekvens: %1" -Basic.Settings.Stream.SpecifyCustomServer="Specificera anpassad server..." +Basic.Settings.Stream.SpecifyCustomServer="Ange anpassad server..." Basic.Settings.Stream.ServiceCustomServer="Anpassad server" Basic.Settings.Stream.EnableMultitrackVideo="Aktivera %1" Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Maximal strömningsbandbredd" @@ -772,6 +773,7 @@ Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Aktivera strömdumpning t Basic.Settings.Stream.MultitrackVideoConfigOverride="Överskrid konfiguration (JSON)" Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Aktivera överskridande av konfiguration" Basic.Settings.Stream.MultitrackVideoLabel="Flerspårsvideo" +Basic.Settings.Stream.AdvancedOptions="Avancerade alternativ" Basic.Settings.Output="Utmatning" Basic.Settings.Output.Format="Inspelningsformat" Basic.Settings.Output.Format.MKV="Matroska-video (.mkv)" @@ -1229,3 +1231,30 @@ YouTube.Errors.messageTextInvalid="Meddelandetexten är ogiltig." YouTube.Errors.rateLimitExceeded="Du skickar meddelanden för snabbt." YouTube.DocksRemoval.Title="Rensa bakåtkompatibelt flytande webbläsarfönster för YouTube" YouTube.DocksRemoval.Text="Dessa flytande webbläsarfönster kommer att tas bort p.g.a. utfasning:\n\n%1\nAnvänd \"Flytande fönster/Kontrollrummet på YouTube\" istället." +ConfigDownload.WarningMessageTitle="Varning" +FailedToStartStream.MissingConfigURL="Ingen konfigurerings-URL är tillgänglig för den nuvarande tjänsten" +FailedToStartStream.NoCustomRTMPURLInSettings="Ingen anpassad webbadress för RTMP har angetts" +FailedToStartStream.InvalidCustomConfig="Ogiltig anpassad konfiguration" +FailedToStartStream.FailedToCreateMultitrackVideoService="Misslyckades att skapa videotjänst med flera spår" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Misslyckades att skapa RTMP-utmatning för video med flera spår" +FailedToStartStream.EncoderNotAvailable="NVENC är inte tillgänglig.\n\nMisslyckades att hitta kodartypen \"%1\"" +FailedToStartStream.FailedToCreateVideoEncoder="Misslyckades att skapa videokodaren \"%1\" (typ: \"%2\")" +FailedToStartStream.FailedToGetOBSVideoInfo="Misslyckades att hämta OBS-videoinformation när kodaren \"%1\" (typ: \"%2\") skapades" +FailedToStartStream.FailedToCreateAudioEncoder="Misslyckades att skapa ljudkodare" +FailedToStartStream.NoRTMPURLInConfig="Konfigurationen innehåller inte RTMP(S)-webbadress för strömmålet" +FailedToStartStream.FallbackToDefault="Misslyckades att starta strömmen med %1. Vill du försöka igen med inställningar för en enda kodare?" +FailedToStartStream.ConfigRequestFailed="Kunde inte hämta konfiguration från %1

HTTP-fel: %2" +FailedToStartStream.WarningUnknownStatus="Fick det okända statusvärdet \"%1\"" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nVill du fortsätta strömma utan %1?" +FailedToStartStream.WarningRetry="\n

\nVill du fortsätta strömma?" +FailedToStartStream.MissingEncoderConfigs="Konfiguration för Direktsänd innehöll inte kodningskonfiguration" +FailedToStartStream.StatusMissingHTML="Begäran om direktsändning returnerade ett ospecificerat fel" +FailedToStartStream.NoConfigSupplied="Konfiguration saknas" +MultitrackVideo.Info="%1 optimerar automatiskt dina inställningar för att koda och skicka flera videokvaliteter. Om detta alternativ väljs kommer %2-information om din dator och mjukvaruinstallation att skickas." +MultitrackVideo.IncompatibleSettings.Title="Inkompatibla inställningar" +MultitrackVideo.IncompatibleSettings.Text="%1 är för närvarande inkompatibelt med:\n\n%2\nFör att fortsätta strömma med %1, inaktivera inkompatibla inställningar:\n\n%3\noch Börja strömma igen." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Inaktivera för denna ström och börja strömma" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Uppdatera inställningar och börja strömma" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 är för tillfället inkompatibel med [Ljud → Allmänt → Kanaler] inställt på \"%2\", %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Ljud → Allmänt → Kanaler] måste vara inställt på \"%1\"" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 kräver flera olika inställningar för [Ljud → Allmänt → Kanaler]" diff --git a/UI/data/locale/th-TH.ini b/UI/data/locale/th-TH.ini index 3746b53516da9e..99d423927d43e5 100644 --- a/UI/data/locale/th-TH.ini +++ b/UI/data/locale/th-TH.ini @@ -108,6 +108,7 @@ MixerToolbarMenu="เมนูตัวแต่งเสียง" SceneFilters="เปิดฟิลเตอร์ฉาก" List="รายชื่อ" Grid="ตาราง" +Automatic="อัตโนมัติ" PluginsFailedToLoad.Title="การโหลดปลั๊กอินเกิดข้อผิดพลาด" PluginsFailedToLoad.Text="ไม่สามารถโหลดปลั๊กอิน OBS ต่อไปนี้ได้:\n\n%1\nโปรดปรับรุ่นหรือลบปลั๊กอินเหล่านี้" AlreadyRunning.Title="OBS กำลังทำงานอยู่" diff --git a/UI/data/locale/tr-TR.ini b/UI/data/locale/tr-TR.ini index 871ee9463627fa..66532dc8e208ac 100644 --- a/UI/data/locale/tr-TR.ini +++ b/UI/data/locale/tr-TR.ini @@ -105,6 +105,7 @@ MixerToolbarMenu="Ses Karıştırıcısı Menüsü" SceneFilters="Sahne Filtrelerini Aç" List="Liste" Grid="Izgara" +Automatic="Otomatik" PluginsFailedToLoad.Title="Eklenti Yükleme Hatası" PluginsFailedToLoad.Text="Aşağıdaki OBS eklentileri yüklenemedi:\n\n%1\nLütfen bu eklentileri güncelleyin veya silin." AlreadyRunning.Title="OBS zaten çalışıyor" @@ -190,6 +191,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Donanım kodlamayı tercih e Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Donanım Kodlama çoğu CPU kullanımını ortadan kaldırır, ancak aynı kalite düzeyini elde etmek için daha fazla bit hızı gerektirebilir." Basic.AutoConfig.StreamPage.StreamWarning.Title="Yayın uyarısı" Basic.AutoConfig.StreamPage.StreamWarning.Text="Bant genişliği testi kanalınızda ses olmayan rastgele video verisi yayını yapmak üzere. Yapabiliyorsanız, test bitene kadar geçici bir süre yayın kaydını kapatmanız ve yayını gizli olarak ayarlamanız önerilir. Devam edilsin mi?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Deneme %1" Basic.AutoConfig.TestPage="Sonuçlar" Basic.AutoConfig.TestPage.SubTitle.Testing="Program en uygun ayarları tahmin etmek için şimdi bir dizi test yürütüyor" Basic.AutoConfig.TestPage.SubTitle.Complete="Test tamamlandı" @@ -208,6 +210,7 @@ Basic.AutoConfig.TestPage.Result.Header="Program tahmin edilen bu ayarların siz Basic.AutoConfig.TestPage.Result.Footer="Bu ayarları kullanmak için, Ayarları Uygula'ya tıklayın. Sihirbazı yeniden yapılandırmak ve denemek için Geri'ye tıklayın. Ayarları kendiniz elle yapılandırmak için İptal'e tıklayın ve Ayarlar'ı açın." Basic.AutoConfig.Info="Otomatik yapılandırma sihirbazı, bilgisayar özellikleriniz ve internet hızınıza göre en iyi ayarlara karar verecektir." Basic.AutoConfig.RunAnytime="Bu, Araçlar menüsüne gidilerek herhangi bir zaman çalıştırılabilir." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Yayın (Ölçeklendirilmiş) Çözünürlüğü" Basic.Stats="İstatistikler" Basic.Stats.CPUUsage="İşlemci Kullanımı" Basic.Stats.HDDSpaceAvailable="Mevcut disk alanı" @@ -554,6 +557,7 @@ Basic.Main.Scenes="Sahneler" Basic.Main.Sources="Kaynaklar" Basic.Main.Source="Kaynak" Basic.Main.Controls="Kontroller" +Basic.Main.PreparingStream="Hazırlanıyor..." Basic.Main.Connecting="Bağlanıyor..." Basic.Main.StartRecording="Kaydı Başlat" Basic.Main.StartReplayBuffer="Tekrar Oynatma Arabelleğini Başlat" @@ -565,6 +569,7 @@ Basic.Main.StopRecording="Kaydı Durdur" Basic.Main.PauseRecording="Kaydı Duraklat" Basic.Main.UnpauseRecording="Kaydı Devam Ettir" Basic.Main.SplitFile="Kayıt Dosyasını Böl" +Basic.Main.AddChapterMarker="Bölüm İşaretçisi Ekle (sadece Hibrit MP4 türünde)" Basic.Main.StoppingRecording="Kayıt Durduruluyor..." Basic.Main.StopReplayBuffer="Tekrar Oynatma Arabelleğini Durdur" Basic.Main.StoppingReplayBuffer="Tekrar Oynatma Arabelleği Durduruluyor..." @@ -673,6 +678,7 @@ Basic.MainMenu.Help.About="H&akkında" Basic.Settings.ProgramRestart="Programın, bu ayarların etkinleşmesi için yeniden başlatılması gerekir." Basic.Settings.ConfirmTitle="Değişiklikleri Onayla" Basic.Settings.Confirm="Kayıt edilmemiş değişiklikleriniz var. Değişiklikler kayıt edilsin mi?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 yayın ayarlarınızın bazılarını kontrol ediyor" Basic.Settings.General="Genel" Basic.Settings.General.Language="Dil" Basic.Settings.General.Updater="Güncellemeler" @@ -739,6 +745,7 @@ Basic.Settings.Appearance.General.Theme="Tema" Basic.Settings.Appearance.General.Variant="Stil" Basic.Settings.Appearance.General.NoVariant="Hiç stil mevcut değil." Basic.Settings.Stream="Yayın" +Basic.Settings.Stream.Destination="Hedef" Basic.Settings.Stream.Custom.UseAuthentication="Kimlik doğrulaması kullan" Basic.Settings.Stream.Custom.Username="Kullanıcı adı" Basic.Settings.Stream.Custom.Password="Şifre" @@ -760,8 +767,21 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="En Yüksek Vidyo Bit Hızı: Basic.Settings.Stream.Recommended.MaxAudioBitrate="En Yüksek Ses Bit Hızı: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="En Yüksek Çözünürlük: %1" Basic.Settings.Stream.Recommended.MaxFPS="En Yüksek FPS: %1" +Basic.Settings.Stream.SpecifyCustomServer="Özel Sunucuyu Belirt..." +Basic.Settings.Stream.ServiceCustomServer="Özel Sunucu" +Basic.Settings.Stream.EnableMultitrackVideo="%1'i Etkinleştir" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="En Yüksek Akış Hızı" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Otomatik" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Maksimum Video Parçaları" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Otomatik" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Yayını FLV formatında kaydetmeyi etkinleştir (basit kayıt dosyası ayarlarını kullanır)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Yapılandırmayı Geçersiz Kıl (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Yapılandırmayı Geçersiz Kılmayı Etkinleştir" +Basic.Settings.Stream.MultitrackVideoLabel="Çok kanallı Video" +Basic.Settings.Stream.AdvancedOptions="Gelişmiş Seçenekler" Basic.Settings.Output="Çıkış" Basic.Settings.Output.Format="Kayıt Biçimi" +Basic.Settings.Output.Format.hMP4="Hibrit MP4 [BETA] (.mp4)" Basic.Settings.Output.Format.fMP4="Parçalı MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Parçalı MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Parçalanmış MOV kaydı parçalar halinde yazar ve geleneksel MOV dosyalarıyla aynı son işlemeyi gerektirmez.\nBu, örneğin bir BSOD (mavi ekran) veya güç kaybı sonucunda diske yazma işlemi kesintiye uğrasa bile dosyanın oynatılabilir kalmasını sağlar.\n\nBu, tüm oynatıcılar ve düzenleyicilerle uyumlu olmayabilir. Gerekirse dosyayı daha uyumlu bir biçime dönüştürmek için Dosya → Kayıtları Remux Et seçeneğini kullanın." @@ -1219,3 +1239,30 @@ YouTube.Errors.messageTextInvalid="Mesaj metni geçerli değil." YouTube.Errors.rateLimitExceeded="Mesajları fazla hızlı gönderiyorsun." YouTube.DocksRemoval.Title="Eski Youtube Tarayıcı Yuvalarını Temizle" YouTube.DocksRemoval.Text="Bu tarayıcı yuvaları kullanım dışı bırakıldığından dolayı kaldırılıcaktır:\n\n%1\nBunun yerine \"Yuvalar/Youtube Canlı Kontrol Odası\" kullanınız." +ConfigDownload.WarningMessageTitle="Uyarı" +FailedToStartStream.MissingConfigURL="Şu anki hizmet için herhangi bir konfigürasyon URL'si bulunamadı." +FailedToStartStream.NoCustomRTMPURLInSettings="Özel RTMP URL'si Belirtilmemiş" +FailedToStartStream.InvalidCustomConfig="Geçersiz özel yapılandırma" +FailedToStartStream.FailedToCreateMultitrackVideoService="Çok kanallı video hizmeti oluşturulamadı" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Çok kanallı video RTMP çıkışı oluşturulamadı" +FailedToStartStream.EncoderNotAvailable="NVENC kullanılamıyor.\n\n'%1' kodlayıcı türü bulunamadı" +FailedToStartStream.FailedToCreateVideoEncoder="'%1' video kodlayıcı oluşturulamadı (tür: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="'%1' kodlayıcı oluşturulurken OBS video bilgisi alınamadı (tür: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Ses kodlayıcı oluşturulamadı" +FailedToStartStream.NoRTMPURLInConfig="Yapılandırma, akış hedefi RTMP(S) URL'sini içermiyor" +FailedToStartStream.FallbackToDefault="Yayını %1 kullanarak başlatmak başarısız oldu; Tek kodlama ayarlarını kullanarak yeniden denemek ister misiniz?" +FailedToStartStream.ConfigRequestFailed="%1'den yapılandırma getirilemedi

HTTP hatası: %2" +FailedToStartStream.WarningUnknownStatus="bilinmeyen durum değeri '%1' alındı" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n%1 olmadan yayına devam etmek istiyor musunuz?" +FailedToStartStream.WarningRetry="\n

\nYayına devam etmek istiyor musunuz?" +FailedToStartStream.MissingEncoderConfigs="Canlı yayına geçiş yapılandırması, kodlayıcı yapılandırmalarını içermiyor" +FailedToStartStream.StatusMissingHTML="Canlı yayına geçme isteği belirtilmemiş bir hata döndürdü" +FailedToStartStream.NoConfigSupplied="Eksik yapılandırma" +MultitrackVideo.Info="%1, birden fazla video kalitesini kodlamak ve göndermek için ayarlarınızı otomatik olarak optimize eder. Bu seçeneği seçtiğinizde bilgisayarınız ve yazılım kurulumunuz hakkında %2 bilgi gönderilecektir." +MultitrackVideo.IncompatibleSettings.Title="Uyumsuz Ayarlar" +MultitrackVideo.IncompatibleSettings.Text="%1 şu anda şununla uyumlu değil:\n\n%2\n%1 ile yayına devam etmek için uyumsuz ayarları devre dışı bırakın:\n\n%3\nve Yayını yeniden başlatın." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Bu yayın için devre dışı bırakın ve Yayını Başlatın" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Ayarları Güncelleyin ve Yayını Başlatın" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 şu anda '%2', %3 olarak ayarlanmış [Ses → Genel → Kanallar] ile uyumlu değil" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Ses → Genel → Kanallar] '%1' olarak ayarlanmalı" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1, [Ses → Genel → Kanallar] için birden fazla farklı ayar gerektirir" diff --git a/UI/data/locale/tt-RU.ini b/UI/data/locale/tt-RU.ini index f9ca84df58af2f..2db212d919b4d7 100644 --- a/UI/data/locale/tt-RU.ini +++ b/UI/data/locale/tt-RU.ini @@ -24,6 +24,7 @@ Show="Күрсәтү" Hide="Яшерү" Untitled="Исемсез" New="Яңа" +Duplicate="Кабатлау" Enable="Кушу" Left="Сул" Right="Уң" @@ -35,6 +36,7 @@ Deprecated="Искергән" Import="Импортлау" Export="Экспортлау" Copy="Күчермә алу" +Paste="Кую" Next="Алга" Back="Артка" None="Юк" diff --git a/UI/data/locale/ug-CN.ini b/UI/data/locale/ug-CN.ini index 7092a02befc3f9..c957b52dbc8ce1 100644 --- a/UI/data/locale/ug-CN.ini +++ b/UI/data/locale/ug-CN.ini @@ -108,9 +108,11 @@ MixerToolbarMenu="ئۈن بىرىكتۈرگۈچ تىزىملىكى" SceneFilters="كۆرۈنۈش سۈزگۈچنى ئاچ" List="تىزىم" Grid="سېتكا" +Automatic="ئاپتوماتىك" PluginsFailedToLoad.Title="قىستۇرما يۈكلەش خاتالىقى" PluginsFailedToLoad.Text="تۆۋەندىكى OBS قىستۇرمىنى يۈكلىيەلمىدى: \n\n%1\nبۇ قىستۇرمىنى يېڭىلاڭ ياكى چىقىرىۋېتىڭ." AlreadyRunning.Title="OBS ئىجرا قىلىنىۋاتىدۇ!" +AlreadyRunning.Text="OBS ئىجرا قىلىنىۋاتىدۇ! ھەقىقەتەن شۇنداق قىلماقچى بولسىڭىز، يېڭى OBS نى قوزغىتىشتىن ئىلگىرى ئىجرا قىلىنىۋاتقان باشقا OBS نى تاقاڭ. ئەگەر OBS نى سىستېما ۋەزىپە بالداققا كىچىكلەتكەن بولسىڭىز، ئۇنىڭ ئىجرا قىلىنغان ياكى قىلىنمىغانلىقىنى تەكشۈرۈڭ." AlreadyRunning.LaunchAnyway="قوزغىتىۋەر" AutoSafeMode.Title="بىخەتەر ھالىتى" ChromeOS.Title="قوللىمايدىغان سۇپا" diff --git a/UI/data/locale/uk-UA.ini b/UI/data/locale/uk-UA.ini index a949b1936e8b2b..6829a223070694 100644 --- a/UI/data/locale/uk-UA.ini +++ b/UI/data/locale/uk-UA.ini @@ -195,6 +195,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="Віддавати пере Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="Апаратне кодування зменшує використання ЦП, але для отримання такої ж якості відео може знадобитись більший бітрейт." Basic.AutoConfig.StreamPage.StreamWarning.Title="Попередження трансляції" Basic.AutoConfig.StreamPage.StreamWarning.Text="Тест пропускної здатності буде транслювати випадкові відеодані без аудіо на ваш канал. Якщо ви можете, рекомендуємо тимчасово вимкнути збереження відео трансляцій та перемкнути трансляцію на приватну до завершення тесту. Продовжити?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="Тестове %1" Basic.AutoConfig.TestPage="Остаточні результати" Basic.AutoConfig.TestPage.SubTitle.Testing="Зараз програма виконує набір тестів для оцінки найкращих налаштувань" Basic.AutoConfig.TestPage.SubTitle.Complete="Тестування завершено" @@ -213,6 +214,7 @@ Basic.AutoConfig.TestPage.Result.Header="Визначено налаштуван Basic.AutoConfig.TestPage.Result.Footer="Щоб використовувати ці налаштування, натисніть «Застосувати налаштування». Щоб змінити параметри майстра та спробувати ще раз, натисніть «Назад». Ви також можете скасувати та ввести налаштування самостійно в «Налаштуваннях»." Basic.AutoConfig.Info="Майстер з автоналаштування визначить найкращі параметри на основі характеристик вашого комп'ютера та швидкості Інтернету." Basic.AutoConfig.RunAnytime="Його можна запустити в будь-який час, в меню «Засоби»." +Basic.AutoConfig.TestPage.Result.StreamingResolution="Роздільна здатність трансляції (масштабована)" Basic.Stats="Статистика" Basic.Stats.CPUUsage="Використання ЦП" Basic.Stats.HDDSpaceAvailable="Доступно місця на диску" @@ -567,6 +569,7 @@ Basic.Main.Scenes="Сцени" Basic.Main.Sources="Джерела" Basic.Main.Source="Джерело" Basic.Main.Controls="Керування" +Basic.Main.PreparingStream="Підготовка..." Basic.Main.Connecting="Підʼєднання…" Basic.Main.StartRecording="Почати записування" Basic.Main.StartReplayBuffer="Запустити буфер повторів" @@ -578,7 +581,7 @@ Basic.Main.StopRecording="Зупинити записування" Basic.Main.PauseRecording="Призупинити запис" Basic.Main.UnpauseRecording="Відновити запис" Basic.Main.SplitFile="Розділити файл Запису" -Basic.Main.AddChapterMarker="Додати позначку розділу" +Basic.Main.AddChapterMarker="Додати мітку розділу (Лише гібридний MP4)" Basic.Main.StoppingRecording="Припинення запису…" Basic.Main.StopReplayBuffer="Зупинити буфер повторів" Basic.Main.StoppingReplayBuffer="Зупинення буферу повторів…" @@ -687,6 +690,7 @@ Basic.MainMenu.Help.About="Про OBS (&A)" Basic.Settings.ProgramRestart="Щоб ці параметри набули чинності – необхідно перезапустити програму." Basic.Settings.ConfirmTitle="Підтвердження змін" Basic.Settings.Confirm="У вас є незбережені зміни. Зберегти їх?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 контролює деякі налаштування вашої трансляції" Basic.Settings.General="Загальні" Basic.Settings.General.Language="Мова" Basic.Settings.General.Updater="Оновлення" @@ -753,6 +757,7 @@ Basic.Settings.Appearance.General.Theme="Тема" Basic.Settings.Appearance.General.Variant="Стиль" Basic.Settings.Appearance.General.NoVariant="Стилів немає в наявності" Basic.Settings.Stream="Трансляція" +Basic.Settings.Stream.Destination="Призначення" Basic.Settings.Stream.Custom.UseAuthentication="Використовувати автентифікацію" Basic.Settings.Stream.Custom.Username="Ім’я користувача" Basic.Settings.Stream.Custom.Password="Пароль" @@ -774,10 +779,22 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="Найбільший бітр Basic.Settings.Stream.Recommended.MaxAudioBitrate="Найбільший бітрейт звуку: %1 Кбіт/с" Basic.Settings.Stream.Recommended.MaxResolution="Найбільша роздільна здатність: %1" Basic.Settings.Stream.Recommended.MaxFPS="Найбільша частота кадрів на секунду: %1" +Basic.Settings.Stream.SpecifyCustomServer="Вказати власний сервер..." +Basic.Settings.Stream.ServiceCustomServer="Власний сервер" +Basic.Settings.Stream.EnableMultitrackVideo="Активувати %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="Максимальна пропускна здатність трансляції" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="Автоматично" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="Максимальна кількість відеодоріжок" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="Автоматично" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="Увімкнути дамп трансляції у FLV (використовує прості налаштування файлу запису)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="Перевизначення конфігурації (JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="Увімкнути перевизначення конфігурації" +Basic.Settings.Stream.MultitrackVideoLabel="Мультитрекове відео" +Basic.Settings.Stream.AdvancedOptions="Розширені налаштування" Basic.Settings.Output="Вивід" Basic.Settings.Output.Format="Формат запису" Basic.Settings.Output.Format.MKV="Відео Matroska (.mkv)" -Basic.Settings.Output.Format.hMP4="Гібридний MP4 [BETA] (.mp4)" +Basic.Settings.Output.Format.hMP4="Гібридний MP4 [БЕТА] (.mp4)" Basic.Settings.Output.Format.fMP4="Фрагментований MP4 (.mp4)" Basic.Settings.Output.Format.fMOV="Фрагментований MOV (.mov)" Basic.Settings.Output.Format.TT.fragmented_mov="Фрагментований MOV записує запис фрагментами та не потребує такої ж обробки, як звичайні файли MOV.\nЦе гарантує, що файл можна буде відтворити, навіть якщо запис на диск буде перервано, наприклад, унаслідок аварійного завершення роботи пристрою або втрати живлення.\n\nЦе може бути несумісним з деякими програвачами та редакторами. За потреби скористайтеся командою Файл → Ремультиплексувати записи, щоб перетворити файл у більш сумісний формат." @@ -1224,3 +1241,30 @@ YouTube.Errors.messageTextInvalid="Неприпустимий текст пов YouTube.Errors.rateLimitExceeded="Ви надсилаєте повідомлення занадто часто." YouTube.DocksRemoval.Title="Очистити застарілі панелі браузера YouTube" YouTube.DocksRemoval.Text="Ці панелі браузера буде вилучено як застарілі:\n\n%1\nВикористовуйте замість них \"Docks/YouTube Live Control Room\"." +ConfigDownload.WarningMessageTitle="Попередження" +FailedToStartStream.MissingConfigURL="Для поточної служби немає URL-адреси конфігурації" +FailedToStartStream.NoCustomRTMPURLInSettings="Не вказано власну URL-адресу RTMP" +FailedToStartStream.InvalidCustomConfig="Некоректна власна конфігурація" +FailedToStartStream.FailedToCreateMultitrackVideoService="Для поточного сервісу немає URL-адреси конфігурації" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Не вдалося створити багатодоріжковий відео вивід RTMP" +FailedToStartStream.EncoderNotAvailable="NVENC недоступний.\n\nНе вдалося знайти тип кодера '%1'" +FailedToStartStream.FailedToCreateVideoEncoder="Не вдалося створити кодувальник відео '%1' (тип: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Не вдалося отримати інформацію про відео OBS під час створення кодера '%1' (тип: '%2')" +FailedToStartStream.FailedToCreateAudioEncoder="Не вдалося створити кодувальник звуку" +FailedToStartStream.NoRTMPURLInConfig="Конфігурація не містить URL-адреси цільового потоку RTMP(S)" +FailedToStartStream.FallbackToDefault="Не вдалося запустити потік за допомогою %1; ви хочете спробувати ще раз, використовуючи налаштування одного кодування?" +FailedToStartStream.ConfigRequestFailed="Не вдалося отримати конфігурацію з %1

Помилка HTTP: %2" +FailedToStartStream.WarningUnknownStatus="Отримано невідоме значення стану '%1'" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\nБажаєте продовжити трансляцію без %1?" +FailedToStartStream.WarningRetry="\n

\nБажаєте продовжити трансляцію?" +FailedToStartStream.MissingEncoderConfigs="Конфігурація для запуску не містить конфігурацій кодерів" +FailedToStartStream.StatusMissingHTML="Запит на запуск повернув невизначену помилку" +FailedToStartStream.NoConfigSupplied="Відсутня конфігурація" +MultitrackVideo.Info="%1 автоматично оптимізує налаштування для кодування та надсилання відео різної якості. Вибравши цей параметр, ви надішлете %2 інформацію про ваш комп'ютер і налаштування програмного забезпечення." +MultitrackVideo.IncompatibleSettings.Title="Несумісні налаштування" +MultitrackVideo.IncompatibleSettings.Text="%1 наразі несумісний з:\n\n%2\nЩоб продовжити трансляцію з %1, вимкніть несумісні налаштування:\n\n%3\nта розпочніть трансляцію заново." +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Вимкнути для цієї трансляції і почати трансляцію" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Оновити налаштування і почати трансляцію" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 наразі не має сумісність з [Аудіо → Загальні → Канали], для якого встановлено значення '%2', '%3'." +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Аудіо → Загальні → Канали] потрібно встановити на '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 вимагає декількох різних налаштувань для [Аудіо → Загальні → Канали]" diff --git a/UI/data/locale/vi-VN.ini b/UI/data/locale/vi-VN.ini index 5e7a7b9fea2e5e..553c7e2709149b 100644 --- a/UI/data/locale/vi-VN.ini +++ b/UI/data/locale/vi-VN.ini @@ -144,7 +144,7 @@ Auth.StreamInfo="Thông tin luồng phát" TwitchAuth.Stats="Thống kê kênh Twitch" TwitchAuth.Feed="Bảng tin kênh Twitch" TwitchAuth.TwoFactorFail.Title="Không thể truy vấn khóa luồng" -TwitchAuth.TwoFactorFail.Text="OBS không thể kết nối với tài khoản Twitch của bạn. Hãy chắc rằng xác minh 2 lớp đã được bật ở phần cài đặt bảo mật Twitch để phát trực tiếp." +TwitchAuth.TwoFactorFail.Text="OBS không thể kết nối với tài khoản Twitch của bạn. Hãy chắc rằng xác minh 2 lớp đã được bật ở phầncài đặt bảo mật Twitch vì điều này là cần để phát luồng." RestreamAuth.Channels="Kênh phát lại" Copy.Filters="Sao chép bộ lọc" Paste.Filters="Dán bộ lọc" @@ -159,7 +159,7 @@ Basic.AutoConfig="Trình cấu hình tự động" Basic.AutoConfig.ApplySettings="Áp dụng các thiết đặt" Basic.AutoConfig.StartPage="Mục đích sử dụng" Basic.AutoConfig.StartPage.SubTitle="Xác định bạn muốn sử dụng chương trình cho mục đích gì" -Basic.AutoConfig.StartPage.PrioritizeStreaming="Tối ưu hóa cho việc phát trực tuyến, quay phim là phụ" +Basic.AutoConfig.StartPage.PrioritizeStreaming="Tối ưu hóa cho việc phát luồng, quay phim là phụ" Basic.AutoConfig.StartPage.PrioritizeRecording="Tối ưu hóa chỉ cho quay hình, tôi sẽ không phát luồng" Basic.AutoConfig.StartPage.PrioritizeVirtualCam="Tôi sẽ chỉ sử dụng máy quay ảo" Basic.AutoConfig.VideoPage="Thiết đặt video" @@ -450,7 +450,7 @@ VolControl.SliderMuted="Thanh trượt âm lượng cho '%1': (hiện bị tắt VolControl.Mute="Tắt tiếng '%1'" VolControl.Properties="Thuộc tính '%1'" VolControl.UnassignedWarning.Title="Nguồn âm thanh chưa được chỉ định" -VolControl.UnassignedWarning.Text="\"%1\" không được gán cho bất kỳ rãnh âm thanh nào và sẽ không nghe thấy được trong luồng hoặc bản ghi âm.\n\nĐể gán nguồn âm thanh cho rãnh, hãy mở Thuộc tính âm thanh nâng cao thông qua menu nhấp chuột phải hoặc nút răng cưa trong thanh công cụ đế trộn." +VolControl.UnassignedWarning.Text="\"%1\" không được gán cho bất kỳ rãnh âm thanh nào và sẽ không nghe thấy được trong luồng hoặc bản ghi hình.\n\nĐể gán nguồn âm thanh cho một rãnh, hãy mở Thuộc tính âm thanh nâng cao thông qua bảng chọn chuột phải hoặc nút răng cưa trong thanh công cụ giá đỡ bộ trộn." Basic.Main.AddSceneDlg.Title="Thêm phân cảnh" Basic.Main.AddSceneDlg.Text="Vui lòng nhập tên phân cảnh" Basic.Main.DefaultSceneName.Text="Phân cảnh %1" @@ -566,7 +566,7 @@ Basic.Main.StopRecording="Dừng ghi" Basic.Main.PauseRecording="Tạm dừng ghi hình" Basic.Main.UnpauseRecording="Tiếp tục ghi hình" Basic.Main.SplitFile="Tách tệp ghi hình" -Basic.Main.AddChapterMarker="Thêm Đánh dấu Chương" +Basic.Main.AddChapterMarker="Thêm phần đánh dấu chương (chỉ MP4 lai)" Basic.Main.StoppingRecording="Dừng ghi video..." Basic.Main.StopReplayBuffer="Dừng Replay Buffer" Basic.Main.StoppingReplayBuffer="Đang dừng Replay Buffer..." @@ -700,9 +700,9 @@ Basic.Settings.General.SourceSnapping="Kéo nguồn tới các nguồn khác" Basic.Settings.General.SnapDistance="Độ nhạy kéo" Basic.Settings.General.SpacingHelpers="Mở bảng lưới pixel lên" Basic.Settings.General.RecordWhenStreaming="Tự động ghi hình khi phát luồng" -Basic.Settings.General.KeepRecordingWhenStreamStops="Quay tiếp khi dừng stream" -Basic.Settings.General.ReplayBufferWhileStreaming="Tự động bắt đầu replay buffer khi stream" -Basic.Settings.General.KeepReplayBufferStreamStops="Để replay buffer tiếp tục chạy khi dừng stream" +Basic.Settings.General.KeepRecordingWhenStreamStops="Quay tiếp khi dừng phát luồng" +Basic.Settings.General.ReplayBufferWhileStreaming="Tự động bắt đầu bộ đệm phát lại khi phát luồng" +Basic.Settings.General.KeepReplayBufferStreamStops="Để bộ đệm phát lại tiếp tục chạy khi dừng phát luồng" Basic.Settings.General.SysTray="Khay hệ thống" Basic.Settings.General.SysTrayWhenStarted="Thu nhỏ về khay hệ thống khi bắt đầu" Basic.Settings.General.SystemTrayHideMinimize="Luôn luôn thu nhỏ về khay hệ thống thay vì thanh tác vụ" @@ -757,9 +757,9 @@ Basic.Settings.Stream.StreamSettingsWarning="Mở thiết đặt" Basic.Settings.Stream.MissingUrlAndApiKey="URL và khoá luồng đang bị thiếu.\n\nMở thiết đặt để nhập URL và khoá luồng trong thẻ 'luồng'." Basic.Settings.Stream.MissingUrl="URL luồng đang bị thiếu.\n\nMở thiết đặt để nhập URL trong thẻ 'Luồng'." Basic.Settings.Stream.MissingStreamKey="Khoá luồng đang bị thiếu.\n\nMở thiết đặt để nhập khoả luồng trong thẻ 'Luồng'." -Basic.Settings.Stream.IgnoreRecommended="Bỏ qua các đề xuất cài đặt dịch vụ phát trực tuyến" +Basic.Settings.Stream.IgnoreRecommended="Bỏ qua các đề xuất về thiết đặt dịch vụ phát luồng" Basic.Settings.Stream.IgnoreRecommended.Warn.Title="Ghi đè cài đặt được đề xuất" -Basic.Settings.Stream.IgnoreRecommended.Warn.Text="Cảnh báo: Việc bỏ qua các giới hạn của dịch vụ có thể dẫn đến chất lượng luồng bị giảm sút hoặc ngăn bạn phát trực tuyến.\n\nCó chắc là bạn muốn tiếp tục không?" +Basic.Settings.Stream.IgnoreRecommended.Warn.Text="Cảnh báo: Việc bỏ qua các giới hạn của dịch vụ có thể dẫn đến chất lượng luồng bị giảm sút hoặc ngăn bạn phát luồng.\n\nCó chắc là bạn muốn tiếp tục không?" Basic.Settings.Stream.Recommended.MaxVideoBitrate="Tốc độ bit tối đa của video: %1 kbps" Basic.Settings.Stream.Recommended.MaxAudioBitrate="Tốc độ bit âm thanh tối đa: %1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="Độ phân giải tối đa: %1" @@ -808,10 +808,10 @@ Basic.Settings.Output.Simple.RecordingQuality.Stream="Giống như luồng" Basic.Settings.Output.Simple.RecordingQuality.Small="Chất lượng cao, kích thước tệp trung bình" Basic.Settings.Output.Simple.RecordingQuality.HQ="Chất lượng gốc, kích thước tệp lớn" Basic.Settings.Output.Simple.RecordingQuality.Lossless="Chất lượng bất tổn, kích thước tệp không lồ" -Basic.Settings.Output.Simple.Warn.VideoBitrate="Cảnh báo: Tốc độ bit luồng phát hình sẽ được đặt thành %1 vì đây là giới hạn tối đa của dịch vụ phát trực tiếp hiện tại." -Basic.Settings.Output.Simple.Warn.AudioBitrate="Cảnh báo: Tốc độ bit luồng phát âm thanh sẽ được đặt thành %1 vì đây là giới hạn tối đa của dịch vụ phát trực tiếp hiện tại." +Basic.Settings.Output.Simple.Warn.VideoBitrate="Cảnh báo: Tốc độ bit luồng phát hình sẽ được đặt thành %1 vì đây là giới hạn tối đa của dịch vụ phát luồng hiện tại." +Basic.Settings.Output.Simple.Warn.AudioBitrate="Cảnh báo: Tốc độ bit luồng phát âm thanh sẽ được đặt thành %1 vì đây là giới hạn tối đa của dịch vụ phát luồng hiện tại." Basic.Settings.Output.Simple.Warn.CannotPause="Cảnh báo: Việc ghi hình không thể được dừng lại nếu chất lượng ghi hình được đặt là \"Giống như luồng\"." -Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Cảnh báo: Định dạng ghi hiện được chọn không tương thích với (các) bộ biên mã đã chọn." +Basic.Settings.Output.Simple.Warn.IncompatibleContainer="Cảnh báo: Định dạng ghi hiện được chọn không tương thích với (các) bộ biên mã luồng đã chọn." Basic.Settings.Output.Simple.Warn.Encoder="Chú ý: Ghi hình với một bộ biên mã phần mềm ở một chất lượng khác so với luồng sẽ yêu cầu sử dụng thêm CPU nếu bạn phát luồng và ghi hình cùng một lúc." Basic.Settings.Output.Simple.Warn.Lossless="Cảnh báo: Chất lượng bất tổn đã tạo ra kích thước tập tin lớn hơn dự định. Chất lượng bất tổn có thể sử dụng trên 7 GB không gian đĩa trong một phút ở độ phân giải/ tốc độ khung hình cao. Bất tổn không nên được dùng cho bản ghi hình dài ngoại trừ trường hợp bạn có nhiều không gian ổ sẵn dùng. Bộ đệm phát lại không khả dụng khi sử dụng chất lượng lossless." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Có chắc là bạn muốn sử dụng chất lượng cao không?" @@ -854,7 +854,7 @@ Basic.Settings.Output.Adv.Rescale="Rescale đầu ra" Basic.Settings.Output.Adv.Rescale.Disabled="Đã tắt" Basic.Settings.Output.Adv.AudioTrack="Rãnh âm thanh" Basic.Settings.Output.Adv.Streaming="Phát luồng" -Basic.Settings.Output.Adv.Streaming.Settings="Cài Đặt Phát Trực Tiếp" +Basic.Settings.Output.Adv.Streaming.Settings="Thiết đặt phát luồng" Basic.Settings.Output.Adv.Audio.Track1="Rãnh âm 1" Basic.Settings.Output.Adv.Audio.Track2="Rãnh âm 2" Basic.Settings.Output.Adv.Audio.Track3="Rãnh âm 3" @@ -892,7 +892,7 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Thiết đặt bộ biên mã Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Thiết đặt bộ ghép (nếu có)" Basic.Settings.Output.Adv.FFmpeg.GOPSize="Thời gian đặt Keyframe (giây)" Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat="Hiển thị tất cả các bộ biên giải mã (ngay cả khi không tương thích)" -Basic.Settings.Output.Adv.FFmpeg.Settings="Cài Đặt FFmpeg" +Basic.Settings.Output.Adv.FFmpeg.Settings="Thiết đặt FFmpeg" Basic.Settings.Output.EnableSplitFile="Tự động phân tách tệp đầu ra" Basic.Settings.Output.SplitFile.TypeTime="Phân tách theo giờ" Basic.Settings.Output.SplitFile.TypeSize="Phân chia theo kích cỡ" @@ -1117,7 +1117,7 @@ CodecCompat.ContainerPlaceholder="Chọn Định dạng..." CodecCompat.CodecMissingOnExit.Title="Không có encoder nào được chọn" CodecCompat.CodecMissingOnExit.Text="Ít nhất một video encoder hoặc âm thanh chưa được đặt. Vui lòng đảm bảo chọn encoder cho cả ghi và phát trực tuyến." CodecCompat.ContainerMissingOnExit.Title="Không có định dạng được chọn" -CodecCompat.ContainerMissingOnExit.Text="Không có định dạng ghi nào được chọn. Vui lòng chọn định dạng ghi tương thích với stream encoder đã chọn." +CodecCompat.ContainerMissingOnExit.Text="Không có định dạng ghi nào được chọn. Vui lòng chọn định dạng ghi tương thích với bộ biên mã luồng đã chọn." FinalScene.Title="Xóa phân cảnh" FinalScene.Text="Cần có ít nhất một phân cảnh." NoSources.Title="Không có nguồn" @@ -1214,7 +1214,7 @@ YouTube.Actions.Error.BroadcastNotFound="Không tìm thấy phát sóng được YouTube.Actions.Error.FileMissing="Tệp được chọn không tồn tại." YouTube.Actions.Error.FileOpeningFailed="Không thể mở tệp được chọn." YouTube.Actions.Error.FileTooLarge="Tệp được chọn quá lớn (Giới hạn: 2 MiB)." -YouTube.Actions.Error.BroadcastTransitionFailed="Chuyển tiếp ghi hình thất bại: %1

Nếu lỗi này tiếp tục mở ghi hình trong YouTube Studio và thử lại" +YouTube.Actions.Error.BroadcastTransitionFailed="Chuyển tiếp phát sóng thất bại: %1

Nếu lỗi này tiếp tục hãy mở mục phát trực tiếp trong YouTube Studio và thử lại" YouTube.Actions.Error.BroadcastTestStarting="Ghi hình đang chuyển sang giai đoạn kiểm tra. Xin thử lại trong 10-30 giây" YouTube.Actions.EventsLoading="Đang tải danh sách sự kiện..." YouTube.Actions.EventCreated.Title="Sự kiện đã được tạo" @@ -1236,7 +1236,7 @@ YouTube.Errors.liveStreamingNotEnabled="Phát trực tiếp chưa được kích YouTube.Errors.livePermissionBlocked="Phát trực tiếp hiện không có sẵn trên kênh YouTube được chọn.
Xin lưu ý rằng có thể cần đến 24 tiếng đồng hồ để có thể phát trực tiếp sau khi kích hoạt nó trong phần thiết đặt kênh của bạn.

Xem youtube.com/features để biết thêm chi tiết." YouTube.Errors.errorExecutingTransition="Chuyển cảnh thất bại do lỗi máy chủ. Xin thử lại trong vài giây" YouTube.Errors.errorStreamInactive="YouTube hiện không nhận dữ liệu từ luồng của bạn. Xin vui lòng kiểm tra cấu hình và thử lại." -YouTube.Errors.invalidTransition="Chuyển cảnh vừa rồi không hợp lệ. Việc này có thể do luồng chưa hoàn phát chuyển cảnh trước đó. Xin chờ vài giây và thử lại." +YouTube.Errors.invalidTransition="Chuyển cảnh vừa rồi không hợp lệ. Việc này có thể do luồng chưa hoàn thành chuyển cảnh trước đó. Xin chờ vài giây và thử lại." YouTube.Errors.liveChatDisabled="Trò chuyện trực tiếp đã bị tắt" YouTube.Errors.liveChatEnded="Buổi phát trực tiếp đã kết thúc" YouTube.Errors.messageTextInvalid="Tin nhắn không hợp lệ" @@ -1248,10 +1248,10 @@ FailedToStartStream.MissingConfigURL="Không có URL cấu hình nào khả dụ FailedToStartStream.NoCustomRTMPURLInSettings="URL RTMP tùy chỉnh không được chỉ định" FailedToStartStream.InvalidCustomConfig="Cấu hình tùy chỉnh không hợp lệ" FailedToStartStream.FailedToCreateMultitrackVideoService="Không tạo được dịch vụ video đa luồng" -FailedToStartStream.FailedToCreateMultitrackVideoOutput="Không tạo được đầu ra video rtmp đa luồng" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="Không tạo được đầu ra video RTMP đa luồng" FailedToStartStream.EncoderNotAvailable="NVENC không khả dụng.\n\nKhông tìm thấy bộ biên mã '%1'" FailedToStartStream.FailedToCreateVideoEncoder="Không tạo được bộ biên mã video '%1' (loại: '%2')" -FailedToStartStream.FailedToGetOBSVideoInfo="Không lấy được thông tin video obs khi tạo bộ biên mã '%1' (loại: '%2')" +FailedToStartStream.FailedToGetOBSVideoInfo="Không lấy được thông tin video OBS khi tạo bộ biên mã '%1' (loại: '%2')" FailedToStartStream.FailedToCreateAudioEncoder="Không tạo được bộ biên mã âm thanh" FailedToStartStream.NoRTMPURLInConfig="Cấu hình không chứa URL RTMP(S) của luồng mục tiêu" FailedToStartStream.FallbackToDefault="Không thể bắt đầu phát trực tiếp bằng %1; bạn có muốn thử lại bằng cách sử dụng cài đặt mã hóa đơn không?" @@ -1267,3 +1267,6 @@ MultitrackVideo.IncompatibleSettings.Title="Cài đặt không tương thích" MultitrackVideo.IncompatibleSettings.Text="%1 hiện không tương thích với:\n\n%2\nĐể tiếp tục phát trực tuyến với %1, hãy tắt các cài đặt không tương thích:\n\n%3\nvà Bắt đầu phát trực tuyến lại." MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Tắt cho lượt phát trực tiếp này và Bắt đầu phát trực tuyến" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Cập nhật Cài đặt và Bắt đầu Phát trực tuyến" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 hiện không tương thích với [Âm thanh → Chung → Kênh] được đặt thành '%2', %3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Âm thanh → Chung → Kênh] cần được đặt thành '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 yêu cầu nhiều cài đặt khác nhau cho [Âm thanh → Chung → Kênh]" diff --git a/UI/data/locale/zh-CN.ini b/UI/data/locale/zh-CN.ini index d8eae8c4b417f3..1e331b3ddc0010 100644 --- a/UI/data/locale/zh-CN.ini +++ b/UI/data/locale/zh-CN.ini @@ -582,7 +582,7 @@ Basic.Main.StopRecording="停止录制" Basic.Main.PauseRecording="暂停录制" Basic.Main.UnpauseRecording="恢复录制" Basic.Main.SplitFile="分割录制文件" -Basic.Main.AddChapterMarker="添加章节标记" +Basic.Main.AddChapterMarker="添加章节标记(仅限混合 MP4)" Basic.Main.StoppingRecording="正在停止录制..." Basic.Main.StopReplayBuffer="关闭回放缓存" Basic.Main.StoppingReplayBuffer="正在关闭回放缓存..." @@ -1280,3 +1280,6 @@ MultitrackVideo.IncompatibleSettings.Title="设置不兼容" MultitrackVideo.IncompatibleSettings.Text="%1 当前与以下设置不兼容:\n\n%2\n若要继续使用 %1 进行串流,请禁用不兼容的设置:\n\n%3\n然后重新开始串流。" MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="禁用此流并开始串流" MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="更新设置并开始串流" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 当前与设置为“%2”、%3 的 [音频 → 常规 → 声道] 不兼容" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[音频 → 常规 → 声道] 需要设置为“%1”" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 需要对 [音频 → 常规 → 声道] 进行多个不同的设置" diff --git a/UI/data/locale/zh-TW.ini b/UI/data/locale/zh-TW.ini index 769217749405f3..8bf8f495358fd0 100644 --- a/UI/data/locale/zh-TW.ini +++ b/UI/data/locale/zh-TW.ini @@ -86,7 +86,7 @@ VerticalLayout="垂直排版" Group="群組" DoNotShowAgain="不再顯示" Default="(預設)" -Calculating="正在計算……" +Calculating="正在計算…" Fullscreen="全螢幕" Windowed="視窗化" RefreshBrowser="重新整理" @@ -108,6 +108,7 @@ MixerToolbarMenu="音效混音器選單" SceneFilters="開啟場景濾鏡" List="清單" Grid="網格" +Automatic="自動" PluginsFailedToLoad.Title="載入外掛程式時發生錯誤" PluginsFailedToLoad.Text="無法載入下述 OBS 外掛程式:\n\n%1\n請更新或移除這些外掛程式。" AlreadyRunning.Title="OBS 已經執行" @@ -115,7 +116,7 @@ AlreadyRunning.Text="OBS 已經啟動!除非您就想這麼做,否則請在 AlreadyRunning.LaunchAnyway="仍然啟動" AutoSafeMode.Title="安全模式" AutoSafeMode.Text="OBS 在上個工作階段沒有正常關閉。\n\n您是否想要在安全模式(也就是停用第三方外掛程式、指令稿和 WebSocket)啟動?" -AutoSafeMode.LaunchSafe="以安全模式運行" +AutoSafeMode.LaunchSafe="以安全模式執行" AutoSafeMode.LaunchNormal="正常執行" SafeMode.Restart="您是否想將 OBS 重新啟動至安全模式(也就是停用第三方外掛程式、指令稿和 WebSocket)?" SafeMode.RestartNormal="你要以一般模式重新啟動 OBS 嗎?" @@ -194,6 +195,7 @@ Basic.AutoConfig.StreamPage.PreferHardwareEncoding="優先使用硬體編碼" Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip="硬體編碼去除了大多數的 CPU 使用率,但可能需要更多的位元率以獲得同等的品質。" Basic.AutoConfig.StreamPage.StreamWarning.Title="串流警告" Basic.AutoConfig.StreamPage.StreamWarning.Text="頻寬測試即將串流隨機沒有音訊的影像資料到您的頻道。 如果能的話,建議暫時關閉保存影像串流並設定串流為私密直到完成測試。 要繼續嗎?" +Basic.AutoConfig.StreamPage.UseMultitrackVideo="測試 %1" Basic.AutoConfig.TestPage="最終結果" Basic.AutoConfig.TestPage.SubTitle.Testing="程式目前正在執行一系列的測試以估計最理想的設定" Basic.AutoConfig.TestPage.SubTitle.Complete="測試完成" @@ -212,6 +214,7 @@ Basic.AutoConfig.TestPage.Result.Header="程式判斷這些估計設定是最適 Basic.AutoConfig.TestPage.Result.Footer="若要使用這些設定,請點選「套用設定」。要重新設定精靈並重試,請點選「返回」。 要手動設定,請點選「取消」,然後打開「設定」。" Basic.AutoConfig.Info="[自動設定精靈] 會根據您電腦的配置及網路速度來判斷最適合的設定。" Basic.AutoConfig.RunAnytime="這隨時都可以在 [工具] 選單啟動。" +Basic.AutoConfig.TestPage.Result.StreamingResolution="串流(縮放)解析度" Basic.Stats="狀態" Basic.Stats.CPUUsage="CPU 使用率" Basic.Stats.HDDSpaceAvailable="可用磁碟空間" @@ -265,7 +268,7 @@ Basic.EnablePreviewProgramMode="啟用工作室模式" Basic.DisablePreviewProgramMode="停用工作室模式" Undo.Undo="復原" Undo.Redo="取消復原" -Undo.Add="新增 '%1'" +Undo.Add="新增「%1」" Undo.Delete="刪除「%1」" Undo.Rename="重新命名「%1」" Undo.SceneCollection.Switch="切到「%1」" @@ -567,6 +570,7 @@ Basic.Main.Scenes="場景" Basic.Main.Sources="來源" Basic.Main.Source="來源" Basic.Main.Controls="控制項" +Basic.Main.PreparingStream="正在準備…" Basic.Main.Connecting="連線中……" Basic.Main.StartRecording="開始錄製" Basic.Main.StartReplayBuffer="開始重播緩衝" @@ -578,7 +582,7 @@ Basic.Main.StopRecording="停止錄製" Basic.Main.PauseRecording="暫停錄影" Basic.Main.UnpauseRecording="繼續錄製" Basic.Main.SplitFile="分割錄影檔案" -Basic.Main.AddChapterMarker="增加章節標記" +Basic.Main.AddChapterMarker="新增章節標記(僅限混合 MP4)" Basic.Main.StoppingRecording="停止錄製..." Basic.Main.StopReplayBuffer="停止重播緩衝" Basic.Main.StoppingReplayBuffer="正在停止重播緩衝..." @@ -683,10 +687,11 @@ Basic.MainMenu.Help.RestartNormal="以一般模式重新啟動" Basic.MainMenu.Help.CrashLogs="錯誤報告 (&R)" Basic.MainMenu.Help.CrashLogs.ShowLogs="顯示當機回報(&S)" Basic.MainMenu.Help.CrashLogs.UploadLastLog="上傳前一個當機報告(&P)" -Basic.MainMenu.Help.About="關於(&A)" +Basic.MainMenu.Help.About="關於 (&A)" Basic.Settings.ProgramRestart="為了套用新設定,請關閉後重啟。" Basic.Settings.ConfirmTitle="確認修改" Basic.Settings.Confirm="您有未儲存的修改。 是否儲存?" +Basic.Settings.MultitrackVideoDisabledSettings="%1 %2 正在控制您的某些串流設定" Basic.Settings.General="一般" Basic.Settings.General.Language="語言" Basic.Settings.General.Updater="更新" @@ -753,6 +758,7 @@ Basic.Settings.Appearance.General.Theme="佈景主題" Basic.Settings.Appearance.General.Variant="樣式" Basic.Settings.Appearance.General.NoVariant="沒有樣式" Basic.Settings.Stream="串流" +Basic.Settings.Stream.Destination="目的地" Basic.Settings.Stream.Custom.UseAuthentication="使用身份驗證" Basic.Settings.Stream.Custom.Username="使用者名稱" Basic.Settings.Stream.Custom.Password="密碼" @@ -774,6 +780,18 @@ Basic.Settings.Stream.Recommended.MaxVideoBitrate="最高視訊位元速率:%1 Basic.Settings.Stream.Recommended.MaxAudioBitrate="最高音訊位元速率:%1 kbps" Basic.Settings.Stream.Recommended.MaxResolution="最高解析度:%1" Basic.Settings.Stream.Recommended.MaxFPS="最高 FPS:%1" +Basic.Settings.Stream.SpecifyCustomServer="指定自訂伺服器…" +Basic.Settings.Stream.ServiceCustomServer="自訂伺服器" +Basic.Settings.Stream.EnableMultitrackVideo="啟用 %1" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate="最大串流頻寬" +Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto="自動" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks="最大影片軌道數" +Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto="自動" +Basic.Settings.Stream.MultitrackVideoStreamDumpEnable="啟用串流轉儲至 FLV(使用簡單錄製檔案設定)" +Basic.Settings.Stream.MultitrackVideoConfigOverride="配置覆寫(JSON)" +Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable="啟用配置覆寫" +Basic.Settings.Stream.MultitrackVideoLabel="多軌視頻" +Basic.Settings.Stream.AdvancedOptions="進階選項 " Basic.Settings.Output="輸出" Basic.Settings.Output.Format="錄影格式" Basic.Settings.Output.Format.MKV="Matroska 視訊檔案 (.mkv)" @@ -1046,7 +1064,7 @@ Basic.Settings.Advanced.Hotkeys.NeverDisableHotkeys="永不停用快速鍵" Basic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus="當主視窗處於焦點,則停用熱鍵" Basic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus="僅在焦點不在主視窗時停用快速鍵" Basic.Settings.Advanced.AutoRemux="自動重新封裝為 %1" -Basic.Settings.Advanced.AutoRemux.MP4="(以 mkv 格式錄製)" +Basic.Settings.Advanced.AutoRemux.MP4="(以 mkv 格式錄製)" Basic.AdvAudio="進階音訊屬性" Basic.AdvAudio.ActiveOnly="僅使用中來源" Basic.AdvAudio.Name="名稱" @@ -1239,7 +1257,7 @@ YouTube.Actions.AutoStartStreamingWarning.Title="需要自行啟動" YouTube.Actions.AutoStartStreamingWarning="已停用本活動的「自動開始」功能。請點選「開始直播」進行直播。" YouTube.Actions.AutoStopStreamingWarning="您之後會無法重新連線。
您的串流會停止,而您本人則會進入離線狀態。" YouTube.Chat.Input.Send="發送" -YouTube.Chat.Input.Placeholder="在此輸入訊息⋯⋯" +YouTube.Chat.Input.Placeholder="在這裡輸入訊息..." YouTube.Chat.Input.Sending="傳送中⋯⋯" YouTube.Chat.Error.Title="傳送訊息時發生錯誤" YouTube.Chat.Error.Text="無法傳送訊息:%1" @@ -1254,3 +1272,30 @@ YouTube.Errors.messageTextInvalid="訊息文字無效。" YouTube.Errors.rateLimitExceeded="傳送訊息的速度太快。" YouTube.DocksRemoval.Title="清除舊版 YouTube 瀏覽器停駐視窗" YouTube.DocksRemoval.Text="這些瀏覽器停駐視窗將被刪除,因為它們已被標記為過時:\n\n%1\n請改用「停駐視窗/YouTube 直播控制室」。" +ConfigDownload.WarningMessageTitle="警告" +FailedToStartStream.MissingConfigURL="目前服務沒有可用設定 URL" +FailedToStartStream.NoCustomRTMPURLInSettings="未指定自訂 RTMP URL" +FailedToStartStream.InvalidCustomConfig="無效的自訂配置" +FailedToStartStream.FailedToCreateMultitrackVideoService="建立多軌視訊服務失敗" +FailedToStartStream.FailedToCreateMultitrackVideoOutput="無法建立多軌視訊 RTMP 輸出" +FailedToStartStream.EncoderNotAvailable="NVENC 不可用。\n\n找不到編碼器類型「%1」" +FailedToStartStream.FailedToCreateVideoEncoder="無法建立視訊編碼器「%1」(類型:「%2」)" +FailedToStartStream.FailedToGetOBSVideoInfo="建立編碼器「%1」時無法取得 OBS 視訊資訊(類型:「%2」)" +FailedToStartStream.FailedToCreateAudioEncoder="建立音訊編碼器失敗" +FailedToStartStream.NoRTMPURLInConfig="設定不包含串流目標 RTMP(S) URL" +FailedToStartStream.FallbackToDefault="使用 %1 啟動直播失敗;您是否想要嘗試使用單一編碼設置重新嘗試?" +FailedToStartStream.ConfigRequestFailed="無法從 %1 獲取配置

HTTP 錯誤:%2" +FailedToStartStream.WarningUnknownStatus="已收到未知的狀態值「%1」" +FailedToStartStream.WarningRetryNonMultitrackVideo="\n

\n您是否想繼續不使用 %1 進行直播?" +FailedToStartStream.WarningRetry="\n

\n您是否想繼續直播?" +FailedToStartStream.MissingEncoderConfigs="啟動直播配置中未包含編碼器配置" +FailedToStartStream.StatusMissingHTML="啟動直播請求返回了一個未指定的錯誤" +FailedToStartStream.NoConfigSupplied="缺少配置" +MultitrackVideo.Info="%1 會對多種視訊品質進行編碼,以自動最佳化設定,並傳送到 。選擇本選項將會將電腦和軟體設定的資訊傳送給 %2。" +MultitrackVideo.IncompatibleSettings.Title="不相容的設定" +MultitrackVideo.IncompatibleSettings.Text="%1 目前不兼容:\n\n%2\n若要繼續使用 %1 進行直播,請禁用不兼容的設置:\n\n%3\n然後再次開始直播。" +MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="停用此直播並開始直播" +MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="更新設定並開始直播" +MultitrackVideo.IncompatibleSettings.AudioChannels="%1 目前不相容 [音效 → 一般 → 頻道] 設置為 '%2',%3" +MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[音效 → 一般 → 頻道] 需要設置為 '%1'" +MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 需要為 [音效 → 一般 → 頻道] 設置多種不同的設定" diff --git a/UI/frontend-plugins/aja-output-ui/data/locale/ro-RO.ini b/UI/frontend-plugins/aja-output-ui/data/locale/ro-RO.ini index be668ef476b601..4ce15303d34f22 100644 --- a/UI/frontend-plugins/aja-output-ui/data/locale/ro-RO.ini +++ b/UI/frontend-plugins/aja-output-ui/data/locale/ro-RO.ini @@ -1,5 +1,6 @@ -AJAOutput.ProgramOutput="Output-ul programului" +AJAOutput.Device="Outputul dispozitivului AJA I/O" +AJAOutput.ProgramOutput="Outputul programului" AJAOutput.PreviewOutput="Previzualizarea outputului" AJAOutput.MiscOutput="Setări adiționale" AJAOutput.MultiViewEnable="Activează vizualizarea multiplă" -AJAOutput.MultiViewAudioSource="Vizualizare multiplă a sursei audio" +AJAOutput.MultiViewAudioSource="Sursă audio pentru vizualizare multiplă" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini b/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini index 61bfb5d0087237..214269637684b1 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini @@ -1,19 +1,19 @@ SceneSwitcher="Cambio scena automatico" SceneSwitcher.OnNoMatch="Quando nessuna finestra coincide:" -SceneSwitcher.OnNoMatch.DontSwitch="Non passare" +SceneSwitcher.OnNoMatch.DontSwitch="Non cambiare" SceneSwitcher.OnNoMatch.SwitchTo="Passa a:" SceneSwitcher.CheckInterval="Controlla il titolo della finestra attiva ogni:" -SceneSwitcher.ActiveOrNotActive="Il cambio scena è:" +SceneSwitcher.ActiveOrNotActive="Il cambio di scena è:" InvalidRegex.Title="Espressione regolare non valida" InvalidRegex.Text="L'espressione regolare che hai inserito non è valida." Active="Attivo" Inactive="Inattivo" Start="Avvia" Captions="Sottotitoli (sperimentale)" -Captions.AudioSource="Fonte audio" -Captions.CurrentSystemLanguage="Lingua del sistema (%1)" -Captions.Provider="Sintetizzatore" -Captions.Error.GenericFail="Impossibile avviare i sottititoli" +Captions.AudioSource="Fonte dell'audio" +Captions.CurrentSystemLanguage="Lingua del sistema corrente (%1)" +Captions.Provider="Fornitore" +Captions.Error.GenericFail="Avvio dei sottotitoli non riuscito" OutputTimer="Conto alla rovescia" OutputTimer.Stream="Termina la diretta dopo:" OutputTimer.Record="Termina la registrazione dopo:" @@ -26,7 +26,7 @@ Scripts="Script" LoadedScripts="Script caricati" AddScripts="Aggiungi script" RemoveScripts="Rimuovi script" -ReloadScripts="Ricarica script" +ReloadScripts="Ricarica gli script" EditScript="Modifica Script" Reload="Ricarica" OpenFileLocation="Apri Posizione File" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini b/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini index 2025575cbf0206..03d30bfe0a90cc 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini @@ -20,7 +20,7 @@ OutputTimer.Stream="Oprește streamingul după:" OutputTimer.Record="Oprește înregistrarea după:" OutputTimer.Stream.StoppingIn="Streamingul se oprește în:" OutputTimer.Record.StoppingIn="Înregistrarea se oprește în:" -OutputTimer.Stream.EnableEverytime="Activează temporizatorul pentru transmisiune de fiecare dată" +OutputTimer.Stream.EnableEverytime="Activează temporizatorul pentru streaming de fiecare dată" OutputTimer.Record.EnableEverytime="Activează temporizatorul pentru înregistrare de fiecare dată" OutputTimer.Record.PauseTimer="Pune pe pauză temporizatorul când înregistrarea este pusă pe pauză" Scripts="Scripturi" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini b/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini index 0be58c6549cad3..15ad05c4555f29 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini @@ -1,5 +1,5 @@ SceneSwitcher="Automatisk scenbytare" -SceneSwitcher.OnNoMatch="När inget fönster matchar:" +SceneSwitcher.OnNoMatch="När inga fönster matchar:" SceneSwitcher.OnNoMatch.DontSwitch="Byt inte" SceneSwitcher.OnNoMatch.SwitchTo="Byt till:" SceneSwitcher.CheckInterval="Kontrollera titel på aktivt fönster varje:" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini b/UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini index d68cf3443074d1..1d47473fc35a2c 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini @@ -18,7 +18,7 @@ Captions.Error.GenericFail="Thất bại trong việc bắt đầu phụ đề" OutputTimer="Hẹn giờ đầu ra" OutputTimer.Stream="Dừng stream sau:" OutputTimer.Record="Dừng ghi video sau:" -OutputTimer.Stream.StoppingIn="Stream sẽ dừng trong:" +OutputTimer.Stream.StoppingIn="Luồng sẽ dừng trong:" OutputTimer.Record.StoppingIn="Quay video sẽ dừng trong:" OutputTimer.Stream.EnableEverytime="Bật hẹn giờ phát luồng mỗi lần" OutputTimer.Record.EnableEverytime="Bật hẹn giờ phát mỗi lần" diff --git a/plugins/aja/data/locale/it-IT.ini b/plugins/aja/data/locale/it-IT.ini index 6815abe964d442..0492cd176df697 100644 --- a/plugins/aja/data/locale/it-IT.ini +++ b/plugins/aja/data/locale/it-IT.ini @@ -5,7 +5,7 @@ Output="Uscita" Input="Ingresso" Mode="Modalità" VideoFormat="Formato Video" -PixelFormat="Formato pixel" +PixelFormat="Formato dei pixel" AutoDetect="Rilevamento automatico" Interlaced="Interlacciato" AutoStart="Avvio automatico all'avvio" diff --git a/plugins/aja/data/locale/ro-RO.ini b/plugins/aja/data/locale/ro-RO.ini index 5a563a267ee269..46161713eca978 100644 --- a/plugins/aja/data/locale/ro-RO.ini +++ b/plugins/aja/data/locale/ro-RO.ini @@ -1,15 +1,18 @@ AJACapture.Device="Captură dispozitiv AJA I/O" +AJAOutput.Device="Outputul dispozitivului AJA I/O" Device="Dispozitiv" Input="Intrare" Mode="Mod" VideoFormat="Format video" -PixelFormat="Format pixel" +PixelFormat="Formatul pixelilor" AutoDetect="Detectează automat" Interlaced="Interpelat" AutoStart="Pornire automată la lansare" Buffering="Folosește bufferingul" DeactivateWhenNotShowing="Dezactivează când nu se afișează" IOSelect="Selectare..." +SDITransport="Transport SDI" +SDITransport4K="Transport SDI 4K" Auto="Automată" ChannelFormat="Canal" ChannelFormat.None="Nimic" diff --git a/plugins/decklink/data/locale/ro-RO.ini b/plugins/decklink/data/locale/ro-RO.ini index 76de51c3548e93..9729fd39fd96c3 100644 --- a/plugins/decklink/data/locale/ro-RO.ini +++ b/plugins/decklink/data/locale/ro-RO.ini @@ -2,7 +2,7 @@ BlackmagicDevice="Dispozitiv Blackmagic" Device="Dispozitiv" Mode="Mod" Buffering="Folosește buffering" -PixelFormat="Format pentru pixeli" +PixelFormat="Formatul pixelilor" ColorSpace="Spațiu de culori" ColorSpace.Default="Implicit" ColorRange="Gamă de culori" diff --git a/plugins/decklink/data/locale/zh-TW.ini b/plugins/decklink/data/locale/zh-TW.ini index ce0f9b6cda9f52..e903cc4cd490eb 100644 --- a/plugins/decklink/data/locale/zh-TW.ini +++ b/plugins/decklink/data/locale/zh-TW.ini @@ -11,7 +11,7 @@ ColorRange.Partial="部分" ColorRange.Full="完整" ChannelFormat="聲道" ChannelFormat.None="無" -ChannelFormat.2_0ch="雙聲道(2ch)" +ChannelFormat.2_0ch="雙聲道" ChannelFormat.2_1ch="2.1聲道" ChannelFormat.4_0ch="4聲道" ChannelFormat.4_1ch="4.1聲道" diff --git a/plugins/linux-alsa/data/locale/ro-RO.ini b/plugins/linux-alsa/data/locale/ro-RO.ini index 8c22a2fb86d71e..d1a05a55a5209c 100644 --- a/plugins/linux-alsa/data/locale/ro-RO.ini +++ b/plugins/linux-alsa/data/locale/ro-RO.ini @@ -1,3 +1,4 @@ AlsaInput="Dispozitiv de captură audio (ALSA)" +Custom="Personalizat" Device="Dispozitiv" Rate="Rată" diff --git a/plugins/linux-capture/data/locale/ar-SA.ini b/plugins/linux-capture/data/locale/ar-SA.ini index e39946cf624867..77808017ea2ae5 100644 --- a/plugins/linux-capture/data/locale/ar-SA.ini +++ b/plugins/linux-capture/data/locale/ar-SA.ini @@ -1,3 +1,5 @@ +X11SharedMemoryDisplayInput="التقاط الشاشة(XSHM)" +Display="الشاشة" CaptureCursor="التقاط المؤشر" AdvancedSettings="إعدادات متقدمة" XCCapture="التقاط النافذة (Xcomposite)" diff --git a/plugins/linux-capture/data/locale/hi-IN.ini b/plugins/linux-capture/data/locale/hi-IN.ini index 80ab60852666c9..9db58029dc7a7a 100644 --- a/plugins/linux-capture/data/locale/hi-IN.ini +++ b/plugins/linux-capture/data/locale/hi-IN.ini @@ -1,3 +1,5 @@ +X11SharedMemoryDisplayInput="डिसप्ले कैप्चर (XSHM)" +Display="डिस्प्ले" CaptureCursor="कर्सर कैप्चर करें" AdvancedSettings="उन्नत सेटिंग्स" XServer="एक्स सर्वर" diff --git a/plugins/linux-capture/data/locale/ka-GE.ini b/plugins/linux-capture/data/locale/ka-GE.ini index 34fd23b5ec0ebb..da0964855b67b4 100644 --- a/plugins/linux-capture/data/locale/ka-GE.ini +++ b/plugins/linux-capture/data/locale/ka-GE.ini @@ -1,3 +1,5 @@ +X11SharedMemoryDisplayInput="ეკრანის გადაღება (XSHM)" +Display="ეკრანი" CaptureCursor="მაჩვენებლის ასახვა" AdvancedSettings="დამატებითი პარამეტრები" XServer="X სერვერი" diff --git a/plugins/linux-pipewire/data/locale/ar-SA.ini b/plugins/linux-pipewire/data/locale/ar-SA.ini index 3d5767d1e461d1..360c7ea109c979 100644 --- a/plugins/linux-pipewire/data/locale/ar-SA.ini +++ b/plugins/linux-pipewire/data/locale/ar-SA.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="التقاط الشاشة (PipeWire)" PipeWireSelectMonitor="حدد الشاشة" PipeWireSelectWindow="حدد نافذة" PipeWireWindowCapture="التقاط الشاشة (PipeWire)" +PipeWireSelectScreenCast="افتح محدد" Resolution="الدقة" ShowCursor="إظهار المؤشر" VideoFormat="تنسيق الفيديو" diff --git a/plugins/linux-pipewire/data/locale/hi-IN.ini b/plugins/linux-pipewire/data/locale/hi-IN.ini index 7bd6bcdd447365..9372f79eb780a6 100644 --- a/plugins/linux-pipewire/data/locale/hi-IN.ini +++ b/plugins/linux-pipewire/data/locale/hi-IN.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="स्क्रीन कैप्चर (PipeWire)" PipeWireSelectMonitor="मॉनिटर चुनें" PipeWireSelectWindow="विंडो चुनें" PipeWireWindowCapture="विंडो कैप्चर (PipeWire)" +PipeWireSelectScreenCast="चयनकर्ता खोलें" Resolution="रिजॉल्यूशन" ShowCursor="कर्सर दिखाएं" VideoFormat="वीडियो प्रारूप" diff --git a/plugins/linux-pipewire/data/locale/ro-RO.ini b/plugins/linux-pipewire/data/locale/ro-RO.ini index 3df08c635a618e..d5e98bcce50a7d 100644 --- a/plugins/linux-pipewire/data/locale/ro-RO.ini +++ b/plugins/linux-pipewire/data/locale/ro-RO.ini @@ -1,5 +1,9 @@ +PipeWireCameraDevice="Dispozitiv" PipeWireDesktopCapture="Captură de ecran (PipeWire)" PipeWireSelectMonitor="Selectați suprapunerea" PipeWireSelectWindow="Selectează fereastra" PipeWireWindowCapture="Captură de fereastră (PipeWire)" +PipeWireSelectScreenCast="Deschide selectorul" +Resolution="Rezoluție" ShowCursor="Afișează cursorul" +VideoFormat="Format video" diff --git a/plugins/linux-pipewire/data/locale/sl-SI.ini b/plugins/linux-pipewire/data/locale/sl-SI.ini index f6e76c2f13216d..23bf0cf9f16bd3 100644 --- a/plugins/linux-pipewire/data/locale/sl-SI.ini +++ b/plugins/linux-pipewire/data/locale/sl-SI.ini @@ -6,6 +6,7 @@ PipeWireDesktopCapture="Zajem zaslona (PipeWire)" PipeWireSelectMonitor="Izberite zaslon" PipeWireSelectWindow="Izberite okno" PipeWireWindowCapture="Zajem okna (PipeWire)" +PipeWireSelectScreenCast="Odpri izbirnik" Resolution="Ločljivost" ShowCursor="Pokaži kazalec" VideoFormat="Video zapis" diff --git a/plugins/linux-pulseaudio/data/locale/it-IT.ini b/plugins/linux-pulseaudio/data/locale/it-IT.ini index 7b1a4bc8d39b1d..99d72d5637f3d3 100644 --- a/plugins/linux-pulseaudio/data/locale/it-IT.ini +++ b/plugins/linux-pulseaudio/data/locale/it-IT.ini @@ -1,4 +1,4 @@ -PulseInput="Acquisizione dell'audio di ingresso (PulseAudio)" -PulseOutput="Acquisizione dell'audio di uscita (PulseAudio)" +PulseInput="Acquisizione dell'ingresso dell'audio (PulseAudio)" +PulseOutput="Acquisizione dell'uscita dell'audio (PulseAudio)" Device="Dispositivo" Default="Predefinito" diff --git a/plugins/mac-avcapture/data/locale/it-IT.ini b/plugins/mac-avcapture/data/locale/it-IT.ini index f0b7a1ebe6dda3..d615c831154b9c 100644 --- a/plugins/mac-avcapture/data/locale/it-IT.ini +++ b/plugins/mac-avcapture/data/locale/it-IT.ini @@ -1,6 +1,6 @@ AVCapture="Dispositivo di cattura video" AVCapture_Fast="Dispositivo scheda di acquisizione" -AVCapture_Legacy="Scheda cattura video (legacy)" +AVCapture_Legacy="Scheda di acquisizione video (legacy)" Device="Dispositivo" UsePreset="Utilizza il preset" Buffering="Utilizza il buffering" diff --git a/plugins/mac-capture/data/locale/it-IT.ini b/plugins/mac-capture/data/locale/it-IT.ini index cfcc2198e53b7d..25d37723841dc0 100644 --- a/plugins/mac-capture/data/locale/it-IT.ini +++ b/plugins/mac-capture/data/locale/it-IT.ini @@ -5,7 +5,7 @@ CoreAudio.Device.Default="Predefinito" CoreAudio.Channel="Canale" CoreAudio.Channel.Default="Predefinito" CoreAudio.Channel.Unnamed="Senza nome" -CoreAudio.Channel.Device="Canale dispositivo" +CoreAudio.Channel.Device="Canale del dispositivo" CoreAudio.None="Nessuno" CoreAudio.Downmix="Abilita downmixing" ApplicationCapture="Cattura applicazione" diff --git a/plugins/mac-videotoolbox/data/locale/it-IT.ini b/plugins/mac-videotoolbox/data/locale/it-IT.ini index 42b3a45dd93f63..51186562c6711b 100644 --- a/plugins/mac-videotoolbox/data/locale/it-IT.ini +++ b/plugins/mac-videotoolbox/data/locale/it-IT.ini @@ -14,7 +14,7 @@ KeyframeIntervalSec="Intervallo fotogramma chiave (0=automatico)" Profile="Profilo" UseBFrames="Utilizza i B-Frame" RateControl="Controllo flusso" -ColorFormatUnsupported="Il formato colore selezionato non è supportato dall'encoder Apple VT selezionato. Seleziona un formato colore compatibile in Impostazioni -> Avanzate o usa un diverso encoder." -FullRangeUnsupported="La gamma completa di colori non è supportata da codifiche Apple VT a 16-bit. Seleziona la gamma limitata di colori in Impostazioni -> Avanzate." +ColorFormatUnsupported="Il formato colore selezionato non è supportato dal codificatore Apple VT selezionato. Selezionare un formato di colore compatibile in Impostazioni -> Avanzate o utilizzare un codificatore diverso." +FullRangeUnsupported="La gamma completa di colori non è supportata dai codificatori Apple VT a 16-bit. Seleziona la gamma limitata di colori in Impostazioni -> Avanzate." ProResCodec="Codec ProRes" ProRes422Proxy="Proxy ProRes 422" diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/it-IT.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/it-IT.ini index bac975fae93814..a8abf2aa85a76c 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/it-IT.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/it-IT.ini @@ -4,6 +4,6 @@ Error.SystemExtension.CameraUnavailable="Impossibile trovare la fotocamera virtu Error.SystemExtension.CameraNotStarted="Impossibile avviare la fotocamera virtuale.\n\nRiprova." Error.SystemExtension.InstallationError="Si è verificato un errore durante l'installazione della fotocamera virtuale:" Error.SystemExtension.WrongLocation="OBS non può installare la videocamera virtuale se non è in \"/Applicazioni\". Sposta OBS nella cartella \"/Applicazioni\"." -Error.SystemExtension.CompleteAfterReboot="L'installazione della camera virtuale sarà completato dopo il riavvio di sistema." +Error.SystemExtension.CompleteAfterReboot="L'installazione della fotocamera virtuale sarà completata dopo il riavvio di sistema." Error.DAL.NotInstalled="Impossibile installare o aggiornare la fotocamera virtuale.\n\nRiprova e inserisci quando richiesto la password di amministratore." Error.DAL.NotUninstalled="Impossibile rimuovere la fotocamera virtuale precedente.\n\nRiprova e inserisci quando richiesto il nome e la password di amministratore quando richiesti." diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/pt-BR.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/pt-BR.ini index b6269e56462b17..a0f4856e6845a3 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/pt-BR.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/pt-BR.ini @@ -2,7 +2,7 @@ Plugin_Name="Câmera virtual do macOS" Error.SystemExtension.NotInstalled="A câmera virtual não está instalada.\n\nPor favor, permita que o OBS instale o software do sistema em Configurações do sistema → Privacidade & Segurança → Segurança.\n\nTalvez seja necessário reiniciar o OBS se esta mensagem ainda aparecer depois." Error.SystemExtension.CameraUnavailable="Não foi possível encontrar a câmera virtual.\n\nPor favor, tente novamente." Error.SystemExtension.CameraNotStarted="Não foi possível iniciar a câmera virtual.\n\nPor favor, tente novamente." -Error.SystemExtension.InstallationError="Ocorreu um erro ao instalar a câmara virtual:" +Error.SystemExtension.InstallationError="Houve um erro ao instalar a câmera virtual:" Error.SystemExtension.WrongLocation="OBS não pode instalar a câmera virtual se esta não estiver em \"/Aplicativos\". Mova o OBS para o diretório \"/Aplicativos\"." Error.SystemExtension.CompleteAfterReboot="A instalação da câmera virtual será finalizada após reiniciar o sistema." Error.DAL.NotInstalled="A câmera virtual não pôde ser instalada ou atualizada.\n\nPor favor, tente novamente e insira o nome e a senha de um administrador quando solicitado." diff --git a/plugins/obs-browser b/plugins/obs-browser index c81851a1d7696c..2a2879b5a69f4a 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit c81851a1d7696c2d7ba319122eec387c1568ad44 +Subproject commit 2a2879b5a69f4a99cd7459d8595af46cdb23115c diff --git a/plugins/obs-ffmpeg/data/locale/it-IT.ini b/plugins/obs-ffmpeg/data/locale/it-IT.ini index f405460d862cee..bdfacf649f2845 100644 --- a/plugins/obs-ffmpeg/data/locale/it-IT.ini +++ b/plugins/obs-ffmpeg/data/locale/it-IT.ini @@ -17,7 +17,7 @@ KeyframeIntervalSec="Intervallo fotogramma chiave (0=automatico)" Level="Livello" FilePath="Percorso file" AMFOpts="Opzioni AMF/FFmpeg" -AMFOpts.ToolTip="Specifica le opzioni AMF o FFmpeg. per esempio, \"level=5.2 profile=main\". Per maggiori dettagli controlla la documentazione dell'encoder AMF." +AMFOpts.ToolTip="Usalo per specificare le opzioni di AMF o FFmpeg personalizzate. Per esempio, \"level=5.2 profile=main\". Per maggiori dettagli controlla la documentazione del codificatore AMF." BFrames="B-frame massimi" VAAPI.Device="Dispositivo VAAPI" NVENC.LookAhead="Previsione (look-ahead)" @@ -74,7 +74,7 @@ MediaFileFilter.AudioFiles="File audio" MediaFileFilter.AllFiles="Tutti i file" ReplayBuffer="Buffer di replay" ReplayBuffer.Save="Salva il replay" -HelperProcessFailed="Impossibile avviare il processo di supporto di registrazione. Verifica che i file di OBS non siano stati bloccati o rimossi da software di sicurezza od antivirus di terze parti." +HelperProcessFailed="Impossibile avviare il processo di supporto alla registrazione. Verifica che i file di OBS non siano stati bloccati o rimossi da qualche software di sicurezza o antivirus di terze parti." UnableToWritePath="Impossibile scrivere su %1. Assicurati che il tuo account utente possa accedere al percorso di registrazione, e che ci sia spazio sufficiente sul disco." WarnWindowsDefender="Se la protezione ransomware di Windows 10 è attivata, può causare questo errore. Prova a disattivare l'accesso alle cartelle controllato, lo puoi trovare in Sicurezza di Windows → Protezione da virus e minacce → Gestisci protezione ransomware." Encoder.Error="Apertura di %1 non riuscita: %2" @@ -87,7 +87,7 @@ NVENC.GenericError="Prova ad installare il aggiornare il driver NVIDIA." NVENC.UnsupportedDevice="Errore NVENC: dispositivo non supportato.\nVerifica che la scheda video supporti NVENC e prova aggiornando il driver NVIDIA." -NVENC.TooManySessions="Errore NVENC: troppe sessioni simultanee.\nProva a chiudere altri software di registrazione che potrebbero uszare NVENC come NVIDIA ShadowPlay o Windows Game DVR." -NVENC.CheckDrivers="Prova ad installare il driver NVIDIA più recente." +NVENC.TooManySessions="Errore NVENC: troppe sessioni simultanee.\nProva a chiudere altri software di registrazione che potrebbero usare NVENC come NVIDIA ShadowPlay o Windows Game DVR." +NVENC.CheckDrivers="Prova a installare il driver NVIDIA più recente." AV1.8bitUnsupportedHdr="OBS non supporta l'uscita a Rec. 2100 8bit." ReconnectDelayTime="Ritardo di riconnessione" diff --git a/plugins/obs-ffmpeg/data/locale/ro-RO.ini b/plugins/obs-ffmpeg/data/locale/ro-RO.ini index ffb71e95e4e027..789ed4a3acb647 100644 --- a/plugins/obs-ffmpeg/data/locale/ro-RO.ini +++ b/plugins/obs-ffmpeg/data/locale/ro-RO.ini @@ -29,7 +29,7 @@ NVENC.10bitUnsupported="Nu se poate efectua codarea pe 10 biți pe acest codific NVENC.16bitUnsupported="Nu se poate efectua codarea pe 16 biți pe acest codificator." NVENC.NoAV1FallbackPossible="Codificarea AV1 nu este disponibilă cu setările actuale. Încercați să dezactivați orice opțiuni de redimensionare sau de GPU care ar putea fi setate. Verificați jurnalul pentru mai multe detalii." NVENC.Preset2.p1="P1: Cel mai rapid (cea mai scăzută calitate)" -NVENC.Preset2.p2="P1: Mai rapid (calitate mai slabă)" +NVENC.Preset2.p2="P1: Mai rapid (calitate mai scăzută)" NVENC.Preset2.p3="P3: Rapid (calitate slabă)" NVENC.Preset2.p4="P4: Mediu (calitate medie)" NVENC.Preset2.p5="P5: Lent (calitate bună)" @@ -55,7 +55,7 @@ HardwareDecode="Folosește decodificarea hardware când este disponibilă" ClearOnMediaEnd="Nu afișa nimic la terminarea redării" RestartWhenActivated="Repornește redarea când sursa devine activă" CloseFileWhenInactive="Închide fișierul când este inactiv" -CloseFileWhenInactive.ToolTip="Închide fișierul atunci când sursa nu este afișată pe transmisiune sau înregistrare.\nAcest lucru permite fișierului să fie schimbat în momentul în care sursa nu este\nactivă, însă poate exista o întârziere la pornire în momentul reactivării sursei." +CloseFileWhenInactive.ToolTip="Închide fișierul atunci când sursa nu este afișată pe stream sau înregistrare.\nAcest lucru permite fișierului să fie schimbat în momentul în care sursa nu este\nactivă, însă poate exista o întârziere la pornire în momentul reactivării sursei." ColorRange="Gamă de culori YUV" ColorRange.Auto="Automată" ColorRange.Partial="Limitat" @@ -82,11 +82,11 @@ AMF.8bitUnsupportedHdr="OBS nu suportă outputul pe 8 biți a Rec. 2100." AMF.10bitUnsupportedAvc="Nu se poate efectua codificarea de 10 biți pe codificatorul AMD H.264." AMF.16bitUnsupported="Nu se poate efectua codarea pe 16 biți pe acest codificator." NVENC.Error="Deschiderea codecului NVENC a eșuat: %1" -NVENC.GenericError="Încercați să instalați cel mai recent driver NVIDIA și închideți alte programe de înregistrare care ar putea utiliza NVENC, cum ar fi NVIDIA ShadowPlay sau Windows Game DVR." +NVENC.GenericError="Încearcă să instalezi ultimul driver NVIDIA și să închizi celelalte programe de înregistrare care ar putea folosi NVENC, precum NVIDIA ShadowPlay sau Windows Game DVR." NVENC.BadGPUIndex="Ai selectat GPU-ul %1 în setările codificatorului de output. Setează acest lucru înapoi la 0 și încearcă din nou." NVENC.OutdatedDriver="Driverul NVIDIA instalat nu acceptă această versiune NVENC, încercați actualizarea driverului." NVENC.UnsupportedDevice="Eroare NVENC: Dispozitiv neacceptat. Verificați dacă placa video acceptă NVENC și încercați să actualizați driverul." -NVENC.TooManySessions="Eroare NVENC: Prea multe sesiuni simultane. Încercați să închideți alte programe de înregistrare care ar putea utiliza NVENC, cum ar fi NVIDIA ShadowPlay sau Windows Game DVR." +NVENC.TooManySessions="Eroare NVENC: Prea multe sesiuni simultane. Încearcă să închizi celelalte programe de înregistrare care ar putea folosi NVENC, precum NVIDIA ShadowPlay sau Windows Game DVR." NVENC.CheckDrivers="Încercați să instalați cel mai recent driver NVIDIA." AV1.8bitUnsupportedHdr="OBS nu suportă outputul pe 8 biți a Rec. 2100." ReconnectDelayTime="Întârziere la reconectare" diff --git a/plugins/obs-filters/data/locale/it-IT.ini b/plugins/obs-filters/data/locale/it-IT.ini index 4bca8177c747b6..374c6b3265d439 100644 --- a/plugins/obs-filters/data/locale/it-IT.ini +++ b/plugins/obs-filters/data/locale/it-IT.ini @@ -7,7 +7,7 @@ HdrTonemapFilter="Mappatura toni HDR (sovrascrivi)" ScrollFilter="Scorrimento" ChromaKeyFilter="Chiave chroma" ColorKeyFilter="Chiave colore" -SharpnessFilter="Nitidizza" +SharpnessFilter="Nitidezza" Sharpness="Nitidezza" ScaleFilter="Ridimensionamento/proporzioni" GPUDelayFilter="Ritardo del rendering" @@ -79,7 +79,7 @@ NoiseSuppress.SuppressLevel="Livello di riduzione" NoiseSuppress.Intensity="Intensità Di Soppressione" NoiseSuppress.Method="Metodo" NoiseSuppress.Method.Speex="Speex (basso utilizzo della CPU, bassa qualità)" -NoiseSuppress.Method.RNNoise="RNNoise (buona qualità, più uso della CPU)" +NoiseSuppress.Method.RNNoise="RNNoise (buona qualità, maggiore utilizzo della CPU)" NoiseSuppress.Method.Nvafx.Denoiser="Rimozione rumore NVIDIA" NoiseSuppress.Method.Nvafx.Dereverb="Rimozione eco ambientale NVIDIA" NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb="Rimozione rumore + rimozione eco ambientale NVIDIA" @@ -117,8 +117,8 @@ Luma.LumaMaxSmooth="Sfumatura massima di Luma" Luma.LumaMinSmooth="Sfumatura minima di Luma" NvidiaGreenscreenFilter="Rimozione sfondo NVIDIA" Greenscreen.Mode="Modo" -Greenscreen.Quality="Qualità (maggiore uso GPU, migliore qualità)" -Greenscreen.Performance="Prestazioni (minore uso GPU, buona qualità)" +Greenscreen.Quality="Qualità (maggiore utilizzo della GPU, migliore qualità)" +Greenscreen.Performance="Prestazioni (minore utilizzo della GPU, buona qualità)" Greenscreen.Threshold="Soglia" Greenscreen.Deprecation="ATTENZIONE: aggiorna l'SDK NVIDIA sia video che audio. La versione attuale dell'SDK video SDK è obsoleta" Greenscreen.Processing="AMsvhera frequenza di aggiornamento in fotogrammi" diff --git a/plugins/obs-filters/data/locale/ro-RO.ini b/plugins/obs-filters/data/locale/ro-RO.ini index 3727f973f46c00..9dda700ab39d3b 100644 --- a/plugins/obs-filters/data/locale/ro-RO.ini +++ b/plugins/obs-filters/data/locale/ro-RO.ini @@ -71,7 +71,7 @@ Base.Canvas="Rezoluție (planșă) de bază" None="Niciuna" ScaleFiltering="Filtrare scalară" ScaleFiltering.Point="Punct" -ScaleFiltering.Bilinear="Bilineară" +ScaleFiltering.Bilinear="Biliniară" ScaleFiltering.Bicubic="Bicubică" ScaleFiltering.Area="Zonă" NoiseSuppress.SuppressLevel="Nivelul suprimării" diff --git a/plugins/obs-outputs/data/locale/ar-SA.ini b/plugins/obs-outputs/data/locale/ar-SA.ini index 967d123f51f4d8..d0db36d681a441 100644 --- a/plugins/obs-outputs/data/locale/ar-SA.ini +++ b/plugins/obs-outputs/data/locale/ar-SA.ini @@ -5,6 +5,10 @@ RTMPStream.NewSocketLoop="حلقة المقبس (Socket) الجديدة" RTMPStream.LowLatencyMode="نمط الاستجابة المنخفض" FLVOutput="إخراج المِلَفّ بصيغة FLV" FLVOutput.FilePath="مسار الملف" +MP4Output="إخراج ملف MP4" +MP4Output.FilePath="مسار الملف" +MP4Output.StartChapter="ابدأ" +MP4Output.UnnamedChapter="بدون اسم" IPFamily="عائلة عنوان IP" IPFamily.Both="IPv4 و IPv6 (افتراضي)" IPFamily.V4Only="IPv4 فقط" diff --git a/plugins/obs-outputs/data/locale/fr-FR.ini b/plugins/obs-outputs/data/locale/fr-FR.ini index 3865b8dff8e6f6..9abd1efb0fa380 100644 --- a/plugins/obs-outputs/data/locale/fr-FR.ini +++ b/plugins/obs-outputs/data/locale/fr-FR.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Mode faible latence" FLVOutput="Sortie vers fichier FLV" FLVOutput.FilePath="Chemin du fichier" Default="Interface par défaut" +MP4Output="Sortie de fichier MP4" +MP4Output.FilePath="Chemin du Fichier" +MP4Output.StartChapter="Démarrer" +MP4Output.UnnamedChapter="Sans nom" IPFamily="Famille d'adresse IP" IPFamily.Both="IPv4 et IPv6 (par défaut)" IPFamily.V4Only="IPv4 uniquement" diff --git a/plugins/obs-outputs/data/locale/hi-IN.ini b/plugins/obs-outputs/data/locale/hi-IN.ini index 2567cd67772e55..626eb4b2561d7b 100644 --- a/plugins/obs-outputs/data/locale/hi-IN.ini +++ b/plugins/obs-outputs/data/locale/hi-IN.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="निम्न विलंबता मोड" FLVOutput="FLV फ़ाइल आउटपुट" FLVOutput.FilePath="फ़ाइल पथ" Default="आरंभिक" +MP4Output="MP4 फ़ाइल आउटपुट" +MP4Output.FilePath="फ़ाइल पथ" +MP4Output.StartChapter="प्रारंभ" +MP4Output.UnnamedChapter="अनाम" IPFamily="IP पता परिवार" IPFamily.Both="IPv4 और IPv6 (डिफ़ॉल्ट)" IPFamily.V4Only="केवल IPv4" diff --git a/plugins/obs-outputs/data/locale/it-IT.ini b/plugins/obs-outputs/data/locale/it-IT.ini index d95fe21567dbfe..ccc67642624b38 100644 --- a/plugins/obs-outputs/data/locale/it-IT.ini +++ b/plugins/obs-outputs/data/locale/it-IT.ini @@ -10,7 +10,7 @@ MP4Output="File MP4 di uscita" MP4Output.FilePath="Percorso del file" MP4Output.StartChapter="Inizio" MP4Output.UnnamedChapter="Senza nome" -IPFamily="Famiglia indirizzo IP" +IPFamily="Famiglia dell'indirizzo IP" IPFamily.Both="IPv4 e IPv6 (predefinito)" IPFamily.V4Only="Solo IPv4" IPFamily.V6Only="Solo IPv6" @@ -23,6 +23,6 @@ NoData="Nome dell'host trovato, ma nessun dato del tipo richiesto. Ciò può ver AddressNotAvailable="L'indirizzo non è disponibile. Probabilmente hai cercato di associare un indirizzo IP non valido (vai a controllare: Impostazioni → Avanzate)." SSLCertVerifyFailed="Il server RTMP ha inviato un certificato SSL non valido." InvalidParameter="Parametri di connessione non validi. Controlla che l'indirizzo del servizio di streaming sia corretto." -NoRoute="Errore nel contatto dell'host. Assicurati che l'interfaccia che hai collegato possa accedere ad internet e che il servizio di streaming supporti la famiglia di indirizzi che hai selezionato (in Impostazioni → Avanzate)." +NoRoute="Errore nel raggiungere l'host. Assicurati che l'interfaccia che hai collegato possa accedere a internet e che il servizio di streaming supporti la famiglia d'indirizzi che hai selezionato (vedi in Impostazioni → Avanzate)." FTLStream="Stream FTL" FTLStream.PeakBitrate="Picco bitrate" diff --git a/plugins/obs-outputs/data/locale/ka-GE.ini b/plugins/obs-outputs/data/locale/ka-GE.ini index 0709a4dca22dba..5a992ef0afd9ba 100644 --- a/plugins/obs-outputs/data/locale/ka-GE.ini +++ b/plugins/obs-outputs/data/locale/ka-GE.ini @@ -6,6 +6,8 @@ RTMPStream.LowLatencyMode="მცირე დაყოვნების რე FLVOutput="გამოტანილი FLV-ფაილი" FLVOutput.FilePath="ფაილის მისამართი" Default="ნაგულისხმევი" +MP4Output.StartChapter="დაწყება" +MP4Output.UnnamedChapter="უსახელო" IPFamily="IP-მისამართების ჯგუფი" IPFamily.Both="IPv4 და IPv6 (ნაგულისხმევი)" IPFamily.V4Only="IPv4 მხოლოდ" diff --git a/plugins/obs-outputs/data/locale/ro-RO.ini b/plugins/obs-outputs/data/locale/ro-RO.ini index 3ef60adfe6869d..3c0944fdb3cb44 100644 --- a/plugins/obs-outputs/data/locale/ro-RO.ini +++ b/plugins/obs-outputs/data/locale/ro-RO.ini @@ -9,12 +9,12 @@ IPFamily="Familia adresei IP" IPFamily.Both="IPv4 și IPv6 (implicit)" IPFamily.V4Only="Numai IPv4" IPFamily.V6Only="Numai IPv6" -ConnectionTimedOut="Conexiunea a expirat. Asigură-te că ai configurat un serviciu de streaming valid și că nici un firewall nu blochează conexiunea." +ConnectionTimedOut="Conexiunea a expirat. Asigură-te că ai configurat un serviciu de streaming valid și că niciun firewall nu blochează conexiunea." PermissionDenied="Conexiunea a fost blocată. Verificați setările firewall-ului / anti-virus pentru a vă asigura că OBS are acces complet la internet." -ConnectionAborted="Conexiunea a fost întreruptă. Acest lucru indică de obicei probleme de conexiune la internet între tine și serviciul de transmisiune." -ConnectionReset="Conexiunea a fost resetată de partener. Acest lucru indică de obicei probleme de conexiune la internet între tine și serviciul de transmisiune." -HostNotFound="Nu a fost găsit numele gazdei. Asigură-te că ai introdus un server de transmisiune valid și că conexiunea la internet/DNS funcționează corect." -NoData="A fost găsit numele gazdei, dar nu există date de tipul solicitat. Acest lucru se poate întâmpla dacă ai o adresă IPv6 și serviciul de transmisiune are doar adrese IPv4 (vezi Setări → Avansate)." +ConnectionAborted="Conexiunea a fost întreruptă. Acest lucru indică de obicei probleme de conexiune la internet între tine și serviciul de streaming." +ConnectionReset="Conexiunea a fost resetată de partener. Acest lucru indică de obicei probleme de conexiune la internet între tine și serviciul de streaming." +HostNotFound="Nu a fost găsit numele gazdei. Asigură-te că ai introdus un server de streaming valid și că conexiunea la internet/DNS-ul funcționează corect." +NoData="A fost găsit numele gazdei, dar nu există date de tipul solicitat. Acest lucru se poate întâmpla dacă ai o adresă IPv6 și serviciul de streaming are doar adrese IPv4 (vezi Setări → Avansate)." AddressNotAvailable="Adresa nu este disponibilă. Este posibil să fi încercat conectarea la o adresă IP invalidă (vezi Setări → Avansat)." SSLCertVerifyFailed="Serverul RTMP a trimis un certificat SSL nevalid." FTLStream="Stream FTL" diff --git a/plugins/obs-outputs/data/locale/sk-SK.ini b/plugins/obs-outputs/data/locale/sk-SK.ini index ef7f0304e8e415..af54200381315a 100644 --- a/plugins/obs-outputs/data/locale/sk-SK.ini +++ b/plugins/obs-outputs/data/locale/sk-SK.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Režim nízkej latencie" FLVOutput="Výstup do súboru FLV" FLVOutput.FilePath="Cesta k súboru" Default="Predvolené" +MP4Output="Výstup do súboru MP4" +MP4Output.FilePath="Cesta k súboru" +MP4Output.StartChapter="Štart" +MP4Output.UnnamedChapter="Nepomenovaný" IPFamily="Rodina IP adresy" IPFamily.Both="IPv4 a IPv6 (Predvolené)" IPFamily.V4Only="Iba IPv4" diff --git a/plugins/obs-outputs/data/locale/sl-SI.ini b/plugins/obs-outputs/data/locale/sl-SI.ini index 718849b48ae893..6a4e404bd0bd6a 100644 --- a/plugins/obs-outputs/data/locale/sl-SI.ini +++ b/plugins/obs-outputs/data/locale/sl-SI.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Način nizke zakasnitve" FLVOutput="Izhod datoteke FLV" FLVOutput.FilePath="Pot datoteke" Default="Privzeto" +MP4Output="Izhod datoteke MP4" +MP4Output.FilePath="Pot datoteke" +MP4Output.StartChapter="Začni" +MP4Output.UnnamedChapter="Neimenovano" IPFamily="Družina naslovov IP" IPFamily.Both="IPv4 in IPv6 (privzeto)" IPFamily.V4Only="Samo IPv4" diff --git a/plugins/obs-outputs/data/locale/tr-TR.ini b/plugins/obs-outputs/data/locale/tr-TR.ini index c584b99343ada2..5b961ec217c36e 100644 --- a/plugins/obs-outputs/data/locale/tr-TR.ini +++ b/plugins/obs-outputs/data/locale/tr-TR.ini @@ -6,6 +6,10 @@ RTMPStream.LowLatencyMode="Düşük Gecikme Modu" FLVOutput="FLV Dosyası Çıkışı" FLVOutput.FilePath="Dosya Yolu" Default="Varsayılan" +MP4Output="MP4 Dosya Çıkışı" +MP4Output.FilePath="Dosya yolu" +MP4Output.StartChapter="Başlat" +MP4Output.UnnamedChapter="İsimiz" IPFamily="IP Adres Ailesi" IPFamily.Both="IPv4 ve IPv6 (Varsayılan)" IPFamily.V4Only="Sadece IPv4" diff --git a/plugins/obs-qsv11/data/locale/ro-RO.ini b/plugins/obs-qsv11/data/locale/ro-RO.ini index c0fb344e8c8cc1..8be52d0660f2ef 100644 --- a/plugins/obs-qsv11/data/locale/ro-RO.ini +++ b/plugins/obs-qsv11/data/locale/ro-RO.ini @@ -1,4 +1,4 @@ -TargetUsage="Utilizare ţintă" +TargetUsage="Utilizare țintă" Bitrate="Rată de biți" MaxBitrate="Rată de biți maximă" RateControl="Controlul ratei" @@ -11,3 +11,10 @@ Latency.ToolTip="Există un compromis între latență și calitate.\nDacă în 10bitUnsupportedAvc="Nu se poate efectua codificarea de 10 biți pe codificatorul Intel QSV H.264." 16bitUnsupported="Nu se poate efectua codarea pe 16 biți pe acest codificator." BFrames="Cadre B" +TargetUsage.TU1="TU1: Cel mai lent (cea mai bună calitate)" +TargetUsage.TU2="TU2: Mai lent" +TargetUsage.TU3="TU3: Lent" +TargetUsage.TU4="TU4: Echilibrat (calitate medie)" +TargetUsage.TU5="TU5: Rapid" +TargetUsage.TU6="TU6: Mai rapid" +TargetUsage.TU7="TU7: Cel mai rapid (cea mai bună viteză)" diff --git a/plugins/obs-transitions/data/locale/pt-PT.ini b/plugins/obs-transitions/data/locale/pt-PT.ini index 5eafc5da17c67d..a0314fc5cd18c2 100644 --- a/plugins/obs-transitions/data/locale/pt-PT.ini +++ b/plugins/obs-transitions/data/locale/pt-PT.ini @@ -20,14 +20,14 @@ TransitionPointType="Tipo de ponto de transição" AudioTransitionPointType="Tipo de ponto transição de áudio" TransitionPointTypeFrame="Fotograma" TransitionPointTypeTime="Tempo" -TrackMatteEnabled="Usar uma máscara nas faixas" +TrackMatteEnabled="Usar uma máscara nas pistas" InvertTrackMatte="Inverter cores da máscara" -TrackMatteVideoFile="Ficheiro vídeo de máscara das faixas" -TrackMatteLayout="Disposição da máscara" +TrackMatteVideoFile="Ficheiro vídeo de máscara das pistas" +TrackMatteLayout="Esquema da máscara" TrackMatteLayoutHorizontal="Mesmo ficheiro, lado a lado (ferrão à esquerda, máscara à direita)" TrackMatteLayoutVertical="Mesmo ficheiro, empilhado (ferrão ao cimo, máscara em baixo)" TrackMatteLayoutSeparateFile="Ficheiro separado (aviso: a máscara pode perder a sincronia)" -TrackMatteLayoutMask="Só máscara" +TrackMatteLayoutMask="Apenas máscara" PreloadVideoToRam="Pré-carregar vídeo para RAM" PreloadVideoToRam.Description="Carregar todo o ficheiro de ferrão para RAM, evitando descodificação em tempo real durante a reprodução.\nRequer muita RAM (um vídeo típico de 5 segundos a 1080p60 leva ~1 GB)." AudioFadeStyle="Estilo de desvanecimento do áudio" diff --git a/plugins/obs-vst/data/locale/sv-SE.ini b/plugins/obs-vst/data/locale/sv-SE.ini index 35f418ad79d4a8..f157fa9214a8ce 100644 --- a/plugins/obs-vst/data/locale/sv-SE.ini +++ b/plugins/obs-vst/data/locale/sv-SE.ini @@ -1,4 +1,4 @@ -OpenPluginInterface="Öppna insticksmodulens gränssnitt" -ClosePluginInterface="Stäng insticksmodulens gränssnitt" -VstPlugin="VST 2.x-insticksmodul" +OpenPluginInterface="Öppna insticksprogrammets gränssnitt" +ClosePluginInterface="Stäng insticksprogrammets gränssnitt" +VstPlugin="VST 2.x-insticksprogram" OpenInterfaceWhenActive="Öppna gränssnitt vid aktiv" diff --git a/plugins/obs-webrtc/data/locale/it-IT.ini b/plugins/obs-webrtc/data/locale/it-IT.ini index 97546d3d35b83e..00f6b4f802da4b 100644 --- a/plugins/obs-webrtc/data/locale/it-IT.ini +++ b/plugins/obs-webrtc/data/locale/it-IT.ini @@ -1,5 +1,5 @@ Output.Name="Output WHIP" Service.Name="Servizio WHIP" -Service.BearerToken="Token Bearer" +Service.BearerToken="Portatore del token" Error.InvalidSDP="Il server WHIP ha risposto con SDP non valido: %1" -Error.NoRemoteDescription="Impossibile impostare descrizione remota: %1" +Error.NoRemoteDescription="Impossibile impostare la descrizione remota: %1" diff --git a/plugins/obs-x264/data/locale/it-IT.ini b/plugins/obs-x264/data/locale/it-IT.ini index e1cde0518204ec..bf049032d697b4 100644 --- a/plugins/obs-x264/data/locale/it-IT.ini +++ b/plugins/obs-x264/data/locale/it-IT.ini @@ -9,5 +9,5 @@ Tune="Regolazione di precisione" None="(nessuno)" EncoderOptions="Parametri x264 (separati da uno spazio)" VFR="Velocità dei fotogrammi variabile (VFR)" -HighPrecisionUnsupported="OBS non supporta l'uso di x264 con formati colori ad alta precisione." +HighPrecisionUnsupported="OBS non supporta l'uso di x264 con formati di colore ad alta precisione." HdrUnsupported="OBS non supporta l'uso di x264 con Rec. 2100." diff --git a/plugins/rtmp-services/data/locale/ar-SA.ini b/plugins/rtmp-services/data/locale/ar-SA.ini index 81f4751760d973..8f810171c4c418 100644 --- a/plugins/rtmp-services/data/locale/ar-SA.ini +++ b/plugins/rtmp-services/data/locale/ar-SA.ini @@ -8,3 +8,5 @@ UseAuth="استخدام المصادقة" Username="اسم المستخدم" Password="كلمة السر" ShowAll="إظهار كافة الخدمات" +MultitrackVideo.Disclaimer="%1 يعمل تلقائيا على تحسين إعداداتك لترميز وإرسال خصائص فيديو متعددة إلى %2.سيؤدي تحديد هذا الخيار إلى إرسال معلومات حول الكمبيوتر وإعداد البرنامج ل%2." +MultitrackVideo.LearnMoreLink="اعرف المزيد." diff --git a/plugins/rtmp-services/data/locale/hi-IN.ini b/plugins/rtmp-services/data/locale/hi-IN.ini index 1d2aaa4bb7b3cd..3aee0d4920a7e0 100644 --- a/plugins/rtmp-services/data/locale/hi-IN.ini +++ b/plugins/rtmp-services/data/locale/hi-IN.ini @@ -8,3 +8,5 @@ UseAuth="प्रमाणीकरण का प्रयोग करें" Username="उपयोगकर्ता का नाम" Password="पासवर्ड" ShowAll="सभी सेवाओं को दिखाएं" +MultitrackVideo.Disclaimer="%1 स्वचालित रूप से आपकी सेटिंग्स को एन्कोड करने और %2 पर एकाधिक वीडियो गुणवत्ता भेजने के लिए अनुकूलित करता है. इस विकल्प को चुनने से आपके कंप्यूटर और सॉफ़्टवेयर सेटअप के बारे में %2 जानकारी भेजी जाएगी." +MultitrackVideo.LearnMoreLink="और जानें" diff --git a/plugins/rtmp-services/data/locale/ka-GE.ini b/plugins/rtmp-services/data/locale/ka-GE.ini index 04d82d07702fff..e2bff0e71edbb9 100644 --- a/plugins/rtmp-services/data/locale/ka-GE.ini +++ b/plugins/rtmp-services/data/locale/ka-GE.ini @@ -8,3 +8,4 @@ UseAuth="ანგარიშზე შესვლით" Username="მომხმარებლის სახელი" Password="პაროლი" ShowAll="ყველა მომსახურების ჩვენება" +MultitrackVideo.LearnMoreLink="გაიგე მეტი" diff --git a/plugins/rtmp-services/data/locale/ko-KR.ini b/plugins/rtmp-services/data/locale/ko-KR.ini index 4e8bcd9d384dad..2f105d2fa2f456 100644 --- a/plugins/rtmp-services/data/locale/ko-KR.ini +++ b/plugins/rtmp-services/data/locale/ko-KR.ini @@ -8,5 +8,5 @@ UseAuth="접속 시 인증 필요" Username="사용자 이름" Password="암호" ShowAll="모든 방송 서비스 표시" -MultitrackVideo.Disclaimer="%1은 여러 영상을 여러 품질로 인코딩하여 %2로 전송할 수 있도록 자동으로 설정을 최적화합니다. 이 옵션을 선택하면 %2 에 컴퓨터 본체와 소프트웨어 설정에 관한 정보를 전송합니다." +MultitrackVideo.Disclaimer="%1 (은)는 설정을 자동으로 최적화하여 여러 비디오 품질을 %2 에 인코딩 및 전송합니다. 이 옵션을 선택하면 컴퓨터 사양 및 소프트웨어 설정에 대한 정보가 %2 (으)로 전송됩니다." MultitrackVideo.LearnMoreLink=" 더 알아보기" diff --git a/plugins/rtmp-services/data/locale/ro-RO.ini b/plugins/rtmp-services/data/locale/ro-RO.ini index aa1074dd95851f..a008dd14426666 100644 --- a/plugins/rtmp-services/data/locale/ro-RO.ini +++ b/plugins/rtmp-services/data/locale/ro-RO.ini @@ -1,9 +1,10 @@ -StreamingServices="Servicii de transmisiune" -CustomStreamingServer="Server personalizat de transmisiune" +StreamingServices="Servicii de streaming" +CustomStreamingServer="Server personalizat de streaming" Service="Serviciu" Server.Auto="Automat (Recomandat)" -StreamKey="Cheie de transmisiune" +StreamKey="Cheie de stream" UseAuth="Folosește autentificarea" Username="Nume de utilizator" Password="Parolă" ShowAll="Afișează toate serviciile" +MultitrackVideo.LearnMoreLink=" Află mai multe" diff --git a/plugins/rtmp-services/data/locale/sk-SK.ini b/plugins/rtmp-services/data/locale/sk-SK.ini index ee282ecbde4617..2846eb9367a577 100644 --- a/plugins/rtmp-services/data/locale/sk-SK.ini +++ b/plugins/rtmp-services/data/locale/sk-SK.ini @@ -7,5 +7,5 @@ UseAuth="Použiť overenie" Username="Užívateľské meno" Password="Heslo" ShowAll="Zobraziť všetky služby" -MultitrackVideo.Disclaimer="%1 automaticky optimalizuje vaše nastavenia pre enkódovanie a pošle viaceré video kvality do %2. Vybratím tejto možnosti pošle %2 informácie of vašom počítači a nastavenie softvéru." +MultitrackVideo.Disclaimer="%1 automaticky optimalizuje vaše nastavenia pre enkódovanie a odosielanie viacerých kvalít videa do %2. Vybratím tejto možnosti pošle %2 informácie o vašom počítači a nastavenie softvéru." MultitrackVideo.LearnMoreLink=" Zistiť viac" diff --git a/plugins/rtmp-services/data/locale/zh-TW.ini b/plugins/rtmp-services/data/locale/zh-TW.ini index bd804252435bc1..e837fe913fb11b 100644 --- a/plugins/rtmp-services/data/locale/zh-TW.ini +++ b/plugins/rtmp-services/data/locale/zh-TW.ini @@ -2,7 +2,7 @@ StreamingServices="串流服務" CustomStreamingServer="自訂串流伺服器" Service="服務商" Server="伺服器" -Server.Auto="自動 (建議)" +Server.Auto="自動(建議)" StreamKey="串流金鑰" UseAuth="使用身份驗證" Username="使用者名稱" diff --git a/plugins/text-freetype2/data/locale/it-IT.ini b/plugins/text-freetype2/data/locale/it-IT.ini index 71a3206d214c4d..da76e325a18f5b 100644 --- a/plugins/text-freetype2/data/locale/it-IT.ini +++ b/plugins/text-freetype2/data/locale/it-IT.ini @@ -1,11 +1,11 @@ TextFreetype2="Testo (FreeType 2)" Font="Carattere" Text="Testo" -TextFile="File testo" +TextFile="File di testo" TextFile.Encoding="UTF-8 o UTF-16" -TextInputMode="Modalità inserimento testo" +TextInputMode="Modalità d'inserimento testo" TextInputMode.Manual="Manuale" -TextInputMode.FromFile="Da file" +TextInputMode.FromFile="Da un file" TextFileFilter="File di testo (*.txt);;" ChatLogMode="Modalità chat" ChatLogLines="Righe da visualizzare in modalità chat" diff --git a/plugins/vlc-video/data/locale/pt-PT.ini b/plugins/vlc-video/data/locale/pt-PT.ini index a4147a6ea87b45..96b3583893fe47 100644 --- a/plugins/vlc-video/data/locale/pt-PT.ini +++ b/plugins/vlc-video/data/locale/pt-PT.ini @@ -12,6 +12,6 @@ Restart="Reiniciar" Stop="Parar" PlaylistNext="Seguinte" PlaylistPrev="Anterior" -AudioTrack="Faixa de áudio" -SubtitleTrack="Faixa de legendas" +AudioTrack="Pista de áudio" +SubtitleTrack="Pista de legendas" SubtitleEnable="Ativar legendas" diff --git a/plugins/vlc-video/data/locale/sv-SE.ini b/plugins/vlc-video/data/locale/sv-SE.ini index 4171e46723d728..a99d3371dedbc8 100644 --- a/plugins/vlc-video/data/locale/sv-SE.ini +++ b/plugins/vlc-video/data/locale/sv-SE.ini @@ -1,13 +1,13 @@ VLCSource="VLC-videokälla" Playlist="Spellista" LoopPlaylist="Slinga spellista" -Shuffle="Slumpa spellista" +Shuffle="Blanda spellista" PlaybackBehavior="Synlighetsbeteende" PlaybackBehavior.StopRestart="Stoppa när den inte syns, starta om när den syns" PlaybackBehavior.PauseUnpause="Pausa när den inte syns, återuppta när den syns" PlaybackBehavior.AlwaysPlay="Spela alltid även när den inte syns" NetworkCaching="Närverkscaching" -PlayPause="Spela/Pausa" +PlayPause="Spela upp/Pausa" Restart="Starta om" Stop="Stoppa" PlaylistNext="Nästa" diff --git a/plugins/win-capture/data/locale/it-IT.ini b/plugins/win-capture/data/locale/it-IT.ini index 2088a98941dd17..2ffa0de2d96594 100644 --- a/plugins/win-capture/data/locale/it-IT.ini +++ b/plugins/win-capture/data/locale/it-IT.ini @@ -17,7 +17,7 @@ SLIFix="Modalità di cattura per SLI/Crossfire (lenta)" AllowTransparency="Permetti la trasparenza" PremultipliedAlpha="Alfa premoltiplicato" Monitor="Schermo" -PrimaryMonitor="Schermo Principale" +PrimaryMonitor="Schermo principale" Method="Metodo di acquisizione" Method.DXGI="Duplicazione Desktop DXGI" Method.WindowsGraphicsCapture="Windows 10 (1903 e superiore)" @@ -38,14 +38,14 @@ GameCapture.HookRate.Fastest="La più veloce" GameCapture.Rgb10a2Space="Spazio colore RGB10A2" Mode="Modalità" CaptureAudio="Cattura audio (BETA)" -CaptureAudio.TT="Quando abilitato, crea una sorgente \"Acquisizione audio applicazione\" che si aggiorna automaticamente alla finestra/applicazione attualmente catturata.\nNota che se l'Audio del desktop è configurato, ciò potrebbe comportare un audio raddoppiato." +CaptureAudio.TT="Quando abilitato, crea una sorgente \"Acquisizione dell'audio dell'applicazione\" che si aggiorna automaticamente alla finestra/applicazione attualmente catturata.\nNota che se l'Audio del desktop è configurato, ciò potrebbe comportare un audio raddoppiato." Compatibility.GameCapture.Admin="%name% potrebbe richiedere che OBS sia eseguito come amministratore per utilizzare Cattura Gioco" Compatibility.GameCapture.Blocked="'%name%' non può essere catturato tramite 'Cattura gioco'. Usa invece 'Cattura finestra' o 'Cattura schermo'." -Compatibility.GameCapture.Blocked.Applications="%name% applicazioni non possono essere catturate tramite 'Cattura gioco'; usa invece 'Cattura finestra' o 'Cattura schermo'." -Compatibility.GameCapture.Blocked.Applications.Built="I giochi creati con '%name%' non possono essere catturati usando 'Cattura gioco'; usa invece 'Cattura finestra' o 'Cattura schermo'." +Compatibility.GameCapture.Blocked.Applications="Le applicazioni %name% non possono essere catturate tramite 'Cattura gioco'. Utilizza invece 'Cattura finestra' o 'Cattura schermo'." +Compatibility.GameCapture.Blocked.Applications.Built="I giochi creati con '%name%' non possono essere catturati usando 'Cattura gioco'. Utilizza invece 'Cattura finestra' o 'Cattura schermo'." Compatibility.GameCapture.WrongGPU="Se l'anteprima è vuota, assicurati che %name% sia in esecuzione sulla stessa GPU di OBS." Compatibility.WindowCapture.BitBlt="%name% potrebbe non essere catturabile usando il metodo di acquisizione selezionato (BitBlt)." -Compatibility.WindowCapture.BitBlt.Applications="'%name%' potrebbe non essere catturabile usando il metodo di acquisizione selezionato (BitBlt)." -Compatibility.WindowCapture.BitBlt.Applications.Based="Le applicazioni basate su ì%name%ì potrebbero non essere catturabili usando il metodo di acquisizione selezionato (BitBlt)." -Compatibility.Application.CSGO="'%name%' per usare 'Cattura gioco' potrebbe richiedere l'opzione di avvio --allow_third_party_software ." +Compatibility.WindowCapture.BitBlt.Applications="Le applicazioni '%name%' potrebbero non essere catturabili utilizzando il metodo di acquisizione selezionato (BitBlt)." +Compatibility.WindowCapture.BitBlt.Applications.Based="Le applicazioni basate su %name% potrebbero non essere catturabili utilizzando il metodo di acquisizione selezionato (BitBlt)." +Compatibility.Application.CSGO="%name% per utilizzare 'Cattura gioco' potrebbe richiedere l'opzione di avvio --allow_third_party_software." Compatibility.Application.Minecraft="Se hai problemi a catturare Minecraft: Java Edition, controlla la nostra guida per la risoluzione dei problemi." From 02c1742b30873e4317ad56147ea0252f1c94e88a Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Mon, 15 Jul 2024 21:35:03 -0400 Subject: [PATCH 0311/1073] libobs/graphics: Prevent heap overflow in libnsgif See upstream commit: https://source.netsurf-browser.org/libnsgif.git/commit/?id=a268d2c15252ac58c19f1b19771822c66bcf73b2 --- libobs/graphics/libnsgif/libnsgif.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libobs/graphics/libnsgif/libnsgif.c b/libobs/graphics/libnsgif/libnsgif.c index a58eb5ee59e7f8..6b36e8a539d051 100644 --- a/libobs/graphics/libnsgif/libnsgif.c +++ b/libobs/graphics/libnsgif/libnsgif.c @@ -1183,6 +1183,10 @@ static bool gif_next_LZW(gif_animation *gif) { incode = code; if (code >= gif->max_code) { + if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) { + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; + } *gif->stack_pointer++ = gif->firstcode; code = gif->oldcode; } @@ -1192,12 +1196,21 @@ static bool gif_next_LZW(gif_animation *gif) { * * Note: our gif->stack is always big enough to hold a complete decompressed chunk. */ while (code >= gif->clear_code) { + if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) { + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; + } *gif->stack_pointer++ = gif->table[1][code]; new_code = gif->table[0][code]; if (new_code < gif->clear_code) { code = new_code; break; } + + if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) { + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; + } *gif->stack_pointer++ = gif->table[1][new_code]; code = gif->table[0][new_code]; if (code == new_code) { @@ -1206,6 +1219,10 @@ static bool gif_next_LZW(gif_animation *gif) { } } + if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) { + gif->current_error = GIF_FRAME_DATA_ERROR; + return false; + } *gif->stack_pointer++ = gif->firstcode = gif->table[1][code]; if ((code = gif->max_code) < (1 << GIF_MAX_LZW)) { From c5a01b7df4471a44154363a0699b30b8ade86032 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 19 Jul 2024 20:07:14 -0400 Subject: [PATCH 0312/1073] obs-websocket: Update version to 5.5.2 Bug Fixes: - Fix an issue where the virtualcam requests would report that the virtualcam is not available. - Fix an issue with the config migration where the migrated settings were not being persisted to disk. Changelog: https://github.com/obsproject/obs-websocket/commit/0548c7798a323fe5296c150e13b898a5ee62fc1e --- plugins/obs-websocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-websocket b/plugins/obs-websocket index f8bc7c4f593c79..0548c7798a323f 160000 --- a/plugins/obs-websocket +++ b/plugins/obs-websocket @@ -1 +1 @@ -Subproject commit f8bc7c4f593c7957ecdb45a24faaff3e95f24e8b +Subproject commit 0548c7798a323fe5296c150e13b898a5ee62fc1e From 2fa77c402149c0bb797b5838e5fc6a7391e6a86d Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Sat, 20 Jul 2024 10:17:43 +0900 Subject: [PATCH 0313/1073] UI: Fix cast of pointer type at invalid current scene setting on load --- UI/window-basic-main.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 20e2d01a7b26f0..5c05f4141cadce 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1293,10 +1293,8 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file) if (!curScene) { auto find_scene_cb = [](void *source_ptr, obs_source_t *scene) { - OBSSourceAutoRelease &source = - reinterpret_cast( - source_ptr); - source = obs_source_get_ref(scene); + *static_cast(source_ptr) = + obs_source_get_ref(scene); return false; }; obs_enum_scenes(find_scene_cb, &curScene); From 7cf497496588b6d8496f70fa30a4937fe88ae43b Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 20 Jul 2024 05:25:37 +0200 Subject: [PATCH 0314/1073] CI: Do not mark legacy MF/AMF plugins as deleted --- .github/actions/windows-patches/config.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/actions/windows-patches/config.toml b/.github/actions/windows-patches/config.toml index 93546e0f288fee..2382fbfeaac6be 100644 --- a/.github/actions/windows-patches/config.toml +++ b/.github/actions/windows-patches/config.toml @@ -15,6 +15,12 @@ skip_sign = true patch_type = "zstd" compress_files = true +# Prevent some legacy plugins from being marked as deleted +exclude_from_removal = [ + "enc-amf", + "win-mf", +] + [package] [package.installer] From 31963f8dacf96e0948b2f29641db836b4b86adfc Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 18 Jul 2024 06:19:26 +0200 Subject: [PATCH 0315/1073] obs-outputs: Set FLV DTS offset based on first audio or video packet --- plugins/obs-outputs/flv-output.c | 14 ++++++++++---- plugins/obs-outputs/rtmp-stream.c | 12 +++++++++--- plugins/obs-outputs/rtmp-stream.h | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/plugins/obs-outputs/flv-output.c b/plugins/obs-outputs/flv-output.c index 9d2159964a57de..9681f279474992 100644 --- a/plugins/obs-outputs/flv-output.c +++ b/plugins/obs-outputs/flv-output.c @@ -51,7 +51,7 @@ struct flv_output { pthread_mutex_t mutex; - bool got_first_video; + bool got_first_packet; int32_t start_dts_offset; }; @@ -500,7 +500,7 @@ static bool flv_output_start(void *data) if (!obs_output_initialize_encoders(stream->output, 0)) return false; - stream->got_first_video = false; + stream->got_first_packet = false; stream->sent_headers = false; os_atomic_set_bool(&stream->stopping, false); @@ -579,10 +579,10 @@ static void flv_output_data(void *data, struct encoder_packet *packet) } if (packet->type == OBS_ENCODER_VIDEO) { - if (!stream->got_first_video) { + if (!stream->got_first_packet) { stream->start_dts_offset = get_ms_time(packet, packet->dts); - stream->got_first_video = true; + stream->got_first_packet = true; } switch (stream->video_codec[packet->track_idx]) { @@ -616,6 +616,12 @@ static void flv_output_data(void *data, struct encoder_packet *packet) } obs_encoder_packet_release(&parsed_packet); } else { + if (!stream->got_first_packet) { + stream->start_dts_offset = + get_ms_time(packet, packet->dts); + stream->got_first_packet = true; + } + if (packet->track_idx != 0) { write_audio_packet_ex(stream, packet, false, packet->track_idx); diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index aa4f84de63ceec..190d80aac18c7b 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -1314,7 +1314,7 @@ static bool init_connect(struct rtmp_stream *stream) stream->total_bytes_sent = 0; stream->dropped_frames = 0; stream->min_priority = 0; - stream->got_first_video = false; + stream->got_first_packet = false; settings = obs_output_get_settings(stream->output); dstr_copy(&stream->path, @@ -1761,10 +1761,10 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) } if (packet->type == OBS_ENCODER_VIDEO) { - if (!stream->got_first_video) { + if (!stream->got_first_packet) { stream->start_dts_offset = get_ms_time(packet, packet->dts); - stream->got_first_video = true; + stream->got_first_packet = true; } switch (stream->video_codec[packet->track_idx]) { @@ -1788,6 +1788,12 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) break; } } else { + if (!stream->got_first_packet) { + stream->start_dts_offset = + get_ms_time(packet, packet->dts); + stream->got_first_packet = true; + } + obs_encoder_packet_ref(&new_packet, packet); } diff --git a/plugins/obs-outputs/rtmp-stream.h b/plugins/obs-outputs/rtmp-stream.h index 580607111a7d1d..3ef0cf9a4f121a 100644 --- a/plugins/obs-outputs/rtmp-stream.h +++ b/plugins/obs-outputs/rtmp-stream.h @@ -60,7 +60,7 @@ struct rtmp_stream { struct deque packets; bool sent_headers; - bool got_first_video; + bool got_first_packet; int64_t start_dts_offset; volatile bool connecting; From 7912d06b65042501837b5fc669e28cfa0412e020 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Wed, 17 Jul 2024 19:11:23 -0700 Subject: [PATCH 0316/1073] libobs-opengl: Disable NV12/P010 on Windows These formats are used for texture encoding but the OpenGL texture encoding pipeline is not supported on Windows. This can also cause failure to initialize on Windows so it's better to disable the formats. --- libobs-opengl/gl-subsystem.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index 249a68ea8957d8..d0b05193dea190 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -1522,13 +1522,21 @@ void gs_swapchain_destroy(gs_swapchain_t *swapchain) bool device_nv12_available(gs_device_t *device) { UNUSED_PARAMETER(device); +#ifdef _WIN32 + return false; +#else return true; // always a split R8,R8G8 texture. +#endif } bool device_p010_available(gs_device_t *device) { UNUSED_PARAMETER(device); +#ifdef _WIN32 + return false; +#else return true; // always a split R16,R16G16 texture. +#endif } uint32_t gs_voltexture_get_width(const gs_texture_t *voltex) From 4f7be9b84b2d287675b4419b14c0389b8cb856f4 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 23 Jul 2024 11:56:19 -0400 Subject: [PATCH 0317/1073] libobs: Update version to 30.2.1 I forgot to update this before tagging 30.2.1, so the updater won't stop telling people there's an update available, even after updating. --- libobs/obs-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 7f2890d4a5d408..a9100bdc7830c3 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 0 +#define LIBOBS_API_PATCH_VER 1 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From 5854f3b9e5861246ea57dd4a26d3d847a8552c4b Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 23 Jul 2024 11:56:59 -0400 Subject: [PATCH 0318/1073] libobs: Update version to 30.2.2 I forgot to update this before tagging 30.2.1, so the updater won't stop telling people there's an update available, even after updating. Update this for real now to 30.2.2. --- libobs/obs-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index a9100bdc7830c3..a975e67477da88 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 1 +#define LIBOBS_API_PATCH_VER 2 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | (minor << 16) | patch) From ce6f9a4742b40b04e68ef759e0698bd5eac4360e Mon Sep 17 00:00:00 2001 From: Gol-D-Ace Date: Wed, 24 Jul 2024 17:17:46 +0200 Subject: [PATCH 0319/1073] win-capture: Add Zenless Zone Zero to compatibility list --- plugins/win-capture/data/compatibility.json | 14 ++++++++++++++ plugins/win-capture/data/package.json | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/win-capture/data/compatibility.json b/plugins/win-capture/data/compatibility.json index aa839d7fd0a403..74fc308dcfa448 100644 --- a/plugins/win-capture/data/compatibility.json +++ b/plugins/win-capture/data/compatibility.json @@ -547,6 +547,20 @@ "window_capture_wgc": false, "message": "Honkai: Star Rail may require OBS to be run as admin to use Game Capture.", "url": "https://obsproject.com/kb/game-capture-troubleshooting" + }, + { + "name": "Zenless Zone Zero", + "translation_key": "Compatibility.GameCapture.Admin", + "severity": 1, + "executable": "ZenlessZoneZero.exe", + "window_class": "", + "window_title": "", + "match_flags": 1, + "game_capture": true, + "window_capture": false, + "window_capture_wgc": false, + "message": "Zenless Zone Zero may require OBS to be run as admin to use Game Capture.", + "url": "https://obsproject.com/kb/game-capture-troubleshooting" } ] } diff --git a/plugins/win-capture/data/package.json b/plugins/win-capture/data/package.json index e893adb3e21f1f..cc01daabe441b9 100644 --- a/plugins/win-capture/data/package.json +++ b/plugins/win-capture/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/win-capture/v1", - "version": 3, + "version": 4, "files": [ { "name": "compatibility.json", - "version": 3 + "version": 4 } ] } From 7a5b9140864d666d7a8147348c5e69f1855fdb15 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 10:19:50 +0200 Subject: [PATCH 0320/1073] Add shared folder in gitignore and format scripts --- .gitignore | 1 + build-aux/.run-format.zsh | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0443d1740aeccb..0748807f414e04 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ !/docs !/libobs* !/plugins +!/shared !/test !/UI !.cirrus.xml diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh index e34e11e3768923..4a0fc21732ddfa 100755 --- a/build-aux/.run-format.zsh +++ b/build-aux/.run-format.zsh @@ -54,7 +54,7 @@ invoke_formatter() { exit 2 fi - if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps)/**/*.(c|cpp|h|hpp|m|mm)(.N)) + if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared)/**/*.(c|cpp|h|hpp|m|mm)(.N)) source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|obs-outputs/ftl-sdk|win-dshow/libdshowcapture)/*}) @@ -75,7 +75,7 @@ invoke_formatter() { exit 2 } - if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|cmake)/**/(CMakeLists.txt|*.cmake)(.N)) + if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared|cmake)/**/(CMakeLists.txt|*.cmake)(.N)) source_files=(${source_files:#*/(obs-outputs/ftl-sdk|jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) From f293a6edd1a891d74842bbf18365f1fad12913d6 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 10:47:45 +0200 Subject: [PATCH 0321/1073] deps,shared,plugins: Move file-updater to shared folder --- deps/CMakeLists.txt | 1 - plugins/rtmp-services/CMakeLists.txt | 2 +- plugins/rtmp-services/cmake/legacy.cmake | 4 ++++ plugins/win-capture/CMakeLists.txt | 2 +- plugins/win-capture/cmake/legacy.cmake | 4 ++++ {deps => shared}/file-updater/CMakeLists.txt | 0 {deps => shared}/file-updater/file-updater/file-updater.c | 0 {deps => shared}/file-updater/file-updater/file-updater.h | 0 8 files changed, 10 insertions(+), 3 deletions(-) rename {deps => shared}/file-updater/CMakeLists.txt (100%) rename {deps => shared}/file-updater/file-updater/file-updater.c (100%) rename {deps => shared}/file-updater/file-updater/file-updater.h (100%) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index f046aba690e224..7f065ec0268762 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -4,7 +4,6 @@ if(OS_WINDOWS) endif() add_subdirectory(blake2) -add_subdirectory(file-updater) add_subdirectory(glad) add_subdirectory(happy-eyeballs) add_subdirectory(libcaption) diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index f2cf7555d258ee..9a1c993a981ebd 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -10,7 +10,7 @@ set(RTMP_SERVICES_URL mark_as_advanced(RTMP_SERVICES_URL) if(NOT TARGET OBS::file-updater) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/file-updater" "${CMAKE_BINARY_DIR}/deps/file-updater") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") endif() find_package(jansson REQUIRED) diff --git a/plugins/rtmp-services/cmake/legacy.cmake b/plugins/rtmp-services/cmake/legacy.cmake index 1215746fc7985c..88362cd75f5faa 100644 --- a/plugins/rtmp-services/cmake/legacy.cmake +++ b/plugins/rtmp-services/cmake/legacy.cmake @@ -13,6 +13,10 @@ add_library(OBS::rtmp-services ALIAS rtmp-services) find_package(Jansson 2.5 REQUIRED) +if(NOT TARGET OBS::file-updater) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") +endif() + target_sources( rtmp-services PRIVATE service-specific/twitch.c diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index ae8bffaff50786..5b9bbeeabea005 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -31,7 +31,7 @@ if(NOT TARGET OBS::ipc-util) endif() if(NOT TARGET OBS::file-updater) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/file-updater" "${CMAKE_BINARY_DIR}/deps/file-updater") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") endif() find_package(jansson REQUIRED) diff --git a/plugins/win-capture/cmake/legacy.cmake b/plugins/win-capture/cmake/legacy.cmake index 1b488a55b5afa3..26930754fabc9a 100644 --- a/plugins/win-capture/cmake/legacy.cmake +++ b/plugins/win-capture/cmake/legacy.cmake @@ -13,6 +13,10 @@ add_library(OBS::capture ALIAS win-capture) find_package(Jansson 2.5 REQUIRED) +if(NOT TARGET OBS::file-updater) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") +endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/compat-config.h.in ${CMAKE_BINARY_DIR}/config/compat-config.h) target_sources( diff --git a/deps/file-updater/CMakeLists.txt b/shared/file-updater/CMakeLists.txt similarity index 100% rename from deps/file-updater/CMakeLists.txt rename to shared/file-updater/CMakeLists.txt diff --git a/deps/file-updater/file-updater/file-updater.c b/shared/file-updater/file-updater/file-updater.c similarity index 100% rename from deps/file-updater/file-updater/file-updater.c rename to shared/file-updater/file-updater/file-updater.c diff --git a/deps/file-updater/file-updater/file-updater.h b/shared/file-updater/file-updater/file-updater.h similarity index 100% rename from deps/file-updater/file-updater/file-updater.h rename to shared/file-updater/file-updater/file-updater.h From fa0ffff7c0f74c86819a11a650af4c2080a66bd3 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 10:59:54 +0200 Subject: [PATCH 0322/1073] deps,shared,obs-outputs: Move happy-eyeballs to shared folder --- deps/CMakeLists.txt | 1 - plugins/obs-outputs/CMakeLists.txt | 2 +- plugins/obs-outputs/cmake/legacy.cmake | 4 ++++ {deps => shared}/happy-eyeballs/CMakeLists.txt | 0 {deps => shared}/happy-eyeballs/happy-eyeballs.c | 0 {deps => shared}/happy-eyeballs/happy-eyeballs.h | 0 6 files changed, 5 insertions(+), 2 deletions(-) rename {deps => shared}/happy-eyeballs/CMakeLists.txt (100%) rename {deps => shared}/happy-eyeballs/happy-eyeballs.c (100%) rename {deps => shared}/happy-eyeballs/happy-eyeballs.h (100%) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 7f065ec0268762..635b6a8471f668 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -5,7 +5,6 @@ endif() add_subdirectory(blake2) add_subdirectory(glad) -add_subdirectory(happy-eyeballs) add_subdirectory(libcaption) add_subdirectory(media-playback) add_subdirectory(obs-scripting) diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index 576911a0c399c7..d0b7c4ba36b3c5 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -6,7 +6,7 @@ find_package(MbedTLS REQUIRED) find_package(ZLIB REQUIRED) if(NOT TARGET happy-eyeballs) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/happy-eyeballs" "${CMAKE_BINARY_DIR}/deps/happy-eyeballs") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/happy-eyeballs" "${CMAKE_BINARY_DIR}/shared/happy-eyeballs") endif() add_library(obs-outputs MODULE) diff --git a/plugins/obs-outputs/cmake/legacy.cmake b/plugins/obs-outputs/cmake/legacy.cmake index 12b474a1e412f9..e8f14b1a3c2515 100644 --- a/plugins/obs-outputs/cmake/legacy.cmake +++ b/plugins/obs-outputs/cmake/legacy.cmake @@ -13,6 +13,10 @@ mark_as_advanced(ENABLE_STATIC_MBEDTLS) add_library(obs-outputs MODULE) add_library(OBS::outputs ALIAS obs-outputs) +if(NOT TARGET happy-eyeballs) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/happy-eyeballs" "${CMAKE_BINARY_DIR}/shared/happy-eyeballs") +endif() + target_sources( obs-outputs PRIVATE obs-outputs.c diff --git a/deps/happy-eyeballs/CMakeLists.txt b/shared/happy-eyeballs/CMakeLists.txt similarity index 100% rename from deps/happy-eyeballs/CMakeLists.txt rename to shared/happy-eyeballs/CMakeLists.txt diff --git a/deps/happy-eyeballs/happy-eyeballs.c b/shared/happy-eyeballs/happy-eyeballs.c similarity index 100% rename from deps/happy-eyeballs/happy-eyeballs.c rename to shared/happy-eyeballs/happy-eyeballs.c diff --git a/deps/happy-eyeballs/happy-eyeballs.h b/shared/happy-eyeballs/happy-eyeballs.h similarity index 100% rename from deps/happy-eyeballs/happy-eyeballs.h rename to shared/happy-eyeballs/happy-eyeballs.h From 4b0b71ad5f9075714d51ceda74532eb40b60860d Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 10:53:00 +0200 Subject: [PATCH 0323/1073] deps,shared,win-capture: Move ipc-util to shared folder --- deps/CMakeLists.txt | 1 - plugins/win-capture/CMakeLists.txt | 2 +- plugins/win-capture/cmake/legacy.cmake | 4 ++++ plugins/win-capture/graphics-hook/CMakeLists.txt | 2 +- plugins/win-capture/graphics-hook/cmake/legacy.cmake | 4 ++++ {deps => shared}/ipc-util/CMakeLists.txt | 0 {deps => shared}/ipc-util/ipc-util/pipe-posix.c | 0 {deps => shared}/ipc-util/ipc-util/pipe-posix.h | 0 {deps => shared}/ipc-util/ipc-util/pipe-windows.c | 0 {deps => shared}/ipc-util/ipc-util/pipe-windows.h | 0 {deps => shared}/ipc-util/ipc-util/pipe.h | 0 11 files changed, 10 insertions(+), 3 deletions(-) rename {deps => shared}/ipc-util/CMakeLists.txt (100%) rename {deps => shared}/ipc-util/ipc-util/pipe-posix.c (100%) rename {deps => shared}/ipc-util/ipc-util/pipe-posix.h (100%) rename {deps => shared}/ipc-util/ipc-util/pipe-windows.c (100%) rename {deps => shared}/ipc-util/ipc-util/pipe-windows.h (100%) rename {deps => shared}/ipc-util/ipc-util/pipe.h (100%) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 635b6a8471f668..d6f79c090e06f8 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,5 +1,4 @@ if(OS_WINDOWS) - add_subdirectory(ipc-util) add_subdirectory(w32-pthreads) endif() diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index 5b9bbeeabea005..c7026bf2c7aa7d 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -27,7 +27,7 @@ mark_as_advanced(COMPAT_URL) # cmake-format: on if(NOT TARGET OBS::ipc-util) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/ipc-util" "${CMAKE_BINARY_DIR}/deps/ipc-util") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") endif() if(NOT TARGET OBS::file-updater) diff --git a/plugins/win-capture/cmake/legacy.cmake b/plugins/win-capture/cmake/legacy.cmake index 26930754fabc9a..7c529a678a2103 100644 --- a/plugins/win-capture/cmake/legacy.cmake +++ b/plugins/win-capture/cmake/legacy.cmake @@ -13,6 +13,10 @@ add_library(OBS::capture ALIAS win-capture) find_package(Jansson 2.5 REQUIRED) +if(NOT TARGET OBS::ipc-util) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") +endif() + if(NOT TARGET OBS::file-updater) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") endif() diff --git a/plugins/win-capture/graphics-hook/CMakeLists.txt b/plugins/win-capture/graphics-hook/CMakeLists.txt index 91378ba9cbe68f..e5ad38324bb9ad 100644 --- a/plugins/win-capture/graphics-hook/CMakeLists.txt +++ b/plugins/win-capture/graphics-hook/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Detours REQUIRED) find_package(Vulkan REQUIRED) if(NOT TARGET OBS::ipc-util) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/ipc-util" "${CMAKE_BINARY_DIR}/deps/ipc-util") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") endif() if(NOT TARGET OBS::obfuscate) diff --git a/plugins/win-capture/graphics-hook/cmake/legacy.cmake b/plugins/win-capture/graphics-hook/cmake/legacy.cmake index bcf2623b0944e0..e23a364efb3eb6 100644 --- a/plugins/win-capture/graphics-hook/cmake/legacy.cmake +++ b/plugins/win-capture/graphics-hook/cmake/legacy.cmake @@ -6,6 +6,10 @@ find_package(Vulkan REQUIRED) add_library(graphics-hook MODULE) add_library(OBS::graphics-hook ALIAS graphics-hook) +if(NOT TARGET OBS::ipc-util) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") +endif() + target_sources( graphics-hook PRIVATE graphics-hook.c diff --git a/deps/ipc-util/CMakeLists.txt b/shared/ipc-util/CMakeLists.txt similarity index 100% rename from deps/ipc-util/CMakeLists.txt rename to shared/ipc-util/CMakeLists.txt diff --git a/deps/ipc-util/ipc-util/pipe-posix.c b/shared/ipc-util/ipc-util/pipe-posix.c similarity index 100% rename from deps/ipc-util/ipc-util/pipe-posix.c rename to shared/ipc-util/ipc-util/pipe-posix.c diff --git a/deps/ipc-util/ipc-util/pipe-posix.h b/shared/ipc-util/ipc-util/pipe-posix.h similarity index 100% rename from deps/ipc-util/ipc-util/pipe-posix.h rename to shared/ipc-util/ipc-util/pipe-posix.h diff --git a/deps/ipc-util/ipc-util/pipe-windows.c b/shared/ipc-util/ipc-util/pipe-windows.c similarity index 100% rename from deps/ipc-util/ipc-util/pipe-windows.c rename to shared/ipc-util/ipc-util/pipe-windows.c diff --git a/deps/ipc-util/ipc-util/pipe-windows.h b/shared/ipc-util/ipc-util/pipe-windows.h similarity index 100% rename from deps/ipc-util/ipc-util/pipe-windows.h rename to shared/ipc-util/ipc-util/pipe-windows.h diff --git a/deps/ipc-util/ipc-util/pipe.h b/shared/ipc-util/ipc-util/pipe.h similarity index 100% rename from deps/ipc-util/ipc-util/pipe.h rename to shared/ipc-util/ipc-util/pipe.h From ade04f4f1cabd60c162337c6f3816bb0814e82aa Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 10:35:26 +0200 Subject: [PATCH 0324/1073] deps,shared,obs-ffmpeg: Move media-playback to shared folder --- deps/CMakeLists.txt | 1 - plugins/obs-ffmpeg/cmake/dependencies.cmake | 2 +- plugins/obs-ffmpeg/cmake/legacy.cmake | 4 ++++ {deps => shared}/media-playback/CMakeLists.txt | 0 {deps => shared}/media-playback/LICENSE.media-playback | 0 {deps => shared}/media-playback/media-playback/cache.c | 0 {deps => shared}/media-playback/media-playback/cache.h | 0 .../media-playback/media-playback/closest-format.h | 0 {deps => shared}/media-playback/media-playback/decode.c | 0 {deps => shared}/media-playback/media-playback/decode.h | 0 .../media-playback/media-playback/media-playback.c | 0 .../media-playback/media-playback/media-playback.h | 0 {deps => shared}/media-playback/media-playback/media.c | 0 {deps => shared}/media-playback/media-playback/media.h | 0 14 files changed, 5 insertions(+), 2 deletions(-) rename {deps => shared}/media-playback/CMakeLists.txt (100%) rename {deps => shared}/media-playback/LICENSE.media-playback (100%) rename {deps => shared}/media-playback/media-playback/cache.c (100%) rename {deps => shared}/media-playback/media-playback/cache.h (100%) rename {deps => shared}/media-playback/media-playback/closest-format.h (100%) rename {deps => shared}/media-playback/media-playback/decode.c (100%) rename {deps => shared}/media-playback/media-playback/decode.h (100%) rename {deps => shared}/media-playback/media-playback/media-playback.c (100%) rename {deps => shared}/media-playback/media-playback/media-playback.h (100%) rename {deps => shared}/media-playback/media-playback/media.c (100%) rename {deps => shared}/media-playback/media-playback/media.h (100%) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index d6f79c090e06f8..bdb8592540eaea 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -5,6 +5,5 @@ endif() add_subdirectory(blake2) add_subdirectory(glad) add_subdirectory(libcaption) -add_subdirectory(media-playback) add_subdirectory(obs-scripting) add_subdirectory(opts-parser) diff --git a/plugins/obs-ffmpeg/cmake/dependencies.cmake b/plugins/obs-ffmpeg/cmake/dependencies.cmake index 087e8c2569b93c..502e46cb749587 100644 --- a/plugins/obs-ffmpeg/cmake/dependencies.cmake +++ b/plugins/obs-ffmpeg/cmake/dependencies.cmake @@ -17,7 +17,7 @@ find_package( # cmake-format: on if(NOT TARGET OBS::media-playback) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/media-playback" "${CMAKE_BINARY_DIR}/deps/media-playback") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/media-playback" "${CMAKE_BINARY_DIR}/shared/media-playback") endif() if(NOT TARGET OBS::opts-parser) diff --git a/plugins/obs-ffmpeg/cmake/legacy.cmake b/plugins/obs-ffmpeg/cmake/legacy.cmake index 558a34df62656e..d8a108b029bcc6 100644 --- a/plugins/obs-ffmpeg/cmake/legacy.cmake +++ b/plugins/obs-ffmpeg/cmake/legacy.cmake @@ -17,6 +17,10 @@ find_package( add_library(obs-ffmpeg MODULE) add_library(OBS::ffmpeg ALIAS obs-ffmpeg) +if(NOT TARGET OBS::media-playback) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/media-playback" "${CMAKE_BINARY_DIR}/shared/media-playback") +endif() + add_subdirectory(ffmpeg-mux) if(ENABLE_NEW_MPEGTS_OUTPUT) find_package(Librist QUIET) diff --git a/deps/media-playback/CMakeLists.txt b/shared/media-playback/CMakeLists.txt similarity index 100% rename from deps/media-playback/CMakeLists.txt rename to shared/media-playback/CMakeLists.txt diff --git a/deps/media-playback/LICENSE.media-playback b/shared/media-playback/LICENSE.media-playback similarity index 100% rename from deps/media-playback/LICENSE.media-playback rename to shared/media-playback/LICENSE.media-playback diff --git a/deps/media-playback/media-playback/cache.c b/shared/media-playback/media-playback/cache.c similarity index 100% rename from deps/media-playback/media-playback/cache.c rename to shared/media-playback/media-playback/cache.c diff --git a/deps/media-playback/media-playback/cache.h b/shared/media-playback/media-playback/cache.h similarity index 100% rename from deps/media-playback/media-playback/cache.h rename to shared/media-playback/media-playback/cache.h diff --git a/deps/media-playback/media-playback/closest-format.h b/shared/media-playback/media-playback/closest-format.h similarity index 100% rename from deps/media-playback/media-playback/closest-format.h rename to shared/media-playback/media-playback/closest-format.h diff --git a/deps/media-playback/media-playback/decode.c b/shared/media-playback/media-playback/decode.c similarity index 100% rename from deps/media-playback/media-playback/decode.c rename to shared/media-playback/media-playback/decode.c diff --git a/deps/media-playback/media-playback/decode.h b/shared/media-playback/media-playback/decode.h similarity index 100% rename from deps/media-playback/media-playback/decode.h rename to shared/media-playback/media-playback/decode.h diff --git a/deps/media-playback/media-playback/media-playback.c b/shared/media-playback/media-playback/media-playback.c similarity index 100% rename from deps/media-playback/media-playback/media-playback.c rename to shared/media-playback/media-playback/media-playback.c diff --git a/deps/media-playback/media-playback/media-playback.h b/shared/media-playback/media-playback/media-playback.h similarity index 100% rename from deps/media-playback/media-playback/media-playback.h rename to shared/media-playback/media-playback/media-playback.h diff --git a/deps/media-playback/media-playback/media.c b/shared/media-playback/media-playback/media.c similarity index 100% rename from deps/media-playback/media-playback/media.c rename to shared/media-playback/media-playback/media.c diff --git a/deps/media-playback/media-playback/media.h b/shared/media-playback/media-playback/media.h similarity index 100% rename from deps/media-playback/media-playback/media.h rename to shared/media-playback/media-playback/media.h From f5e2f15d28fec1877d0bbaaa175fd1e676a928e7 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 11:07:51 +0200 Subject: [PATCH 0325/1073] deps,shared,frontend-tools: Move obs-scripting to shared folder --- UI/frontend-plugins/frontend-tools/CMakeLists.txt | 2 +- UI/frontend-plugins/frontend-tools/cmake/legacy.cmake | 2 ++ deps/CMakeLists.txt | 1 - {deps => shared}/obs-scripting/CMakeLists.txt | 0 {deps => shared}/obs-scripting/cmake/cstrcache.cmake | 0 {deps => shared}/obs-scripting/cmake/legacy.cmake | 3 +++ {deps => shared}/obs-scripting/cmake/lua.cmake | 0 {deps => shared}/obs-scripting/cmake/python.cmake | 0 {deps => shared}/obs-scripting/cmake/windows/obs-module.rc.in | 0 {deps => shared}/obs-scripting/cstrcache.cpp | 0 {deps => shared}/obs-scripting/cstrcache.h | 0 {deps => shared}/obs-scripting/obs-scripting-callback.h | 0 {deps => shared}/obs-scripting/obs-scripting-config.h.in | 0 {deps => shared}/obs-scripting/obs-scripting-internal.h | 0 {deps => shared}/obs-scripting/obs-scripting-logging.c | 0 {deps => shared}/obs-scripting/obs-scripting-lua-frontend.c | 0 {deps => shared}/obs-scripting/obs-scripting-lua-source.c | 0 {deps => shared}/obs-scripting/obs-scripting-lua.c | 0 {deps => shared}/obs-scripting/obs-scripting-lua.h | 0 {deps => shared}/obs-scripting/obs-scripting-python-frontend.c | 0 {deps => shared}/obs-scripting/obs-scripting-python-import.c | 0 {deps => shared}/obs-scripting/obs-scripting-python-import.h | 0 {deps => shared}/obs-scripting/obs-scripting-python.c | 0 {deps => shared}/obs-scripting/obs-scripting-python.h | 0 {deps => shared}/obs-scripting/obs-scripting.c | 0 {deps => shared}/obs-scripting/obs-scripting.h | 0 {deps => shared}/obs-scripting/obslua/CMakeLists.txt | 0 {deps => shared}/obs-scripting/obslua/cmake/legacy.cmake | 0 {deps => shared}/obs-scripting/obslua/obslua.i | 0 {deps => shared}/obs-scripting/obspython/CMakeLists.txt | 0 {deps => shared}/obs-scripting/obspython/cmake/legacy.cmake | 0 {deps => shared}/obs-scripting/obspython/obspython.i | 0 32 files changed, 6 insertions(+), 2 deletions(-) rename {deps => shared}/obs-scripting/CMakeLists.txt (100%) rename {deps => shared}/obs-scripting/cmake/cstrcache.cmake (100%) rename {deps => shared}/obs-scripting/cmake/legacy.cmake (97%) rename {deps => shared}/obs-scripting/cmake/lua.cmake (100%) rename {deps => shared}/obs-scripting/cmake/python.cmake (100%) rename {deps => shared}/obs-scripting/cmake/windows/obs-module.rc.in (100%) rename {deps => shared}/obs-scripting/cstrcache.cpp (100%) rename {deps => shared}/obs-scripting/cstrcache.h (100%) rename {deps => shared}/obs-scripting/obs-scripting-callback.h (100%) rename {deps => shared}/obs-scripting/obs-scripting-config.h.in (100%) rename {deps => shared}/obs-scripting/obs-scripting-internal.h (100%) rename {deps => shared}/obs-scripting/obs-scripting-logging.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-lua-frontend.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-lua-source.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-lua.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-lua.h (100%) rename {deps => shared}/obs-scripting/obs-scripting-python-frontend.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-python-import.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-python-import.h (100%) rename {deps => shared}/obs-scripting/obs-scripting-python.c (100%) rename {deps => shared}/obs-scripting/obs-scripting-python.h (100%) rename {deps => shared}/obs-scripting/obs-scripting.c (100%) rename {deps => shared}/obs-scripting/obs-scripting.h (100%) rename {deps => shared}/obs-scripting/obslua/CMakeLists.txt (100%) rename {deps => shared}/obs-scripting/obslua/cmake/legacy.cmake (100%) rename {deps => shared}/obs-scripting/obslua/obslua.i (100%) rename {deps => shared}/obs-scripting/obspython/CMakeLists.txt (100%) rename {deps => shared}/obs-scripting/obspython/cmake/legacy.cmake (100%) rename {deps => shared}/obs-scripting/obspython/obspython.i (100%) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index bdc6582ec73ebe..7db278041441da 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -51,7 +51,7 @@ target_link_libraries( $<$:X11::X11> $<$:Qt::GuiPrivate>) -add_subdirectory("${CMAKE_SOURCE_DIR}/deps/obs-scripting" "${CMAKE_BINARY_DIR}/deps/obs-scripting") +add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-scripting" "${CMAKE_BINARY_DIR}/shared/obs-scripting") if(ENABLE_SCRIPTING AND TARGET OBS::scripting) target_sources(frontend-tools PRIVATE scripts.cpp scripts.hpp) diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index 0524ca042ec845..9ded76029b76ca 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -53,6 +53,8 @@ if(OS_POSIX AND NOT OS_MACOS) target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) endif() +add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-scripting" "${CMAKE_BINARY_DIR}/shared/obs-scripting") + if(ENABLE_SCRIPTING AND TARGET OBS::scripting) target_compile_definitions(frontend-tools PRIVATE ENABLE_SCRIPTING) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index bdb8592540eaea..1e5196ccc3d6d5 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -5,5 +5,4 @@ endif() add_subdirectory(blake2) add_subdirectory(glad) add_subdirectory(libcaption) -add_subdirectory(obs-scripting) add_subdirectory(opts-parser) diff --git a/deps/obs-scripting/CMakeLists.txt b/shared/obs-scripting/CMakeLists.txt similarity index 100% rename from deps/obs-scripting/CMakeLists.txt rename to shared/obs-scripting/CMakeLists.txt diff --git a/deps/obs-scripting/cmake/cstrcache.cmake b/shared/obs-scripting/cmake/cstrcache.cmake similarity index 100% rename from deps/obs-scripting/cmake/cstrcache.cmake rename to shared/obs-scripting/cmake/cstrcache.cmake diff --git a/deps/obs-scripting/cmake/legacy.cmake b/shared/obs-scripting/cmake/legacy.cmake similarity index 97% rename from deps/obs-scripting/cmake/legacy.cmake rename to shared/obs-scripting/cmake/legacy.cmake index bf4d9a4ef78e38..75a7d72aa3e7ac 100644 --- a/deps/obs-scripting/cmake/legacy.cmake +++ b/shared/obs-scripting/cmake/legacy.cmake @@ -176,3 +176,6 @@ target_compile_definitions(obs-scripting PRIVATE SCRIPT_DIR="${OBS_SCRIPT_PLUGIN $<$:ENABLE_UI>) setup_binary_target(obs-scripting) + +# Dirty workaround: CMake 2.0 seems to fail without this file +file(TOUCH "${CMAKE_BINARY_DIR}/shared/cmake_install.cmake") diff --git a/deps/obs-scripting/cmake/lua.cmake b/shared/obs-scripting/cmake/lua.cmake similarity index 100% rename from deps/obs-scripting/cmake/lua.cmake rename to shared/obs-scripting/cmake/lua.cmake diff --git a/deps/obs-scripting/cmake/python.cmake b/shared/obs-scripting/cmake/python.cmake similarity index 100% rename from deps/obs-scripting/cmake/python.cmake rename to shared/obs-scripting/cmake/python.cmake diff --git a/deps/obs-scripting/cmake/windows/obs-module.rc.in b/shared/obs-scripting/cmake/windows/obs-module.rc.in similarity index 100% rename from deps/obs-scripting/cmake/windows/obs-module.rc.in rename to shared/obs-scripting/cmake/windows/obs-module.rc.in diff --git a/deps/obs-scripting/cstrcache.cpp b/shared/obs-scripting/cstrcache.cpp similarity index 100% rename from deps/obs-scripting/cstrcache.cpp rename to shared/obs-scripting/cstrcache.cpp diff --git a/deps/obs-scripting/cstrcache.h b/shared/obs-scripting/cstrcache.h similarity index 100% rename from deps/obs-scripting/cstrcache.h rename to shared/obs-scripting/cstrcache.h diff --git a/deps/obs-scripting/obs-scripting-callback.h b/shared/obs-scripting/obs-scripting-callback.h similarity index 100% rename from deps/obs-scripting/obs-scripting-callback.h rename to shared/obs-scripting/obs-scripting-callback.h diff --git a/deps/obs-scripting/obs-scripting-config.h.in b/shared/obs-scripting/obs-scripting-config.h.in similarity index 100% rename from deps/obs-scripting/obs-scripting-config.h.in rename to shared/obs-scripting/obs-scripting-config.h.in diff --git a/deps/obs-scripting/obs-scripting-internal.h b/shared/obs-scripting/obs-scripting-internal.h similarity index 100% rename from deps/obs-scripting/obs-scripting-internal.h rename to shared/obs-scripting/obs-scripting-internal.h diff --git a/deps/obs-scripting/obs-scripting-logging.c b/shared/obs-scripting/obs-scripting-logging.c similarity index 100% rename from deps/obs-scripting/obs-scripting-logging.c rename to shared/obs-scripting/obs-scripting-logging.c diff --git a/deps/obs-scripting/obs-scripting-lua-frontend.c b/shared/obs-scripting/obs-scripting-lua-frontend.c similarity index 100% rename from deps/obs-scripting/obs-scripting-lua-frontend.c rename to shared/obs-scripting/obs-scripting-lua-frontend.c diff --git a/deps/obs-scripting/obs-scripting-lua-source.c b/shared/obs-scripting/obs-scripting-lua-source.c similarity index 100% rename from deps/obs-scripting/obs-scripting-lua-source.c rename to shared/obs-scripting/obs-scripting-lua-source.c diff --git a/deps/obs-scripting/obs-scripting-lua.c b/shared/obs-scripting/obs-scripting-lua.c similarity index 100% rename from deps/obs-scripting/obs-scripting-lua.c rename to shared/obs-scripting/obs-scripting-lua.c diff --git a/deps/obs-scripting/obs-scripting-lua.h b/shared/obs-scripting/obs-scripting-lua.h similarity index 100% rename from deps/obs-scripting/obs-scripting-lua.h rename to shared/obs-scripting/obs-scripting-lua.h diff --git a/deps/obs-scripting/obs-scripting-python-frontend.c b/shared/obs-scripting/obs-scripting-python-frontend.c similarity index 100% rename from deps/obs-scripting/obs-scripting-python-frontend.c rename to shared/obs-scripting/obs-scripting-python-frontend.c diff --git a/deps/obs-scripting/obs-scripting-python-import.c b/shared/obs-scripting/obs-scripting-python-import.c similarity index 100% rename from deps/obs-scripting/obs-scripting-python-import.c rename to shared/obs-scripting/obs-scripting-python-import.c diff --git a/deps/obs-scripting/obs-scripting-python-import.h b/shared/obs-scripting/obs-scripting-python-import.h similarity index 100% rename from deps/obs-scripting/obs-scripting-python-import.h rename to shared/obs-scripting/obs-scripting-python-import.h diff --git a/deps/obs-scripting/obs-scripting-python.c b/shared/obs-scripting/obs-scripting-python.c similarity index 100% rename from deps/obs-scripting/obs-scripting-python.c rename to shared/obs-scripting/obs-scripting-python.c diff --git a/deps/obs-scripting/obs-scripting-python.h b/shared/obs-scripting/obs-scripting-python.h similarity index 100% rename from deps/obs-scripting/obs-scripting-python.h rename to shared/obs-scripting/obs-scripting-python.h diff --git a/deps/obs-scripting/obs-scripting.c b/shared/obs-scripting/obs-scripting.c similarity index 100% rename from deps/obs-scripting/obs-scripting.c rename to shared/obs-scripting/obs-scripting.c diff --git a/deps/obs-scripting/obs-scripting.h b/shared/obs-scripting/obs-scripting.h similarity index 100% rename from deps/obs-scripting/obs-scripting.h rename to shared/obs-scripting/obs-scripting.h diff --git a/deps/obs-scripting/obslua/CMakeLists.txt b/shared/obs-scripting/obslua/CMakeLists.txt similarity index 100% rename from deps/obs-scripting/obslua/CMakeLists.txt rename to shared/obs-scripting/obslua/CMakeLists.txt diff --git a/deps/obs-scripting/obslua/cmake/legacy.cmake b/shared/obs-scripting/obslua/cmake/legacy.cmake similarity index 100% rename from deps/obs-scripting/obslua/cmake/legacy.cmake rename to shared/obs-scripting/obslua/cmake/legacy.cmake diff --git a/deps/obs-scripting/obslua/obslua.i b/shared/obs-scripting/obslua/obslua.i similarity index 100% rename from deps/obs-scripting/obslua/obslua.i rename to shared/obs-scripting/obslua/obslua.i diff --git a/deps/obs-scripting/obspython/CMakeLists.txt b/shared/obs-scripting/obspython/CMakeLists.txt similarity index 100% rename from deps/obs-scripting/obspython/CMakeLists.txt rename to shared/obs-scripting/obspython/CMakeLists.txt diff --git a/deps/obs-scripting/obspython/cmake/legacy.cmake b/shared/obs-scripting/obspython/cmake/legacy.cmake similarity index 100% rename from deps/obs-scripting/obspython/cmake/legacy.cmake rename to shared/obs-scripting/obspython/cmake/legacy.cmake diff --git a/deps/obs-scripting/obspython/obspython.i b/shared/obs-scripting/obspython/obspython.i similarity index 100% rename from deps/obs-scripting/obspython/obspython.i rename to shared/obs-scripting/obspython/obspython.i From 2a53015ad7492c57e348e0403d67d8359bc475eb Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 2 Aug 2023 10:39:15 +0200 Subject: [PATCH 0326/1073] deps,shared,plugins: Move opts-parser to shared folder --- deps/CMakeLists.txt | 1 - plugins/obs-ffmpeg/cmake/dependencies.cmake | 2 +- plugins/obs-ffmpeg/cmake/legacy.cmake | 4 ++++ plugins/obs-outputs/CMakeLists.txt | 4 ++++ plugins/obs-outputs/cmake/legacy.cmake | 4 ++++ plugins/obs-x264/CMakeLists.txt | 2 +- plugins/obs-x264/cmake/legacy.cmake | 4 ++++ {deps => shared}/opts-parser/CMakeLists.txt | 0 {deps => shared}/opts-parser/opts-parser.c | 0 {deps => shared}/opts-parser/opts-parser.h | 0 10 files changed, 18 insertions(+), 3 deletions(-) rename {deps => shared}/opts-parser/CMakeLists.txt (100%) rename {deps => shared}/opts-parser/opts-parser.c (100%) rename {deps => shared}/opts-parser/opts-parser.h (100%) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 1e5196ccc3d6d5..b99cb2cd2635e0 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -5,4 +5,3 @@ endif() add_subdirectory(blake2) add_subdirectory(glad) add_subdirectory(libcaption) -add_subdirectory(opts-parser) diff --git a/plugins/obs-ffmpeg/cmake/dependencies.cmake b/plugins/obs-ffmpeg/cmake/dependencies.cmake index 502e46cb749587..6e809bbc273960 100644 --- a/plugins/obs-ffmpeg/cmake/dependencies.cmake +++ b/plugins/obs-ffmpeg/cmake/dependencies.cmake @@ -21,7 +21,7 @@ if(NOT TARGET OBS::media-playback) endif() if(NOT TARGET OBS::opts-parser) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/opts-parser" "${CMAKE_BINARY_DIR}/deps/opts-parser") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") endif() if(OS_WINDOWS) diff --git a/plugins/obs-ffmpeg/cmake/legacy.cmake b/plugins/obs-ffmpeg/cmake/legacy.cmake index d8a108b029bcc6..04866b90ab0b92 100644 --- a/plugins/obs-ffmpeg/cmake/legacy.cmake +++ b/plugins/obs-ffmpeg/cmake/legacy.cmake @@ -21,6 +21,10 @@ if(NOT TARGET OBS::media-playback) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/media-playback" "${CMAKE_BINARY_DIR}/shared/media-playback") endif() +if(NOT TARGET OBS::opts-parser) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") +endif() + add_subdirectory(ffmpeg-mux) if(ENABLE_NEW_MPEGTS_OUTPUT) find_package(Librist QUIET) diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index d0b7c4ba36b3c5..1e0a2fe41a6c9d 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -9,6 +9,10 @@ if(NOT TARGET happy-eyeballs) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/happy-eyeballs" "${CMAKE_BINARY_DIR}/shared/happy-eyeballs") endif() +if(NOT TARGET OBS::opts-parser) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") +endif() + add_library(obs-outputs MODULE) add_library(OBS::outputs ALIAS obs-outputs) diff --git a/plugins/obs-outputs/cmake/legacy.cmake b/plugins/obs-outputs/cmake/legacy.cmake index e8f14b1a3c2515..bc2607ec1ce8d6 100644 --- a/plugins/obs-outputs/cmake/legacy.cmake +++ b/plugins/obs-outputs/cmake/legacy.cmake @@ -17,6 +17,10 @@ if(NOT TARGET happy-eyeballs) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/happy-eyeballs" "${CMAKE_BINARY_DIR}/shared/happy-eyeballs") endif() +if(NOT TARGET OBS::opts-parser) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") +endif() + target_sources( obs-outputs PRIVATE obs-outputs.c diff --git a/plugins/obs-x264/CMakeLists.txt b/plugins/obs-x264/CMakeLists.txt index 196c5ab7ae5396..557d9655da1747 100644 --- a/plugins/obs-x264/CMakeLists.txt +++ b/plugins/obs-x264/CMakeLists.txt @@ -5,7 +5,7 @@ legacy_check() find_package(Libx264 REQUIRED) if(NOT TARGET OBS::opts-parser) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/opts-parser" "${CMAKE_BINARY_DIR}/deps/opts-parser") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") endif() add_library(obs-x264 MODULE) diff --git a/plugins/obs-x264/cmake/legacy.cmake b/plugins/obs-x264/cmake/legacy.cmake index a7049c1d848e79..c9d0fa21317c24 100644 --- a/plugins/obs-x264/cmake/legacy.cmake +++ b/plugins/obs-x264/cmake/legacy.cmake @@ -6,6 +6,10 @@ add_library(obs-x264 MODULE) add_library(OBS::x264 ALIAS obs-x264) add_executable(obs-x264-test) +if(NOT TARGET OBS::opts-parser) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") +endif() + target_sources(obs-x264-test PRIVATE obs-x264-test.c) target_link_libraries(obs-x264-test PRIVATE OBS::opts-parser) diff --git a/deps/opts-parser/CMakeLists.txt b/shared/opts-parser/CMakeLists.txt similarity index 100% rename from deps/opts-parser/CMakeLists.txt rename to shared/opts-parser/CMakeLists.txt diff --git a/deps/opts-parser/opts-parser.c b/shared/opts-parser/opts-parser.c similarity index 100% rename from deps/opts-parser/opts-parser.c rename to shared/opts-parser/opts-parser.c diff --git a/deps/opts-parser/opts-parser.h b/shared/opts-parser/opts-parser.h similarity index 100% rename from deps/opts-parser/opts-parser.h rename to shared/opts-parser/opts-parser.h From 4882db4ec41260554ef3fc345f5414a09e2d3023 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Tue, 18 Jul 2023 21:15:15 +0200 Subject: [PATCH 0327/1073] UI: Move QTToGSWindow outside of Qt wrappers --- UI/qt-display.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ UI/qt-wrappers.cpp | 40 ---------------------------------------- UI/qt-wrappers.hpp | 3 --- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/UI/qt-display.cpp b/UI/qt-display.cpp index 7444c6cb58868c..81652988b6d186 100644 --- a/UI/qt-display.cpp +++ b/UI/qt-display.cpp @@ -13,6 +13,14 @@ #include #endif +#if !defined(_WIN32) && !defined(__APPLE__) +#include +#endif + +#ifdef ENABLE_WAYLAND +#include +#endif + class SurfaceEventFilter : public QObject { OBSQTDisplay *display; @@ -62,6 +70,38 @@ static inline QColor rgba_to_color(uint32_t rgba) (rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF); } +static bool QTToGSWindow(QWindow *window, gs_window &gswindow) +{ + bool success = true; + +#ifdef _WIN32 + gswindow.hwnd = (HWND)window->winId(); +#elif __APPLE__ + gswindow.view = (id)window->winId(); +#else + switch (obs_get_nix_platform()) { + case OBS_NIX_PLATFORM_X11_EGL: + gswindow.id = window->winId(); + gswindow.display = obs_get_nix_platform_display(); + break; +#ifdef ENABLE_WAYLAND + case OBS_NIX_PLATFORM_WAYLAND: { + QPlatformNativeInterface *native = + QGuiApplication::platformNativeInterface(); + gswindow.display = + native->nativeResourceForWindow("surface", window); + success = gswindow.display != nullptr; + break; + } +#endif + default: + success = false; + break; + } +#endif + return success; +} + OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags) { diff --git a/UI/qt-wrappers.cpp b/UI/qt-wrappers.cpp index c91fab5fa6ebbf..3eaac324f69d10 100644 --- a/UI/qt-wrappers.cpp +++ b/UI/qt-wrappers.cpp @@ -32,14 +32,6 @@ #include #include -#if !defined(_WIN32) && !defined(__APPLE__) -#include -#endif - -#ifdef ENABLE_WAYLAND -#include -#endif - static inline void OBSErrorBoxva(QWidget *parent, const char *msg, va_list args) { char full_message[8192]; @@ -120,38 +112,6 @@ void OBSMessageBox::critical(QWidget *parent, const QString &title, mb.exec(); } -bool QTToGSWindow(QWindow *window, gs_window &gswindow) -{ - bool success = true; - -#ifdef _WIN32 - gswindow.hwnd = (HWND)window->winId(); -#elif __APPLE__ - gswindow.view = (id)window->winId(); -#else - switch (obs_get_nix_platform()) { - case OBS_NIX_PLATFORM_X11_EGL: - gswindow.id = window->winId(); - gswindow.display = obs_get_nix_platform_display(); - break; -#ifdef ENABLE_WAYLAND - case OBS_NIX_PLATFORM_WAYLAND: { - QPlatformNativeInterface *native = - QGuiApplication::platformNativeInterface(); - gswindow.display = - native->nativeResourceForWindow("surface", window); - success = gswindow.display != nullptr; - break; - } -#endif - default: - success = false; - break; - } -#endif - return success; -} - uint32_t TranslateQtKeyboardEventModifiers(Qt::KeyboardModifiers mods) { int obsModifiers = INTERACT_NONE; diff --git a/UI/qt-wrappers.hpp b/UI/qt-wrappers.hpp index 4a45ebfc99de0e..7ad47de27e1bc6 100644 --- a/UI/qt-wrappers.hpp +++ b/UI/qt-wrappers.hpp @@ -37,7 +37,6 @@ class QComboBox; class QWidget; class QLayout; class QString; -struct gs_window; class QLabel; class QToolBar; @@ -60,8 +59,6 @@ class OBSMessageBox { void OBSErrorBox(QWidget *parent, const char *msg, ...); -bool QTToGSWindow(QWindow *window, gs_window &gswindow); - uint32_t TranslateQtKeyboardEventModifiers(Qt::KeyboardModifiers mods); QDataStream & From 48f139729f3dc27a0b8e77850c0367876df311ca Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 17 Jan 2024 08:24:40 +0100 Subject: [PATCH 0328/1073] UI,shared: Move Qt Wrappers to its own directory --- UI/adv-audio-control.cpp | 2 +- UI/api-interface.cpp | 2 +- UI/auth-listener.cpp | 2 +- UI/auth-youtube.cpp | 2 +- UI/cmake/legacy.cmake | 17 +++++++++++++---- UI/cmake/ui-elements.cmake | 2 -- UI/cmake/ui-qt.cmake | 6 +++++- UI/context-bar-controls.cpp | 2 +- .../aja-output-ui/CMakeLists.txt | 5 +++++ .../aja-output-ui/cmake/legacy.cmake | 8 +++++--- .../decklink-output-ui/CMakeLists.txt | 5 +++++ .../decklink-output-ui/cmake/legacy.cmake | 8 +++++--- .../frontend-tools/CMakeLists.txt | 5 +++++ .../frontend-tools/cmake/legacy.cmake | 8 +++++--- UI/frontend-plugins/frontend-tools/scripts.cpp | 2 +- UI/hotkey-edit.cpp | 2 +- UI/log-viewer.cpp | 2 +- UI/media-controls.hpp | 2 +- UI/obs-app.cpp | 2 +- UI/platform-windows.cpp | 2 +- UI/properties-view.cpp | 2 +- UI/qt-display.cpp | 2 +- UI/remote-text.cpp | 2 +- UI/source-tree.cpp | 2 +- UI/update/mac-update.cpp | 2 +- UI/update/win-update.cpp | 2 +- UI/visibility-item-widget.cpp | 3 ++- UI/volume-control.cpp | 3 ++- UI/window-basic-about.cpp | 2 +- UI/window-basic-adv-audio.cpp | 2 +- UI/window-basic-auto-config-test.cpp | 2 +- UI/window-basic-auto-config.cpp | 2 +- UI/window-basic-filters.cpp | 2 +- UI/window-basic-interaction.cpp | 2 +- UI/window-basic-main-browser.cpp | 2 +- UI/window-basic-main-dropfiles.cpp | 2 +- UI/window-basic-main-outputs.cpp | 2 +- UI/window-basic-main-profiles.cpp | 2 +- UI/window-basic-main-scene-collections.cpp | 2 +- UI/window-basic-main-screenshot.cpp | 3 ++- UI/window-basic-main-transitions.cpp | 2 +- UI/window-basic-main.cpp | 2 +- UI/window-basic-properties.cpp | 2 +- UI/window-basic-settings-a11y.cpp | 2 +- UI/window-basic-settings-stream.cpp | 2 +- UI/window-basic-settings.cpp | 2 +- UI/window-basic-source-select.cpp | 2 +- UI/window-basic-stats.cpp | 2 +- UI/window-basic-vcam-config.cpp | 2 +- UI/window-extra-browsers.cpp | 2 +- UI/window-importer.cpp | 2 +- UI/window-missing-files.cpp | 2 +- UI/window-namedialog.cpp | 2 +- UI/window-projector.cpp | 2 +- UI/window-remux.cpp | 2 +- UI/window-youtube-actions.cpp | 2 +- UI/youtube-api-wrappers.cpp | 2 +- shared/qt/wrappers/CMakeLists.txt | 11 +++++++++++ {UI => shared/qt/wrappers}/qt-wrappers.cpp | 11 +++++------ {UI => shared/qt/wrappers}/qt-wrappers.hpp | 3 ++- 60 files changed, 117 insertions(+), 71 deletions(-) create mode 100644 shared/qt/wrappers/CMakeLists.txt rename {UI => shared/qt/wrappers}/qt-wrappers.cpp (97%) rename {UI => shared/qt/wrappers}/qt-wrappers.hpp (98%) diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 277ac24d44d11c..161534766c9dc7 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -5,7 +5,7 @@ #include #include #include -#include "qt-wrappers.hpp" +#include #include "obs-app.hpp" #include "adv-audio-control.hpp" #include "window-basic-main.hpp" diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 12f03604963ec0..691f507c424933 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -1,6 +1,6 @@ #include +#include #include "obs-app.hpp" -#include "qt-wrappers.hpp" #include "window-basic-main.hpp" #include "window-basic-main-outputs.hpp" diff --git a/UI/auth-listener.cpp b/UI/auth-listener.cpp index 12a7d72fef4118..b6c7a9e0473004 100644 --- a/UI/auth-listener.cpp +++ b/UI/auth-listener.cpp @@ -4,9 +4,9 @@ #include #include #include +#include #include "obs-app.hpp" -#include "qt-wrappers.hpp" #define LOGO_URL "https://obsproject.com/assets/images/new_icon_small-r.png" diff --git a/UI/auth-youtube.cpp b/UI/auth-youtube.cpp index cd07c74cc79759..d3902da5599e8c 100644 --- a/UI/auth-youtube.cpp +++ b/UI/auth-youtube.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -18,7 +19,6 @@ #include "auth-listener.hpp" #include "obs-app.hpp" -#include "qt-wrappers.hpp" #include "ui-config.h" #include "youtube-api-wrappers.hpp" #include "window-basic-main.hpp" diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 487c2a5c0996c4..1c82457e4aac31 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -71,6 +71,10 @@ find_package(CURL REQUIRED) add_subdirectory(frontend-plugins) add_executable(obs) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + find_qt(COMPONENTS Widgets Network Svg Xml COMPONENTS_LINUX Gui DBus) target_link_libraries(obs PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network) @@ -147,8 +151,6 @@ target_sources( platform.hpp qt-display.cpp qt-display.hpp - qt-wrappers.cpp - qt-wrappers.hpp ui-validation.cpp ui-validation.hpp multiview.cpp @@ -304,8 +306,15 @@ target_compile_features(obs PRIVATE cxx_std_17) target_include_directories(obs PRIVATE ${CMAKE_SOURCE_DIR}/deps/json11) -target_link_libraries(obs PRIVATE CURL::libcurl FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat OBS::libobs - OBS::frontend-api) +target_link_libraries( + obs + PRIVATE CURL::libcurl + FFmpeg::avcodec + FFmpeg::avutil + FFmpeg::avformat + OBS::libobs + OBS::frontend-api + OBS::qt-wrappers) set_target_properties(obs PROPERTIES FOLDER "frontend") diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index d3f6f44ec29aac..fccdf42a0ddf50 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -14,8 +14,6 @@ target_sources( properties-view.cpp properties-view.hpp properties-view.moc.hpp - qt-wrappers.cpp - qt-wrappers.hpp slider-ignorewheel.cpp slider-ignorewheel.hpp spinbox-ignorewheel.cpp diff --git a/UI/cmake/ui-qt.cmake b/UI/cmake/ui-qt.cmake index 6148fda8854924..d34de0ad9f7f4f 100644 --- a/UI/cmake/ui-qt.cmake +++ b/UI/cmake/ui-qt.cmake @@ -8,7 +8,11 @@ if(OS_LINUX find_package(Qt6 REQUIRED Gui DBus) endif() -target_link_libraries(obs-studio PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + +target_link_libraries(obs-studio PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network OBS::qt-wrappers) set_target_properties( obs-studio diff --git a/UI/context-bar-controls.cpp b/UI/context-bar-controls.cpp index 3eef5188c279c6..cb4b2f8fc58812 100644 --- a/UI/context-bar-controls.cpp +++ b/UI/context-bar-controls.cpp @@ -1,8 +1,8 @@ #include "window-basic-main.hpp" #include "context-bar-controls.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" +#include #include #include #include diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 56c4be21e49d37..9c7e8c9e656799 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -23,6 +23,10 @@ if(NOT TARGET OBS::aja-support) add_subdirectory("${CMAKE_SOURCE_DIR}/plugins/aja" "${CMAKE_BINARY_DIR}/plugins/aja") endif() +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + add_library(aja-output-ui MODULE) add_library(OBS::aja-output-ui ALIAS aja-output-ui) @@ -40,6 +44,7 @@ target_link_libraries( PRIVATE OBS::libobs OBS::aja-support OBS::frontend-api + OBS::qt-wrappers OBS::ui-support Qt::Widgets AJA::LibAJANTV2 diff --git a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake index 3e0a465895b204..7346834880d0b6 100644 --- a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake @@ -9,6 +9,10 @@ find_package(LibAJANTV2 REQUIRED) add_library(aja-output-ui MODULE) add_library(OBS::aja-output-ui ALIAS aja-output-ui) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) set_target_properties( @@ -53,8 +57,6 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp - ${CMAKE_SOURCE_DIR}/UI/qt-wrappers.cpp - ${CMAKE_SOURCE_DIR}/UI/qt-wrappers.hpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp @@ -62,7 +64,7 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp) -target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api Qt::Widgets AJA::LibAJANTV2) +target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers Qt::Widgets AJA::LibAJANTV2) if(OS_MACOS) find_library(IOKIT_FRAMEWORK Iokit) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index 96e5ba2812fb24..7bdd668ec33d72 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -20,6 +20,10 @@ endif() add_library(decklink-output-ui MODULE) add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + target_sources(decklink-output-ui PRIVATE forms/output.ui) target_sources(decklink-output-ui PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp @@ -31,6 +35,7 @@ target_link_libraries( decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api + OBS::qt-wrappers OBS::ui-support Qt::Widgets "$<$:$>" diff --git a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake index 40de2d1eb04294..709c047887eca1 100644 --- a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake @@ -7,6 +7,10 @@ endif() add_library(decklink-output-ui MODULE) add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) set_target_properties( @@ -35,8 +39,6 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp - ${CMAKE_SOURCE_DIR}/UI/qt-wrappers.hpp - ${CMAKE_SOURCE_DIR}/UI/qt-wrappers.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp @@ -44,7 +46,7 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp) -target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api Qt::Widgets) +target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers Qt::Widgets) target_compile_features(decklink-output-ui PRIVATE cxx_std_17) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 7db278041441da..53786eca24a1ee 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -14,6 +14,10 @@ endif() add_library(frontend-tools MODULE) add_library(OBS::frontend-tools ALIAS frontend-tools) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + target_sources( frontend-tools PRIVATE # cmake-format: sortable @@ -45,6 +49,7 @@ target_link_libraries( frontend-tools PRIVATE OBS::frontend-api OBS::libobs + OBS::qt-wrappers OBS::ui-support Qt::Widgets "$<$:$>" diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index 9ded76029b76ca..8eb109023cddfd 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -3,6 +3,10 @@ project(frontend-tools) add_library(frontend-tools MODULE) add_library(OBS::frontend-tools ALIAS frontend-tools) +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) set_target_properties( @@ -34,8 +38,6 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp - ${CMAKE_SOURCE_DIR}/UI/qt-wrappers.cpp - ${CMAKE_SOURCE_DIR}/UI/qt-wrappers.hpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp @@ -47,7 +49,7 @@ target_sources( target_compile_features(frontend-tools PRIVATE cxx_std_17) -target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::libobs Qt::Widgets) +target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::libobs Qt::Widgets) if(OS_POSIX AND NOT OS_MACOS) target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 3295de7b7750bc..924efdc520c718 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -1,7 +1,6 @@ #include "obs-module.h" #include "scripts.hpp" #include "../../properties-view.hpp" -#include "../../qt-wrappers.hpp" #include "../../plain-text-edit.hpp" #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/UI/hotkey-edit.cpp b/UI/hotkey-edit.cpp index bcd049428dcb96..ebc40361b36b58 100644 --- a/UI/hotkey-edit.cpp +++ b/UI/hotkey-edit.cpp @@ -22,9 +22,9 @@ #include #include #include +#include #include "obs-app.hpp" -#include "qt-wrappers.hpp" void OBSHotkeyEdit::keyPressEvent(QKeyEvent *event) { diff --git a/UI/log-viewer.cpp b/UI/log-viewer.cpp index f02bcd1a54ab4f..dedc04f492126f 100644 --- a/UI/log-viewer.cpp +++ b/UI/log-viewer.cpp @@ -8,9 +8,9 @@ #include #include #include +#include #include "log-viewer.hpp" -#include "qt-wrappers.hpp" OBSLogViewer::OBSLogViewer(QWidget *parent) : QDialog(parent), diff --git a/UI/media-controls.hpp b/UI/media-controls.hpp index 652ddadc4ff270..6a5ec5953c46a5 100644 --- a/UI/media-controls.hpp +++ b/UI/media-controls.hpp @@ -4,7 +4,7 @@ #include #include #include -#include "qt-wrappers.hpp" +#include class Ui_MediaControls; diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 9a7e9fd1063bd2..d2337dae0bc411 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -38,7 +39,6 @@ #include #include -#include "qt-wrappers.hpp" #include "obs-app.hpp" #include "obs-proxy-style.hpp" #include "log-viewer.hpp" diff --git a/UI/platform-windows.cpp b/UI/platform-windows.cpp index 303d918a9becdf..229045a64330cc 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -19,9 +19,9 @@ #include #include "obs-config.h" #include "obs-app.hpp" -#include "qt-wrappers.hpp" #include "platform.hpp" +#include #include #include diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 69f176b1474d10..bd0288b171d983 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -27,12 +27,12 @@ #include "double-slider.hpp" #include "slider-ignorewheel.hpp" #include "spinbox-ignorewheel.hpp" -#include "qt-wrappers.hpp" #include "properties-view.hpp" #include "properties-view.moc.hpp" #include "plain-text-edit.hpp" #include "obs-app.hpp" +#include #include #include #include diff --git a/UI/qt-display.cpp b/UI/qt-display.cpp index 81652988b6d186..136fbc8f32b65f 100644 --- a/UI/qt-display.cpp +++ b/UI/qt-display.cpp @@ -1,11 +1,11 @@ #include "qt-display.hpp" -#include "qt-wrappers.hpp" #include "display-helpers.hpp" #include #include #include #include +#include #include #ifdef _WIN32 diff --git a/UI/remote-text.cpp b/UI/remote-text.cpp index 4a372e156ba765..a5c6dd013d2d0e 100644 --- a/UI/remote-text.cpp +++ b/UI/remote-text.cpp @@ -16,8 +16,8 @@ ******************************************************************************/ #include +#include #include "obs-app.hpp" -#include "qt-wrappers.hpp" #include "remote-text.hpp" using namespace std; diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index 947b217ce04f66..340d6e3b5f90ab 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -1,10 +1,10 @@ #include "window-basic-main.hpp" #include "obs-app.hpp" #include "source-tree.hpp" -#include "qt-wrappers.hpp" #include "platform.hpp" #include "source-label.hpp" +#include #include #include diff --git a/UI/update/mac-update.cpp b/UI/update/mac-update.cpp index 469b01e92ab2fc..c6b35b0e08277a 100644 --- a/UI/update/mac-update.cpp +++ b/UI/update/mac-update.cpp @@ -1,11 +1,11 @@ #include "update-helpers.hpp" #include "shared-update.hpp" -#include "qt-wrappers.hpp" #include "mac-update.hpp" #include "obs-app.hpp" #include +#include #include /* ------------------------------------------------------------------------ */ diff --git a/UI/update/win-update.cpp b/UI/update/win-update.cpp index 5b21d34d843e0c..a0263ad829c3bf 100644 --- a/UI/update/win-update.cpp +++ b/UI/update/win-update.cpp @@ -3,10 +3,10 @@ #include "shared-update.hpp" #include "update-window.hpp" #include "remote-text.hpp" -#include "qt-wrappers.hpp" #include "win-update.hpp" #include "obs-app.hpp" +#include #include #include diff --git a/UI/visibility-item-widget.cpp b/UI/visibility-item-widget.cpp index 23571e9f6c3589..c310007ed9936b 100644 --- a/UI/visibility-item-widget.cpp +++ b/UI/visibility-item-widget.cpp @@ -1,7 +1,8 @@ #include "visibility-item-widget.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" #include "source-label.hpp" + +#include #include #include #include diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index ab9b7f095fda4c..b629d8e670d540 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -1,10 +1,11 @@ #include "window-basic-main.hpp" #include "volume-control.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" #include "mute-checkbox.hpp" #include "absolute-slider.hpp" #include "source-label.hpp" + +#include #include #include #include diff --git a/UI/window-basic-about.cpp b/UI/window-basic-about.cpp index 10751bb1af21ba..d5bdc3eed178fb 100644 --- a/UI/window-basic-about.cpp +++ b/UI/window-basic-about.cpp @@ -1,7 +1,7 @@ #include "window-basic-about.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "remote-text.hpp" +#include #include #include #include diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index b2ef201dc7d515..9d80e29ff54188 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -3,7 +3,7 @@ #include "item-widget-helpers.hpp" #include "adv-audio-control.hpp" #include "obs-app.hpp" -#include "qt-wrappers.hpp" +#include #include "ui_OBSAdvAudio.h" diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index 90487578305234..117a8bc6d51d93 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -8,10 +8,10 @@ #include #include #include +#include #include "window-basic-auto-config.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" #include "ui_AutoConfigTestPage.h" diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 243dce9720f3f1..555304daed7300 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -2,12 +2,12 @@ #include #include +#include #include #include "window-basic-auto-config.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" #include "url-push-button.hpp" diff --git a/UI/window-basic-filters.cpp b/UI/window-basic-filters.cpp index fcab696d61b108..4d0bf5ea8177d4 100644 --- a/UI/window-basic-filters.cpp +++ b/UI/window-basic-filters.cpp @@ -20,12 +20,12 @@ #include "window-basic-main.hpp" #include "window-basic-filters.hpp" #include "display-helpers.hpp" -#include "qt-wrappers.hpp" #include "visibility-item-widget.hpp" #include "item-widget-helpers.hpp" #include "obs-app.hpp" #include "undo-stack-obs.hpp" +#include #include #include #include diff --git a/UI/window-basic-interaction.cpp b/UI/window-basic-interaction.cpp index 777da93655b45c..2507d7cc41fba3 100644 --- a/UI/window-basic-interaction.cpp +++ b/UI/window-basic-interaction.cpp @@ -18,9 +18,9 @@ #include "obs-app.hpp" #include "window-basic-interaction.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "display-helpers.hpp" +#include #include #include #include diff --git a/UI/window-basic-main-browser.cpp b/UI/window-basic-main-browser.cpp index ec99f141e4987b..24be69fe7713e2 100644 --- a/UI/window-basic-main-browser.cpp +++ b/UI/window-basic-main-browser.cpp @@ -18,8 +18,8 @@ #include #include #include +#include #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include diff --git a/UI/window-basic-main-dropfiles.cpp b/UI/window-basic-main-dropfiles.cpp index f8d119b33b11b5..880ae3014055cd 100644 --- a/UI/window-basic-main-dropfiles.cpp +++ b/UI/window-basic-main-dropfiles.cpp @@ -9,9 +9,9 @@ #include #endif #include +#include #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" using namespace std; diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 9e644e90acb8df..d77d31a6924943 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -3,7 +3,7 @@ #include #include #include -#include "qt-wrappers.hpp" +#include #include "audio-encoders.hpp" #include "multitrack-video-error.hpp" #include "window-basic-main.hpp" diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index f9d3f4ea1efcb6..08b869aebe5e73 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -21,10 +21,10 @@ #include #include #include +#include #include "window-basic-main.hpp" #include "window-basic-auto-config.hpp" #include "window-namedialog.hpp" -#include "qt-wrappers.hpp" extern void DestroyPanelCookieManager(); extern void DuplicateCurrentCookieProfile(ConfigFile &config); diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index 0fe0867ed45137..26b5506ae4ee59 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -21,11 +21,11 @@ #include #include #include +#include #include "item-widget-helpers.hpp" #include "window-basic-main.hpp" #include "window-importer.hpp" #include "window-namedialog.hpp" -#include "qt-wrappers.hpp" using namespace std; diff --git a/UI/window-basic-main-screenshot.cpp b/UI/window-basic-main-screenshot.cpp index 4a1c208560b784..ba9c4d2bcf430a 100644 --- a/UI/window-basic-main-screenshot.cpp +++ b/UI/window-basic-main-screenshot.cpp @@ -17,7 +17,8 @@ #include "window-basic-main.hpp" #include "screenshot-obj.hpp" -#include "qt-wrappers.hpp" + +#include #ifdef _WIN32 #include diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 2894c6387b6903..304da0d592baa2 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "window-basic-main.hpp" #include "window-basic-main-outputs.hpp" #include "window-basic-vcam-config.hpp" @@ -27,7 +28,6 @@ #include "window-namedialog.hpp" #include "menu-button.hpp" #include "slider-ignorewheel.hpp" -#include "qt-wrappers.hpp" #include "obs-hotkey.h" diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 5c05f4141cadce..94ba8633e5504d 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,6 @@ #include "window-youtube-actions.hpp" #include "youtube-api-wrappers.hpp" #endif -#include "qt-wrappers.hpp" #include "context-bar-controls.hpp" #include "obs-proxy-style.hpp" #include "display-helpers.hpp" diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index a10475f23c004f..51a88d481b958b 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -18,10 +18,10 @@ #include "obs-app.hpp" #include "window-basic-properties.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "display-helpers.hpp" #include "properties-view.hpp" +#include #include #include #include diff --git a/UI/window-basic-settings-a11y.cpp b/UI/window-basic-settings-a11y.cpp index ac9de41dab93d0..899541d5c75759 100644 --- a/UI/window-basic-settings-a11y.cpp +++ b/UI/window-basic-settings-a11y.cpp @@ -2,7 +2,7 @@ #include "window-basic-main.hpp" #include "obs-frontend-api.h" #include "obs-app.hpp" -#include "qt-wrappers.hpp" +#include #include enum ColorPreset { diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 881e1d54247f53..907a53222e2e08 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -1,12 +1,12 @@ #include #include #include +#include #include "window-basic-settings.hpp" #include "obs-frontend-api.h" #include "obs-app.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "url-push-button.hpp" #ifdef BROWSER_AVAILABLE diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index dfcd2dd653ca91..1f5812b42cf5db 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "audio-encoders.hpp" #include "hotkey-edit.hpp" @@ -42,7 +43,6 @@ #include "obs-app.hpp" #include "platform.hpp" #include "properties-view.hpp" -#include "qt-wrappers.hpp" #include "window-basic-main.hpp" #include "window-basic-settings.hpp" #include "window-basic-main-outputs.hpp" diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index a500e3a72144e1..b494e5d282ef37 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -16,9 +16,9 @@ ******************************************************************************/ #include +#include #include "window-basic-main.hpp" #include "window-basic-source-select.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" struct AddSourceData { diff --git a/UI/window-basic-stats.cpp b/UI/window-basic-stats.cpp index d403e717900a57..95ea092ff2ad0a 100644 --- a/UI/window-basic-stats.cpp +++ b/UI/window-basic-stats.cpp @@ -4,8 +4,8 @@ #include "window-basic-main.hpp" #include "platform.hpp" #include "obs-app.hpp" -#include "qt-wrappers.hpp" +#include #include #include #include diff --git a/UI/window-basic-vcam-config.cpp b/UI/window-basic-vcam-config.cpp index 9cb5d15ebfdf9a..e8249967d282d0 100644 --- a/UI/window-basic-vcam-config.cpp +++ b/UI/window-basic-vcam-config.cpp @@ -1,7 +1,7 @@ #include "window-basic-vcam-config.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" +#include #include #include diff --git a/UI/window-extra-browsers.cpp b/UI/window-extra-browsers.cpp index 5ee8c686c73edd..ecfe0766fe895f 100644 --- a/UI/window-extra-browsers.cpp +++ b/UI/window-extra-browsers.cpp @@ -1,8 +1,8 @@ #include "window-extra-browsers.hpp" #include "window-dock-browser.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" +#include #include #include #include diff --git a/UI/window-importer.cpp b/UI/window-importer.cpp index ff1d0789fb801d..c5aa05c9f6e838 100644 --- a/UI/window-importer.cpp +++ b/UI/window-importer.cpp @@ -26,8 +26,8 @@ #include #include #include +#include -#include "qt-wrappers.hpp" #include "importers/importers.hpp" extern bool SceneCollectionExists(const char *findName); diff --git a/UI/window-missing-files.cpp b/UI/window-missing-files.cpp index 449e8614dd6bce..01eb2977eb2460 100644 --- a/UI/window-missing-files.cpp +++ b/UI/window-missing-files.cpp @@ -24,7 +24,7 @@ #include #include -#include "qt-wrappers.hpp" +#include enum MissingFilesColumn { Source, diff --git a/UI/window-namedialog.cpp b/UI/window-namedialog.cpp index c25cae214807de..86e54aefeed7f6 100644 --- a/UI/window-namedialog.cpp +++ b/UI/window-namedialog.cpp @@ -16,9 +16,9 @@ ******************************************************************************/ #include "window-namedialog.hpp" -#include "qt-wrappers.hpp" #include "obs-app.hpp" +#include #include NameDialog::NameDialog(QWidget *parent) : QDialog(parent) diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index e61f231825254d..d5322f54790829 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -3,10 +3,10 @@ #include #include #include +#include #include "obs-app.hpp" #include "window-basic-main.hpp" #include "display-helpers.hpp" -#include "qt-wrappers.hpp" #include "platform.hpp" #include "multiview.hpp" diff --git a/UI/window-remux.cpp b/UI/window-remux.cpp index 62ded65ac04608..410ab83d9090d0 100644 --- a/UI/window-remux.cpp +++ b/UI/window-remux.cpp @@ -31,8 +31,8 @@ #include #include #include +#include -#include "qt-wrappers.hpp" #include "window-basic-main.hpp" #include diff --git a/UI/window-youtube-actions.cpp b/UI/window-youtube-actions.cpp index 52d905f911f122..a76ce498926fde 100644 --- a/UI/window-youtube-actions.cpp +++ b/UI/window-youtube-actions.cpp @@ -2,9 +2,9 @@ #include "window-youtube-actions.hpp" #include "obs-app.hpp" -#include "qt-wrappers.hpp" #include "youtube-api-wrappers.hpp" +#include #include #include #include diff --git a/UI/youtube-api-wrappers.cpp b/UI/youtube-api-wrappers.cpp index 2582d5ab30276f..83e6a48996b265 100644 --- a/UI/youtube-api-wrappers.cpp +++ b/UI/youtube-api-wrappers.cpp @@ -6,11 +6,11 @@ #include #include +#include #include "auth-youtube.hpp" #include "obs-app.hpp" #include "window-basic-main.hpp" -#include "qt-wrappers.hpp" #include "remote-text.hpp" #include "ui-config.h" #include "obf.h" diff --git a/shared/qt/wrappers/CMakeLists.txt b/shared/qt/wrappers/CMakeLists.txt new file mode 100644 index 00000000000000..e634e6f6112f20 --- /dev/null +++ b/shared/qt/wrappers/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +find_package(Qt6 REQUIRED Core Widgets) + +add_library(qt-wrappers INTERFACE) +add_library(OBS::qt-wrappers ALIAS qt-wrappers) + +target_sources(qt-wrappers INTERFACE qt-wrappers.cpp qt-wrappers.hpp) +target_include_directories(qt-wrappers INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + +target_link_libraries(qt-wrappers INTERFACE Qt::Core Qt::Widgets OBS::libobs) diff --git a/UI/qt-wrappers.cpp b/shared/qt/wrappers/qt-wrappers.cpp similarity index 97% rename from UI/qt-wrappers.cpp rename to shared/qt/wrappers/qt-wrappers.cpp index 3eaac324f69d10..7e016b0d784d7c 100644 --- a/UI/qt-wrappers.cpp +++ b/shared/qt/wrappers/qt-wrappers.cpp @@ -16,7 +16,6 @@ ******************************************************************************/ #include "qt-wrappers.hpp" -#include "obs-app.hpp" #include #include @@ -60,12 +59,12 @@ OBSMessageBox::question(QWidget *parent, const QString &title, if (buttons & QMessageBox::Ok) { QPushButton *button = mb.addButton(QMessageBox::Ok); - button->setText(QTStr("OK")); + button->setText(tr("OK")); } #define add_button(x) \ if (buttons & QMessageBox::x) { \ QPushButton *button = mb.addButton(QMessageBox::x); \ - button->setText(QTStr(#x)); \ + button->setText(tr(#x)); \ } add_button(Open); add_button(Save); @@ -88,7 +87,7 @@ void OBSMessageBox::information(QWidget *parent, const QString &title, { QMessageBox mb(QMessageBox::Information, title, text, QMessageBox::NoButton, parent); - mb.addButton(QTStr("OK"), QMessageBox::AcceptRole); + mb.addButton(tr("OK"), QMessageBox::AcceptRole); mb.exec(); } @@ -99,7 +98,7 @@ void OBSMessageBox::warning(QWidget *parent, const QString &title, parent); if (enableRichText) mb.setTextFormat(Qt::RichText); - mb.addButton(QTStr("OK"), QMessageBox::AcceptRole); + mb.addButton(tr("OK"), QMessageBox::AcceptRole); mb.exec(); } @@ -108,7 +107,7 @@ void OBSMessageBox::critical(QWidget *parent, const QString &title, { QMessageBox mb(QMessageBox::Critical, title, text, QMessageBox::NoButton, parent); - mb.addButton(QTStr("OK"), QMessageBox::AcceptRole); + mb.addButton(tr("OK"), QMessageBox::AcceptRole); mb.exec(); } diff --git a/UI/qt-wrappers.hpp b/shared/qt/wrappers/qt-wrappers.hpp similarity index 98% rename from UI/qt-wrappers.hpp rename to shared/qt/wrappers/qt-wrappers.hpp index 7ad47de27e1bc6..b3223abb952433 100644 --- a/UI/qt-wrappers.hpp +++ b/shared/qt/wrappers/qt-wrappers.hpp @@ -40,7 +40,8 @@ class QString; class QLabel; class QToolBar; -class OBSMessageBox { +class OBSMessageBox : QObject { + Q_OBJECT public: static QMessageBox::StandardButton question(QWidget *parent, const QString &title, const QString &text, From f5c25c66afae9f9a58b0adc316225e7176518a27 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 17 Jan 2024 08:25:36 +0100 Subject: [PATCH 0329/1073] UI,shared: Move OBSPlainTextEdit to its own directory --- UI/cmake/legacy.cmake | 9 ++++++--- UI/cmake/ui-elements.cmake | 8 ++++++-- UI/frontend-plugins/aja-output-ui/CMakeLists.txt | 5 +++++ UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake | 9 ++++++--- UI/frontend-plugins/decklink-output-ui/CMakeLists.txt | 5 +++++ .../decklink-output-ui/cmake/legacy.cmake | 9 ++++++--- UI/frontend-plugins/frontend-tools/CMakeLists.txt | 5 +++++ UI/frontend-plugins/frontend-tools/cmake/legacy.cmake | 11 +++++++---- UI/frontend-plugins/frontend-tools/scripts.cpp | 2 +- UI/properties-view.cpp | 2 +- shared/qt/plain-text-edit/CMakeLists.txt | 11 +++++++++++ {UI => shared/qt/plain-text-edit}/plain-text-edit.cpp | 2 +- {UI => shared/qt/plain-text-edit}/plain-text-edit.hpp | 0 13 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 shared/qt/plain-text-edit/CMakeLists.txt rename {UI => shared/qt/plain-text-edit}/plain-text-edit.cpp (93%) rename {UI => shared/qt/plain-text-edit}/plain-text-edit.hpp (100%) diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 1c82457e4aac31..f385dd2d51fcd8 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -71,6 +71,10 @@ find_package(CURL REQUIRED) add_subdirectory(frontend-plugins) add_executable(obs) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -195,8 +199,6 @@ target_sources( menu-button.hpp mute-checkbox.hpp noncheckable-button.hpp - plain-text-edit.cpp - plain-text-edit.hpp properties-view.cpp properties-view.hpp properties-view.moc.hpp @@ -314,7 +316,8 @@ target_link_libraries( FFmpeg::avformat OBS::libobs OBS::frontend-api - OBS::qt-wrappers) + OBS::qt-wrappers + OBS::qt-plain-text-edit) set_target_properties(obs PROPERTIES FOLDER "frontend") diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index fccdf42a0ddf50..8d0ed150a17f9d 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -9,8 +9,6 @@ target_sources( double-slider.hpp horizontal-scroll-area.cpp horizontal-scroll-area.hpp - plain-text-edit.cpp - plain-text-edit.hpp properties-view.cpp properties-view.hpp properties-view.moc.hpp @@ -26,6 +24,12 @@ target_compile_options(obs-ui-support INTERFACE $<$:-Wno-erro target_link_libraries(obs-studio PRIVATE OBS::ui-support) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + +target_link_libraries(obs-studio PRIVATE OBS::qt-plain-text-edit) + target_sources( obs-studio PRIVATE # cmake-format: sortable diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 9c7e8c9e656799..fb55d672f41d1e 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -23,6 +23,10 @@ if(NOT TARGET OBS::aja-support) add_subdirectory("${CMAKE_SOURCE_DIR}/plugins/aja" "${CMAKE_BINARY_DIR}/plugins/aja") endif() +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -44,6 +48,7 @@ target_link_libraries( PRIVATE OBS::libobs OBS::aja-support OBS::frontend-api + OBS::qt-plain-text-edit OBS::qt-wrappers OBS::ui-support Qt::Widgets diff --git a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake index 7346834880d0b6..3988b52409f6cf 100644 --- a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake @@ -9,6 +9,10 @@ find_package(LibAJANTV2 REQUIRED) add_library(aja-output-ui MODULE) add_library(OBS::aja-output-ui ALIAS aja-output-ui) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -52,8 +56,6 @@ target_sources( ${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.hpp ${CMAKE_SOURCE_DIR}/UI/double-slider.cpp ${CMAKE_SOURCE_DIR}/UI/double-slider.hpp - ${CMAKE_SOURCE_DIR}/UI/plain-text-edit.hpp - ${CMAKE_SOURCE_DIR}/UI/plain-text-edit.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp @@ -64,7 +66,8 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp) -target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers Qt::Widgets AJA::LibAJANTV2) +target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit + Qt::Widgets AJA::LibAJANTV2) if(OS_MACOS) find_library(IOKIT_FRAMEWORK Iokit) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index 7bdd668ec33d72..4bbbf0a43e1689 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -20,6 +20,10 @@ endif() add_library(decklink-output-ui MODULE) add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -35,6 +39,7 @@ target_link_libraries( decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api + OBS::qt-plain-text-edit OBS::qt-wrappers OBS::ui-support Qt::Widgets diff --git a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake index 709c047887eca1..43c4010d36cdfd 100644 --- a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake @@ -7,6 +7,10 @@ endif() add_library(decklink-output-ui MODULE) add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -34,8 +38,6 @@ target_sources( decklink-ui-main.h ${CMAKE_SOURCE_DIR}/UI/double-slider.cpp ${CMAKE_SOURCE_DIR}/UI/double-slider.hpp - ${CMAKE_SOURCE_DIR}/UI/plain-text-edit.hpp - ${CMAKE_SOURCE_DIR}/UI/plain-text-edit.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp @@ -46,7 +48,8 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp) -target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers Qt::Widgets) +target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit + Qt::Widgets) target_compile_features(decklink-output-ui PRIVATE cxx_std_17) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 53786eca24a1ee..1f69293be671db 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -14,6 +14,10 @@ endif() add_library(frontend-tools MODULE) add_library(OBS::frontend-tools ALIAS frontend-tools) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -49,6 +53,7 @@ target_link_libraries( frontend-tools PRIVATE OBS::frontend-api OBS::libobs + OBS::qt-plain-text-edit OBS::qt-wrappers OBS::ui-support Qt::Widgets diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index 8eb109023cddfd..74bbc88c372d21 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -3,6 +3,10 @@ project(frontend-tools) add_library(frontend-tools MODULE) add_library(OBS::frontend-tools ALIAS frontend-tools) +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -43,13 +47,12 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp - ${CMAKE_SOURCE_DIR}/UI/plain-text-edit.cpp - ${CMAKE_SOURCE_DIR}/UI/plain-text-edit.hpp) + ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp) target_compile_features(frontend-tools PRIVATE cxx_std_17) -target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::libobs Qt::Widgets) +target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit OBS::libobs + Qt::Widgets) if(OS_POSIX AND NOT OS_MACOS) target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 924efdc520c718..3407fc72580134 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -1,7 +1,6 @@ #include "obs-module.h" #include "scripts.hpp" #include "../../properties-view.hpp" -#include "../../plain-text-edit.hpp" #include #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index bd0288b171d983..2a4e031f0eb90d 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -29,10 +29,10 @@ #include "spinbox-ignorewheel.hpp" #include "properties-view.hpp" #include "properties-view.moc.hpp" -#include "plain-text-edit.hpp" #include "obs-app.hpp" #include +#include #include #include #include diff --git a/shared/qt/plain-text-edit/CMakeLists.txt b/shared/qt/plain-text-edit/CMakeLists.txt new file mode 100644 index 00000000000000..0abe0b56260073 --- /dev/null +++ b/shared/qt/plain-text-edit/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +find_package(Qt6 REQUIRED Core Widgets) + +add_library(qt-plain-text-edit INTERFACE) +add_library(OBS::qt-plain-text-edit ALIAS qt-plain-text-edit) + +target_sources(qt-plain-text-edit INTERFACE plain-text-edit.cpp plain-text-edit.hpp) +target_include_directories(qt-plain-text-edit INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + +target_link_libraries(qt-plain-text-edit INTERFACE Qt::Core Qt::Widgets) diff --git a/UI/plain-text-edit.cpp b/shared/qt/plain-text-edit/plain-text-edit.cpp similarity index 93% rename from UI/plain-text-edit.cpp rename to shared/qt/plain-text-edit/plain-text-edit.cpp index d8f03b56131358..89e9ba8a16b8cf 100644 --- a/UI/plain-text-edit.cpp +++ b/shared/qt/plain-text-edit/plain-text-edit.cpp @@ -1,4 +1,4 @@ -#include "plain-text-edit.hpp" +#include #include OBSPlainTextEdit::OBSPlainTextEdit(QWidget *parent, bool monospace) diff --git a/UI/plain-text-edit.hpp b/shared/qt/plain-text-edit/plain-text-edit.hpp similarity index 100% rename from UI/plain-text-edit.hpp rename to shared/qt/plain-text-edit/plain-text-edit.hpp From 5e250414915497dcdc9d72406bd0340af622ca14 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 17 Jan 2024 08:35:12 +0100 Subject: [PATCH 0330/1073] UI,shared: Move VScrollArea to its own directory --- UI/cmake/legacy.cmake | 10 +++++++--- UI/cmake/ui-elements.cmake | 11 ++++++---- .../aja-output-ui/CMakeLists.txt | 6 ++++++ .../aja-output-ui/cmake/legacy.cmake | 20 ++++++++++++++----- .../decklink-output-ui/CMakeLists.txt | 6 ++++++ .../decklink-output-ui/cmake/legacy.cmake | 11 ++++++---- .../frontend-tools/CMakeLists.txt | 6 ++++++ .../frontend-tools/cmake/legacy.cmake | 13 +++++++----- UI/properties-view.hpp | 2 +- shared/qt/vertical-scroll-area/CMakeLists.txt | 11 ++++++++++ .../vertical-scroll-area.cpp | 0 .../vertical-scroll-area.hpp | 0 12 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 shared/qt/vertical-scroll-area/CMakeLists.txt rename {UI => shared/qt/vertical-scroll-area}/vertical-scroll-area.cpp (100%) rename {UI => shared/qt/vertical-scroll-area}/vertical-scroll-area.hpp (100%) diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index f385dd2d51fcd8..37c2cb1aa4143e 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -75,6 +75,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -221,8 +226,6 @@ target_sources( undo-stack-obs.hpp volume-control.cpp volume-control.hpp - vertical-scroll-area.cpp - vertical-scroll-area.hpp visibility-item-widget.cpp visibility-item-widget.hpp) @@ -317,7 +320,8 @@ target_link_libraries( OBS::libobs OBS::frontend-api OBS::qt-wrappers - OBS::qt-plain-text-edit) + OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area) set_target_properties(obs PROPERTIES FOLDER "frontend") diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index 8d0ed150a17f9d..e7ac5d43052891 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -15,9 +15,7 @@ target_sources( slider-ignorewheel.cpp slider-ignorewheel.hpp spinbox-ignorewheel.cpp - spinbox-ignorewheel.hpp - vertical-scroll-area.cpp - vertical-scroll-area.hpp) + spinbox-ignorewheel.hpp) target_include_directories(obs-ui-support INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") target_compile_options(obs-ui-support INTERFACE $<$:-Wno-error=enum-conversion>) @@ -28,7 +26,12 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() -target_link_libraries(obs-studio PRIVATE OBS::qt-plain-text-edit) +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + +target_link_libraries(obs-studio PRIVATE OBS::qt-plain-text-edit OBS::qt-vertical-scroll-area) target_sources( obs-studio diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index fb55d672f41d1e..324f1f885e0976 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -27,6 +27,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -49,6 +54,7 @@ target_link_libraries( OBS::aja-support OBS::frontend-api OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area OBS::qt-wrappers OBS::ui-support Qt::Widgets diff --git a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake index 3988b52409f6cf..60c4d441a901a0 100644 --- a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake @@ -13,6 +13,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -62,12 +67,17 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp) + ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp) -target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit - Qt::Widgets AJA::LibAJANTV2) +target_link_libraries( + aja-output-ui + PRIVATE OBS::libobs + OBS::frontend-api + OBS::qt-wrappers + OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area + Qt::Widgets + AJA::LibAJANTV2) if(OS_MACOS) find_library(IOKIT_FRAMEWORK Iokit) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index 4bbbf0a43e1689..3307854d59826b 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -24,6 +24,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -40,6 +45,7 @@ target_link_libraries( PRIVATE OBS::libobs OBS::frontend-api OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area OBS::qt-wrappers OBS::ui-support Qt::Widgets diff --git a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake index 43c4010d36cdfd..7f2cd4744afbd6 100644 --- a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake @@ -11,6 +11,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -44,12 +49,10 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp) + ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp) target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit - Qt::Widgets) + OBS::qt-vertical-scroll-area Qt::Widgets) target_compile_features(decklink-output-ui PRIVATE cxx_std_17) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 1f69293be671db..185c1bbf8bbbe1 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -18,6 +18,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -54,6 +59,7 @@ target_link_libraries( PRIVATE OBS::frontend-api OBS::libobs OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area OBS::qt-wrappers OBS::ui-support Qt::Widgets diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index 74bbc88c372d21..06167de7294662 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -7,6 +7,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -45,14 +50,12 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.hpp - ${CMAKE_SOURCE_DIR}/UI/vertical-scroll-area.cpp) + ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp) target_compile_features(frontend-tools PRIVATE cxx_std_17) -target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit OBS::libobs - Qt::Widgets) +target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area OBS::libobs Qt::Widgets) if(OS_POSIX AND NOT OS_MACOS) target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) diff --git a/UI/properties-view.hpp b/UI/properties-view.hpp index 42d9b36b89cdb4..dcf90347b8d4c8 100644 --- a/UI/properties-view.hpp +++ b/UI/properties-view.hpp @@ -1,6 +1,6 @@ #pragma once -#include "vertical-scroll-area.hpp" +#include #include #include #include diff --git a/shared/qt/vertical-scroll-area/CMakeLists.txt b/shared/qt/vertical-scroll-area/CMakeLists.txt new file mode 100644 index 00000000000000..16d301643be5f7 --- /dev/null +++ b/shared/qt/vertical-scroll-area/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +find_package(Qt6 REQUIRED Core Widgets) + +add_library(qt-vertical-scroll-area INTERFACE) +add_library(OBS::qt-vertical-scroll-area ALIAS qt-vertical-scroll-area) + +target_sources(qt-vertical-scroll-area INTERFACE vertical-scroll-area.cpp vertical-scroll-area.hpp) +target_include_directories(qt-vertical-scroll-area INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + +target_link_libraries(qt-vertical-scroll-area INTERFACE Qt::Core Qt::Widgets) diff --git a/UI/vertical-scroll-area.cpp b/shared/qt/vertical-scroll-area/vertical-scroll-area.cpp similarity index 100% rename from UI/vertical-scroll-area.cpp rename to shared/qt/vertical-scroll-area/vertical-scroll-area.cpp diff --git a/UI/vertical-scroll-area.hpp b/shared/qt/vertical-scroll-area/vertical-scroll-area.hpp similarity index 100% rename from UI/vertical-scroll-area.hpp rename to shared/qt/vertical-scroll-area/vertical-scroll-area.hpp From 5eb10135da0dce2b85948c200910c7f81f948f36 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 17 Jan 2024 08:38:17 +0100 Subject: [PATCH 0331/1073] UI,shared: Move SliderIgnoreScroll to its own directory --- UI/absolute-slider.hpp | 2 +- UI/cmake/legacy.cmake | 10 +++++++--- UI/cmake/ui-elements.cmake | 10 +++++++--- UI/double-slider.hpp | 2 +- .../aja-output-ui/CMakeLists.txt | 6 ++++++ .../aja-output-ui/cmake/legacy.cmake | 10 +++++++--- .../decklink-output-ui/CMakeLists.txt | 6 ++++++ .../decklink-output-ui/cmake/legacy.cmake | 20 ++++++++++++++----- .../frontend-tools/CMakeLists.txt | 6 ++++++ .../frontend-tools/cmake/legacy.cmake | 20 ++++++++++++++----- UI/obs-app.cpp | 1 + UI/properties-view.cpp | 2 +- UI/volume-control.cpp | 1 + UI/window-basic-main-transitions.cpp | 2 +- shared/qt/slider-ignorewheel/CMakeLists.txt | 11 ++++++++++ .../slider-ignorewheel.cpp | 0 .../slider-ignorewheel.hpp | 2 +- 17 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 shared/qt/slider-ignorewheel/CMakeLists.txt rename {UI => shared/qt/slider-ignorewheel}/slider-ignorewheel.cpp (100%) rename {UI => shared/qt/slider-ignorewheel}/slider-ignorewheel.hpp (97%) diff --git a/UI/absolute-slider.hpp b/UI/absolute-slider.hpp index a5469932411003..67e0adb1fe04c4 100644 --- a/UI/absolute-slider.hpp +++ b/UI/absolute-slider.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "slider-ignorewheel.hpp" +#include class AbsoluteSlider : public SliderIgnoreScroll { Q_OBJECT diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 37c2cb1aa4143e..25d0ab87de1899 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -75,6 +75,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-vertical-scroll-area) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") @@ -212,8 +217,6 @@ target_sources( scene-tree.cpp scene-tree.hpp screenshot-obj.hpp - slider-ignorewheel.cpp - slider-ignorewheel.hpp source-label.cpp source-label.hpp spinbox-ignorewheel.cpp @@ -321,7 +324,8 @@ target_link_libraries( OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area) + OBS::qt-vertical-scroll-area + OBS::qt-slider-ignorewheel) set_target_properties(obs PROPERTIES FOLDER "frontend") diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index e7ac5d43052891..65f73f41a28f07 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -12,8 +12,6 @@ target_sources( properties-view.cpp properties-view.hpp properties-view.moc.hpp - slider-ignorewheel.cpp - slider-ignorewheel.hpp spinbox-ignorewheel.cpp spinbox-ignorewheel.hpp) @@ -26,12 +24,18 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-vertical-scroll-area) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") endif() -target_link_libraries(obs-studio PRIVATE OBS::qt-plain-text-edit OBS::qt-vertical-scroll-area) +target_link_libraries(obs-studio PRIVATE OBS::qt-plain-text-edit OBS::qt-slider-ignorewheel + OBS::qt-vertical-scroll-area) target_sources( obs-studio diff --git a/UI/double-slider.hpp b/UI/double-slider.hpp index cf0e966cf5b7ab..8274506d02a5cd 100644 --- a/UI/double-slider.hpp +++ b/UI/double-slider.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include "slider-ignorewheel.hpp" +#include class DoubleSlider : public SliderIgnoreScroll { Q_OBJECT diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 324f1f885e0976..0281b54627ef18 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -27,6 +27,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-vertical-scroll-area) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") @@ -54,6 +59,7 @@ target_link_libraries( OBS::aja-support OBS::frontend-api OBS::qt-plain-text-edit + OBS::qt-slider-ignorewheel OBS::qt-vertical-scroll-area OBS::qt-wrappers OBS::ui-support diff --git a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake index 60c4d441a901a0..617be178af1c35 100644 --- a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake @@ -13,6 +13,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-vertical-scroll-area) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") @@ -65,9 +70,7 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp) + ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp) target_link_libraries( aja-output-ui @@ -76,6 +79,7 @@ target_link_libraries( OBS::qt-wrappers OBS::qt-plain-text-edit OBS::qt-vertical-scroll-area + OBS::qt-slider-ignorewheel Qt::Widgets AJA::LibAJANTV2) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index 3307854d59826b..f8e9a356f67f5b 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -29,6 +29,11 @@ if(NOT TARGET OBS::qt-vertical-scroll-area) "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -45,6 +50,7 @@ target_link_libraries( PRIVATE OBS::libobs OBS::frontend-api OBS::qt-plain-text-edit + OBS::qt-slider-ignorewheel OBS::qt-vertical-scroll-area OBS::qt-wrappers OBS::ui-support diff --git a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake index 7f2cd4744afbd6..e325b55c5586d3 100644 --- a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake @@ -11,6 +11,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-vertical-scroll-area) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") @@ -47,12 +52,17 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp) + ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp) -target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area Qt::Widgets) +target_link_libraries( + decklink-output-ui + PRIVATE OBS::libobs + OBS::frontend-api + OBS::qt-wrappers + OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area + OBS::qt-slider-ignorewheel + Qt::Widgets) target_compile_features(decklink-output-ui PRIVATE cxx_std_17) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 185c1bbf8bbbe1..7c9885472afbc1 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -23,6 +23,11 @@ if(NOT TARGET OBS::qt-vertical-scroll-area) "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() @@ -59,6 +64,7 @@ target_link_libraries( PRIVATE OBS::frontend-api OBS::libobs OBS::qt-plain-text-edit + OBS::qt-slider-ignorewheel OBS::qt-vertical-scroll-area OBS::qt-wrappers OBS::ui-support diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index 06167de7294662..4d5a5547ea9656 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -7,6 +7,11 @@ if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + if(NOT TARGET OBS::qt-vertical-scroll-area) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") @@ -48,14 +53,19 @@ target_sources( ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/slider-ignorewheel.hpp) + ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp) target_compile_features(frontend-tools PRIVATE cxx_std_17) -target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area OBS::libobs Qt::Widgets) +target_link_libraries( + frontend-tools + PRIVATE OBS::frontend-api + OBS::qt-wrappers + OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area + OBS::qt-slider-ignorewheel + OBS::libobs + Qt::Widgets) if(OS_POSIX AND NOT OS_MACOS) target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index d2337dae0bc411..849226f451e661 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 2a4e031f0eb90d..67b541455bf93c 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -25,7 +25,6 @@ #include #include #include "double-slider.hpp" -#include "slider-ignorewheel.hpp" #include "spinbox-ignorewheel.hpp" #include "properties-view.hpp" #include "properties-view.moc.hpp" @@ -33,6 +32,7 @@ #include #include +#include #include #include #include diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index b629d8e670d540..6ab17110388c88 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -5,6 +5,7 @@ #include "absolute-slider.hpp" #include "source-label.hpp" +#include #include #include #include diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 304da0d592baa2..ffb28344e39bed 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -21,13 +21,13 @@ #include #include #include +#include #include "window-basic-main.hpp" #include "window-basic-main-outputs.hpp" #include "window-basic-vcam-config.hpp" #include "display-helpers.hpp" #include "window-namedialog.hpp" #include "menu-button.hpp" -#include "slider-ignorewheel.hpp" #include "obs-hotkey.h" diff --git a/shared/qt/slider-ignorewheel/CMakeLists.txt b/shared/qt/slider-ignorewheel/CMakeLists.txt new file mode 100644 index 00000000000000..b60ae3702889f8 --- /dev/null +++ b/shared/qt/slider-ignorewheel/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +find_package(Qt6 REQUIRED Core Widgets) + +add_library(qt-slider-ignorewheel INTERFACE) +add_library(OBS::qt-slider-ignorewheel ALIAS qt-slider-ignorewheel) + +target_sources(qt-slider-ignorewheel INTERFACE slider-ignorewheel.cpp slider-ignorewheel.hpp) +target_include_directories(qt-slider-ignorewheel INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + +target_link_libraries(qt-slider-ignorewheel INTERFACE Qt::Core Qt::Widgets OBS::libobs) diff --git a/UI/slider-ignorewheel.cpp b/shared/qt/slider-ignorewheel/slider-ignorewheel.cpp similarity index 100% rename from UI/slider-ignorewheel.cpp rename to shared/qt/slider-ignorewheel/slider-ignorewheel.cpp diff --git a/UI/slider-ignorewheel.hpp b/shared/qt/slider-ignorewheel/slider-ignorewheel.hpp similarity index 97% rename from UI/slider-ignorewheel.hpp rename to shared/qt/slider-ignorewheel/slider-ignorewheel.hpp index 24e4056f21230b..5d8f355096cb05 100644 --- a/UI/slider-ignorewheel.hpp +++ b/shared/qt/slider-ignorewheel/slider-ignorewheel.hpp @@ -1,6 +1,6 @@ #pragma once -#include "obs.hpp" +#include #include #include #include From 390429c8384dcc762285017d66103070396e1d85 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 19 Jul 2023 09:20:50 +0200 Subject: [PATCH 0332/1073] frontend-tools: Remove unused source files in legacy path --- UI/frontend-plugins/frontend-tools/cmake/legacy.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index 4d5a5547ea9656..f4001186dcdaf8 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -47,8 +47,6 @@ target_sources( output-timer.cpp ${CMAKE_SOURCE_DIR}/UI/double-slider.cpp ${CMAKE_SOURCE_DIR}/UI/double-slider.hpp - ${CMAKE_SOURCE_DIR}/UI/horizontal-scroll-area.cpp - ${CMAKE_SOURCE_DIR}/UI/horizontal-scroll-area.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp From f051fdd75e9f898bac227f4a0dc8d0b219f4bcdd Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 17 Jan 2024 08:12:20 +0100 Subject: [PATCH 0333/1073] UI,shared: Move OBSPropertiesView to its own directory --- UI/cmake/legacy.cmake | 14 ++-- UI/cmake/ui-elements.cmake | 21 ++--- .../aja-output-ui/AJAOutputUI.h | 2 +- .../aja-output-ui/CMakeLists.txt | 24 +----- .../aja-output-ui/cmake/legacy.cmake | 41 ++-------- .../decklink-output-ui/CMakeLists.txt | 24 +----- .../decklink-output-ui/DecklinkOutputUI.h | 2 +- .../decklink-output-ui/cmake/legacy.cmake | 45 ++--------- .../frontend-tools/CMakeLists.txt | 18 ++--- .../frontend-tools/cmake/legacy.cmake | 42 ++-------- .../frontend-tools/scripts.cpp | 2 +- UI/window-basic-filters.hpp | 3 +- UI/window-basic-interaction.hpp | 3 +- UI/window-basic-properties.cpp | 2 +- shared/properties-view/CMakeLists.txt | 51 ++++++++++++ .../properties-view}/double-slider.cpp | 0 .../properties-view}/double-slider.hpp | 0 .../properties-view}/properties-view.cpp | 79 +++++++++---------- .../properties-view}/properties-view.hpp | 0 .../properties-view}/properties-view.moc.hpp | 0 .../properties-view}/spinbox-ignorewheel.cpp | 0 .../properties-view}/spinbox-ignorewheel.hpp | 0 22 files changed, 139 insertions(+), 234 deletions(-) create mode 100644 shared/properties-view/CMakeLists.txt rename {UI => shared/properties-view}/double-slider.cpp (100%) rename {UI => shared/properties-view}/double-slider.hpp (100%) rename {UI => shared/properties-view}/properties-view.cpp (96%) rename {UI => shared/properties-view}/properties-view.hpp (100%) rename {UI => shared/properties-view}/properties-view.moc.hpp (100%) rename {UI => shared/properties-view}/spinbox-ignorewheel.cpp (100%) rename {UI => shared/properties-view}/spinbox-ignorewheel.hpp (100%) diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 25d0ab87de1899..c38c11d3e784af 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -71,6 +71,10 @@ find_package(CURL REQUIRED) add_subdirectory(frontend-plugins) add_executable(obs) +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") +endif() + if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() @@ -187,8 +191,6 @@ target_sources( basic-controls.cpp basic-controls.hpp clickable-label.hpp - double-slider.cpp - double-slider.hpp horizontal-scroll-area.cpp horizontal-scroll-area.hpp item-widget-helpers.cpp @@ -209,9 +211,6 @@ target_sources( menu-button.hpp mute-checkbox.hpp noncheckable-button.hpp - properties-view.cpp - properties-view.hpp - properties-view.moc.hpp remote-text.cpp remote-text.hpp scene-tree.cpp @@ -219,8 +218,6 @@ target_sources( screenshot-obj.hpp source-label.cpp source-label.hpp - spinbox-ignorewheel.cpp - spinbox-ignorewheel.hpp source-tree.cpp source-tree.hpp url-push-button.cpp @@ -325,7 +322,8 @@ target_link_libraries( OBS::qt-wrappers OBS::qt-plain-text-edit OBS::qt-vertical-scroll-area - OBS::qt-slider-ignorewheel) + OBS::qt-slider-ignorewheel + OBS::properties-view) set_target_properties(obs PROPERTIES FOLDER "frontend") diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index 65f73f41a28f07..7c66c6ecd056c2 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -1,25 +1,18 @@ add_library(obs-ui-support INTERFACE) add_library(OBS::ui-support ALIAS obs-ui-support) -target_sources( - obs-ui-support - INTERFACE # cmake-format: sortable - clickable-label.hpp - double-slider.cpp - double-slider.hpp - horizontal-scroll-area.cpp - horizontal-scroll-area.hpp - properties-view.cpp - properties-view.hpp - properties-view.moc.hpp - spinbox-ignorewheel.cpp - spinbox-ignorewheel.hpp) +target_sources(obs-ui-support INTERFACE # cmake-format: sortable + clickable-label.hpp horizontal-scroll-area.cpp horizontal-scroll-area.hpp) target_include_directories(obs-ui-support INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") target_compile_options(obs-ui-support INTERFACE $<$:-Wno-error=enum-conversion>) target_link_libraries(obs-studio PRIVATE OBS::ui-support) +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") +endif() + if(NOT TARGET OBS::qt-plain-text-edit) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() @@ -34,7 +27,7 @@ if(NOT TARGET OBS::qt-vertical-scroll-area) "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") endif() -target_link_libraries(obs-studio PRIVATE OBS::qt-plain-text-edit OBS::qt-slider-ignorewheel +target_link_libraries(obs-studio PRIVATE OBS::properties-view OBS::qt-plain-text-edit OBS::qt-slider-ignorewheel OBS::qt-vertical-scroll-area) target_sources( diff --git a/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h b/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h index dfeb2b09fc77f7..0bf91314866fd9 100644 --- a/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h +++ b/UI/frontend-plugins/aja-output-ui/AJAOutputUI.h @@ -1,9 +1,9 @@ #pragma once #include +#include #include "ui_output.h" -#include "../../UI/properties-view.hpp" namespace aja { class CardManager; diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 0281b54627ef18..2be9f389379bff 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -23,22 +23,8 @@ if(NOT TARGET OBS::aja-support) add_subdirectory("${CMAKE_SOURCE_DIR}/plugins/aja" "${CMAKE_BINARY_DIR}/plugins/aja") endif() -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") -endif() - -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") -endif() - -if(NOT TARGET OBS::qt-wrappers) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") endif() add_library(aja-output-ui MODULE) @@ -58,11 +44,7 @@ target_link_libraries( PRIVATE OBS::libobs OBS::aja-support OBS::frontend-api - OBS::qt-plain-text-edit - OBS::qt-slider-ignorewheel - OBS::qt-vertical-scroll-area - OBS::qt-wrappers - OBS::ui-support + OBS::properties-view Qt::Widgets AJA::LibAJANTV2 $<$:X11::X11> diff --git a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake index 617be178af1c35..df7e2b2adc41cd 100644 --- a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake @@ -9,22 +9,8 @@ find_package(LibAJANTV2 REQUIRED) add_library(aja-output-ui MODULE) add_library(OBS::aja-output-ui ALIAS aja-output-ui) -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") -endif() - -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") -endif() - -if(NOT TARGET OBS::qt-wrappers) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") endif() find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) @@ -63,25 +49,10 @@ target_sources( ${CMAKE_SOURCE_DIR}/plugins/aja/aja-vpid-data.cpp ${CMAKE_SOURCE_DIR}/plugins/aja/aja-vpid-data.hpp ${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.hpp - ${CMAKE_SOURCE_DIR}/UI/double-slider.cpp - ${CMAKE_SOURCE_DIR}/UI/double-slider.hpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp) - -target_link_libraries( - aja-output-ui - PRIVATE OBS::libobs - OBS::frontend-api - OBS::qt-wrappers - OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area - OBS::qt-slider-ignorewheel - Qt::Widgets - AJA::LibAJANTV2) + ${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.hpp) + +target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::properties-view Qt::Widgets + AJA::LibAJANTV2) if(OS_MACOS) find_library(IOKIT_FRAMEWORK Iokit) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index f8e9a356f67f5b..db08c2aa5972fc 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -20,22 +20,8 @@ endif() add_library(decklink-output-ui MODULE) add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") -endif() - -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") -endif() - -if(NOT TARGET OBS::qt-wrappers) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") endif() target_sources(decklink-output-ui PRIVATE forms/output.ui) @@ -49,11 +35,7 @@ target_link_libraries( decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api - OBS::qt-plain-text-edit - OBS::qt-slider-ignorewheel - OBS::qt-vertical-scroll-area - OBS::qt-wrappers - OBS::ui-support + OBS::properties-view Qt::Widgets "$<$:$>" $<$:X11::X11> diff --git a/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h b/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h index f550a4635cfdc3..12ba60530334a4 100644 --- a/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h +++ b/UI/frontend-plugins/decklink-output-ui/DecklinkOutputUI.h @@ -1,9 +1,9 @@ #pragma once #include +#include #include "ui_output.h" -#include "../../UI/properties-view.hpp" class DecklinkOutputUI : public QDialog { Q_OBJECT diff --git a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake index e325b55c5586d3..45c9cc2bce165c 100644 --- a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake +++ b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake @@ -7,22 +7,8 @@ endif() add_library(decklink-output-ui MODULE) add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") -endif() - -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") -endif() - -if(NOT TARGET OBS::qt-wrappers) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") endif() find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) @@ -40,29 +26,10 @@ endif() target_sources(decklink-output-ui PRIVATE forms/output.ui) -target_sources( - decklink-output-ui - PRIVATE DecklinkOutputUI.cpp - DecklinkOutputUI.h - decklink-ui-main.cpp - decklink-ui-main.h - ${CMAKE_SOURCE_DIR}/UI/double-slider.cpp - ${CMAKE_SOURCE_DIR}/UI/double-slider.hpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp) - -target_link_libraries( - decklink-output-ui - PRIVATE OBS::libobs - OBS::frontend-api - OBS::qt-wrappers - OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area - OBS::qt-slider-ignorewheel - Qt::Widgets) +target_sources(decklink-output-ui PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp + decklink-ui-main.h) + +target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::properties-view Qt::Widgets) target_compile_features(decklink-output-ui PRIVATE cxx_std_17) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 7c9885472afbc1..6b88874f1075c4 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -14,18 +14,12 @@ endif() add_library(frontend-tools MODULE) add_library(OBS::frontend-tools ALIAS frontend-tools) -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") endif() -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() if(NOT TARGET OBS::qt-wrappers) @@ -63,11 +57,9 @@ target_link_libraries( frontend-tools PRIVATE OBS::frontend-api OBS::libobs + OBS::properties-view OBS::qt-plain-text-edit - OBS::qt-slider-ignorewheel - OBS::qt-vertical-scroll-area OBS::qt-wrappers - OBS::ui-support Qt::Widgets "$<$:$>" $<$:X11::X11> diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake index f4001186dcdaf8..cbfbaa5e0df3a9 100644 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake @@ -3,18 +3,12 @@ project(frontend-tools) add_library(frontend-tools MODULE) add_library(OBS::frontend-tools ALIAS frontend-tools) -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +if(NOT TARGET OBS::properties-view) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") endif() -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") endif() if(NOT TARGET OBS::qt-wrappers) @@ -37,33 +31,13 @@ endif() target_sources(frontend-tools PRIVATE forms/auto-scene-switcher.ui forms/captions.ui forms/output-timer.ui forms/scripts.ui) -target_sources( - frontend-tools - PRIVATE frontend-tools.c - auto-scene-switcher.hpp - auto-scene-switcher.cpp - output-timer.hpp - tool-helpers.hpp - output-timer.cpp - ${CMAKE_SOURCE_DIR}/UI/double-slider.cpp - ${CMAKE_SOURCE_DIR}/UI/double-slider.hpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.cpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.hpp - ${CMAKE_SOURCE_DIR}/UI/properties-view.moc.hpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.cpp - ${CMAKE_SOURCE_DIR}/UI/spinbox-ignorewheel.hpp) +target_sources(frontend-tools PRIVATE frontend-tools.c auto-scene-switcher.hpp auto-scene-switcher.cpp output-timer.hpp + tool-helpers.hpp output-timer.cpp) target_compile_features(frontend-tools PRIVATE cxx_std_17) -target_link_libraries( - frontend-tools - PRIVATE OBS::frontend-api - OBS::qt-wrappers - OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area - OBS::qt-slider-ignorewheel - OBS::libobs - Qt::Widgets) +target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit + OBS::properties-view OBS::libobs Qt::Widgets) if(OS_POSIX AND NOT OS_MACOS) target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 3407fc72580134..2906f7e7a50d55 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -1,6 +1,5 @@ #include "obs-module.h" #include "scripts.hpp" -#include "../../properties-view.hpp" #include #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/UI/window-basic-filters.hpp b/UI/window-basic-filters.hpp index 1d170582a4cfbc..9d9d9999b90bc1 100644 --- a/UI/window-basic-filters.hpp +++ b/UI/window-basic-filters.hpp @@ -21,8 +21,7 @@ #include #include #include - -#include "properties-view.hpp" +#include class OBSBasic; class QMenu; diff --git a/UI/window-basic-interaction.hpp b/UI/window-basic-interaction.hpp index 029a5fe08e508f..df6a2b7d2bb6eb 100644 --- a/UI/window-basic-interaction.hpp +++ b/UI/window-basic-interaction.hpp @@ -22,8 +22,7 @@ #include #include - -#include "properties-view.hpp" +#include class OBSBasic; diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index 51a88d481b958b..722812265b170e 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -19,9 +19,9 @@ #include "window-basic-properties.hpp" #include "window-basic-main.hpp" #include "display-helpers.hpp" -#include "properties-view.hpp" #include +#include #include #include #include diff --git a/shared/properties-view/CMakeLists.txt b/shared/properties-view/CMakeLists.txt new file mode 100644 index 00000000000000..b66393fcf53f7a --- /dev/null +++ b/shared/properties-view/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +find_package(Qt6 REQUIRED Core Widgets) + +if(NOT TARGET OBS::qt-wrappers) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") +endif() + +if(NOT TARGET OBS::qt-plain-text-edit) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") +endif() + +if(NOT TARGET OBS::qt-vertical-scroll-area) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") +endif() + +if(NOT TARGET OBS::qt-slider-ignorewheel) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") +endif() + +add_library(properties-view INTERFACE) +add_library(OBS::properties-view ALIAS properties-view) + +target_sources( + properties-view + INTERFACE # cmake-format: sortable + double-slider.cpp + double-slider.hpp + properties-view.cpp + properties-view.hpp + properties-view.moc.hpp + spinbox-ignorewheel.cpp + spinbox-ignorewheel.hpp) +target_include_directories(properties-view INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + +if(OS_LINUX AND Qt6_VERSION VERSION_LESS 6.3) + target_compile_options(properties-view INTERFACE -Wno-error=enum-conversion) +endif() + +target_link_libraries( + properties-view + INTERFACE OBS::frontend-api + OBS::libobs + OBS::qt-wrappers + OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area + OBS::qt-slider-ignorewheel + Qt::Core + Qt::Widgets) diff --git a/UI/double-slider.cpp b/shared/properties-view/double-slider.cpp similarity index 100% rename from UI/double-slider.cpp rename to shared/properties-view/double-slider.cpp diff --git a/UI/double-slider.hpp b/shared/properties-view/double-slider.hpp similarity index 100% rename from UI/double-slider.hpp rename to shared/properties-view/double-slider.hpp diff --git a/UI/properties-view.cpp b/shared/properties-view/properties-view.cpp similarity index 96% rename from UI/properties-view.cpp rename to shared/properties-view/properties-view.cpp index 67b541455bf93c..ad861ea7ee52d3 100644 --- a/UI/properties-view.cpp +++ b/shared/properties-view/properties-view.cpp @@ -28,7 +28,6 @@ #include "spinbox-ignorewheel.hpp" #include "properties-view.hpp" #include "properties-view.moc.hpp" -#include "obs-app.hpp" #include #include @@ -39,6 +38,7 @@ #include #include #include +#include using namespace std; @@ -106,7 +106,7 @@ void OBSPropertiesView::ReloadProperties() RefreshProperties(); } -#define NO_PROPERTIES_STRING QTStr("Basic.PropertiesWindow.NoProperties") +#define NO_PROPERTIES_STRING QObject::tr("Basic.PropertiesWindow.NoProperties") void OBSPropertiesView::RefreshProperties() { @@ -306,7 +306,7 @@ QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, QLineEdit *edit = new QLineEdit(); QPushButton *show = new QPushButton(); - show->setText(QTStr("Show")); + show->setText(tr("Show")); show->setCheckable(true); edit->setText(QT_UTF8(val)); edit->setEchoMode(QLineEdit::Password); @@ -318,7 +318,7 @@ QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, connect(show, &QAbstractButton::toggled, info, &WidgetInfo::TogglePasswordText); connect(show, &QAbstractButton::toggled, [=](bool hide) { - show->setText(hide ? QTStr("Hide") : QTStr("Show")); + show->setText(hide ? tr("Hide") : tr("Show")); }); children.emplace_back(info); @@ -345,7 +345,7 @@ QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, label = new QLabel(desc); if (long_desc != NULL && !info_label->text().isEmpty()) { - QString file = !App()->IsThemeDark() + QString file = !obs_frontend_is_theme_dark() ? ":/res/images/help.svg" : ":/res/images/help_light.svg"; QString lStr = "%1 directory && strcmp(ent->d_name, ".") != 0 && - strcmp(ent->d_name, "..") != 0) { - strcat(path, "/"); - strcat(path, ent->d_name); - strcat(path, "/basic.ini"); - - ConfigFile config; - int ret; - - ret = config.Open(path, CONFIG_OPEN_EXISTING); - if (ret == CONFIG_SUCCESS) { - if (update_ffmpeg_output(config) || - update_reconnect(config)) { - config_save_safe(config, "tmp", - nullptr); - } - } - - if (config) { - const char *sEnc = config_get_string( - config, "AdvOut", "Encoder"); - const char *rEnc = config_get_string( - config, "AdvOut", "RecEncoder"); - - /* replace "cbr" option with "rate_control" for - * each profile's encoder data */ - path[pathlen] = 0; - strcat(path, "/"); - strcat(path, ent->d_name); - strcat(path, "/recordEncoder.json"); - convert_28_1_encoder_setting(rEnc, path); - - path[pathlen] = 0; - strcat(path, "/"); - strcat(path, ent->d_name); - strcat(path, "/streamEncoder.json"); - convert_28_1_encoder_setting(sEnc, path); - } - - path[pathlen] = 0; - } - - ent = os_readdir(dir); - } - - os_closedir(dir); -} - static void check_safe_mode_sentinel(void) { #ifndef NDEBUG @@ -3122,7 +2798,6 @@ int main(int argc, char *argv[]) #endif check_safe_mode_sentinel(); - upgrade_settings(); fstream logFile; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 0acaa41f1c874e..69f09bcec28657 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1868,7 +1868,6 @@ bool OBSBasic::InitBasicConfigDefaults() } extern bool EncoderAvailable(const char *encoder); -extern bool update_nvenc_presets(ConfigFile &config); void OBSBasic::InitBasicConfigDefaults2() { @@ -1893,9 +1892,6 @@ void OBSBasic::InitBasicConfigDefaults2() aac_default); config_set_default_string(basicConfig, "AdvOut", "RecAudioEncoder", aac_default); - - if (update_nvenc_presets(basicConfig)) - config_save_safe(basicConfig, "tmp", nullptr); } bool OBSBasic::InitBasicConfig() From 4d5e0e89ad9b11ae2333565935e79d9ba2bf75c9 Mon Sep 17 00:00:00 2001 From: Paul Hindt Date: Thu, 25 Jan 2024 22:42:14 -0800 Subject: [PATCH 0338/1073] aja: Add v210 pixel format support. Allow selecting v210 in AJA capture plugin. Enable auto-detection of SDI v210 pixel format. --- plugins/aja/aja-common.cpp | 11 ++++++----- plugins/aja/aja-common.hpp | 1 + plugins/aja/aja-output.cpp | 4 +++- plugins/aja/aja-source.cpp | 10 ++++++++-- plugins/aja/aja-vpid-data.cpp | 35 +++++++++++++++++++++++++++++++---- plugins/aja/aja-vpid-data.hpp | 3 +++ 6 files changed, 52 insertions(+), 12 deletions(-) diff --git a/plugins/aja/aja-common.cpp b/plugins/aja/aja-common.cpp index f65ae481dc968c..e057802c56153c 100644 --- a/plugins/aja/aja-common.cpp +++ b/plugins/aja/aja-common.cpp @@ -201,12 +201,11 @@ void populate_video_format_list(NTV2DeviceID deviceID, obs_property_t *list, } } -void populate_pixel_format_list(NTV2DeviceID deviceID, obs_property_t *list) +void populate_pixel_format_list(NTV2DeviceID deviceID, + const std::vector &fmts, + obs_property_t *list) { - const NTV2PixelFormat supported_pix_fmts[] = {kDefaultAJAPixelFormat, - NTV2_FBF_24BIT_BGR}; - - for (auto &&pf : supported_pix_fmts) { + for (auto &&pf : fmts) { if (NTV2DeviceCanDoFrameBufferFormat(deviceID, pf)) { obs_property_list_add_int( list, @@ -300,6 +299,8 @@ video_format AJAPixelFormatToOBSVideoFormat(NTV2PixelFormat pf) obs_video_format = VIDEO_FORMAT_BGRA; break; case NTV2_FBF_10BIT_YCBCR: + obs_video_format = VIDEO_FORMAT_V210; + break; case NTV2_FBF_10BIT_RGB: case NTV2_FBF_8BIT_YCBCR_YUY2: case NTV2_FBF_10BIT_DPX: diff --git a/plugins/aja/aja-common.hpp b/plugins/aja/aja-common.hpp index caa9dd87d2ffc5..f8e0d751675013 100644 --- a/plugins/aja/aja-common.hpp +++ b/plugins/aja/aja-common.hpp @@ -44,6 +44,7 @@ populate_video_format_list(NTV2DeviceID deviceID, obs_property_t *list, NTV2VideoFormat genlockFormat = NTV2_FORMAT_UNKNOWN, bool want4KHFR = false, bool matchFPS = false); extern void populate_pixel_format_list(NTV2DeviceID deviceID, + const std::vector &fmts, obs_property_t *list); extern void populate_sdi_transport_list(obs_property_t *list, NTV2DeviceID deviceID, diff --git a/plugins/aja/aja-output.cpp b/plugins/aja/aja-output.cpp index 02e9f7d90fc741..94e0816f1f5a60 100644 --- a/plugins/aja/aja-output.cpp +++ b/plugins/aja/aja-output.cpp @@ -936,7 +936,9 @@ bool aja_output_device_changed(void *data, obs_properties_t *props, false, MATCH_OBS_FRAMERATE); obs_property_list_clear(pix_fmt_list); - populate_pixel_format_list(deviceID, pix_fmt_list); + populate_pixel_format_list(deviceID, + {kDefaultAJAPixelFormat, NTV2_FBF_24BIT_BGR}, + pix_fmt_list); IOSelection io_select = static_cast( obs_data_get_int(settings, kUIPropOutput.id)); diff --git a/plugins/aja/aja-source.cpp b/plugins/aja/aja-source.cpp index 097c12c46ef627..25a40c29c33f56 100644 --- a/plugins/aja/aja-source.cpp +++ b/plugins/aja/aja-source.cpp @@ -614,7 +614,10 @@ bool AJASource::ReadWireFormats(NTV2DeviceID device_id, IOSelection io_select, if (vpids.size() > 0) { auto vpid = *vpids.begin(); if (vpid.Sampling() == VPIDSampling_YUV_422) { - pf = NTV2_FBF_8BIT_YCBCR; + if (vpid.BitDepth() == VPIDBitDepth_8) + pf = NTV2_FBF_8BIT_YCBCR; + else if (vpid.BitDepth() == VPIDBitDepth_10) + pf = NTV2_FBF_10BIT_YCBCR; blog(LOG_INFO, "AJASource::ReadWireFormats - Detected pixel format %s", NTV2FrameBufferFormatToString(pf, true) @@ -745,7 +748,10 @@ bool aja_source_device_changed(void *data, obs_properties_t *props, obs_property_list_clear(pix_fmt_list); obs_property_list_add_int(pix_fmt_list, obs_module_text("Auto"), kAutoDetect); - populate_pixel_format_list(deviceID, pix_fmt_list); + populate_pixel_format_list(deviceID, + {kDefaultAJAPixelFormat, + NTV2_FBF_10BIT_YCBCR, NTV2_FBF_24BIT_BGR}, + pix_fmt_list); IOSelection io_select = static_cast( obs_data_get_int(settings, kUIPropInput.id)); diff --git a/plugins/aja/aja-vpid-data.cpp b/plugins/aja/aja-vpid-data.cpp index 13e71cdbafb471..132e36f5c35197 100644 --- a/plugins/aja/aja-vpid-data.cpp +++ b/plugins/aja/aja-vpid-data.cpp @@ -6,7 +6,9 @@ VPIDData::VPIDData() mStandardA{VPIDStandard_Unknown}, mStandardB{VPIDStandard_Unknown}, mSamplingA{VPIDSampling_XYZ_444}, - mSamplingB{VPIDSampling_XYZ_444} + mSamplingB{VPIDSampling_XYZ_444}, + mBitDepthA{VPIDBitDepth_8}, + mBitDepthB{VPIDBitDepth_8} { } @@ -16,7 +18,9 @@ VPIDData::VPIDData(ULWord vpidA, ULWord vpidB) mStandardA{VPIDStandard_Unknown}, mStandardB{VPIDStandard_Unknown}, mSamplingA{VPIDSampling_XYZ_444}, - mSamplingB{VPIDSampling_XYZ_444} + mSamplingB{VPIDSampling_XYZ_444}, + mBitDepthA{VPIDBitDepth_8}, + mBitDepthB{VPIDBitDepth_8} { Parse(); } @@ -27,7 +31,9 @@ VPIDData::VPIDData(const VPIDData &other) mStandardA{VPIDStandard_Unknown}, mStandardB{VPIDStandard_Unknown}, mSamplingA{VPIDSampling_XYZ_444}, - mSamplingB{VPIDSampling_XYZ_444} + mSamplingB{VPIDSampling_XYZ_444}, + mBitDepthA{VPIDBitDepth_8}, + mBitDepthB{VPIDBitDepth_8} { Parse(); } @@ -37,7 +43,9 @@ VPIDData::VPIDData(VPIDData &&other) mStandardA{VPIDStandard_Unknown}, mStandardB{VPIDStandard_Unknown}, mSamplingA{VPIDSampling_XYZ_444}, - mSamplingB{VPIDSampling_XYZ_444} + mSamplingB{VPIDSampling_XYZ_444}, + mBitDepthA{VPIDBitDepth_8}, + mBitDepthB{VPIDBitDepth_8} { Parse(); } @@ -46,6 +54,12 @@ VPIDData &VPIDData::operator=(const VPIDData &other) { mVpidA = other.mVpidA; mVpidB = other.mVpidB; + mStandardA = other.mStandardA; + mStandardB = other.mStandardB; + mSamplingA = other.mSamplingA; + mSamplingB = other.mSamplingB; + mBitDepthA = other.mBitDepthA; + mBitDepthB = other.mBitDepthB; return *this; } @@ -53,6 +67,12 @@ VPIDData &VPIDData::operator=(VPIDData &&other) { mVpidA = other.mVpidA; mVpidB = other.mVpidB; + mStandardA = other.mStandardA; + mStandardB = other.mStandardB; + mSamplingA = other.mSamplingA; + mSamplingB = other.mSamplingB; + mBitDepthA = other.mBitDepthA; + mBitDepthB = other.mBitDepthB; return *this; } @@ -82,11 +102,13 @@ void VPIDData::Parse() parserA.SetVPID(mVpidA); mStandardA = parserA.GetStandard(); mSamplingA = parserA.GetSampling(); + mBitDepthA = parserA.GetBitDepth(); CNTV2VPID parserB; parserB.SetVPID(mVpidB); mStandardB = parserB.GetStandard(); mSamplingB = parserB.GetSampling(); + mBitDepthB = parserB.GetBitDepth(); } bool VPIDData::IsRGB() const @@ -111,3 +133,8 @@ VPIDSampling VPIDData::Sampling() const { return mSamplingA; } + +VPIDBitDepth VPIDData::BitDepth() const +{ + return mBitDepthA; +} diff --git a/plugins/aja/aja-vpid-data.hpp b/plugins/aja/aja-vpid-data.hpp index 39fbfb66d3e864..7800c4dda669a0 100644 --- a/plugins/aja/aja-vpid-data.hpp +++ b/plugins/aja/aja-vpid-data.hpp @@ -30,6 +30,7 @@ class VPIDData { VPIDStandard Standard() const; VPIDSampling Sampling() const; + VPIDBitDepth BitDepth() const; private: ULWord mVpidA; @@ -38,6 +39,8 @@ class VPIDData { VPIDSampling mSamplingA; VPIDStandard mStandardB; VPIDSampling mSamplingB; + VPIDBitDepth mBitDepthA; + VPIDBitDepth mBitDepthB; }; using VPIDDataList = std::vector; From e12ef51068ce065419ec20163f82c739f91781f2 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 12 Jun 2024 23:38:59 +0200 Subject: [PATCH 0339/1073] UI: Return std::optional from ParseThemeMeta --- UI/obs-app-theming.cpp | 80 +++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 790235b149abf6..7884836e453844 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -44,42 +44,42 @@ struct CFParser { cf_parser *operator->() { return &cfp; } }; -static OBSTheme *ParseThemeMeta(const QString &path) +static optional ParseThemeMeta(const QString &path) { QFile themeFile(path); if (!themeFile.open(QIODeviceBase::ReadOnly)) - return nullptr; + return nullopt; - OBSTheme *meta = nullptr; + OBSTheme meta; const QByteArray data = themeFile.readAll(); CFParser cfp; int ret; if (!cf_parser_parse(cfp, data.constData(), QT_TO_UTF8(path))) - return nullptr; + return nullopt; if (cf_token_is(cfp, "@") || cf_go_to_token(cfp, "@", nullptr)) { while (cf_next_token(cfp)) { - if (cf_token_is(cfp, "OBSThemeMeta")) + if (cf_token_is(cfp, "OBSThemeMeta")) { break; + } if (!cf_go_to_token(cfp, "@", nullptr)) - return nullptr; + return nullopt; } + if (!cf_token_is(cfp, "OBSThemeMeta")) + return nullopt; + if (!cf_next_token(cfp)) - return nullptr; + return nullopt; if (!cf_token_is(cfp, "{")) - return nullptr; - - meta = new OBSTheme(); + return nullopt; for (;;) { - if (!cf_next_token(cfp)) { - delete meta; - return nullptr; - } + if (!cf_next_token(cfp)) + return nullopt; ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", nullptr); @@ -93,10 +93,8 @@ static OBSTheme *ParseThemeMeta(const QString &path) if (ret != PARSE_SUCCESS) continue; - if (!cf_next_token(cfp)) { - delete meta; - return nullptr; - } + if (!cf_next_token(cfp)) + return nullopt; ret = cf_token_is_type(cfp, CFTOKEN_STRING, "value", ";"); @@ -109,39 +107,34 @@ static OBSTheme *ParseThemeMeta(const QString &path) if (str) { if (name == "dark") - meta->isDark = strcmp(str, "true") == 0; + meta.isDark = strcmp(str, "true") == 0; else if (name == "extends") - meta->extends = str; + meta.extends = str; else if (name == "author") - meta->author = str; + meta.author = str; else if (name == "id") - meta->id = str; + meta.id = str; else if (name == "name") - meta->name = str; + meta.name = str; } - if (!cf_go_to_token(cfp, ";", nullptr)) { - delete meta; - return nullptr; - } + if (!cf_go_to_token(cfp, ";", nullptr)) + return nullopt; } } - if (meta) { - auto filepath = filesystem::u8path(path.toStdString()); - meta->isBaseTheme = filepath.extension() == ".obt"; - meta->filename = filepath.stem(); + auto filepath = filesystem::u8path(path.toStdString()); + meta.isBaseTheme = filepath.extension() == ".obt"; + meta.filename = filepath.stem(); - if (meta->id.isEmpty() || meta->name.isEmpty() || - (!meta->isBaseTheme && meta->extends.isEmpty())) { - /* Theme is invalid */ - delete meta; - meta = nullptr; - } else { - meta->location = absolute(filepath); - meta->isHighContrast = path.endsWith(".oha"); - meta->isVisible = !path.contains("System"); - } + if (meta.id.isEmpty() || meta.name.isEmpty() || + (!meta.isBaseTheme && meta.extends.isEmpty())) { + /* Theme is invalid */ + return nullopt; + } else { + meta.location = absolute(filepath); + meta.isHighContrast = path.endsWith(".oha"); + meta.isVisible = !path.contains("System"); } return meta; @@ -417,7 +410,6 @@ static vector ParseThemeVariables(const char *themeData) void OBSApp::FindThemes() { string themeDir; - unique_ptr theme; QStringList filters; filters << "*.obt" // OBS Base Theme @@ -428,7 +420,7 @@ void OBSApp::FindThemes() GetDataFilePath("themes/", themeDir); QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); while (it.hasNext()) { - theme.reset(ParseThemeMeta(it.next())); + auto theme = ParseThemeMeta(it.next()); if (theme && !themes.contains(theme->id)) themes[theme->id] = std::move(*theme); } @@ -440,7 +432,7 @@ void OBSApp::FindThemes() QDir::Files); while (it.hasNext()) { - theme.reset(ParseThemeMeta(it.next())); + auto theme = ParseThemeMeta(it.next()); if (theme && !themes.contains(theme->id)) themes[theme->id] = std::move(*theme); } From 5e5865b716d18ef7e61b4daf457a184ff583cf7f Mon Sep 17 00:00:00 2001 From: tytan652 Date: Tue, 18 Jun 2024 17:25:51 +0200 Subject: [PATCH 0340/1073] frontend-tools: Avoid initializing the scene switcher on Wayland The Linux implementation of the automatic scene switcher is X11-only and the design itself of the feature is incompatible with how Wayland works. --- UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp index 8691d852973e19..5f4818c4fbe858 100644 --- a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp @@ -513,6 +513,11 @@ static void OBSEvent(enum obs_frontend_event event, void *) extern "C" void InitSceneSwitcher() { +#if !defined(__APPLE__) && !defined(_WIN32) + if (QApplication::platformName().contains("wayland")) + return; +#endif + QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction( obs_module_text("SceneSwitcher")); From 4bbf8df2ba82407b4319e32fc9f02d04fccf8ecf Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 14 Jul 2024 00:24:01 +0200 Subject: [PATCH 0341/1073] cmake: Set CFBundleDisplayName for camera extension macOS shows this in the settings so we should set it, otherwise it will fall back on the identifier which isn't great for users. --- plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt b/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt index a25dece634e178..8636bac09e9d63 100644 --- a/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt @@ -51,6 +51,7 @@ set_target_xcode_properties( MARKETING_VERSION ${OBS_VERSION_CANONICAL} COPY_PHASE_STRIP NO GENERATE_INFOPLIST_FILE YES + INFOPLIST_KEY_CFBundleDisplayName "OBS Virtual Camera" INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2022-${CURRENT_YEAR} Sebastian Beckmann, Patrick Heyer" INFOPLIST_KEY_NSSystemExtensionUsageDescription "This Camera Extension enables virtual camera functionality in OBS Studio.") # cmake-format: on From 1aa4bd9fc7f235357e93b0fb1ad89b4da6c56350 Mon Sep 17 00:00:00 2001 From: "Amin.MasterkinG" Date: Tue, 30 Jul 2024 17:14:48 +0330 Subject: [PATCH 0342/1073] rtmp-services: Update MasterStream.iR ingest https://github.com/obsproject/obs-studio/pull/10978 https://github.com/obsproject/obs-studio/pull/11044 --- plugins/rtmp-services/data/package.json | 4 +-- plugins/rtmp-services/data/services.json | 36 ++++++------------------ 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 4081f38ea651b5..3c819b05caaf48 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 257, + "version": 258, "files": [ { "name": "services.json", - "version": 257 + "version": 258 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 8370b1fa90cbb3..1a63db0a5844cd 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2565,40 +2565,20 @@ "stream_key_link": "https://masterstream.ir/managestreams.php", "servers": [ { - "name": "Auto (Iran Servers)", - "url": "rtmp://auto.masterstream.ir/live" + "name": "Iran Server 1 - Bandwidth Required", + "url": "rtmp://live-vip1.masterstream.ir/live" }, { - "name": "Iran Server 1", - "url": "rtmp://live1.masterstream.ir/live" - }, - { - "name": "Iran Server 2", - "url": "rtmp://live2.masterstream.ir/live" - }, - { - "name": "Iran Server 3", - "url": "rtmp://live3.masterstream.ir/live" - }, - { - "name": "Iran Server 4", - "url": "rtmp://live4.masterstream.ir/live" - }, - { - "name": "Iran Server 5", - "url": "rtmp://live5.masterstream.ir/live" - }, - { - "name": "Iran Server 6", - "url": "rtmp://live6.masterstream.ir/live" - }, - { - "name": "Iran Server 7", - "url": "rtmp://live7.masterstream.ir/live" + "name": "Iran Server 2 - Bandwidth Required", + "url": "rtmp://live-vip2.masterstream.ir/live" }, { "name": "Turkey Server 1", "url": "rtmp://tr-live1.masterstream.ir/live" + }, + { + "name": "Russia Server 1", + "url": "rtmp://ru-live1.masterstream.ir/live" } ], "protocol": "RTMP", From e7bf19d13927ff04575851e8ff53b90edc43e7d5 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 12 Jul 2024 16:04:50 +0200 Subject: [PATCH 0343/1073] obs-ffmpeg: Disable NVENC DTS adjustment for AV1 --- plugins/obs-ffmpeg/obs-nvenc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-nvenc.c b/plugins/obs-ffmpeg/obs-nvenc.c index 33dec0548be270..818b8ca894294a 100644 --- a/plugins/obs-ffmpeg/obs-nvenc.c +++ b/plugins/obs-ffmpeg/obs-nvenc.c @@ -1980,8 +1980,9 @@ static bool nvenc_encode_shared(struct nvenc_data *enc, struct nv_bitstream *bs, int64_t dts; deque_pop_front(&enc->dts_list, &dts, sizeof(dts)); - /* subtract bframe delay from dts */ - dts -= (int64_t)enc->bframes * packet->timebase_num; + /* subtract bframe delay from dts for H.264 and HEVC */ + if (enc->codec != CODEC_AV1) + dts -= (int64_t)enc->bframes * packet->timebase_num; *received_packet = true; packet->data = enc->packet_data.array; From b2d83efcf345c5f1450de24f11567d1ed5a52deb Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Fri, 12 Jul 2024 16:21:20 +0200 Subject: [PATCH 0344/1073] obs-ffmpeg: Disable AMF DTS adjustment for AV1 --- plugins/obs-ffmpeg/texture-amf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/texture-amf.cpp b/plugins/obs-ffmpeg/texture-amf.cpp index 0bdc71cc5660af..763382345772cc 100644 --- a/plugins/obs-ffmpeg/texture-amf.cpp +++ b/plugins/obs-ffmpeg/texture-amf.cpp @@ -575,7 +575,7 @@ static void convert_to_encoder_packet(amf_base *enc, AMFDataPtr &data, packet->dts = convert_to_obs_ts(enc, data->GetPts()); packet->keyframe = type == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR; - if (enc->dts_offset) + if (enc->dts_offset && enc->codec != amf_codec_type::AV1) packet->dts -= enc->dts_offset; } From 8b975c45636edbce8b1bf8a37d4842fe21fe2487 Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Wed, 31 Jul 2024 20:45:50 +1000 Subject: [PATCH 0345/1073] obs-browser: Update version to 2.24.0 2a2879b - Update translations from Crowdin 07255f1 - Enable building with CEF 5938 on macOS 5db6494 - Enable building with CEF 6261 5db6494 - Enable building with CEF 6261 879a9d2 - Enable building with CEF 6367 --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index 2a2879b5a69f4a..75d3eb11371498 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 2a2879b5a69f4a99cd7459d8595af46cdb23115c +Subproject commit 75d3eb113714985743cd14176b0c82ce25798ede From ff2fa24dc25519475687041ba6af829e9ba41335 Mon Sep 17 00:00:00 2001 From: jcm <6864788+jcm93@users.noreply.github.com> Date: Sat, 20 Jul 2024 18:35:09 -0500 Subject: [PATCH 0346/1073] mac-virtualcam: Update NotInstalled error text for macOS 15 --- .../mac-virtualcam/src/obs-plugin/data/locale/en-US.ini | 1 + plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini index 75301ff8ede8b4..e76c56b0a7763d 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini @@ -1,5 +1,6 @@ Plugin_Name="macOS Virtual Webcam" Error.SystemExtension.NotInstalled="The virtual camera is not installed.\n\nPlease allow OBS to install system software in System Settings → Privacy & Security → Security.\n\nYou may need to restart OBS if this message still appears afterward." +Error.SystemExtension.NotInstalled.MacOS15="The virtual camera is not installed.\n\nPlease allow OBS to install the camera system extension in System Settings → General → Login Items & Extensions → Camera Extensions.\n\nYou may need to restart OBS if this message still appears afterward." Error.SystemExtension.CameraUnavailable="Could not find virtual camera.\n\nPlease try again." Error.SystemExtension.CameraNotStarted="Unable to start virtual camera.\n\nPlease try again." Error.SystemExtension.InstallationError="An error has occured while installing the virtual camera:" diff --git a/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm b/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm index 62d39b8d6388b7..b9f373e6ec2f01 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm +++ b/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm @@ -306,7 +306,12 @@ static bool virtualcam_output_start(void *data) delegate.lastErrorMessage] .UTF8String); } else { - obs_output_set_last_error(vcam->output, obs_module_text("Error.SystemExtension.NotInstalled")); + if (@available(macOS 15.0, *)) { + obs_output_set_last_error(vcam->output, + obs_module_text("Error.SystemExtension.NotInstalled.MacOS15")); + } else { + obs_output_set_last_error(vcam->output, obs_module_text("Error.SystemExtension.NotInstalled")); + } } return false; From 19abab097d6482de805bf6d73bd17975050b673c Mon Sep 17 00:00:00 2001 From: pkv Date: Wed, 31 Jul 2024 11:06:29 +0200 Subject: [PATCH 0347/1073] UI: Fix leak with paint event of volume slider This fixes a memory leak introduced in [1] where a new QColor is not balanced by a delete. [1] UI: Update volume meter appearance https://github.com/obsproject/obs-studio/commit/52ae5fc4bd518c67edd8ab94121ace05d9076892 Signed-off-by: pkv --- UI/volume-control.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 6ab17110388c88..04dde1bc230b75 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -1557,8 +1557,7 @@ void VolumeSlider::paintEvent(QPaintEvent *event) } QPainter painter(this); - QColor *tickColor = new QColor; - tickColor->setRgb(91, 98, 115, 255); + QColor tickColor(91, 98, 115, 255); obs_fader_conversion_t fader_db_to_def = obs_fader_db_to_def(fad); @@ -1584,7 +1583,7 @@ void VolumeSlider::paintEvent(QPaintEvent *event) float xPos = groove.left() + (tickValue * sliderWidth) + (handle.width() / 2); - painter.fillRect(xPos, yPos, 1, tickLength, *tickColor); + painter.fillRect(xPos, yPos, 1, tickLength, tickColor); } } @@ -1603,7 +1602,7 @@ void VolumeSlider::paintEvent(QPaintEvent *event) float yPos = groove.height() + groove.top() - (tickValue * sliderHeight) - (handle.height() / 2); - painter.fillRect(xPos, yPos, tickLength, 1, *tickColor); + painter.fillRect(xPos, yPos, tickLength, 1, tickColor); } } From 7b2a6351b08c5c8e29e02dd58767e645161437fa Mon Sep 17 00:00:00 2001 From: tt2468 Date: Wed, 31 Jul 2024 03:08:52 -0700 Subject: [PATCH 0348/1073] shared/media-playback: Unref sw_frame before reuse By not performing an unref on sw_frame before using it again, a memory "leak" was being created if the frame had side data. This removes a previously-added check that optionally unrefs sw_frame, and just does it every tick. Co-authored-by: pkviet --- shared/media-playback/media-playback/decode.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/shared/media-playback/media-playback/decode.c b/shared/media-playback/media-playback/decode.c index e159a489038b40..40be7842667fc3 100644 --- a/shared/media-playback/media-playback/decode.c +++ b/shared/media-playback/media-playback/decode.c @@ -337,16 +337,7 @@ static int decode_packet(struct mp_decode *d, int *got_frame) return ret; } - /* does not check for color format or other parameter changes which would require frame buffer realloc */ - if (d->sw_frame->data[0] && - (d->sw_frame->width != d->hw_frame->width || - d->sw_frame->height != d->hw_frame->height)) { - blog(LOG_DEBUG, - "MP: hardware frame size changed from %dx%d to %dx%d. reallocating frame", - d->sw_frame->width, d->sw_frame->height, - d->hw_frame->width, d->hw_frame->height); - av_frame_unref(d->sw_frame); - } + av_frame_unref(d->sw_frame); int err = av_hwframe_transfer_data(d->sw_frame, d->hw_frame, 0); if (err == 0) { From edf753b31ed6fbf4f32acd30b2484a801177ab20 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Fri, 2 Aug 2024 15:19:24 +0900 Subject: [PATCH 0349/1073] obs-vst: Remove min and max macro workaround --- plugins/obs-vst/VSTPlugin.cpp | 5 ++--- plugins/obs-vst/headers/EditorWidget.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/obs-vst/VSTPlugin.cpp b/plugins/obs-vst/VSTPlugin.cpp index fca7dc137b91d7..08d4dd432eeb73 100644 --- a/plugins/obs-vst/VSTPlugin.cpp +++ b/plugins/obs-vst/VSTPlugin.cpp @@ -96,7 +96,7 @@ void VSTPlugin::createChannelBuffers(size_t count) cleanupChannelBuffers(); int blocksize = BLOCK_SIZE; - numChannels = (std::max)((size_t)0, count); + numChannels = std::max((size_t)0, count); if (numChannels > 0) { inputs = (float **)bmalloc(sizeof(float *) * numChannels); @@ -170,8 +170,7 @@ void VSTPlugin::loadEffectFromPath(const std::string &path) return; } - int maxchans = - (std::max)(effect->numInputs, effect->numOutputs); + int maxchans = std::max(effect->numInputs, effect->numOutputs); // sanity check if (maxchans < 0 || maxchans > 256) { blog(LOG_WARNING, diff --git a/plugins/obs-vst/headers/EditorWidget.h b/plugins/obs-vst/headers/EditorWidget.h index ce14b584a21078..e20429acdf62ae 100644 --- a/plugins/obs-vst/headers/EditorWidget.h +++ b/plugins/obs-vst/headers/EditorWidget.h @@ -22,6 +22,7 @@ along with this program. If not, see . #include #if defined(_WIN32) +#define NOMINMAX #include #include #elif defined(__linux__) From 58a8d79be13e85d9266c12f74ce4da72d66e0931 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Fri, 2 Aug 2024 15:20:14 +0900 Subject: [PATCH 0350/1073] UI: Remove min and max macro workaround --- UI/window-basic-main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 69f09bcec28657..6fa3325ae8d701 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -9068,10 +9068,10 @@ void OBSBasic::CenterSelectedSceneItems(const CenterType ¢erType) GetItemBox(item, tl, br); - left = (std::min)(tl.x, left); - top = (std::min)(tl.y, top); - right = (std::max)(br.x, right); - bottom = (std::max)(br.y, bottom); + left = std::min(tl.x, left); + top = std::min(tl.y, top); + right = std::max(br.x, right); + bottom = std::max(br.y, bottom); } center.x = (right + left) / 2.0f; From 3b266fec22462719b1dfe0b56380811ec791437a Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 11 Apr 2024 16:28:40 +0200 Subject: [PATCH 0351/1073] UI,docs: Send a custom event to the dock widget when closing --- UI/window-basic-main.cpp | 2 ++ UI/window-dock.cpp | 5 +++++ docs/sphinx/reference-frontend-api.rst | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 6fa3325ae8d701..15eb25ed6cb2d8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -329,6 +329,8 @@ OBSBasic::OBSBasic(QWidget *parent) setContextMenuPolicy(Qt::CustomContextMenu); + QEvent::registerEventType(QEvent::User + QEvent::Close); + api = InitializeAPIInterface(this); ui->setupUi(this); diff --git a/UI/window-dock.cpp b/UI/window-dock.cpp index eadd3724acebfe..a057add5b17ced 100644 --- a/UI/window-dock.cpp +++ b/UI/window-dock.cpp @@ -34,6 +34,11 @@ void OBSDock::closeEvent(QCloseEvent *event) } QDockWidget::closeEvent(event); + + if (widget() && event->isAccepted()) { + QEvent widgetEvent(QEvent::Type(QEvent::User + QEvent::Close)); + qApp->sendEvent(widget(), &widgetEvent); + } } void OBSDock::showEvent(QShowEvent *event) diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 0a88451c2c95da..5e95b4c09302f0 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -480,6 +480,12 @@ Functions Adds a dock with the widget to the UI with a toggle in the Docks menu. + When the dock is closed, a custom QEvent of type `QEvent::User + QEvent::Close` + is sent to the widget to enable it to react to the event (e.g., unload elements + to save resources). + A generic QShowEvent is already sent by default when the widget is being + shown (e.g., dock opened). + Note: Use :c:func:`obs_frontend_remove_dock` to remove the dock and the id from the UI. From b0faf38a76310ee38cfe1d45cb49778c885ba6b5 Mon Sep 17 00:00:00 2001 From: jcm <6864788+jcm93@users.noreply.github.com> Date: Wed, 31 Jul 2024 17:51:56 -0500 Subject: [PATCH 0352/1073] mac-virtualcam: Improve 'not found' error message --- plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini b/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini index e76c56b0a7763d..212aba732b0a26 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini +++ b/plugins/mac-virtualcam/src/obs-plugin/data/locale/en-US.ini @@ -1,7 +1,7 @@ Plugin_Name="macOS Virtual Webcam" Error.SystemExtension.NotInstalled="The virtual camera is not installed.\n\nPlease allow OBS to install system software in System Settings → Privacy & Security → Security.\n\nYou may need to restart OBS if this message still appears afterward." Error.SystemExtension.NotInstalled.MacOS15="The virtual camera is not installed.\n\nPlease allow OBS to install the camera system extension in System Settings → General → Login Items & Extensions → Camera Extensions.\n\nYou may need to restart OBS if this message still appears afterward." -Error.SystemExtension.CameraUnavailable="Could not find virtual camera.\n\nPlease try again." +Error.SystemExtension.CameraUnavailable="Could not find the virtual camera system process.\n\nThis may be resolved by logging out of and back into your macOS user account, or by restarting your computer." Error.SystemExtension.CameraNotStarted="Unable to start virtual camera.\n\nPlease try again." Error.SystemExtension.InstallationError="An error has occured while installing the virtual camera:" Error.SystemExtension.WrongLocation="OBS cannot install the virtual camera if it's not in \"/Applications\". Please move OBS to the \"/Applications\" directory." From c723b3ba049d6152edacb5f09fd250553580bcc5 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 28 Jul 2024 19:10:28 +0200 Subject: [PATCH 0353/1073] UI: Ensure collection name is set before creating default scene --- UI/window-basic-main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 15eb25ed6cb2d8..1b39cb0e107bbf 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1166,6 +1166,11 @@ void OBSBasic::Load(const char *file) if (!data) { disableSaving--; blog(LOG_INFO, "No scene file found, creating default scene"); + const string name = filesystem::u8path(file).stem().u8string(); + config_set_string(App()->GlobalConfig(), "Basic", + "SceneCollection", name.c_str()); + config_set_string(App()->GlobalConfig(), "Basic", + "SceneCollectionFile", name.c_str()); CreateDefaultScene(true); SaveProject(); return; From 6d20327bbcfb8f50e91fa7b3e212b631883dcf80 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 29 Jul 2024 22:46:38 +0200 Subject: [PATCH 0354/1073] UI: Rename existing (corrupt) collection file if loading fails --- UI/window-basic-main.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 1b39cb0e107bbf..22f6517297f8ea 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1165,12 +1165,34 @@ void OBSBasic::Load(const char *file) obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak"); if (!data) { disableSaving--; - blog(LOG_INFO, "No scene file found, creating default scene"); - const string name = filesystem::u8path(file).stem().u8string(); + const auto path = filesystem::u8path(file); + const string name = path.stem().u8string(); + /* Check if file exists but failed to load. */ + if (filesystem::exists(path)) { + /* Assume the file is corrupt and rename it to allow + * for manual recovery if possible. */ + auto newPath = path; + newPath.concat(".invalid"); + + blog(LOG_WARNING, + "File exists but appears to be corrupt, renaming " + "to \"%s\" before continuing.", + newPath.filename().u8string().c_str()); + + error_code ec; + filesystem::rename(path, newPath, ec); + if (ec) { + blog(LOG_ERROR, + "Failed renaming corrupt file with %d", + ec.value()); + } + } + config_set_string(App()->GlobalConfig(), "Basic", "SceneCollection", name.c_str()); config_set_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile", name.c_str()); + blog(LOG_INFO, "No scene file found, creating default scene"); CreateDefaultScene(true); SaveProject(); return; From 15ec21106a92416d7e330bc43931bbaadbba601d Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Wed, 31 Jul 2024 18:14:17 -0700 Subject: [PATCH 0355/1073] obs-qsv11: Check for null response on free During destruction of the QSV_Encoder_Internal the response may not be initialized and we can segfault. Add a check for this similar to the Windows implementation. --- plugins/obs-qsv11/common_utils_linux.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/obs-qsv11/common_utils_linux.cpp b/plugins/obs-qsv11/common_utils_linux.cpp index a54cdd0328b86c..ced570017b6787 100644 --- a/plugins/obs-qsv11/common_utils_linux.cpp +++ b/plugins/obs-qsv11/common_utils_linux.cpp @@ -210,6 +210,9 @@ mfxStatus simple_gethdl(mfxHDL pthis, mfxMemId mid, mfxHDL *handle) mfxStatus simple_free(mfxHDL pthis, mfxFrameAllocResponse *response) { + if (response == nullptr) + return MFX_ERR_NULL_PTR; + if (response->mids == nullptr || response->NumFrameActual == 0) return MFX_ERR_NONE; From e36352dadd8b295cd7078a2ad3e9d2f47595a1a0 Mon Sep 17 00:00:00 2001 From: Florian Zwoch Date: Thu, 23 May 2024 18:32:54 +0200 Subject: [PATCH 0356/1073] libobs/util: Fix potential memory error in text parser Fixes memory access when parsing '#' comment tokens when the file immediately was EOF after this token. --- libobs/util/text-lookup.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libobs/util/text-lookup.c b/libobs/util/text-lookup.c index ade2c3dccbbdc4..bf5c38bd4ad92b 100644 --- a/libobs/util/text-lookup.c +++ b/libobs/util/text-lookup.c @@ -88,8 +88,9 @@ static bool lookup_gettoken(struct lexer *lex, struct strref *str) if (!str->array) { /* comments are designated with a #, and end at LF */ if (ch == '#') { - while (ch != '\n' && ch != 0) - ch = *(++lex->offset); + while (*lex->offset != '\n' && + *lex->offset != 0) + ++lex->offset; } else if (temp.type == BASETOKEN_WHITESPACE) { strref_copy(str, &temp.text); break; From 95a753b9d8f16ba1cc73ba83936368ff2c74ff5e Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 12 Jun 2024 23:15:08 +0200 Subject: [PATCH 0357/1073] libobs: Fix buffer overrun in os_wcs_to_utf8() --- libobs/util/platform.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libobs/util/platform.c b/libobs/util/platform.c index af2e80840ba6d9..068b6d60e8e1b4 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -429,8 +429,7 @@ size_t os_wcs_to_utf8(const wchar_t *str, size_t len, char *dst, return 0; if (out_len) - out_len = - wchar_to_utf8(str, in_len, dst, out_len + 1, 0); + out_len = wchar_to_utf8(str, in_len, dst, out_len, 0); dst[out_len] = 0; } From 82466751ea5e9cdcabdfc26235a4cddfff0513cf Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:35:11 +0900 Subject: [PATCH 0358/1073] linux-alsa: Remove unused function declaration --- plugins/linux-alsa/alsa-input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/linux-alsa/alsa-input.c b/plugins/linux-alsa/alsa-input.c index ee866e5172a088..5af8fade0bcfe0 100644 --- a/plugins/linux-alsa/alsa-input.c +++ b/plugins/linux-alsa/alsa-input.c @@ -70,8 +70,10 @@ static bool alsa_devices_changed(obs_properties_t *props, obs_property_t *p, static obs_properties_t *alsa_get_properties(void *); static void *alsa_create(obs_data_t *, obs_source_t *); static void alsa_destroy(void *); +#if SHUTDOWN_ON_DEACTIVATE static void alsa_activate(void *); static void alsa_deactivate(void *); +#endif static void alsa_get_defaults(obs_data_t *); static void alsa_update(void *, obs_data_t *); From f811903e4f773f27c9be34d3dcb63fb999d973d9 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:36:49 +0900 Subject: [PATCH 0359/1073] linux-pipewire: Remove unused function --- plugins/linux-pipewire/camera-portal.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/plugins/linux-pipewire/camera-portal.c b/plugins/linux-pipewire/camera-portal.c index 360d0b86adc8f2..bf5f435ae17949 100644 --- a/plugins/linux-pipewire/camera-portal.c +++ b/plugins/linux-pipewire/camera-portal.c @@ -108,23 +108,6 @@ static GDBusProxy *get_camera_portal_proxy(void) return camera_proxy; } -static uint32_t get_camera_version(void) -{ - g_autoptr(GVariant) cached_version = NULL; - uint32_t version; - - ensure_camera_portal_proxy(); - - if (!camera_proxy) - return 0; - - cached_version = - g_dbus_proxy_get_cached_property(camera_proxy, "version"); - version = cached_version ? g_variant_get_uint32(cached_version) : 0; - - return version; -} - /* ------------------------------------------------- */ struct camera_device { From e9c440401cf4e9be1bbd8db22d0e3cad9ee74137 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:37:41 +0900 Subject: [PATCH 0360/1073] obs-ffmpeg: Resolve unused-function warnings --- plugins/obs-ffmpeg/obs-ffmpeg-formats.h | 2 +- plugins/obs-ffmpeg/obs-ffmpeg-output.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-formats.h b/plugins/obs-ffmpeg/obs-ffmpeg-formats.h index b9cc7ac5d42af9..977b3c6d2bee35 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-formats.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-formats.h @@ -69,7 +69,7 @@ obs_to_ffmpeg_video_format(enum video_format format) } } -static enum AVChromaLocation +static inline enum AVChromaLocation determine_chroma_location(enum AVPixelFormat pix_fmt, enum AVColorSpace colorspace) { diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 899f5e2d03b6ca..440d58ef79e309 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -565,6 +565,7 @@ static inline const char *safe_str(const char *s) return s; } +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) static enum AVCodecID get_codec_id(const char *name, int id) { const AVCodec *codec; @@ -582,7 +583,6 @@ static enum AVCodecID get_codec_id(const char *name, int id) return codec->id; } -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) static void set_encoder_ids(struct ffmpeg_data *data) { data->output->oformat->video_codec = get_codec_id( From d2971d1aaa2bc2b442b5b648a07bc4d72df8f847 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:38:30 +0900 Subject: [PATCH 0361/1073] obs-filters: Remove unused functions when NvAFX is disabled --- plugins/obs-filters/noise-suppress-filter.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c index 13af68a7cbec22..22e363d5314832 100644 --- a/plugins/obs-filters/noise-suppress-filter.c +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -236,9 +236,9 @@ static void noise_suppress_destroy(void *data) bfree(ng); } +#ifdef LIBNVAFX_ENABLED static bool nvafx_initialize_internal(void *data) { -#ifdef LIBNVAFX_ENABLED struct noise_suppress_data *ng = data; NvAFX_Status err; @@ -320,16 +320,12 @@ static bool nvafx_initialize_internal(void *data) failure: ng->use_nvafx = false; return false; - -#else - UNUSED_PARAMETER(data); - return false; -#endif } +#endif +#ifdef LIBNVAFX_ENABLED static void *nvafx_initialize(void *data) { -#ifdef LIBNVAFX_ENABLED struct noise_suppress_data *ng = data; NvAFX_Status err; @@ -382,12 +378,8 @@ static void *nvafx_initialize(void *data) pthread_mutex_unlock(&nvafx_initializer_mutex); pthread_mutex_unlock(&ng->nvafx_mutex); return NULL; - -#else - UNUSED_PARAMETER(data); - return NULL; -#endif } +#endif static inline void alloc_channel(struct noise_suppress_data *ng, uint32_t sample_rate, size_t channel, From 6532be41400960e7c146f56b6f48fefc578203d4 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:39:43 +0900 Subject: [PATCH 0362/1073] obs-outputs: Remove unused functions on non-Windows Also adjusted mixed usage of `#if defined` and `#ifdef`. --- plugins/obs-outputs/rtmp-stream.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 190d80aac18c7b..da5980072ee4a0 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -350,6 +350,7 @@ static void droptest_cap_data_rate(struct rtmp_stream *stream, size_t size) } #endif +#ifdef _WIN32 static int socket_queue_data(RTMPSockBuf *sb, const char *data, int len, void *arg) { @@ -384,6 +385,7 @@ static int socket_queue_data(RTMPSockBuf *sb, const char *data, int len, return len; } +#endif // _WIN32 static int handle_socket_read(struct rtmp_stream *stream) { @@ -639,7 +641,6 @@ static void dbr_set_bitrate(struct rtmp_stream *stream); #ifdef _WIN32 #define socklen_t int -#endif static void log_sndbuf_size(struct rtmp_stream *stream) { @@ -651,6 +652,7 @@ static void log_sndbuf_size(struct rtmp_stream *stream) info("Socket send buffer is %d bytes", cur_sendbuf_size); } } +#endif static void *send_thread(void *data) { @@ -658,7 +660,7 @@ static void *send_thread(void *data) os_set_thread_name("rtmp-stream: send_thread"); -#if defined(_WIN32) +#ifdef _WIN32 log_sndbuf_size(stream); #endif @@ -733,7 +735,7 @@ static void *send_thread(void *data) send_footers(stream); // Y2023 spec } -#if defined(_WIN32) +#ifdef _WIN32 log_sndbuf_size(stream); #endif From 384a5e7f3013e402450e8128ea7f2bb05807a512 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:40:58 +0900 Subject: [PATCH 0363/1073] obs-qsv11: Remove unused functions --- plugins/obs-qsv11/common_utils_linux.cpp | 23 ----------------------- plugins/obs-qsv11/obs-qsv11.c | 9 --------- 2 files changed, 32 deletions(-) diff --git a/plugins/obs-qsv11/common_utils_linux.cpp b/plugins/obs-qsv11/common_utils_linux.cpp index ced570017b6787..430fea11538bfb 100644 --- a/plugins/obs-qsv11/common_utils_linux.cpp +++ b/plugins/obs-qsv11/common_utils_linux.cpp @@ -494,29 +494,6 @@ static uint32_t vaapi_check_support(VADisplay display, VAProfile profile, return (rc & VA_RC_CBR || rc & VA_RC_CQP || rc & VA_RC_VBR); } -static bool vaapi_supports_h264(VADisplay display) -{ - bool ret = false; - ret |= vaapi_check_support(display, VAProfileH264ConstrainedBaseline, - VAEntrypointEncSlice); - ret |= vaapi_check_support(display, VAProfileH264Main, - VAEntrypointEncSlice); - ret |= vaapi_check_support(display, VAProfileH264High, - VAEntrypointEncSlice); - - if (!ret) { - ret |= vaapi_check_support(display, - VAProfileH264ConstrainedBaseline, - VAEntrypointEncSliceLP); - ret |= vaapi_check_support(display, VAProfileH264Main, - VAEntrypointEncSliceLP); - ret |= vaapi_check_support(display, VAProfileH264High, - VAEntrypointEncSliceLP); - } - - return ret; -} - static bool vaapi_supports_av1(VADisplay display) { bool ret = false; diff --git a/plugins/obs-qsv11/obs-qsv11.c b/plugins/obs-qsv11/obs-qsv11.c index b899087acb6353..6b42c88ad90430 100644 --- a/plugins/obs-qsv11/obs-qsv11.c +++ b/plugins/obs-qsv11/obs-qsv11.c @@ -136,8 +136,6 @@ static const char *obs_qsv_getname_hevc(void *type_data) return "QuickSync HEVC"; } -static void obs_qsv_stop(void *data); - static void clear_data(struct obs_qsv *obsqsv) { if (obsqsv->context) { @@ -240,13 +238,6 @@ static inline void add_translated_strings(obs_property_t *list, #define TEXT_KEYINT_SEC obs_module_text("KeyframeIntervalSec") #define TEXT_BFRAMES obs_module_text("BFrames") -static inline bool is_skl_or_greater_platform() -{ - enum qsv_cpu_platform plat = qsv_get_cpu_platform(); - return (plat >= QSV_CPU_PLATFORM_SKL || - plat == QSV_CPU_PLATFORM_UNKNOWN); -} - static bool update_latency(obs_data_t *settings) { bool update = false; From bec9f0c544321c447961d05e21dbd7e715572f6d Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:41:19 +0900 Subject: [PATCH 0364/1073] obs-x264: Remove unused function --- plugins/obs-x264/obs-x264.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c index 5d95adb0940deb..23b019fe1b7e27 100644 --- a/plugins/obs-x264/obs-x264.c +++ b/plugins/obs-x264/obs-x264.c @@ -74,8 +74,6 @@ static const char *obs_x264_getname(void *unused) return "x264"; } -static void obs_x264_stop(void *data); - static void clear_data(struct obs_x264 *obsx264) { if (obsx264->context) { From 123231c97c9c448cfdca9707d83a5f4f7b534a0f Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:42:47 +0900 Subject: [PATCH 0365/1073] text-freetype2: Move static function declarations from header file --- plugins/text-freetype2/text-freetype2.c | 15 +++++++++++++++ plugins/text-freetype2/text-freetype2.h | 18 ------------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index 3de3dc4c68f1cf..ac97c6cc630056 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -35,6 +35,21 @@ MODULE_EXPORT const char *obs_module_description(void) uint32_t texbuf_w = 2048, texbuf_h = 2048; +static const char *ft2_source_get_name(void *unused); +static void *ft2_source_create(obs_data_t *settings, obs_source_t *source); +static void ft2_source_destroy(void *data); +static void ft2_source_update(void *data, obs_data_t *settings); +static obs_missing_files_t *ft2_missing_files(void *data); + +static void ft2_source_render(void *data, gs_effect_t *effect); +static void ft2_video_tick(void *data, float seconds); +static uint32_t ft2_source_get_width(void *data); +static uint32_t ft2_source_get_height(void *data); + +static void ft2_source_defaults_v1(obs_data_t *settings); +static void ft2_source_defaults_v2(obs_data_t *settings); +static obs_properties_t *ft2_source_properties(void *unused); + static struct obs_source_info freetype2_source_info_v1 = { .id = "text_ft2_source", .type = OBS_SOURCE_TYPE_INPUT, diff --git a/plugins/text-freetype2/text-freetype2.h b/plugins/text-freetype2/text-freetype2.h index 98e404f842d944..fe0da333d38c8c 100644 --- a/plugins/text-freetype2/text-freetype2.h +++ b/plugins/text-freetype2/text-freetype2.h @@ -70,27 +70,9 @@ struct ft2_source { extern FT_Library ft2_lib; -static void *ft2_source_create(obs_data_t *settings, obs_source_t *source); -static void ft2_source_destroy(void *data); -static void ft2_source_update(void *data, obs_data_t *settings); -static void ft2_source_render(void *data, gs_effect_t *effect); -static void ft2_video_tick(void *data, float seconds); - void draw_outlines(struct ft2_source *srcdata); void draw_drop_shadow(struct ft2_source *srcdata); -static uint32_t ft2_source_get_width(void *data); -static uint32_t ft2_source_get_height(void *data); - -static void ft2_source_defaults_v1(obs_data_t *settings); -static void ft2_source_defaults_v2(obs_data_t *settings); - -static obs_properties_t *ft2_source_properties(void *unused); - -static const char *ft2_source_get_name(void *unused); - -static obs_missing_files_t *ft2_missing_files(void *data); - uint32_t get_ft2_text_width(wchar_t *text, struct ft2_source *srcdata); time_t get_modified_timestamp(char *filename); From 71736ffb7d94b9fa3b84612219febf25ce033750 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 29 Jul 2024 23:44:28 +0900 Subject: [PATCH 0366/1073] libobs/graphics: Add inline qualifier to functions in header files --- libobs/graphics/half.h | 4 ++-- libobs/graphics/image-file.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libobs/graphics/half.h b/libobs/graphics/half.h index 0d864cab688b31..f597ea0b184d3b 100644 --- a/libobs/graphics/half.h +++ b/libobs/graphics/half.h @@ -51,7 +51,7 @@ struct half { }; /* adapted from DirectXMath XMConvertFloatToHalf */ -static struct half half_from_float(float f) +static inline struct half half_from_float(float f) { uint32_t Result; @@ -90,7 +90,7 @@ static struct half half_from_float(float f) return h; } -static struct half half_from_bits(uint16_t u) +static inline struct half half_from_bits(uint16_t u) { struct half h; h.u = u; diff --git a/libobs/graphics/image-file.h b/libobs/graphics/image-file.h index 1f6bc97aa4ad0d..855a08acefade6 100644 --- a/libobs/graphics/image-file.h +++ b/libobs/graphics/image-file.h @@ -94,33 +94,33 @@ EXPORT bool gs_image_file4_tick(gs_image_file4_t *if4, uint64_t elapsed_time_ns); EXPORT void gs_image_file4_update_texture(gs_image_file4_t *if4); -static void gs_image_file2_free(gs_image_file2_t *if2) +static inline void gs_image_file2_free(gs_image_file2_t *if2) { gs_image_file_free(&if2->image); if2->mem_usage = 0; } -static void gs_image_file2_init_texture(gs_image_file2_t *if2) +static inline void gs_image_file2_init_texture(gs_image_file2_t *if2) { gs_image_file_init_texture(&if2->image); } -static void gs_image_file3_free(gs_image_file3_t *if3) +static inline void gs_image_file3_free(gs_image_file3_t *if3) { gs_image_file2_free(&if3->image2); } -static void gs_image_file3_init_texture(gs_image_file3_t *if3) +static inline void gs_image_file3_init_texture(gs_image_file3_t *if3) { gs_image_file2_init_texture(&if3->image2); } -static void gs_image_file4_free(gs_image_file4_t *if4) +static inline void gs_image_file4_free(gs_image_file4_t *if4) { gs_image_file3_free(&if4->image3); } -static void gs_image_file4_init_texture(gs_image_file4_t *if4) +static inline void gs_image_file4_init_texture(gs_image_file4_t *if4) { gs_image_file3_init_texture(&if4->image3); } From d76f4b3aad413cdd322741bdc9fdc06b33197f2e Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Tue, 30 Jul 2024 00:02:04 +0900 Subject: [PATCH 0367/1073] libobs: Remove unused static-inline functions --- libobs/obs-source-transition.c | 33 --------------------------------- libobs/obs-source.c | 12 ------------ libobs/obs.c | 5 ----- 3 files changed, 50 deletions(-) diff --git a/libobs/obs-source-transition.c b/libobs/obs-source-transition.c index d18b3c3557f973..234d2f1f1a3e7d 100644 --- a/libobs/obs-source-transition.c +++ b/libobs/obs-source-transition.c @@ -318,26 +318,6 @@ obs_source_t *obs_transition_get_active_source(obs_source_t *transition) return ret; } -static inline bool activate_child(obs_source_t *transition, size_t idx) -{ - bool success = true; - - lock_transition(transition); - - if (transition->transition_sources[idx] && - !transition->transition_source_active[idx]) { - - success = obs_source_add_active_child( - transition, transition->transition_sources[idx]); - if (success) - transition->transition_source_active[idx] = true; - } - - unlock_transition(transition); - - return success; -} - static bool activate_transition(obs_source_t *transition, size_t idx, obs_source_t *child) { @@ -639,19 +619,6 @@ static inline void copy_transition_state(obs_source_t *transition, state->transitioning_audio = transition->transitioning_audio; } -static inline void enum_child(obs_source_t *tr, obs_source_t *child, - obs_source_enum_proc_t enum_callback, void *param) -{ - if (!child) - return; - - if (child->context.data && child->info.enum_active_sources) - child->info.enum_active_sources(child->context.data, - enum_callback, param); - - enum_callback(tr, child, param); -} - void obs_transition_enum_sources(obs_source_t *transition, obs_source_enum_proc_t cb, void *param) { diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 28182d8130e20f..8207cb07484bca 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -161,12 +161,6 @@ static void allocate_audio_mix_buffer(struct obs_source *source) } } -static inline bool is_async_video_source(const struct obs_source *source) -{ - return (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO) == - OBS_SOURCE_ASYNC_VIDEO; -} - static inline bool is_audio_source(const struct obs_source *source) { return source->info.output_flags & OBS_SOURCE_AUDIO; @@ -2366,12 +2360,6 @@ static inline void set_eparam(gs_effect_t *effect, const char *name, float val) gs_effect_set_float(param, val); } -static inline void set_eparami(gs_effect_t *effect, const char *name, int val) -{ - gs_eparam_t *param = gs_effect_get_param_by_name(effect, name); - gs_effect_set_int(param, val); -} - static bool update_async_texrender(struct obs_source *source, const struct obs_source_frame *frame, gs_texture_t *tex[MAX_AV_PLANES], diff --git a/libobs/obs.c b/libobs/obs.c index 9b739c1f307dfe..9f8658d5a31388 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -2043,11 +2043,6 @@ static inline void *obs_service_addref_safe_(void *ref) return obs_service_get_ref(ref); } -static inline void *obs_id_(void *data) -{ - return data; -} - obs_source_t *obs_get_source_by_name(const char *name) { return get_context_by_name(&obs->data.public_sources, name, From 4837a3417fce8c33c5d4e89d73c68f98953b46b7 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Tue, 30 Jul 2024 00:02:31 +0900 Subject: [PATCH 0368/1073] libobs/util: Remove unused static-inline function The commit 862f16285f commented the function out and the function became unused. --- libobs/util/config-file.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/libobs/util/config-file.c b/libobs/util/config-file.c index ac9bfe1cb6653a..2771162f414790 100644 --- a/libobs/util/config-file.c +++ b/libobs/util/config-file.c @@ -87,19 +87,6 @@ config_t *config_create(const char *file) return config; } -static inline void remove_ref_whitespace(struct strref *ref) -{ - if (ref->array) { - while (ref->len && is_whitespace(*ref->array)) { - ref->array++; - ref->len--; - } - - while (ref->len && is_whitespace(ref->array[ref->len - 1])) - ref->len--; - } -} - static bool config_parse_string(struct lexer *lex, struct strref *ref, char end) { bool success = end != 0; From 9a9975890ed0d3fadfbd432c28fc97cf87c756bb Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Tue, 30 Jul 2024 00:04:08 +0900 Subject: [PATCH 0369/1073] libobs/callback: Remove unused static-inline function --- libobs/callback/calldata.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libobs/callback/calldata.c b/libobs/callback/calldata.c index e99570783041c9..01bea89d942bfd 100644 --- a/libobs/callback/calldata.c +++ b/libobs/callback/calldata.c @@ -41,12 +41,6 @@ * direct referencing. */ -static inline void cd_serialize(uint8_t **pos, void *ptr, size_t size) -{ - memcpy(ptr, *pos, size); - *pos += size; -} - static inline size_t cd_serialize_size(uint8_t **pos) { size_t size = 0; From 5dd97b36884cb6d1a82688e3b32dcdf37cd0b8b2 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Tue, 30 Jul 2024 00:06:00 +0900 Subject: [PATCH 0370/1073] libobs-opengl: Remove unused static-inline function --- libobs-opengl/gl-shader.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libobs-opengl/gl-shader.c b/libobs-opengl/gl-shader.c index 56eefca6964518..5da8a1edac1b83 100644 --- a/libobs-opengl/gl-shader.c +++ b/libobs-opengl/gl-shader.c @@ -25,11 +25,6 @@ #include "gl-subsystem.h" #include "gl-shaderparser.h" -static inline void shader_param_init(struct gs_shader_param *param) -{ - memset(param, 0, sizeof(struct gs_shader_param)); -} - static inline void shader_param_free(struct gs_shader_param *param) { bfree(param->name); From 84b321e95e752cf59f4f4b30d347f15e8db06105 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Tue, 30 Jul 2024 00:07:05 +0900 Subject: [PATCH 0371/1073] UI: Remove unused static-inline function --- UI/window-basic-settings-a11y.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/UI/window-basic-settings-a11y.cpp b/UI/window-basic-settings-a11y.cpp index 899541d5c75759..2437b2c11de0f8 100644 --- a/UI/window-basic-settings-a11y.cpp +++ b/UI/window-basic-settings-a11y.cpp @@ -11,11 +11,6 @@ enum ColorPreset { COLOR_PRESET_CUSTOM = 99, }; -static inline bool WidgetChanged(QWidget *widget) -{ - return widget->property("changed").toBool(); -} - static inline QColor color_from_int(long long val) { return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, From 37b40184a058a90aefb15b9b3281bfc60fa61865 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Wed, 31 Jul 2024 01:39:49 +0900 Subject: [PATCH 0372/1073] shared/obs-scripting: Add inline qualifiers to functions in header file --- shared/obs-scripting/obs-scripting-lua.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/obs-scripting/obs-scripting-lua.h b/shared/obs-scripting/obs-scripting-lua.h index d5f63506006ec2..2631d292eb5c5b 100644 --- a/shared/obs-scripting/obs-scripting-lua.h +++ b/shared/obs-scripting/obs-scripting-lua.h @@ -184,17 +184,17 @@ static inline void free_lua_obs_callback(struct lua_obs_callback *cb) /* ------------------------------------------------ */ -static int is_ptr(lua_State *script, int idx) +static inline int is_ptr(lua_State *script, int idx) { return lua_isuserdata(script, idx) || lua_isnil(script, idx); } -static int is_table(lua_State *script, int idx) +static inline int is_table(lua_State *script, int idx) { return lua_istable(script, idx); } -static int is_function(lua_State *script, int idx) +static inline int is_function(lua_State *script, int idx) { return lua_isfunction(script, idx); } From 00c495b203a28c24402fa8d03031062d73f2d108 Mon Sep 17 00:00:00 2001 From: pkv Date: Thu, 18 May 2023 22:38:49 +0200 Subject: [PATCH 0373/1073] obs-filters: Move NVIDIA filters in their own project This commit does the following: 1. Factor out NVIDIA Audio Effects from Noise Suppression filter. 2. Move NVIDIA Audio Effects to a new filter in a new nv-filters project. 3. Migrate Noise Suppression filter settings to the new filter when NVIDIA Audio effects were used. 4. Migrate NVIDIA AI Greenscreen to the new nv-filters project for easier maintainance of all NVIDIA Maxine effects. Context: Currently, the three NVIDIA Audio Effects (noise suppression, room echo removal, noise suppression + room echo removal combined) are part of the noise suppression filter. Historically, it's because a lot of code was shared between speex, rnnoise & NVIDIA noise suppression. But the NVIDIA code has become bulkier & cumbersome due to: - addition of other effects; - addition of a deferred loading thread. The factorisation makes the code very difficult to maintain for (un)readability reasons. This will make it easier to add other audio effects, should we wish to. Developers life will be easier too when debugging. The code has been reorganized and comments added. I also added a mutex in the process_fx function to avoid a crash when swapping effects. Signed-off-by: pkv --- plugins/CMakeLists.txt | 2 + plugins/nv-filters/CMakeLists.txt | 32 + .../nv-filters/cmake/windows/obs-module.rc.in | 24 + plugins/nv-filters/data/color.effect | 95 ++ plugins/nv-filters/data/locale/en-US.ini | 16 + .../nv-filters/data/rtx_greenscreen.effect | 183 ++++ plugins/nv-filters/nv-filters.c | 46 + plugins/nv-filters/nv_sdk_versions.h | 2 + plugins/nv-filters/nvafx-load.h | 339 +++++++ plugins/nv-filters/nvidia-audiofx-filter.c | 936 ++++++++++++++++++ .../nvidia-greenscreen-filter.c | 20 +- .../{obs-filters => nv-filters}/nvvfx-load.h | 2 +- plugins/obs-filters/CMakeLists.txt | 6 +- plugins/obs-filters/cmake/nvidia.cmake | 15 - plugins/obs-filters/data/locale/en-US.ini | 2 +- plugins/obs-filters/noise-suppress-filter.c | 663 +------------ plugins/obs-filters/obs-filters.c | 28 - 17 files changed, 1746 insertions(+), 665 deletions(-) create mode 100644 plugins/nv-filters/CMakeLists.txt create mode 100644 plugins/nv-filters/cmake/windows/obs-module.rc.in create mode 100644 plugins/nv-filters/data/color.effect create mode 100644 plugins/nv-filters/data/locale/en-US.ini create mode 100644 plugins/nv-filters/data/rtx_greenscreen.effect create mode 100644 plugins/nv-filters/nv-filters.c create mode 100644 plugins/nv-filters/nv_sdk_versions.h create mode 100644 plugins/nv-filters/nvafx-load.h create mode 100644 plugins/nv-filters/nvidia-audiofx-filter.c rename plugins/{obs-filters => nv-filters}/nvidia-greenscreen-filter.c (98%) rename plugins/{obs-filters => nv-filters}/nvvfx-load.h (99%) delete mode 100644 plugins/obs-filters/cmake/nvidia.cmake diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 92a3812e4686c5..68cb1024970cab 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -55,6 +55,7 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) add_obs_plugin(mac-syphon PLATFORMS MACOS) add_obs_plugin(mac-videotoolbox PLATFORMS MACOS) add_obs_plugin(mac-virtualcam PLATFORMS MACOS) + add_obs_plugin(nv-filters PLATFORMS WINDOWS) check_obs_browser() @@ -114,6 +115,7 @@ if(OS_WINDOWS) add_subdirectory(obs-text) add_subdirectory(vlc-video) add_subdirectory(obs-vst) + add_subdirectory(nv-filters) check_obs_browser() elseif(OS_MACOS) diff --git a/plugins/nv-filters/CMakeLists.txt b/plugins/nv-filters/CMakeLists.txt new file mode 100644 index 00000000000000..e74fb755809401 --- /dev/null +++ b/plugins/nv-filters/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.22...3.25) +if(OS_WINDOWS) + add_library(nv-filters MODULE) + add_library(OBS::nv-filters ALIAS nv-filters) + + option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON) + option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON) + + if(ENABLE_NVAFX) + target_enable_feature(nv-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) + target_sources(nv-filters PRIVATE nvidia-audiofx-filter.c) + else() + target_disable_feature(nv-filters "NVIDIA Audio FX support") + endif() + + if(ENABLE_NVVFX) + target_enable_feature(nv-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED) + target_sources(nv-filters PRIVATE nvidia-greenscreen-filter.c) + else() + target_disable_feature(nv-filters "NVIDIA Video FX support") + endif() + + configure_file(cmake/windows/obs-module.rc.in nv-filters.rc) + target_sources(nv-filters PRIVATE nv-filters.rc) + target_sources(nv-filters PRIVATE nv-filters.c) + + target_link_libraries(nv-filters PRIVATE OBS::libobs $<$:OBS::w32-pthreads>) + + # cmake-format: off + set_target_properties_obs(nv-filters PROPERTIES FOLDER plugins PREFIX "") + # cmake-format: on +endif() diff --git a/plugins/nv-filters/cmake/windows/obs-module.rc.in b/plugins/nv-filters/cmake/windows/obs-module.rc.in new file mode 100644 index 00000000000000..4081e0a8ce2670 --- /dev/null +++ b/plugins/nv-filters/cmake/windows/obs-module.rc.in @@ -0,0 +1,24 @@ +1 VERSIONINFO +FILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "${OBS_COMPANY_NAME}" + VALUE "FileDescription", "NVIDIA A/V Filters" + VALUE "FileVersion", "${OBS_VERSION_CANONICAL}" + VALUE "ProductName", "${OBS_PRODUCT_NAME}" + VALUE "ProductVersion", "${OBS_VERSION_CANONICAL}" + VALUE "Comments", "${OBS_COMMENTS}" + VALUE "LegalCopyright", "${OBS_LEGAL_COPYRIGHT}" + VALUE "InternalName", "nv-filters" + VALUE "OriginalFilename", "nv-filters" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04B0 + END +END diff --git a/plugins/nv-filters/data/color.effect b/plugins/nv-filters/data/color.effect new file mode 100644 index 00000000000000..5de529424a6e41 --- /dev/null +++ b/plugins/nv-filters/data/color.effect @@ -0,0 +1,95 @@ +float srgb_linear_to_nonlinear_channel(float u) +{ + return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055); +} + +float3 srgb_linear_to_nonlinear(float3 v) +{ + return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b)); +} + +float srgb_nonlinear_to_linear_channel(float u) +{ + return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4); +} + +float3 srgb_nonlinear_to_linear(float3 v) +{ + return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b)); +} + +float3 rec709_to_rec2020(float3 v) +{ + float r = dot(v, float3(0.62740389593469903, 0.32928303837788370, 0.043313065687417225)); + float g = dot(v, float3(0.069097289358232075, 0.91954039507545871, 0.011362315566309178)); + float b = dot(v, float3(0.016391438875150280, 0.088013307877225749, 0.89559525324762401)); + return float3(r, g, b); +} + +float3 rec2020_to_rec709(float3 v) +{ + float r = dot(v, float3(1.6604910021084345, -0.58764113878854951, -0.072849863319884883)); + float g = dot(v, float3(-0.12455047452159074, 1.1328998971259603, -0.0083494226043694768)); + float b = dot(v, float3(-0.018150763354905303, -0.10057889800800739, 1.1187296613629127)); + return float3(r, g, b); +} + +float3 reinhard(float3 rgb) +{ + rgb /= rgb + float3(1., 1., 1.); + rgb = saturate(rgb); + rgb = pow(rgb, float3(1. / 2.4, 1. / 2.4, 1. / 2.4)); + rgb = srgb_nonlinear_to_linear(rgb); + return rgb; +} + +float linear_to_st2084_channel(float x) +{ + float c = pow(abs(x), 0.1593017578); + return pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375); +} + +float st2084_to_linear_channel(float u) +{ + float c = pow(abs(u), 1. / 78.84375); + return pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578); +} + +float eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax) +{ + float Lw_pq = linear_to_st2084_channel(Lw / 10000.); + float E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie + float maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq; + float KS = (1.5 * maxLum) - 0.5; + float E2 = E1; + if (E1 > KS) + { + float T = (E1 - KS) / (1. - KS); + float Tsquared = T * T; + float Tcubed = Tsquared * T; + float P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum; + E2 = P; + } + float E3 = E2; + float E4 = E3 * Lw_pq; + return E4; +} + +float3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax) +{ + float maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax); + float maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq); + + // avoid divide-by-zero possibility + maxRGB1_linear = max(6.10352e-5, maxRGB1_linear); + + rgb_linear *= maxRGB2_linear / maxRGB1_linear; + return rgb_linear; +} + +float3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax) +{ + float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b); + float maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear); + return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax); +} diff --git a/plugins/nv-filters/data/locale/en-US.ini b/plugins/nv-filters/data/locale/en-US.ini new file mode 100644 index 00000000000000..e1da7f429e06ab --- /dev/null +++ b/plugins/nv-filters/data/locale/en-US.ini @@ -0,0 +1,16 @@ +Nvafx="NVIDIA Audio Effects" +Nvafx.SuppressLevel="Suppression Level" +Nvafx.Intensity="Suppression Intensity" +Nvafx.Method="Method" +Nvafx.Method.Denoiser="NVIDIA Noise Removal" +Nvafx.Method.Dereverb="NVIDIA Room Echo Removal" +Nvafx.Method.DenoiserPlusDereverb="NVIDIA Noise Removal + Room Echo Removal" +Nvafx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Audio SDK is outdated." +Nvvfx.Method.Greenscreen="NVIDIA Background Removal" +Nvvfx.Method.Greenscreen.Mode="Mode" +Nvvfx.Method.Greenscreen.Quality="Quality (higher GPU usage, better quality)" +Nvvfx.Method.Greenscreen.Performance="Performance (lower GPU usage, good quality)" +Nvvfx.Method.Greenscreen.Threshold="Threshold" +Nvvfx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Video SDK is outdated." +Nvvfx.Method.Greenscreen.Processing="Mask refresh frequency in frames" +Nvvfx.Method.Greenscreen.Processing.Hint="This alleviates GPU load by generating a mask every N frames only (2 on default)." diff --git a/plugins/nv-filters/data/rtx_greenscreen.effect b/plugins/nv-filters/data/rtx_greenscreen.effect new file mode 100644 index 00000000000000..fc945d2327f4b4 --- /dev/null +++ b/plugins/nv-filters/data/rtx_greenscreen.effect @@ -0,0 +1,183 @@ +#include "color.effect" + +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float multiplier; + +uniform texture2d mask; +uniform float threshold; + +sampler_state texSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertInOut { + float2 uv : TEXCOORD0; + float4 pos : POSITION; +}; + +struct FragData { + float2 uv : TEXCOORD0; +}; + +struct FragPos { + float4 pos : POSITION; +}; + +VertInOut VSDefault(VertData v_in) +{ + VertInOut v_out; + v_out.uv = v_in.uv; + v_out.pos = mul(float4(v_in.pos.xyz, 1.), ViewProj); + return v_out; +} + +FragPos VSConvertUnorm(uint id : VERTEXID) +{ + float idHigh = float(id >> 1); + float idLow = float(id & uint(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + FragPos vert_out; + vert_out.pos = float4(x, y, 0.0, 1.0); + return vert_out; +} + +float4 Mask(FragData f_in) +{ + float4 rgba = image.Sample(texSampler, f_in.uv); + rgba *= smoothstep(threshold - 0.1,threshold,mask.Sample(texSampler, f_in.uv).a); + return rgba; +} + +float4 PSMask(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + return rgba; +} + +float4 PSMaskMultiply(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb *= multiplier; + return rgba; +} + +float4 PSMaskTonemap(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + return rgba; +} + +float4 PSMaskMultiplyTonemap(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb *= multiplier; + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + return rgba; +} + +float4 PSConvertUnorm(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + return rgba; +} + +float4 PSConvertUnormTonemap(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + return rgba; +} + +float4 PSConvertUnormMultiplyTonemap(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb *= multiplier; + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb); + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMask(f_in); + } +} + +technique DrawMultiply +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskMultiply(f_in); + } +} + +technique DrawTonemap +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskTonemap(f_in); + } +} + +technique DrawMultiplyTonemap +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskMultiplyTonemap(f_in); + } +} + +technique ConvertUnorm +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertUnorm(f_in); + } +} + +technique ConvertUnormTonemap +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertUnormTonemap(f_in); + } +} + +technique ConvertUnormMultiplyTonemap +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertUnormMultiplyTonemap(f_in); + } +} diff --git a/plugins/nv-filters/nv-filters.c b/plugins/nv-filters/nv-filters.c new file mode 100644 index 00000000000000..a9f590357bab13 --- /dev/null +++ b/plugins/nv-filters/nv-filters.c @@ -0,0 +1,46 @@ +#include + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("nv-filters", "en-US") +MODULE_EXPORT const char *obs_module_description(void) +{ + return "NVIDIA filters"; +} + +#ifdef LIBNVAFX_ENABLED +extern struct obs_source_info nvidia_audiofx_filter; +extern bool load_nvidia_afx(void); +extern void unload_nvidia_afx(void); +#endif +#ifdef LIBNVVFX_ENABLED +extern struct obs_source_info nvidia_greenscreen_filter_info; +extern bool load_nvidia_vfx(void); +extern void unload_nvidia_vfx(void); +#endif + +bool obs_module_load(void) +{ +#ifdef LIBNVAFX_ENABLED + /* load nvidia audio fx dll */ + if (load_nvidia_afx()) + obs_register_source(&nvidia_audiofx_filter); +#endif +#ifdef LIBNVVFX_ENABLED + obs_enter_graphics(); + const bool direct3d = gs_get_device_type() == GS_DEVICE_DIRECT3D_11; + obs_leave_graphics(); + if (direct3d && load_nvidia_vfx()) + obs_register_source(&nvidia_greenscreen_filter_info); +#endif + return true; +} + +void obs_module_unload(void) +{ +#ifdef LIBNVAFX_ENABLED + unload_nvidia_afx(); +#endif +#ifdef LIBNVVFX_ENABLED + unload_nvidia_vfx(); +#endif +} diff --git a/plugins/nv-filters/nv_sdk_versions.h b/plugins/nv-filters/nv_sdk_versions.h new file mode 100644 index 00000000000000..bf93deaebbd9e8 --- /dev/null +++ b/plugins/nv-filters/nv_sdk_versions.h @@ -0,0 +1,2 @@ +#define MIN_VFX_SDK_VERSION (0 << 24 | 7 << 16 | 2 << 8 | 0 << 0) +#define MIN_AFX_SDK_VERSION (1 << 24 | 3 << 16 | 0 << 0) diff --git a/plugins/nv-filters/nvafx-load.h b/plugins/nv-filters/nvafx-load.h new file mode 100644 index 00000000000000..8cd4eecdabb63e --- /dev/null +++ b/plugins/nv-filters/nvafx-load.h @@ -0,0 +1,339 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "nv_sdk_versions.h" + +#define NVAFX_API + +#ifdef LIBNVAFX_ENABLED +static HMODULE nv_audiofx = NULL; +static HMODULE nv_cuda = NULL; + +/** Effects @ref NvAFX_EffectSelector */ +#define NVAFX_EFFECT_DENOISER "denoiser" +#define NVAFX_EFFECT_DEREVERB "dereverb" +#define NVAFX_EFFECT_DEREVERB_DENOISER "dereverb_denoiser" +#define NVAFX_EFFECT_AEC "aec" +#define NVAFX_EFFECT_SUPERRES "superres" + +/** Model paths */ +#define NVAFX_EFFECT_DENOISER_MODEL "\\models\\denoiser_48k.trtpkg" +#define NVAFX_EFFECT_DEREVERB_MODEL "\\models\\dereverb_48k.trtpkg" +#define NVAFX_EFFECT_DEREVERB_DENOISER_MODEL \ + "\\models\\dereverb_denoiser_48k.trtpkg" + +#define NVAFX_CHAINED_EFFECT_DENOISER_16k_SUPERRES_16k_TO_48k \ + "denoiser16k_superres16kto48k" +#define NVAFX_CHAINED_EFFECT_DEREVERB_16k_SUPERRES_16k_TO_48k \ + "dereverb16k_superres16kto48k" +#define NVAFX_CHAINED_EFFECT_DEREVERB_DENOISER_16k_SUPERRES_16k_TO_48k \ + "dereverb_denoiser16k_superres16kto48k" +#define NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DENOISER_16k \ + "superres8kto16k_denoiser16k" +#define NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DEREVERB_16k \ + "superres8kto16k_dereverb16k" +#define NVAFX_CHAINED_EFFECT_SUPERRES_8k_TO_16k_DEREVERB_DENOISER_16k \ + "superres8kto16k_dereverb_denoiser16k" + +/** Parameter selectors */ + +#define NVAFX_PARAM_NUM_STREAMS "num_streams" +#define NVAFX_PARAM_USE_DEFAULT_GPU "use_default_gpu" +#define NVAFX_PARAM_USER_CUDA_CONTEXT "user_cuda_context" +#define NVAFX_PARAM_DISABLE_CUDA_GRAPH "disable_cuda_graph" +#define NVAFX_PARAM_ENABLE_VAD "enable_vad" +/** Effect parameters. @ref NvAFX_ParameterSelector */ +#define NVAFX_PARAM_MODEL_PATH "model_path" +#define NVAFX_PARAM_INPUT_SAMPLE_RATE "input_sample_rate" +#define NVAFX_PARAM_OUTPUT_SAMPLE_RATE "output_sample_rate" +#define NVAFX_PARAM_NUM_INPUT_SAMPLES_PER_FRAME "num_input_samples_per_frame" +#define NVAFX_PARAM_NUM_OUTPUT_SAMPLES_PER_FRAME "num_output_samples_per_frame" +#define NVAFX_PARAM_NUM_INPUT_CHANNELS "num_input_channels" +#define NVAFX_PARAM_NUM_OUTPUT_CHANNELS "num_output_channels" +#define NVAFX_PARAM_INTENSITY_RATIO "intensity_ratio" + +#pragma deprecated(NVAFX_PARAM_DENOISER_MODEL_PATH) +#define NVAFX_PARAM_DENOISER_MODEL_PATH NVAFX_PARAM_MODEL_PATH +#pragma deprecated(NVAFX_PARAM_DENOISER_SAMPLE_RATE) +#define NVAFX_PARAM_DENOISER_SAMPLE_RATE NVAFX_PARAM_SAMPLE_RATE +#pragma deprecated(NVAFX_PARAM_DENOISER_NUM_SAMPLES_PER_FRAME) +#define NVAFX_PARAM_DENOISER_NUM_SAMPLES_PER_FRAME \ + NVAFX_PARAM_NUM_SAMPLES_PER_FRAME +#pragma deprecated(NVAFX_PARAM_DENOISER_NUM_CHANNELS) +#define NVAFX_PARAM_DENOISER_NUM_CHANNELS NVAFX_PARAM_NUM_CHANNELS +#pragma deprecated(NVAFX_PARAM_DENOISER_INTENSITY_RATIO) +#define NVAFX_PARAM_DENOISER_INTENSITY_RATIO NVAFX_PARAM_INTENSITY_RATIO +/** Number of audio channels **/ +#pragma deprecated(NVAFX_PARAM_NUM_CHANNELS) +#define NVAFX_PARAM_NUM_CHANNELS "num_channels" +/** Sample rate (unsigned int). Currently supported sample rate(s): 48000, 16000 */ +#pragma deprecated(NVAFX_PARAM_SAMPLE_RATE) +#define NVAFX_PARAM_SAMPLE_RATE "sample_rate" +/** Number of samples per frame (unsigned int). This is immutable parameter */ +#pragma deprecated(NVAFX_PARAM_NUM_SAMPLES_PER_FRAME) +#define NVAFX_PARAM_NUM_SAMPLES_PER_FRAME "num_samples_per_frame" + +typedef enum { + /** Success */ + NVAFX_STATUS_SUCCESS = 0, + /** Failure */ + NVAFX_STATUS_FAILED = 1, + /** Handle invalid */ + NVAFX_STATUS_INVALID_HANDLE = 2, + /** Parameter value invalid */ + NVAFX_STATUS_INVALID_PARAM = 3, + /** Parameter value immutable */ + NVAFX_STATUS_IMMUTABLE_PARAM = 4, + /** Insufficient data to process */ + NVAFX_STATUS_INSUFFICIENT_DATA = 5, + /** Effect not supported */ + NVAFX_STATUS_EFFECT_NOT_AVAILABLE = 6, + /** Given buffer length too small to hold requested data */ + NVAFX_STATUS_OUTPUT_BUFFER_TOO_SMALL = 7, + /** Model file could not be loaded */ + NVAFX_STATUS_MODEL_LOAD_FAILED = 8, + + /** (32 bit SDK only) COM server was not registered, please see user manual for details */ + NVAFX_STATUS_32_SERVER_NOT_REGISTERED = 9, + /** (32 bit SDK only) COM operation failed */ + NVAFX_STATUS_32_COM_ERROR = 10, + /** GPU supported. The SDK requires Turing and above GPU with Tensor cores */ + NVAFX_STATUS_GPU_UNSUPPORTED = 11, +} NvAFX_Status; + +#define NVAFX_TRUE 1 +#define NVAFX_FALSE 0 +typedef char NvAFX_Bool; + +typedef const char *NvAFX_EffectSelector; +typedef const char *NvAFX_ParameterSelector; +typedef void *NvAFX_Handle; + +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetEffectList_t)(int *num_effects, + NvAFX_EffectSelector *effects[]); +typedef NvAFX_Status + NVAFX_API (*NvAFX_CreateEffect_t)(NvAFX_EffectSelector code, + NvAFX_Handle *effect); +typedef NvAFX_Status + NVAFX_API (*NvAFX_CreateChainedEffect_t)(NvAFX_EffectSelector code, + NvAFX_Handle *effect); +typedef NvAFX_Status NVAFX_API (*NvAFX_DestroyEffect_t)(NvAFX_Handle effect); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetU32_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + unsigned int val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetU32List_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + unsigned int *val, unsigned int size); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetString_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + const char *val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetStringList_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + const char **val, unsigned int size); +typedef NvAFX_Status NVAFX_API (*NvAFX_SetFloat_t)( + NvAFX_Handle effect, NvAFX_ParameterSelector param_name, float val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_SetFloatList_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + float *val, unsigned int size); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetU32_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + unsigned int *val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetString_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + char *val, int max_length); +typedef NvAFX_Status NVAFX_API (*NvAFX_GetStringList_t)( + NvAFX_Handle effect, NvAFX_ParameterSelector param_name, char **val, + int *max_length, unsigned int size); +typedef NvAFX_Status NVAFX_API (*NvAFX_GetFloat_t)( + NvAFX_Handle effect, NvAFX_ParameterSelector param_name, float *val); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetFloatList_t)(NvAFX_Handle effect, + NvAFX_ParameterSelector param_name, + float *val, unsigned int size); +typedef NvAFX_Status NVAFX_API (*NvAFX_Load_t)(NvAFX_Handle effect); +typedef NvAFX_Status + NVAFX_API (*NvAFX_GetSupportedDevices_t)(NvAFX_Handle effect, int *num, + int *devices); +typedef NvAFX_Status NVAFX_API (*NvAFX_Run_t)(NvAFX_Handle effect, + const float **input, + float **output, + unsigned num_samples, + unsigned num_channels); +typedef NvAFX_Status NVAFX_API (*NvAFX_Reset_t)(NvAFX_Handle effect); + +/* cuda */ +typedef enum cudaError_enum { + CUDA_SUCCESS = 0, + CUDA_ERROR_INVALID_VALUE = 1, + CUDA_ERROR_OUT_OF_MEMORY = 2, + CUDA_ERROR_NOT_INITIALIZED = 3, + CUDA_ERROR_DEINITIALIZED = 4, + CUDA_ERROR_PROFILER_DISABLED = 5, + CUDA_ERROR_PROFILER_NOT_INITIALIZED = 6, + CUDA_ERROR_PROFILER_ALREADY_STARTED = 7, + CUDA_ERROR_PROFILER_ALREADY_STOPPED = 8, + CUDA_ERROR_NO_DEVICE = 100, + CUDA_ERROR_INVALID_DEVICE = 101, + CUDA_ERROR_INVALID_IMAGE = 200, + CUDA_ERROR_INVALID_CONTEXT = 201, + CUDA_ERROR_CONTEXT_ALREADY_CURRENT = 202, + CUDA_ERROR_MAP_FAILED = 205, + CUDA_ERROR_UNMAP_FAILED = 206, + CUDA_ERROR_ARRAY_IS_MAPPED = 207, + CUDA_ERROR_ALREADY_MAPPED = 208, + CUDA_ERROR_NO_BINARY_FOR_GPU = 209, + CUDA_ERROR_ALREADY_ACQUIRED = 210, + CUDA_ERROR_NOT_MAPPED = 211, + CUDA_ERROR_NOT_MAPPED_AS_ARRAY = 212, + CUDA_ERROR_NOT_MAPPED_AS_POINTER = 213, + CUDA_ERROR_ECC_UNCORRECTABLE = 214, + CUDA_ERROR_UNSUPPORTED_LIMIT = 215, + CUDA_ERROR_CONTEXT_ALREADY_IN_USE = 216, + CUDA_ERROR_INVALID_SOURCE = 300, + CUDA_ERROR_FILE_NOT_FOUND = 301, + CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302, + CUDA_ERROR_SHARED_OBJECT_INIT_FAILED = 303, + CUDA_ERROR_OPERATING_SYSTEM = 304, + CUDA_ERROR_INVALID_HANDLE = 400, + CUDA_ERROR_NOT_FOUND = 500, + CUDA_ERROR_NOT_READY = 600, + CUDA_ERROR_LAUNCH_FAILED = 700, + CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES = 701, + CUDA_ERROR_LAUNCH_TIMEOUT = 702, + CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING = 703, + CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED = 704, + CUDA_ERROR_PEER_ACCESS_NOT_ENABLED = 705, + CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE = 708, + CUDA_ERROR_CONTEXT_IS_DESTROYED = 709, + CUDA_ERROR_ASSERT = 710, + CUDA_ERROR_TOO_MANY_PEERS = 711, + CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED = 712, + CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED = 713, + CUDA_ERROR_UNKNOWN = 999 +} CUresult; +typedef struct CUctx_st *CUcontext; +typedef CUresult(__stdcall *cuCtxGetCurrent_t)(CUcontext *pctx); +typedef CUresult(__stdcall *cuCtxPopCurrent_t)(CUcontext *pctx); +typedef CUresult(__stdcall *cuInit_t)(unsigned int Flags); + +static NvAFX_GetEffectList_t NvAFX_GetEffectList = NULL; +static NvAFX_CreateEffect_t NvAFX_CreateEffect = NULL; +static NvAFX_CreateChainedEffect_t NvAFX_CreateChainedEffect = NULL; +static NvAFX_DestroyEffect_t NvAFX_DestroyEffect = NULL; +static NvAFX_SetU32_t NvAFX_SetU32 = NULL; +static NvAFX_SetU32List_t NvAFX_SetU32List = NULL; +static NvAFX_SetString_t NvAFX_SetString = NULL; +static NvAFX_SetStringList_t NvAFX_SetStringList = NULL; +static NvAFX_SetFloat_t NvAFX_SetFloat = NULL; +static NvAFX_SetFloatList_t NvAFX_SetFloatList = NULL; +static NvAFX_GetU32_t NvAFX_GetU32 = NULL; +static NvAFX_GetString_t NvAFX_GetString = NULL; +static NvAFX_GetStringList_t NvAFX_GetStringList = NULL; +static NvAFX_GetFloat_t NvAFX_GetFloat = NULL; +static NvAFX_GetFloatList_t NvAFX_GetFloatList = NULL; +static NvAFX_Load_t NvAFX_Load = NULL; +static NvAFX_GetSupportedDevices_t NvAFX_GetSupportedDevices = NULL; +static NvAFX_Run_t NvAFX_Run = NULL; +static NvAFX_Reset_t NvAFX_Reset; +/* cuda */ +static cuCtxGetCurrent_t cuCtxGetCurrent = NULL; +static cuCtxPopCurrent_t cuCtxPopCurrent = NULL; +static cuInit_t cuInit = NULL; + +void release_lib(void) +{ + NvAFX_GetEffectList = NULL; + NvAFX_CreateEffect = NULL; + NvAFX_CreateChainedEffect = NULL; + NvAFX_DestroyEffect = NULL; + NvAFX_SetU32 = NULL; + NvAFX_SetU32List = NULL; + NvAFX_SetString = NULL; + NvAFX_SetStringList = NULL; + NvAFX_SetFloat = NULL; + NvAFX_SetFloatList = NULL; + NvAFX_GetU32 = NULL; + NvAFX_GetString = NULL; + NvAFX_GetStringList = NULL; + NvAFX_GetFloat = NULL; + NvAFX_GetFloatList = NULL; + NvAFX_Load = NULL; + NvAFX_GetSupportedDevices = NULL; + NvAFX_Run = NULL; + NvAFX_Reset = NULL; + if (nv_audiofx) { + FreeLibrary(nv_audiofx); + nv_audiofx = NULL; + } + cuCtxGetCurrent = NULL; + cuCtxPopCurrent = NULL; + cuInit = NULL; + if (nv_cuda) { + FreeLibrary(nv_cuda); + nv_cuda = NULL; + } +} + +static bool nvafx_get_sdk_path(char *buffer, const size_t len) +{ + DWORD ret = + GetEnvironmentVariableA("NVAFX_SDK_DIR", buffer, (DWORD)len); + + if (!ret || ret >= len - 1) + return false; + + return true; +} + +static bool load_lib(void) +{ + char path[MAX_PATH]; + if (!nvafx_get_sdk_path(path, sizeof(path))) + return false; + + SetDllDirectoryA(path); + nv_audiofx = LoadLibrary(L"NVAudioEffects.dll"); + SetDllDirectoryA(NULL); + nv_cuda = LoadLibrary(L"nvcuda.dll"); + return !!nv_audiofx && !!nv_cuda; +} + +static unsigned int get_lib_version(void) +{ + static unsigned int version = 0; + static bool version_checked = false; + + if (version_checked) + return version; + + version_checked = true; + + char path[MAX_PATH]; + if (!nvafx_get_sdk_path(path, sizeof(path))) + return 0; + + SetDllDirectoryA(path); + + struct win_version_info nto_ver = {0}; + if (get_dll_ver(L"NVAudioEffects.dll", &nto_ver)) + version = nto_ver.major << 24 | nto_ver.minor << 16 | + nto_ver.build << 8 | nto_ver.revis << 0; + + SetDllDirectoryA(NULL); + return version; +} + +#endif diff --git a/plugins/nv-filters/nvidia-audiofx-filter.c b/plugins/nv-filters/nvidia-audiofx-filter.c new file mode 100644 index 00000000000000..c63e687f612e1f --- /dev/null +++ b/plugins/nv-filters/nvidia-audiofx-filter.c @@ -0,0 +1,936 @@ +#include +#include +#include +#include +#include +#include +#include "nvafx-load.h" +#include + +/* -------------------------------------------------------- */ +#define do_log(level, format, ...) \ + blog(level, "[NVIDIA Audio Effects: '%s'] " format, \ + obs_source_get_name(ng->context), ##__VA_ARGS__) + +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) + +#ifdef _DEBUG +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) +#else +#define debug(format, ...) +#endif +/* -------------------------------------------------------- */ + +#define S_NVAFX_INTENSITY "intensity" +#define S_METHOD "method" +#define S_METHOD_NVAFX_DENOISER NVAFX_EFFECT_DENOISER +#define S_METHOD_NVAFX_DEREVERB NVAFX_EFFECT_DEREVERB +#define S_METHOD_NVAFX_DEREVERB_DENOISER NVAFX_EFFECT_DEREVERB_DENOISER + +#define MT_ obs_module_text +#define TEXT_NVAFX_INTENSITY MT_("Nvafx.Intensity") +#define TEXT_METHOD MT_("Nvafx.Method") +#define TEXT_METHOD_NVAFX_DENOISER MT_("Nvafx.Method.Denoiser") +#define TEXT_METHOD_NVAFX_DEREVERB MT_("Nvafx.Method.Dereverb") +#define TEXT_METHOD_NVAFX_DEREVERB_DENOISER \ + MT_("Nvafx.Method.DenoiserPlusDereverb") +#define TEXT_METHOD_NVAFX_DEPRECATION MT_("Nvafx.OutdatedSDK") + +#define MAX_PREPROC_CHANNELS 8 +#define BUFFER_SIZE_MSEC 10 + +/* NVAFX constants, these can't be changed */ +#define NVAFX_SAMPLE_RATE 48000 +/* The SDK does not explicitly set this as a constant though it relies on it.*/ +#define NVAFX_FRAME_SIZE 480 + +#ifdef _MSC_VER +#define ssize_t intptr_t +#endif + +struct nvidia_audio_data { + obs_source_t *context; + + uint64_t last_timestamp; + uint64_t latency; + + size_t frames; + size_t channels; + + struct deque info_buffer; + struct deque input_buffers[MAX_PREPROC_CHANNELS]; + struct deque output_buffers[MAX_PREPROC_CHANNELS]; + + /* This bool is quite important but it is easy to get lost. So let's + * explain how it's used. One big issue is that the NVIDIA FX takes + * ages to load an FX; so its initialization is deferred to a separate + * thread. + * First stage (creation): + * - use_nvafx is set to true at creation of the filter, IF the SDK dir + * path is set. + * - if initialization fails, the bool is set to false & the filter is + * destroyed. + * Later stages (running or updating of the FX): + * - they are executed ONLY if initialization was successful; + * - if at any step, there's an FX failure, the bool is updated to false + * & the filter is destroyed. + */ + bool use_nvafx; + + /* this tracks if the SDK is found */ + bool nvidia_sdk_dir_found; + + bool has_mono_src; + volatile bool reinit_done; + + /* NVAFX handle, one per audio channel */ + NvAFX_Handle handle[MAX_PREPROC_CHANNELS]; + + uint32_t sample_rate; + float intensity_ratio; + unsigned int num_samples_per_frame, num_channels; + char *model; + bool nvafx_initialized; + const char *fx; + char *sdk_path; + + /* Resampler */ + audio_resampler_t *nvafx_resampler; + audio_resampler_t *nvafx_resampler_back; + + /* We load the DLL in a separate thread because its loading is very + * long and unnecessarily blocks OBS initial loading. + * This bool is true once the thread which side loads the FX DLL is started */ + bool nvafx_loading; + pthread_t nvafx_thread; + pthread_mutex_t nvafx_mutex; + + /* PCM buffers */ + float *copy_buffers[MAX_PREPROC_CHANNELS]; + float *nvafx_segment_buffers[MAX_PREPROC_CHANNELS]; + + /* output data */ + struct obs_audio_data output_audio; + DARRAY(float) output_data; +}; + +static const char *nvidia_audio_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("Nvafx"); +} + +static void nvidia_audio_destroy(void *data) +{ + struct nvidia_audio_data *ng = data; + + if (ng->nvidia_sdk_dir_found) + pthread_mutex_lock(&ng->nvafx_mutex); + + for (size_t i = 0; i < ng->channels; i++) { + if (ng->handle[0]) { + if (NvAFX_DestroyEffect) { + NvAFX_Status err = + NvAFX_DestroyEffect(ng->handle[i]); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_Release() failed"); + } + } + } + deque_free(&ng->input_buffers[i]); + deque_free(&ng->output_buffers[i]); + } + + bfree(ng->nvafx_segment_buffers[0]); + + if (ng->nvafx_resampler) { + audio_resampler_destroy(ng->nvafx_resampler); + audio_resampler_destroy(ng->nvafx_resampler_back); + } + bfree(ng->model); + bfree(ng->sdk_path); + bfree((void *)ng->fx); + if (ng->nvidia_sdk_dir_found) { + pthread_join(ng->nvafx_thread, NULL); + pthread_mutex_unlock(&ng->nvafx_mutex); + pthread_mutex_destroy(&ng->nvafx_mutex); + } + + bfree(ng->copy_buffers[0]); + deque_free(&ng->info_buffer); + da_free(ng->output_data); + bfree(ng); +} + +bool nvidia_afx_initializer_mutex_initialized; +pthread_mutex_t nvidia_afx_initializer_mutex; +bool nvidia_afx_loaded = false; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4706) +#endif +void release_afxlib(void) +{ + NvAFX_GetEffectList = NULL; + NvAFX_CreateEffect = NULL; + NvAFX_CreateChainedEffect = NULL; + NvAFX_DestroyEffect = NULL; + NvAFX_SetU32 = NULL; + NvAFX_SetU32List = NULL; + NvAFX_SetString = NULL; + NvAFX_SetStringList = NULL; + NvAFX_SetFloat = NULL; + NvAFX_SetFloatList = NULL; + NvAFX_GetU32 = NULL; + NvAFX_GetString = NULL; + NvAFX_GetStringList = NULL; + NvAFX_GetFloat = NULL; + NvAFX_GetFloatList = NULL; + NvAFX_Load = NULL; + NvAFX_GetSupportedDevices = NULL; + NvAFX_Run = NULL; + NvAFX_Reset = NULL; + if (nv_audiofx) { + FreeLibrary(nv_audiofx); + nv_audiofx = NULL; + } + cuCtxGetCurrent = NULL; + cuCtxPopCurrent = NULL; + cuInit = NULL; + if (nv_cuda) { + FreeLibrary(nv_cuda); + nv_cuda = NULL; + } +} + +bool load_nvidia_afx(void) +{ + unsigned int version = get_lib_version(); + uint8_t major = (version >> 24) & 0xff; + uint8_t minor = (version >> 16) & 0x00ff; + uint8_t build = (version >> 8) & 0x0000ff; + uint8_t revision = (version >> 0) & 0x000000ff; + if (version) { + blog(LOG_INFO, "[NVIDIA Audio Effects:] version: %i.%i.%i.%i", + major, minor, build, revision); + if (version < MIN_AFX_SDK_VERSION) { + blog(LOG_INFO, + "[NVIDIA Audio Effects:]: SDK is outdated. Please update both audio & video SDK.\nRequired SDK versions, audio: %i.%i.%i; video: %i.%i.%i", + (MIN_AFX_SDK_VERSION >> 24) & 0xff, + (MIN_AFX_SDK_VERSION >> 16) & 0x00ff, + (MIN_AFX_SDK_VERSION >> 8) & 0x0000ff, + (MIN_VFX_SDK_VERSION >> 24) & 0xff, + (MIN_VFX_SDK_VERSION >> 16) & 0x00ff, + (MIN_VFX_SDK_VERSION >> 8) & 0x0000ff); + } + } + if (!load_lib()) { + blog(LOG_INFO, + "[NVIDIA Audio Effects:] NVIDIA denoiser disabled, redistributable not found or could not be loaded."); + return false; + } + + nvidia_afx_initializer_mutex_initialized = + pthread_mutex_init(&nvidia_afx_initializer_mutex, NULL) == 0; + +#define LOAD_SYM_FROM_LIB(sym, lib, dll) \ + if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \ + DWORD err = GetLastError(); \ + printf("[noise suppress]: Couldn't load " #sym " from " dll \ + ": %lu (0x%lx)", \ + err, err); \ + goto unload_everything; \ + } + +#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_audiofx, "NVAudioEffects.dll") + LOAD_SYM(NvAFX_GetEffectList); + LOAD_SYM(NvAFX_CreateEffect); + LOAD_SYM(NvAFX_CreateChainedEffect); + LOAD_SYM(NvAFX_DestroyEffect); + LOAD_SYM(NvAFX_SetU32); + LOAD_SYM(NvAFX_SetU32List); + LOAD_SYM(NvAFX_SetString); + LOAD_SYM(NvAFX_SetStringList); + LOAD_SYM(NvAFX_SetFloat); + LOAD_SYM(NvAFX_SetFloatList); + LOAD_SYM(NvAFX_GetU32); + LOAD_SYM(NvAFX_GetString); + LOAD_SYM(NvAFX_GetStringList); + LOAD_SYM(NvAFX_GetFloat); + LOAD_SYM(NvAFX_GetFloatList); + LOAD_SYM(NvAFX_Load); + LOAD_SYM(NvAFX_GetSupportedDevices); + LOAD_SYM(NvAFX_Run); + LOAD_SYM(NvAFX_Reset); +#undef LOAD_SYM +#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_cuda, "nvcuda.dll") + LOAD_SYM(cuCtxGetCurrent); + LOAD_SYM(cuCtxPopCurrent); + LOAD_SYM(cuInit); +#undef LOAD_SYM + + NvAFX_Status err; + CUresult cudaerr; + + NvAFX_Handle h = NULL; + + cudaerr = cuInit(0); + if (cudaerr != CUDA_SUCCESS) { + goto cuda_errors; + } + CUcontext old = {0}; + CUcontext curr = {0}; + cudaerr = cuCtxGetCurrent(&old); + if (cudaerr != CUDA_SUCCESS) { + goto cuda_errors; + } + + err = NvAFX_CreateEffect(NVAFX_EFFECT_DENOISER, &h); + cudaerr = cuCtxGetCurrent(&curr); + if (cudaerr != CUDA_SUCCESS) { + goto cuda_errors; + } + + if (curr != old) { + cudaerr = cuCtxPopCurrent(NULL); + if (cudaerr != CUDA_SUCCESS) + goto cuda_errors; + } + + if (err != NVAFX_STATUS_SUCCESS) { + if (err == NVAFX_STATUS_GPU_UNSUPPORTED) { + blog(LOG_INFO, + "[NVIDIA Audio Effects:] disabled: unsupported GPU"); + } else { + blog(LOG_ERROR, + "[NVIDIA Audio Effects:] disabled, error %i", err); + } + goto unload_everything; + } + + err = NvAFX_DestroyEffect(h); + if (err != NVAFX_STATUS_SUCCESS) { + blog(LOG_ERROR, "[NVIDIA Audio Effects:]: disabled, error %i", + err); + goto unload_everything; + } + + nvidia_afx_loaded = true; + blog(LOG_INFO, "[NVIDIA Audio Effects:] enabled"); + return true; + +cuda_errors: + blog(LOG_ERROR, "[NVIDIA Audio Effects:] disabled, CUDA error %i", + cudaerr); +unload_everything: + release_afxlib(); + + return false; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +void unload_nvidia_afx(void) +{ + release_afxlib(); + + if (nvidia_afx_initializer_mutex_initialized) { + pthread_mutex_destroy(&nvidia_afx_initializer_mutex); + nvidia_afx_initializer_mutex_initialized = false; + } +} + +static bool nvidia_audio_initialize_internal(void *data) +{ + struct nvidia_audio_data *ng = data; + NvAFX_Status err; + + if (!ng->handle[0]) { + ng->sample_rate = NVAFX_SAMPLE_RATE; + for (size_t i = 0; i < ng->channels; i++) { + // Create FX + CUcontext old = {0}; + CUcontext curr = {0}; + if (cuCtxGetCurrent(&old) != CUDA_SUCCESS) { + goto failure; + } + err = NvAFX_CreateEffect(ng->fx, &ng->handle[i]); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "%s FX creation failed, error %i", + ng->fx, err); + goto failure; + } + if (cuCtxGetCurrent(&curr) != CUDA_SUCCESS) { + goto failure; + } + if (curr != old) { + cuCtxPopCurrent(NULL); + } + // Set sample rate of FX + err = NvAFX_SetU32(ng->handle[i], + NVAFX_PARAM_INPUT_SAMPLE_RATE, + ng->sample_rate); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetU32(Sample Rate: %u) failed, error %i", + ng->sample_rate, err); + goto failure; + } + + // Set intensity of FX + err = NvAFX_SetFloat(ng->handle[i], + NVAFX_PARAM_INTENSITY_RATIO, + ng->intensity_ratio); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", + ng->intensity_ratio, err); + goto failure; + } + + // Set AI models path + err = NvAFX_SetString(ng->handle[i], + NVAFX_PARAM_MODEL_PATH, + ng->model); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetString() failed, error %i", + err); + goto failure; + } + + // Load FX (this is a very long step, about 2 seconds) + err = NvAFX_Load(ng->handle[i]); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_Load() failed with error %i", + err); + goto failure; + } + os_atomic_set_bool(&ng->reinit_done, true); + } + } + return true; + +failure: + ng->use_nvafx = false; + return false; +} + +static void *nvidia_audio_initialize(void *data) +{ + struct nvidia_audio_data *ng = data; + NvAFX_Status err; + + if (!ng->use_nvafx && !nvidia_afx_loaded) { + return NULL; + } + pthread_mutex_lock(&ng->nvafx_mutex); + pthread_mutex_lock(&nvidia_afx_initializer_mutex); + if (!nvidia_audio_initialize_internal(data)) { + goto failure; + } + if (ng->use_nvafx) { + err = NvAFX_GetU32(ng->handle[0], + NVAFX_PARAM_NUM_INPUT_CHANNELS, + &ng->num_channels); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_GetU32() failed to get the number of channels, error %i", + err); + goto failure; + } + if (ng->num_channels != 1) { + do_log(LOG_ERROR, + "The number of channels is not 1 in the sdk any more ==> update code"); + goto failure; + } + NvAFX_Status err = NvAFX_GetU32( + ng->handle[0], NVAFX_PARAM_NUM_INPUT_SAMPLES_PER_FRAME, + &ng->num_samples_per_frame); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_GetU32() failed to get the number of samples per frame, error %i", + err); + goto failure; + } + if (ng->num_samples_per_frame != NVAFX_FRAME_SIZE) { + do_log(LOG_ERROR, + "The number of samples per frame has changed from 480 (= 10 ms) ==> update code"); + goto failure; + } + } + ng->nvafx_initialized = true; + ng->nvafx_loading = false; + pthread_mutex_unlock(&nvidia_afx_initializer_mutex); + pthread_mutex_unlock(&ng->nvafx_mutex); + return NULL; + +failure: + ng->use_nvafx = false; + pthread_mutex_unlock(&nvidia_afx_initializer_mutex); + pthread_mutex_unlock(&ng->nvafx_mutex); + nvidia_audio_destroy(ng); + return NULL; +} + +static inline enum speaker_layout nv_convert_speaker_layout(uint8_t channels) +{ + switch (channels) { + case 0: + return SPEAKERS_UNKNOWN; + case 1: + return SPEAKERS_MONO; + case 2: + return SPEAKERS_STEREO; + case 3: + return SPEAKERS_2POINT1; + case 4: + return SPEAKERS_4POINT0; + case 5: + return SPEAKERS_4POINT1; + case 6: + return SPEAKERS_5POINT1; + case 8: + return SPEAKERS_7POINT1; + default: + return SPEAKERS_UNKNOWN; + } +} + +static void set_nv_model(void *data, const char *method) +{ + struct nvidia_audio_data *ng = data; + const char *file; + + if (strcmp(NVAFX_EFFECT_DEREVERB, method) == 0) + file = NVAFX_EFFECT_DEREVERB_MODEL; + else if (strcmp(NVAFX_EFFECT_DEREVERB_DENOISER, method) == 0) + file = NVAFX_EFFECT_DEREVERB_DENOISER_MODEL; + else + file = NVAFX_EFFECT_DENOISER_MODEL; + + size_t size = strlen(ng->sdk_path) + strlen(file) + 1; + char *buffer = (char *)bmalloc(size); + + strcpy(buffer, ng->sdk_path); + strcat(buffer, file); + ng->model = buffer; +} + +static void nvidia_audio_update(void *data, obs_data_t *s) +{ + struct nvidia_audio_data *ng = data; + + if (!ng->use_nvafx) + return; + + const char *method = obs_data_get_string(s, S_METHOD); + ng->latency = 1000000000LL / (1000 / BUFFER_SIZE_MSEC); + + float intensity = (float)obs_data_get_double(s, S_NVAFX_INTENSITY); + /*-------------------------------------------------------------------*/ + /* STAGE 1 : the following is run only when the filter is created. */ + + /* If the DLL hasn't been loaded & isn't loading, start the side loading. */ + if (!ng->nvafx_initialized && !ng->nvafx_loading) { + ng->intensity_ratio = intensity; + ng->nvafx_loading = true; + pthread_create(&ng->nvafx_thread, NULL, nvidia_audio_initialize, + ng); + } + + /*-------------------------------------------------------------------*/ + /* STAGE 2 : this is executed only after the FX has been initialized */ + if (ng->nvafx_initialized) { + /* updating the intensity of the FX */ + if (intensity != ng->intensity_ratio && + (strcmp(ng->fx, method) == 0)) { + NvAFX_Status err; + ng->intensity_ratio = intensity; + pthread_mutex_lock(&ng->nvafx_mutex); + for (size_t i = 0; i < ng->channels; i++) { + err = NvAFX_SetFloat( + ng->handle[i], + NVAFX_PARAM_INTENSITY_RATIO, + ng->intensity_ratio); + if (err != NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", + ng->intensity_ratio, err); + nvidia_audio_destroy(ng); + } + } + pthread_mutex_unlock(&ng->nvafx_mutex); + } + /* swapping to a new FX requires a reinitialization */ + if ((strcmp(ng->fx, method) != 0)) { + pthread_mutex_lock(&ng->nvafx_mutex); + bfree((void *)ng->fx); + ng->fx = bstrdup(method); + ng->intensity_ratio = intensity; + set_nv_model(ng, method); + os_atomic_set_bool(&ng->reinit_done, false); + for (int i = 0; i < (int)ng->channels; i++) { + /* Destroy previous FX */ + if (NvAFX_DestroyEffect(ng->handle[i]) != + NVAFX_STATUS_SUCCESS) { + do_log(LOG_ERROR, + "FX failed to be destroyed."); + nvidia_audio_destroy(ng); + } else { + ng->handle[i] = NULL; + } + } + if (!nvidia_audio_initialize_internal(data)) + nvidia_audio_destroy(ng); + + pthread_mutex_unlock(&ng->nvafx_mutex); + } + } +} + +static void *nvidia_audio_create(obs_data_t *settings, obs_source_t *filter) +{ + struct nvidia_audio_data *ng = + bzalloc(sizeof(struct nvidia_audio_data)); + + ng->context = filter; + + char sdk_path[MAX_PATH]; + + /* find SDK */ + if (!nvafx_get_sdk_path(sdk_path, sizeof(sdk_path))) { + ng->nvidia_sdk_dir_found = false; + do_log(LOG_ERROR, "NVAFX redist is not installed."); + nvidia_audio_destroy(ng); + return NULL; + } else { + size_t size = sizeof(sdk_path) + 1; + ng->sdk_path = bmalloc(size); + strcpy(ng->sdk_path, sdk_path); + ng->nvidia_sdk_dir_found = true; + ng->nvafx_initialized = false; + ng->nvafx_loading = false; + ng->fx = NULL; + + pthread_mutex_init(&ng->nvafx_mutex, NULL); + + info("NVAFX SDK redist path was found here %s", sdk_path); + // set FX + const char *method = obs_data_get_string(settings, S_METHOD); + set_nv_model(ng, method); + ng->fx = bstrdup(method); + ng->use_nvafx = true; + } + + /* Process 10 millisecond segments to keep latency low. */ + /* At 48kHz, NVAFX processes 480 samples which corresponds to 10 ms.*/ + uint32_t sample_rate = audio_output_get_sample_rate(obs_get_audio()); + size_t channels = audio_output_get_channels(obs_get_audio()); + size_t frames = (size_t)sample_rate / (1000 / BUFFER_SIZE_MSEC); + ng->frames = frames; + ng->channels = channels; + + /* allocate buffers */ + ng->copy_buffers[0] = bmalloc(frames * channels * sizeof(float)); + ng->nvafx_segment_buffers[0] = + bmalloc(NVAFX_FRAME_SIZE * channels * sizeof(float)); + for (size_t c = 1; c < channels; ++c) { + ng->copy_buffers[c] = ng->copy_buffers[c - 1] + frames; + ng->nvafx_segment_buffers[c] = + ng->nvafx_segment_buffers[c - 1] + NVAFX_FRAME_SIZE; + } + + /* reserve circular buffers */ + for (size_t i = 0; i < channels; i++) { + deque_reserve(&ng->input_buffers[i], frames * sizeof(float)); + deque_reserve(&ng->output_buffers[i], frames * sizeof(float)); + } + + /* create resampler if the source is not at 48 kHz */ + if (sample_rate == NVAFX_SAMPLE_RATE) { + ng->nvafx_resampler = NULL; + ng->nvafx_resampler_back = NULL; + } else { + struct resample_info src, dst; + src.samples_per_sec = sample_rate; + src.format = AUDIO_FORMAT_FLOAT_PLANAR; + src.speakers = nv_convert_speaker_layout((uint8_t)channels); + + dst.samples_per_sec = NVAFX_SAMPLE_RATE; + dst.format = AUDIO_FORMAT_FLOAT_PLANAR; + dst.speakers = nv_convert_speaker_layout((uint8_t)channels); + + ng->nvafx_resampler = audio_resampler_create(&dst, &src); + ng->nvafx_resampler_back = audio_resampler_create(&src, &dst); + } + + nvidia_audio_update(ng, settings); + return ng; +} + +static inline void process_fx(struct nvidia_audio_data *ng) +{ + /* Resample if necessary */ + if (ng->nvafx_resampler) { + float *output[MAX_PREPROC_CHANNELS]; + uint32_t out_frames; + uint64_t ts_offset; + audio_resampler_resample(ng->nvafx_resampler, + (uint8_t **)output, &out_frames, + &ts_offset, + (const uint8_t **)ng->copy_buffers, + (uint32_t)ng->frames); + + for (size_t i = 0; i < ng->channels; i++) { + for (ssize_t j = 0, + k = (ssize_t)out_frames - NVAFX_FRAME_SIZE; + j < NVAFX_FRAME_SIZE; ++j, ++k) { + if (k >= 0) { + ng->nvafx_segment_buffers[i][j] = + output[i][k]; + } else { + ng->nvafx_segment_buffers[i][j] = 0; + } + } + } + } else { + for (size_t i = 0; i < ng->channels; i++) { + for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { + ng->nvafx_segment_buffers[i][j] = + ng->copy_buffers[i][j]; + } + } + } + + /* Execute */ + size_t runs = ng->has_mono_src ? 1 : ng->channels; + if (ng->reinit_done) { + pthread_mutex_lock(&ng->nvafx_mutex); + for (size_t i = 0; i < runs; i++) { + NvAFX_Status err = NvAFX_Run( + ng->handle[i], &ng->nvafx_segment_buffers[i], + &ng->nvafx_segment_buffers[i], + ng->num_samples_per_frame, ng->num_channels); + if (err != NVAFX_STATUS_SUCCESS) { + if (err == NVAFX_STATUS_FAILED) { + do_log(LOG_DEBUG, + "NvAFX_Run() failed, error NVAFX_STATUS_FAILED.\n" + "This can occur when changing the FX and is not consequential."); + // stop all processing; this will be reset at new init + os_atomic_set_bool(&ng->reinit_done, + false); + } else { + do_log(LOG_ERROR, + "NvAFX_Run() failed, error %i.\n", + err); + } + } + } + pthread_mutex_unlock(&ng->nvafx_mutex); + } + if (ng->has_mono_src) { + memcpy(ng->nvafx_segment_buffers[1], + ng->nvafx_segment_buffers[0], + NVAFX_FRAME_SIZE * sizeof(float)); + } + /* Revert signal level adjustment, resample back if necessary */ + if (ng->nvafx_resampler) { + float *output[MAX_PREPROC_CHANNELS]; + uint32_t out_frames; + uint64_t ts_offset; + audio_resampler_resample( + ng->nvafx_resampler_back, (uint8_t **)output, + &out_frames, &ts_offset, + (const uint8_t **)ng->nvafx_segment_buffers, + NVAFX_FRAME_SIZE); + + for (size_t i = 0; i < ng->channels; i++) { + for (ssize_t j = 0, + k = (ssize_t)out_frames - ng->frames; + j < (ssize_t)ng->frames; ++j, ++k) { + if (k >= 0) { + ng->copy_buffers[i][j] = output[i][k]; + } else { + ng->copy_buffers[i][j] = 0; + } + } + } + } else { + for (size_t i = 0; i < ng->channels; i++) { + for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { + ng->copy_buffers[i][j] = + ng->nvafx_segment_buffers[i][j]; + } + } + } +} + +static inline void process(struct nvidia_audio_data *ng) +{ + /* Pop from input deque */ + for (size_t i = 0; i < ng->channels; i++) + deque_pop_front(&ng->input_buffers[i], ng->copy_buffers[i], + ng->frames * sizeof(float)); + + if (ng->use_nvafx && nvidia_afx_loaded && ng->nvafx_initialized) { + process_fx(ng); + } + + /* Push to output deque */ + for (size_t i = 0; i < ng->channels; i++) + deque_push_back(&ng->output_buffers[i], ng->copy_buffers[i], + ng->frames * sizeof(float)); +} + +struct nv_audio_info { + uint32_t frames; + uint64_t timestamp; +}; + +static inline void clear_deque(struct deque *buf) +{ + deque_pop_front(buf, NULL, buf->size); +} + +static void reset_data(struct nvidia_audio_data *ng) +{ + for (size_t i = 0; i < ng->channels; i++) { + clear_deque(&ng->input_buffers[i]); + clear_deque(&ng->output_buffers[i]); + } + + clear_deque(&ng->info_buffer); +} + +static struct obs_audio_data * +nvidia_audio_filter_audio(void *data, struct obs_audio_data *audio) +{ + struct nvidia_audio_data *ng = data; + struct nv_audio_info info; + size_t segment_size = ng->frames * sizeof(float); + size_t out_size; + obs_source_t *parent = obs_filter_get_parent(ng->context); + if (!parent) + return NULL; + enum speaker_layout layout = obs_source_get_speaker_layout(parent); + ng->has_mono_src = layout == SPEAKERS_MONO && ng->channels == 2; + + /* ----------------------------------------------- + * If timestamp has dramatically changed, consider it a new stream of + * audio data. Clear all circular buffers to prevent old audio data + * from being processed as part of the new data. */ + if (ng->last_timestamp) { + int64_t diff = llabs((int64_t)ng->last_timestamp - + (int64_t)audio->timestamp); + + if (diff > 1000000000LL) + reset_data(ng); + } + + ng->last_timestamp = audio->timestamp; + + /* ----------------------------------------------- + * push audio packet info (timestamp/frame count) to info deque */ + info.frames = audio->frames; + info.timestamp = audio->timestamp; + deque_push_back(&ng->info_buffer, &info, sizeof(info)); + + /* ----------------------------------------------- + * push back current audio data to input deque */ + for (size_t i = 0; i < ng->channels; i++) + deque_push_back(&ng->input_buffers[i], audio->data[i], + audio->frames * sizeof(float)); + + /* ----------------------------------------------- + * pop/process each 10ms segments, push back to output deque */ + while (ng->input_buffers[0].size >= segment_size) + process(ng); + + /* ----------------------------------------------- + * peek front of info deque, check to see if we have enough to + * pop the expected packet size, if not, return null */ + memset(&info, 0, sizeof(info)); + deque_peek_front(&ng->info_buffer, &info, sizeof(info)); + out_size = info.frames * sizeof(float); + + if (ng->output_buffers[0].size < out_size) + return NULL; + + /* ----------------------------------------------- + * if there's enough audio data buffered in the output deque, + * pop and return a packet */ + deque_pop_front(&ng->info_buffer, NULL, sizeof(info)); + da_resize(ng->output_data, out_size * ng->channels); + + for (size_t i = 0; i < ng->channels; i++) { + ng->output_audio.data[i] = + (uint8_t *)&ng->output_data.array[i * out_size]; + + deque_pop_front(&ng->output_buffers[i], + ng->output_audio.data[i], out_size); + } + + ng->output_audio.frames = info.frames; + ng->output_audio.timestamp = info.timestamp - ng->latency; + return &ng->output_audio; +} + +static void nvidia_audio_defaults(obs_data_t *s) +{ + obs_data_set_default_double(s, S_NVAFX_INTENSITY, 1.0); + obs_data_set_default_string(s, S_METHOD, S_METHOD_NVAFX_DENOISER); +} + +static obs_properties_t *nvidia_audio_properties(void *data) +{ + obs_properties_t *ppts = obs_properties_create(); + struct nvidia_audio_data *ng = (struct nvidia_audio_data *)data; + obs_property_t *method = obs_properties_add_list( + ppts, S_METHOD, TEXT_METHOD, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + if (ng->nvidia_sdk_dir_found) { + obs_property_list_add_string(method, TEXT_METHOD_NVAFX_DENOISER, + S_METHOD_NVAFX_DENOISER); + obs_property_list_add_string(method, TEXT_METHOD_NVAFX_DEREVERB, + S_METHOD_NVAFX_DEREVERB); + obs_property_list_add_string( + method, TEXT_METHOD_NVAFX_DEREVERB_DENOISER, + S_METHOD_NVAFX_DEREVERB_DENOISER); + obs_property_t *slider = obs_properties_add_float_slider( + ppts, S_NVAFX_INTENSITY, TEXT_NVAFX_INTENSITY, 0.0f, + 1.0f, 0.01f); + + unsigned int version = get_lib_version(); + obs_property_t *warning = obs_properties_add_text( + ppts, "deprecation", NULL, OBS_TEXT_INFO); + if (version && version < MIN_AFX_SDK_VERSION) { + obs_property_text_set_info_type(warning, + OBS_TEXT_INFO_WARNING); + obs_property_set_long_description( + warning, TEXT_METHOD_NVAFX_DEPRECATION); + } else { + obs_property_set_visible(warning, 0); + } + } + + return ppts; +} + +struct obs_source_info nvidia_audiofx_filter = { + .id = "nvidia_audiofx_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_AUDIO, + .get_name = nvidia_audio_name, + .create = nvidia_audio_create, + .destroy = nvidia_audio_destroy, + .update = nvidia_audio_update, + .filter_audio = nvidia_audio_filter_audio, + .get_defaults = nvidia_audio_defaults, + .get_properties = nvidia_audio_properties, +}; diff --git a/plugins/obs-filters/nvidia-greenscreen-filter.c b/plugins/nv-filters/nvidia-greenscreen-filter.c similarity index 98% rename from plugins/obs-filters/nvidia-greenscreen-filter.c rename to plugins/nv-filters/nvidia-greenscreen-filter.c index 93f833234acc8a..646c3ad5bcb1d4 100644 --- a/plugins/obs-filters/nvidia-greenscreen-filter.c +++ b/plugins/nv-filters/nvidia-greenscreen-filter.c @@ -30,13 +30,13 @@ #define S_PROCESSING "processing_interval" #define MT_ obs_module_text -#define TEXT_MODE MT_("Greenscreen.Mode") -#define TEXT_MODE_QUALITY MT_("Greenscreen.Quality") -#define TEXT_MODE_PERF MT_("Greenscreen.Performance") -#define TEXT_MODE_THRESHOLD MT_("Greenscreen.Threshold") -#define TEXT_DEPRECATION MT_("Greenscreen.Deprecation") -#define TEXT_PROCESSING MT_("Greenscreen.Processing") -#define TEXT_PROCESSING_HINT MT_("Greenscreen.Processing.Hint") +#define TEXT_MODE MT_("Nvvfx.Method.Greenscreen.Mode") +#define TEXT_MODE_QUALITY MT_("Nvvfx.Method.Greenscreen.Quality") +#define TEXT_MODE_PERF MT_("Nvvfx.Method.Greenscreen.Performance") +#define TEXT_MODE_THRESHOLD MT_("Nvvfx.Method.Greenscreen.Threshold") +#define TEXT_DEPRECATION MT_("Nvvfx.OutdatedSDK") +#define TEXT_PROCESSING MT_("Nvvfx.Method.Greenscreen.Processing") +#define TEXT_PROCESSING_HINT MT_("Nvvfx.Method.Greenscreen.Processing.Hint") bool nvvfx_loaded = false; bool nvvfx_new_sdk = false; @@ -87,7 +87,7 @@ struct nv_greenscreen_data { static const char *nv_greenscreen_filter_name(void *unused) { UNUSED_PARAMETER(unused); - return obs_module_text("NvidiaGreenscreenFilter"); + return obs_module_text("Nvvfx.Method.Greenscreen"); } static void nv_greenscreen_filter_update(void *data, obs_data_t *settings) @@ -883,7 +883,7 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) UNUSED_PARAMETER(effect); } -bool load_nvvfx(void) +bool load_nvidia_vfx(void) { bool old_sdk_loaded = false; unsigned int version = get_lib_version(); @@ -1032,7 +1032,7 @@ bool load_nvvfx(void) } #ifdef LIBNVVFX_ENABLED -void unload_nvvfx(void) +void unload_nvidia_vfx(void) { release_nv_vfx(); } diff --git a/plugins/obs-filters/nvvfx-load.h b/plugins/nv-filters/nvvfx-load.h similarity index 99% rename from plugins/obs-filters/nvvfx-load.h rename to plugins/nv-filters/nvvfx-load.h index 96e8e62a3e43dd..3ef4c0587844df 100644 --- a/plugins/obs-filters/nvvfx-load.h +++ b/plugins/nv-filters/nvvfx-load.h @@ -7,6 +7,7 @@ #include #include #include +#include "nv_sdk_versions.h" #ifdef __cplusplus extern "C" { @@ -39,7 +40,6 @@ extern "C" { #define CUDARTAPI #ifdef LIBNVVFX_ENABLED -#define MIN_VFX_SDK_VERSION (0 << 24 | 7 << 16 | 2 << 8 | 0 << 0) static HMODULE nv_videofx = NULL; static HMODULE nv_cvimage = NULL; static HMODULE nv_cudart = NULL; diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 697b4985afee23..0a9aeaa7d09938 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -5,6 +5,10 @@ legacy_check() add_library(obs-filters MODULE) add_library(OBS::filters ALIAS obs-filters) +if(OS_WINDOWS) + target_enable_feature(obs-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) +endif() + target_sources( obs-filters PRIVATE # cmake-format: sortable @@ -36,8 +40,6 @@ include(cmake/speexdsp.cmake) include(cmake/rnnoise.cmake) if(OS_WINDOWS) - include(cmake/nvidia.cmake) - configure_file(cmake/windows/obs-module.rc.in obs-filters.rc) target_sources(obs-filters PRIVATE obs-filters.rc) endif() diff --git a/plugins/obs-filters/cmake/nvidia.cmake b/plugins/obs-filters/cmake/nvidia.cmake deleted file mode 100644 index 22d1a003bb4b75..00000000000000 --- a/plugins/obs-filters/cmake/nvidia.cmake +++ /dev/null @@ -1,15 +0,0 @@ -option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON) -option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON) - -if(ENABLE_NVAFX) - target_enable_feature(obs-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) -else() - target_disable_feature(obs-filters "NVIDIA Audio FX support") -endif() - -if(ENABLE_NVVFX) - target_enable_feature(obs-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED) - target_sources(obs-filters PRIVATE nvidia-greenscreen-filter.c) -else() - target_disable_feature(obs-filters "NVIDIA Video FX support") -endif() diff --git a/plugins/obs-filters/data/locale/en-US.ini b/plugins/obs-filters/data/locale/en-US.ini index c4de876bc6b6c2..4ebf3f34e66e20 100644 --- a/plugins/obs-filters/data/locale/en-US.ini +++ b/plugins/obs-filters/data/locale/en-US.ini @@ -90,7 +90,7 @@ NoiseSuppress.Method.RNNoise="RNNoise (good quality, more CPU usage)" NoiseSuppress.Method.Nvafx.Denoiser="NVIDIA Noise Removal" NoiseSuppress.Method.Nvafx.Dereverb="NVIDIA Room Echo Removal" NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb="NVIDIA Noise Removal + Room Echo Removal" -NoiseSuppress.Method.Nvafx.Deprecation="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Audio SDK is outdated." +NoiseSuppress.Method.Nvafx.Deprecation2="WARNING: NVIDIA Audio Effects will be automatically migrated to a new dedicated filter 'NVIDIA Audio Effects' once the source is enabled." Saturation="Saturation" HueShift="Hue Shift" Amount="Amount" diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c index 22e363d5314832..cde1c7b9b55eb5 100644 --- a/plugins/obs-filters/noise-suppress-filter.c +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -61,6 +61,8 @@ bool nvafx_loaded = false; MT_("NoiseSuppress.Method.Nvafx.DenoiserPlusDereverb") #define TEXT_METHOD_NVAFX_DEPRECATION \ MT_("NoiseSuppress.Method.Nvafx.Deprecation") +#define TEXT_METHOD_NVAFX_DEPRECATION2 \ + MT_("NoiseSuppress.Method.Nvafx.Deprecation2") #define MAX_PREPROC_CHANNELS 8 @@ -93,8 +95,11 @@ struct noise_suppress_data { struct deque output_buffers[MAX_PREPROC_CHANNELS]; bool use_rnnoise; - bool use_nvafx; bool nvafx_enabled; + bool nvafx_migrated; +#ifdef LIBNVAFX_ENABLED + obs_source_t *migrated_filter; +#endif bool has_mono_src; volatile bool reinit_done; #ifdef LIBSPEEXDSP_ENABLED @@ -110,28 +115,6 @@ struct noise_suppress_data { audio_resampler_t *rnn_resampler; audio_resampler_t *rnn_resampler_back; #endif - -#ifdef LIBNVAFX_ENABLED - /* NVAFX handle, one per audio channel */ - NvAFX_Handle handle[MAX_PREPROC_CHANNELS]; - - uint32_t sample_rate; - float intensity_ratio; - unsigned int num_samples_per_frame, num_channels; - char *model; - bool nvafx_initialized; - const char *fx; - char *sdk_path; - - /* Resampler */ - audio_resampler_t *nvafx_resampler; - audio_resampler_t *nvafx_resampler_back; - - /* Initialization */ - bool nvafx_loading; - pthread_t nvafx_thread; - pthread_mutex_t nvafx_mutex; -#endif /* PCM buffers */ float *copy_buffers[MAX_PREPROC_CHANNELS]; #ifdef LIBSPEEXDSP_ENABLED @@ -140,21 +123,11 @@ struct noise_suppress_data { #ifdef LIBRNNOISE_ENABLED float *rnn_segment_buffers[MAX_PREPROC_CHANNELS]; #endif -#ifdef LIBNVAFX_ENABLED - float *nvafx_segment_buffers[MAX_PREPROC_CHANNELS]; -#endif - /* output data */ struct obs_audio_data output_audio; DARRAY(float) output_data; }; -#ifdef LIBNVAFX_ENABLED -/* global mutex for nvafx load functions since they aren't thread-safe */ -bool nvafx_initializer_mutex_initialized; -pthread_mutex_t nvafx_initializer_mutex; -#endif - /* -------------------------------------------------------- */ #define SUP_MIN -60 @@ -177,25 +150,12 @@ static void noise_suppress_destroy(void *data) { struct noise_suppress_data *ng = data; -#ifdef LIBNVAFX_ENABLED - if (ng->nvafx_enabled) - pthread_mutex_lock(&ng->nvafx_mutex); -#endif - for (size_t i = 0; i < ng->channels; i++) { #ifdef LIBSPEEXDSP_ENABLED speex_preprocess_state_destroy(ng->spx_states[i]); #endif #ifdef LIBRNNOISE_ENABLED rnnoise_destroy(ng->rnn_states[i]); -#endif -#ifdef LIBNVAFX_ENABLED - if (ng->handle[0]) { - if (NvAFX_DestroyEffect(ng->handle[i]) != - NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, "NvAFX_Release() failed"); - } - } #endif deque_free(&ng->input_buffers[i]); deque_free(&ng->output_buffers[i]); @@ -212,175 +172,12 @@ static void noise_suppress_destroy(void *data) audio_resampler_destroy(ng->rnn_resampler_back); } #endif -#ifdef LIBNVAFX_ENABLED - bfree(ng->nvafx_segment_buffers[0]); - - if (ng->nvafx_resampler) { - audio_resampler_destroy(ng->nvafx_resampler); - audio_resampler_destroy(ng->nvafx_resampler_back); - } - bfree(ng->model); - bfree(ng->sdk_path); - bfree((void *)ng->fx); - if (ng->nvafx_enabled) { - if (ng->use_nvafx) - pthread_join(ng->nvafx_thread, NULL); - pthread_mutex_unlock(&ng->nvafx_mutex); - pthread_mutex_destroy(&ng->nvafx_mutex); - } -#endif - bfree(ng->copy_buffers[0]); deque_free(&ng->info_buffer); da_free(ng->output_data); bfree(ng); } -#ifdef LIBNVAFX_ENABLED -static bool nvafx_initialize_internal(void *data) -{ - struct noise_suppress_data *ng = data; - NvAFX_Status err; - - if (!ng->handle[0]) { - ng->sample_rate = NVAFX_SAMPLE_RATE; - for (size_t i = 0; i < ng->channels; i++) { - // Create FX - CUcontext old = {0}; - CUcontext curr = {0}; - if (cuCtxGetCurrent(&old) != CUDA_SUCCESS) { - goto failure; - } - // if initialization was with rnnoise or speex - if (strcmp(ng->fx, S_METHOD_NVAFX_DENOISER) != 0 && - strcmp(ng->fx, S_METHOD_NVAFX_DEREVERB) != 0 && - strcmp(ng->fx, S_METHOD_NVAFX_DEREVERB_DENOISER) != - 0) { - ng->fx = bstrdup(S_METHOD_NVAFX_DENOISER); - } - err = NvAFX_CreateEffect(ng->fx, &ng->handle[i]); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "%s FX creation failed, error %i", - ng->fx, err); - goto failure; - } - if (cuCtxGetCurrent(&curr) != CUDA_SUCCESS) { - goto failure; - } - if (curr != old) { - cuCtxPopCurrent(NULL); - } - // Set sample rate of FX - err = NvAFX_SetU32(ng->handle[i], - NVAFX_PARAM_INPUT_SAMPLE_RATE, - ng->sample_rate); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetU32(Sample Rate: %u) failed, error %i", - ng->sample_rate, err); - goto failure; - } - - // Set intensity of FX - err = NvAFX_SetFloat(ng->handle[i], - NVAFX_PARAM_INTENSITY_RATIO, - ng->intensity_ratio); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", - ng->intensity_ratio, err); - goto failure; - } - - // Set AI models path - err = NvAFX_SetString(ng->handle[i], - NVAFX_PARAM_MODEL_PATH, - ng->model); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetString() failed, error %i", - err); - goto failure; - } - - // Load FX - err = NvAFX_Load(ng->handle[i]); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_Load() failed with error %i", - err); - goto failure; - } - os_atomic_set_bool(&ng->reinit_done, true); - } - } - return true; - -failure: - ng->use_nvafx = false; - return false; -} -#endif - -#ifdef LIBNVAFX_ENABLED -static void *nvafx_initialize(void *data) -{ - struct noise_suppress_data *ng = data; - NvAFX_Status err; - - if (!ng->use_nvafx || !nvafx_loaded) { - return NULL; - } - pthread_mutex_lock(&ng->nvafx_mutex); - pthread_mutex_lock(&nvafx_initializer_mutex); - if (!nvafx_initialize_internal(data)) { - goto failure; - } - if (ng->use_nvafx) { - err = NvAFX_GetU32(ng->handle[0], - NVAFX_PARAM_NUM_INPUT_CHANNELS, - &ng->num_channels); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_GetU32() failed to get the number of channels, error %i", - err); - goto failure; - } - if (ng->num_channels != 1) { - do_log(LOG_ERROR, - "The number of channels is not 1 in the sdk any more ==> update code"); - goto failure; - } - NvAFX_Status err = NvAFX_GetU32( - ng->handle[0], NVAFX_PARAM_NUM_INPUT_SAMPLES_PER_FRAME, - &ng->num_samples_per_frame); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_GetU32() failed to get the number of samples per frame, error %i", - err); - goto failure; - } - if (ng->num_samples_per_frame != NVAFX_FRAME_SIZE) { - do_log(LOG_ERROR, - "The number of samples per frame has changed from 480 (= 10 ms) ==> update code"); - goto failure; - } - } - ng->nvafx_initialized = true; - ng->nvafx_loading = false; - pthread_mutex_unlock(&nvafx_initializer_mutex); - pthread_mutex_unlock(&ng->nvafx_mutex); - return NULL; - -failure: - ng->use_nvafx = false; - pthread_mutex_unlock(&nvafx_initializer_mutex); - pthread_mutex_unlock(&ng->nvafx_mutex); - return NULL; -} -#endif - static inline void alloc_channel(struct noise_suppress_data *ng, uint32_t sample_rate, size_t channel, size_t frames) @@ -421,25 +218,7 @@ static inline enum speaker_layout convert_speaker_layout(uint8_t channels) return SPEAKERS_UNKNOWN; } } -#ifdef LIBNVAFX_ENABLED -static void set_model(void *data, const char *method) -{ - struct noise_suppress_data *ng = data; - const char *file; - if (strcmp(NVAFX_EFFECT_DEREVERB, method) == 0) - file = NVAFX_EFFECT_DEREVERB_MODEL; - else if (strcmp(NVAFX_EFFECT_DEREVERB_DENOISER, method) == 0) - file = NVAFX_EFFECT_DEREVERB_DENOISER_MODEL; - else - file = NVAFX_EFFECT_DENOISER_MODEL; - size_t size = strlen(ng->sdk_path) + strlen(file) + 1; - char *buffer = (char *)bmalloc(size); - - strcpy(buffer, ng->sdk_path); - strcat(buffer, file); - ng->model = buffer; -} -#endif + static void noise_suppress_update(void *data, obs_data_t *s) { struct noise_suppress_data *ng = data; @@ -453,79 +232,14 @@ static void noise_suppress_update(void *data, obs_data_t *s) ng->latency = 1000000000LL / (1000 / BUFFER_SIZE_MSEC); ng->use_rnnoise = strcmp(method, S_METHOD_RNN) == 0; - bool nvafx_requested = - strcmp(method, S_METHOD_NVAFX_DENOISER) == 0 || - strcmp(method, S_METHOD_NVAFX_DEREVERB) == 0 || - strcmp(method, S_METHOD_NVAFX_DEREVERB_DENOISER) == 0; -#ifdef LIBNVAFX_ENABLED - if (nvafx_requested && ng->nvafx_enabled) - set_model(ng, method); - float intensity = (float)obs_data_get_double(s, S_NVAFX_INTENSITY); - if (ng->use_nvafx && ng->nvafx_initialized) { - if (intensity != ng->intensity_ratio && - (strcmp(ng->fx, method) == 0)) { - NvAFX_Status err; - ng->intensity_ratio = intensity; - pthread_mutex_lock(&ng->nvafx_mutex); - for (size_t i = 0; i < ng->channels; i++) { - err = NvAFX_SetFloat( - ng->handle[i], - NVAFX_PARAM_INTENSITY_RATIO, - ng->intensity_ratio); - if (err != NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "NvAFX_SetFloat(Intensity Ratio: %f) failed, error %i", - ng->intensity_ratio, err); - ng->use_nvafx = false; - } - } - pthread_mutex_unlock(&ng->nvafx_mutex); - } - if ((strcmp(ng->fx, method) != 0)) { - pthread_mutex_lock(&ng->nvafx_mutex); - bfree((void *)ng->fx); - ng->fx = bstrdup(method); - ng->intensity_ratio = intensity; - set_model(ng, method); - os_atomic_set_bool(&ng->reinit_done, false); - for (int i = 0; i < (int)ng->channels; i++) { - /* Destroy previous FX */ - if (NvAFX_DestroyEffect(ng->handle[i]) != - NVAFX_STATUS_SUCCESS) { - do_log(LOG_ERROR, - "FX failed to be destroyed."); - ng->use_nvafx = false; - } else - ng->handle[i] = NULL; - } - if (ng->use_nvafx) { - nvafx_initialize_internal(data); - } - pthread_mutex_unlock(&ng->nvafx_mutex); - } - } else { - ng->fx = bstrdup(method); - } - -#endif - ng->use_nvafx = ng->nvafx_enabled && nvafx_requested; - /* Process 10 millisecond segments to keep latency low. */ /* Also RNNoise only supports buffers of this exact size. */ - /* At 48kHz, NVAFX processes 480 samples which corresponds to 10 ms.*/ ng->frames = frames; ng->channels = channels; -#ifdef LIBNVAFX_ENABLED - -#endif /* Ignore if already allocated */ #if defined(LIBSPEEXDSP_ENABLED) - if (!ng->use_rnnoise && !ng->use_nvafx && ng->spx_states[0]) - return; -#endif -#ifdef LIBNVAFX_ENABLED - if (ng->use_nvafx && (ng->nvafx_initialized || ng->nvafx_loading)) + if (!ng->use_rnnoise && ng->spx_states[0]) return; #endif #ifdef LIBRNNOISE_ENABLED @@ -541,10 +255,6 @@ static void noise_suppress_update(void *data, obs_data_t *s) #ifdef LIBRNNOISE_ENABLED ng->rnn_segment_buffers[0] = bmalloc(RNNOISE_FRAME_SIZE * channels * sizeof(float)); -#endif -#ifdef LIBNVAFX_ENABLED - ng->nvafx_segment_buffers[0] = - bmalloc(NVAFX_FRAME_SIZE * channels * sizeof(float)); #endif for (size_t c = 1; c < channels; ++c) { ng->copy_buffers[c] = ng->copy_buffers[c - 1] + frames; @@ -555,20 +265,8 @@ static void noise_suppress_update(void *data, obs_data_t *s) #ifdef LIBRNNOISE_ENABLED ng->rnn_segment_buffers[c] = ng->rnn_segment_buffers[c - 1] + RNNOISE_FRAME_SIZE; -#endif -#ifdef LIBNVAFX_ENABLED - ng->nvafx_segment_buffers[c] = - ng->nvafx_segment_buffers[c - 1] + NVAFX_FRAME_SIZE; #endif } - -#ifdef LIBNVAFX_ENABLED - if (!ng->nvafx_initialized && ng->use_nvafx && !ng->nvafx_loading) { - ng->intensity_ratio = intensity; - ng->nvafx_loading = true; - pthread_create(&ng->nvafx_thread, NULL, nvafx_initialize, ng); - } -#endif for (size_t i = 0; i < channels; i++) alloc_channel(ng, sample_rate, i, frames); @@ -590,168 +288,7 @@ static void noise_suppress_update(void *data, obs_data_t *s) ng->rnn_resampler_back = audio_resampler_create(&src, &dst); } #endif -#ifdef LIBNVAFX_ENABLED - if (sample_rate == NVAFX_SAMPLE_RATE) { - ng->nvafx_resampler = NULL; - ng->nvafx_resampler_back = NULL; - } else { - struct resample_info src, dst; - src.samples_per_sec = sample_rate; - src.format = AUDIO_FORMAT_FLOAT_PLANAR; - src.speakers = convert_speaker_layout((uint8_t)channels); - - dst.samples_per_sec = NVAFX_SAMPLE_RATE; - dst.format = AUDIO_FORMAT_FLOAT_PLANAR; - dst.speakers = convert_speaker_layout((uint8_t)channels); - - ng->nvafx_resampler = audio_resampler_create(&dst, &src); - ng->nvafx_resampler_back = audio_resampler_create(&src, &dst); - } -#endif -} - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4706) -#endif -bool load_nvafx(void) -{ -#ifdef LIBNVAFX_ENABLED - unsigned int version = get_lib_version(); - uint8_t major = (version >> 24) & 0xff; - uint8_t minor = (version >> 16) & 0x00ff; - uint8_t build = (version >> 8) & 0x0000ff; - uint8_t revision = (version >> 0) & 0x000000ff; - if (version) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA AUDIO FX version: %i.%i.%i.%i", - major, minor, build, revision); - if (version < MIN_AFX_SDK_VERSION) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA AUDIO Effects SDK is outdated. Please update both audio & video SDK."); - } - } - if (!load_lib()) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA denoiser disabled, redistributable not found or could not be loaded."); - return false; - } - - nvafx_initializer_mutex_initialized = - pthread_mutex_init(&nvafx_initializer_mutex, NULL) == 0; - -#define LOAD_SYM_FROM_LIB(sym, lib, dll) \ - if (!(sym = (sym##_t)GetProcAddress(lib, #sym))) { \ - DWORD err = GetLastError(); \ - printf("[noise suppress]: Couldn't load " #sym " from " dll \ - ": %lu (0x%lx)", \ - err, err); \ - goto unload_everything; \ - } - -#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_audiofx, "NVAudioEffects.dll") - LOAD_SYM(NvAFX_GetEffectList); - LOAD_SYM(NvAFX_CreateEffect); - LOAD_SYM(NvAFX_CreateChainedEffect); - LOAD_SYM(NvAFX_DestroyEffect); - LOAD_SYM(NvAFX_SetU32); - LOAD_SYM(NvAFX_SetU32List); - LOAD_SYM(NvAFX_SetString); - LOAD_SYM(NvAFX_SetStringList); - LOAD_SYM(NvAFX_SetFloat); - LOAD_SYM(NvAFX_SetFloatList); - LOAD_SYM(NvAFX_GetU32); - LOAD_SYM(NvAFX_GetString); - LOAD_SYM(NvAFX_GetStringList); - LOAD_SYM(NvAFX_GetFloat); - LOAD_SYM(NvAFX_GetFloatList); - LOAD_SYM(NvAFX_Load); - LOAD_SYM(NvAFX_GetSupportedDevices); - LOAD_SYM(NvAFX_Run); - LOAD_SYM(NvAFX_Reset); -#undef LOAD_SYM -#define LOAD_SYM(sym) LOAD_SYM_FROM_LIB(sym, nv_cuda, "nvcuda.dll") - LOAD_SYM(cuCtxGetCurrent); - LOAD_SYM(cuCtxPopCurrent); - LOAD_SYM(cuInit); -#undef LOAD_SYM - - NvAFX_Status err; - CUresult cudaerr; - - NvAFX_Handle h = NULL; - - cudaerr = cuInit(0); - if (cudaerr != CUDA_SUCCESS) { - goto cuda_errors; - } - CUcontext old = {0}; - CUcontext curr = {0}; - cudaerr = cuCtxGetCurrent(&old); - if (cudaerr != CUDA_SUCCESS) { - goto cuda_errors; - } - - err = NvAFX_CreateEffect(NVAFX_EFFECT_DENOISER, &h); - cudaerr = cuCtxGetCurrent(&curr); - if (cudaerr != CUDA_SUCCESS) { - goto cuda_errors; - } - - if (curr != old) { - cudaerr = cuCtxPopCurrent(NULL); - if (cudaerr != CUDA_SUCCESS) - goto cuda_errors; - } - - if (err != NVAFX_STATUS_SUCCESS) { - if (err == NVAFX_STATUS_GPU_UNSUPPORTED) { - blog(LOG_INFO, - "[noise suppress]: NVIDIA AUDIO FX disabled: unsupported GPU"); - } else { - blog(LOG_ERROR, - "[noise suppress]: NVIDIA AUDIO FX disabled, error %i", - err); - } - goto unload_everything; - } - - err = NvAFX_DestroyEffect(h); - if (err != NVAFX_STATUS_SUCCESS) { - blog(LOG_ERROR, - "[noise suppress]: NVIDIA AUDIO FX disabled, error %i", - err); - goto unload_everything; - } - - nvafx_loaded = true; - blog(LOG_INFO, "[noise suppress]: NVIDIA AUDIO FX enabled"); - return true; - -cuda_errors: - blog(LOG_ERROR, - "[noise suppress]: NVIDIA AUDIO FX disabled, CUDA error %i", - cudaerr); -unload_everything: - release_lib(); -#endif - return false; } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#ifdef LIBNVAFX_ENABLED -void unload_nvafx(void) -{ - release_lib(); - - if (nvafx_initializer_mutex_initialized) { - pthread_mutex_destroy(&nvafx_initializer_mutex); - nvafx_initializer_mutex_initialized = false; - } -} -#endif static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter) { @@ -759,25 +296,31 @@ static void *noise_suppress_create(obs_data_t *settings, obs_source_t *filter) bzalloc(sizeof(struct noise_suppress_data)); ng->context = filter; - + ng->nvafx_enabled = false; + ng->nvafx_migrated = false; #ifdef LIBNVAFX_ENABLED - char sdk_path[MAX_PATH]; - - if (!nvafx_get_sdk_path(sdk_path, sizeof(sdk_path))) { - ng->nvafx_enabled = false; - do_log(LOG_ERROR, "NVAFX redist is not installed."); - } else { - size_t size = sizeof(sdk_path) + 1; - ng->sdk_path = bmalloc(size); - strcpy(ng->sdk_path, sdk_path); - ng->nvafx_enabled = true; - ng->nvafx_initialized = false; - ng->nvafx_loading = false; - ng->fx = NULL; - - pthread_mutex_init(&ng->nvafx_mutex, NULL); - - info("NVAFX SDK redist path was found here %s", sdk_path); + ng->migrated_filter = NULL; + // If a NVAFX entry is detected, create a new instance of NVAFX filter. + const char *method = obs_data_get_string(settings, S_METHOD); + ng->nvafx_enabled = strcmp(method, S_METHOD_NVAFX_DENOISER) == 0 || + strcmp(method, S_METHOD_NVAFX_DEREVERB) == 0 || + strcmp(method, S_METHOD_NVAFX_DEREVERB_DENOISER) == + 0; + if (ng->nvafx_enabled) { + const char *str1 = obs_source_get_name(filter); + char *str2 = "_ported"; + char *new_name = + (char *)malloc(1 + strlen(str1) + strlen(str2)); + strcpy(new_name, str1); + strcat(new_name, str2); + obs_data_t *new_settings = obs_data_create(); + obs_data_set_string(new_settings, S_METHOD, method); + double intensity = + obs_data_get_double(settings, S_NVAFX_INTENSITY); + obs_data_set_double(new_settings, S_NVAFX_INTENSITY, intensity); + ng->migrated_filter = obs_source_create( + "nvidia_audiofx_filter", new_name, new_settings, NULL); + obs_data_release(new_settings); } #endif noise_suppress_update(ng, settings); @@ -898,115 +441,10 @@ static inline void process_rnnoise(struct noise_suppress_data *ng) #endif } -static inline void process_nvafx(struct noise_suppress_data *ng) -{ -#ifdef LIBNVAFX_ENABLED - if (nvafx_loaded && ng->use_nvafx && ng->nvafx_initialized) { - /* Resample if necessary */ - if (ng->nvafx_resampler) { - float *output[MAX_PREPROC_CHANNELS]; - uint32_t out_frames; - uint64_t ts_offset; - audio_resampler_resample( - ng->nvafx_resampler, (uint8_t **)output, - &out_frames, &ts_offset, - (const uint8_t **)ng->copy_buffers, - (uint32_t)ng->frames); - - for (size_t i = 0; i < ng->channels; i++) { - for (ssize_t j = 0, k = (ssize_t)out_frames - - NVAFX_FRAME_SIZE; - j < NVAFX_FRAME_SIZE; ++j, ++k) { - if (k >= 0) { - ng->nvafx_segment_buffers[i][j] = - output[i][k]; - } else { - ng->nvafx_segment_buffers[i][j] = - 0; - } - } - } - } else { - for (size_t i = 0; i < ng->channels; i++) { - for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { - ng->nvafx_segment_buffers[i][j] = - ng->copy_buffers[i][j]; - } - } - } - - /* Execute */ - size_t runs = ng->has_mono_src ? 1 : ng->channels; - if (ng->reinit_done) { - pthread_mutex_lock(&ng->nvafx_mutex); - for (size_t i = 0; i < runs; i++) { - NvAFX_Status err = - NvAFX_Run(ng->handle[i], - &ng->nvafx_segment_buffers[i], - &ng->nvafx_segment_buffers[i], - ng->num_samples_per_frame, - ng->num_channels); - if (err != NVAFX_STATUS_SUCCESS) { - if (err == NVAFX_STATUS_FAILED) { - do_log(LOG_DEBUG, - "NvAFX_Run() failed, error NVAFX_STATUS_FAILED.\n" - "This can occur when changing the FX and is not consequential."); - os_atomic_set_bool( - &ng->reinit_done, - false); // stop all processing; this will be reset at new init - } else { - do_log(LOG_ERROR, - "NvAFX_Run() failed, error %i.\n", - err); - } - } - } - pthread_mutex_unlock(&ng->nvafx_mutex); - } - if (ng->has_mono_src) { - memcpy(ng->nvafx_segment_buffers[1], - ng->nvafx_segment_buffers[0], - NVAFX_FRAME_SIZE * sizeof(float)); - } - /* Revert signal level adjustment, resample back if necessary */ - if (ng->nvafx_resampler) { - float *output[MAX_PREPROC_CHANNELS]; - uint32_t out_frames; - uint64_t ts_offset; - audio_resampler_resample( - ng->nvafx_resampler_back, (uint8_t **)output, - &out_frames, &ts_offset, - (const uint8_t **)ng->nvafx_segment_buffers, - NVAFX_FRAME_SIZE); - - for (size_t i = 0; i < ng->channels; i++) { - for (ssize_t j = 0, k = (ssize_t)out_frames - - ng->frames; - j < (ssize_t)ng->frames; ++j, ++k) { - if (k >= 0) { - ng->copy_buffers[i][j] = - output[i][k]; - } else { - ng->copy_buffers[i][j] = 0; - } - } - } - } else { - for (size_t i = 0; i < ng->channels; i++) { - for (size_t j = 0; j < NVAFX_FRAME_SIZE; ++j) { - ng->copy_buffers[i][j] = - ng->nvafx_segment_buffers[i][j]; - } - } - } - } -#else - UNUSED_PARAMETER(ng); -#endif -} - static inline void process(struct noise_suppress_data *ng) { + if (ng->nvafx_enabled) + return; /* Pop from input deque */ for (size_t i = 0; i < ng->channels; i++) deque_pop_front(&ng->input_buffers[i], ng->copy_buffers[i], @@ -1014,10 +452,6 @@ static inline void process(struct noise_suppress_data *ng) if (ng->use_rnnoise) { process_rnnoise(ng); - } else if (ng->use_nvafx) { - if (nvafx_loaded) { - process_nvafx(ng); - } } else { process_speexdsp(ng); } @@ -1058,7 +492,18 @@ noise_suppress_filter_audio(void *data, struct obs_audio_data *audio) obs_source_t *parent = obs_filter_get_parent(ng->context); enum speaker_layout layout = obs_source_get_speaker_layout(parent); ng->has_mono_src = layout == SPEAKERS_MONO && ng->channels == 2; - +#ifdef LIBNVAFX_ENABLED + /* Migrate nvafx to new filter. */ + if (ng->nvafx_enabled) { + if (!ng->nvafx_migrated) { + obs_source_filter_add(parent, ng->migrated_filter); + obs_source_set_enabled(ng->migrated_filter, true); + obs_source_filter_remove(parent, ng->context); + ng->nvafx_migrated = true; + } + return audio; + } +#endif #ifdef LIBSPEEXDSP_ENABLED if (!ng->spx_states[0]) return audio; @@ -1200,6 +645,9 @@ static obs_properties_t *noise_suppress_properties(void *data) obs_property_list_add_string( method, TEXT_METHOD_NVAFX_DEREVERB_DENOISER, S_METHOD_NVAFX_DEREVERB_DENOISER); + obs_property_list_item_disable(method, 2, true); + obs_property_list_item_disable(method, 3, true); + obs_property_list_item_disable(method, 4, true); } #endif @@ -1219,15 +667,14 @@ static obs_properties_t *noise_suppress_properties(void *data) obs_properties_add_float_slider(ppts, S_NVAFX_INTENSITY, TEXT_NVAFX_INTENSITY, 0.0f, 1.0f, 0.01f); - } - unsigned int version = get_lib_version(); - if (version && version < MIN_AFX_SDK_VERSION) { - obs_property_t *warning = obs_properties_add_text( - ppts, "deprecation", NULL, OBS_TEXT_INFO); - obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING); + obs_property_t *warning2 = obs_properties_add_text( + ppts, "deprecation2", NULL, OBS_TEXT_INFO); + obs_property_text_set_info_type(warning2, + OBS_TEXT_INFO_WARNING); obs_property_set_long_description( - warning, TEXT_METHOD_NVAFX_DEPRECATION); + warning2, TEXT_METHOD_NVAFX_DEPRECATION2); } + #if defined(LIBRNNOISE_ENABLED) && defined(LIBSPEEXDSP_ENABLED) if (!nvafx_loaded) { obs_property_list_item_disable(method, 2, true); diff --git a/plugins/obs-filters/obs-filters.c b/plugins/obs-filters/obs-filters.c index 7966f6788f9c33..3d4244895a616f 100644 --- a/plugins/obs-filters/obs-filters.c +++ b/plugins/obs-filters/obs-filters.c @@ -29,8 +29,6 @@ extern struct obs_source_info async_delay_filter; #if defined(HAS_NOISEREDUCTION) extern struct obs_source_info noise_suppress_filter; extern struct obs_source_info noise_suppress_filter_v2; -extern bool load_nvafx(void); -extern void unload_nvafx(void); #endif extern struct obs_source_info invert_polarity_filter; extern struct obs_source_info noise_gate_filter; @@ -40,11 +38,6 @@ extern struct obs_source_info expander_filter; extern struct obs_source_info upward_compressor_filter; extern struct obs_source_info luma_key_filter; extern struct obs_source_info luma_key_filter_v2; -#ifdef LIBNVVFX_ENABLED -extern struct obs_source_info nvidia_greenscreen_filter_info; -extern bool load_nvvfx(void); -extern void unload_nvvfx(void); -#endif bool obs_module_load(void) { @@ -68,10 +61,6 @@ bool obs_module_load(void) obs_register_source(&chroma_key_filter_v2); obs_register_source(&async_delay_filter); #if defined(HAS_NOISEREDUCTION) -#ifdef LIBNVAFX_ENABLED - /* load nvidia audio fx dll */ - load_nvafx(); -#endif obs_register_source(&noise_suppress_filter); obs_register_source(&noise_suppress_filter_v2); #endif @@ -83,22 +72,5 @@ bool obs_module_load(void) obs_register_source(&upward_compressor_filter); obs_register_source(&luma_key_filter); obs_register_source(&luma_key_filter_v2); -#ifdef LIBNVVFX_ENABLED - obs_enter_graphics(); - const bool direct3d = gs_get_device_type() == GS_DEVICE_DIRECT3D_11; - obs_leave_graphics(); - if (direct3d && load_nvvfx()) - obs_register_source(&nvidia_greenscreen_filter_info); -#endif return true; } - -void obs_module_unload(void) -{ -#ifdef LIBNVAFX_ENABLED - unload_nvafx(); -#endif -#ifdef LIBNVVFX_ENABLED - unload_nvvfx(); -#endif -} From af555b9372a70d6c83370bf6c2f08c8b6100b915 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 5 Apr 2024 05:23:50 +0200 Subject: [PATCH 0374/1073] obs-ffmpeg: Remove native/disable FFmpeg NVENC --- plugins/obs-ffmpeg/CMakeLists.txt | 14 +- plugins/obs-ffmpeg/cmake/dependencies.cmake | 26 - plugins/obs-ffmpeg/cmake/legacy.cmake | 29 +- plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 43 +- plugins/obs-ffmpeg/obs-ffmpeg.c | 54 +- plugins/obs-ffmpeg/obs-nvenc-helpers.c | 489 ---- .../obs-ffmpeg/obs-nvenc-test/CMakeLists.txt | 14 - .../obs-nvenc-test/cmake/legacy.cmake | 13 - .../obs-nvenc-test/obs-nvenc-test.c | 238 -- plugins/obs-ffmpeg/obs-nvenc-ver.h | 7 - plugins/obs-ffmpeg/obs-nvenc.c | 2485 ----------------- plugins/obs-ffmpeg/obs-nvenc.h | 71 - 12 files changed, 39 insertions(+), 3444 deletions(-) delete mode 100644 plugins/obs-ffmpeg/obs-nvenc-helpers.c delete mode 100644 plugins/obs-ffmpeg/obs-nvenc-test/CMakeLists.txt delete mode 100644 plugins/obs-ffmpeg/obs-nvenc-test/cmake/legacy.cmake delete mode 100644 plugins/obs-ffmpeg/obs-nvenc-test/obs-nvenc-test.c delete mode 100644 plugins/obs-ffmpeg/obs-nvenc-ver.h delete mode 100644 plugins/obs-ffmpeg/obs-nvenc.c delete mode 100644 plugins/obs-ffmpeg/obs-nvenc.h diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 4a928c82cf5208..906f0bca5b70fc 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -5,8 +5,8 @@ legacy_check() option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF) option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON) -if(OS_LINUX) - option(ENABLE_NATIVE_NVENC "Use native NVENC implementation" ON) +if(OS_LINUX OR OS_WINDOWS) + option(ENABLE_FFMPEG_NVENC "Enable legacy FFmpeg NVENC encoder" OFF) endif() include(cmake/dependencies.cmake) @@ -20,6 +20,7 @@ target_sources( obs-ffmpeg PRIVATE # cmake-format: sortable $<$:obs-ffmpeg-logging.c> + $<$:obs-ffmpeg-nvenc.c> $<$:obs-ffmpeg-mpegts.c> $<$:obs-ffmpeg-rist.h> $<$:obs-ffmpeg-srt.h> @@ -36,7 +37,6 @@ target_sources( obs-ffmpeg-hls-mux.c obs-ffmpeg-mux.c obs-ffmpeg-mux.h - obs-ffmpeg-nvenc.c obs-ffmpeg-output.c obs-ffmpeg-output.h obs-ffmpeg-source.c @@ -44,8 +44,11 @@ target_sources( obs-ffmpeg.c) target_compile_options(obs-ffmpeg PRIVATE $<$:-Wno-shorten-64-to-32>) -target_compile_definitions(obs-ffmpeg PRIVATE $<$:ENABLE_FFMPEG_LOGGING> - $<$:NEW_MPEGTS_OUTPUT>) +target_compile_definitions( + obs-ffmpeg + PRIVATE $<$:ENABLE_FFMPEG_LOGGING> + $<$:ENABLE_FFMPEG_NVENC> + $<$:NEW_MPEGTS_OUTPUT>) target_link_libraries( obs-ffmpeg @@ -59,7 +62,6 @@ target_link_libraries( FFmpeg::avutil FFmpeg::swscale FFmpeg::swresample - $ $<$:OBS::w32-pthreads> $<$:AMF::AMF> $<$:ws2_32> diff --git a/plugins/obs-ffmpeg/cmake/dependencies.cmake b/plugins/obs-ffmpeg/cmake/dependencies.cmake index 6e809bbc273960..da110e110b1a4f 100644 --- a/plugins/obs-ffmpeg/cmake/dependencies.cmake +++ b/plugins/obs-ffmpeg/cmake/dependencies.cmake @@ -36,32 +36,6 @@ elseif( find_package(Libdrm REQUIRED) endif() -if(OS_WINDOWS OR (OS_LINUX AND ENABLE_NATIVE_NVENC)) - add_library(obs-nvenc-version INTERFACE) - add_library(OBS::obs-nvenc-version ALIAS obs-nvenc-version) - target_sources(obs-nvenc-version INTERFACE obs-nvenc-ver.h) - target_include_directories(obs-nvenc-version INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - find_package(FFnvcodec 12.0.0.0...<12.2.0.0 REQUIRED) - - if(OS_LINUX AND NOT TARGET OBS::glad) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/glad" "${CMAKE_BINARY_DIR}/deps/glad") - endif() - - add_library(obs-nvenc-native INTERFACE) - add_library(OBS::obs-nvenc-native ALIAS obs-nvenc-native) - target_sources(obs-nvenc-native INTERFACE obs-nvenc-helpers.c obs-nvenc.c obs-nvenc.h) - target_compile_definitions(obs-nvenc-native INTERFACE $<$:NVCODEC_AVAILABLE>) - target_include_directories(obs-nvenc-native INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - target_link_libraries(obs-nvenc-native INTERFACE FFnvcodec::FFnvcodec OBS::obs-nvenc-version - $<$:OBS::glad>) - - if(OS_WINDOWS) - add_subdirectory(obs-nvenc-test) - endif() -endif() - if(ENABLE_NEW_MPEGTS_OUTPUT) find_package(Librist QUIET) find_package(Libsrt QUIET) diff --git a/plugins/obs-ffmpeg/cmake/legacy.cmake b/plugins/obs-ffmpeg/cmake/legacy.cmake index 04866b90ab0b92..6bc1b6b2f090b7 100644 --- a/plugins/obs-ffmpeg/cmake/legacy.cmake +++ b/plugins/obs-ffmpeg/cmake/legacy.cmake @@ -2,7 +2,10 @@ project(obs-ffmpeg) option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF) option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON) -option(ENABLE_NATIVE_NVENC "Use native NVENC implementation" ON) + +if(OS_LINUX OR OS_WINDOWS) + option(ENABLE_FFMPEG_NVENC "Enables legacy FFmpeg NVENC encoder" OFF) +endif() find_package( FFmpeg REQUIRED @@ -49,7 +52,6 @@ target_sources( obs-ffmpeg-video-encoders.c obs-ffmpeg-audio-encoders.c obs-ffmpeg-av1.c - obs-ffmpeg-nvenc.c obs-ffmpeg-output.c obs-ffmpeg-output.h obs-ffmpeg-mux.c @@ -85,6 +87,11 @@ if(ENABLE_NEW_MPEGTS_OUTPUT) target_compile_definitions(obs-ffmpeg PRIVATE NEW_MPEGTS_OUTPUT) endif() +if(ENABLE_FFMPEG_NVENC) + target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-nvenc.c) + target_compile_definitions(obs-ffmpeg PRIVATE ENABLE_FFMPEG_NVENC) +endif() + if(ENABLE_FFMPEG_LOGGING) target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-logging.c) endif() @@ -96,7 +103,6 @@ if(OS_WINDOWS) find_package(FFnvcodec 12 REQUIRED) add_subdirectory(obs-amf-test) - add_subdirectory(obs-nvenc-test) if(MSVC) target_link_libraries(obs-ffmpeg PRIVATE OBS::w32-pthreads) @@ -106,15 +112,7 @@ if(OS_WINDOWS) set(MODULE_DESCRIPTION "OBS FFmpeg module") configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-ffmpeg.rc) - target_sources( - obs-ffmpeg - PRIVATE texture-amf.cpp - texture-amf-opts.hpp - obs-nvenc.c - obs-nvenc.h - obs-nvenc-helpers.c - obs-nvenc-ver.h - obs-ffmpeg.rc) + target_sources(obs-ffmpeg PRIVATE texture-amf.cpp texture-amf-opts.hpp obs-ffmpeg.rc) elseif(OS_POSIX AND NOT OS_MACOS) find_package(Libva REQUIRED) @@ -122,13 +120,6 @@ elseif(OS_POSIX AND NOT OS_MACOS) find_package(Libdrm REQUIRED) target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-vaapi.c vaapi-utils.c vaapi-utils.h) target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI Libdrm::Libdrm) - - if(ENABLE_NATIVE_NVENC) - find_package(FFnvcodec 12.0.0.0...<12.2.0.0 REQUIRED) - target_sources(obs-ffmpeg PRIVATE obs-nvenc.c obs-nvenc.h obs-nvenc-helpers.c obs-nvenc-ver.h) - target_link_libraries(obs-ffmpeg PRIVATE FFnvcodec::FFnvcodec OBS::obsglad) - target_compile_definitions(obs-ffmpeg PRIVATE NVCODEC_AVAILABLE) - endif() endif() setup_plugin_target(obs-ffmpeg) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index 3740cbc1a2a2de..ba151b834b4df8 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -494,7 +494,7 @@ static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, return true; } -obs_properties_t *nvenc_properties_internal(enum codec_type codec, bool ffmpeg) +obs_properties_t *nvenc_properties_internal(enum codec_type codec) { obs_properties_t *props = obs_properties_create(); obs_property_t *p; @@ -587,15 +587,6 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec, bool ffmpeg) } #undef add_profile - if (!ffmpeg) { - p = obs_properties_add_bool(props, "lookahead", - obs_module_text("NVENC.LookAhead")); - obs_property_set_long_description( - p, obs_module_text("NVENC.LookAhead.ToolTip")); - p = obs_properties_add_bool(props, "repeat_headers", - "repeat_headers"); - obs_property_set_visible(p, false); - } p = obs_properties_add_bool( props, "psycho_aq", obs_module_text("NVENC.PsychoVisualTuning")); @@ -610,37 +601,17 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec, bool ffmpeg) return props; } -obs_properties_t *h264_nvenc_properties(void *unused) -{ - UNUSED_PARAMETER(unused); - return nvenc_properties_internal(CODEC_H264, false); -} - -#ifdef ENABLE_HEVC -obs_properties_t *hevc_nvenc_properties(void *unused) -{ - UNUSED_PARAMETER(unused); - return nvenc_properties_internal(CODEC_HEVC, false); -} -#endif - -obs_properties_t *av1_nvenc_properties(void *unused) -{ - UNUSED_PARAMETER(unused); - return nvenc_properties_internal(CODEC_AV1, false); -} - obs_properties_t *h264_nvenc_properties_ffmpeg(void *unused) { UNUSED_PARAMETER(unused); - return nvenc_properties_internal(CODEC_H264, true); + return nvenc_properties_internal(CODEC_H264); } #ifdef ENABLE_HEVC obs_properties_t *hevc_nvenc_properties_ffmpeg(void *unused) { UNUSED_PARAMETER(unused); - return nvenc_properties_internal(CODEC_HEVC, true); + return nvenc_properties_internal(CODEC_HEVC); } #endif @@ -676,11 +647,7 @@ struct obs_encoder_info h264_nvenc_encoder_info = { .get_extra_data = nvenc_extra_data, .get_sei_data = nvenc_sei_data, .get_video_info = nvenc_video_info, -#if defined(_WIN32) || defined(NVCODEC_AVAILABLE) - .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL, -#else .caps = OBS_ENCODER_CAP_DYN_BITRATE, -#endif }; #ifdef ENABLE_HEVC @@ -698,10 +665,6 @@ struct obs_encoder_info hevc_nvenc_encoder_info = { .get_extra_data = nvenc_extra_data, .get_sei_data = nvenc_sei_data, .get_video_info = nvenc_video_info, -#if defined(_WIN32) || defined(NVCODEC_AVAILABLE) - .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_INTERNAL, -#else .caps = OBS_ENCODER_CAP_DYN_BITRATE, -#endif }; #endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c index c20281f631986b..04c715e420fc42 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg.c @@ -5,14 +5,8 @@ #include #ifdef _WIN32 +#define INITGUID #include -#include -#endif - -#if defined(_WIN32) || defined(NVCODEC_AVAILABLE) -#include "obs-nvenc.h" - -#define OBS_NVENC_AVAILABLE #endif #if !defined(_WIN32) && !defined(__APPLE__) @@ -41,10 +35,12 @@ extern struct obs_encoder_info pcm24_encoder_info; extern struct obs_encoder_info pcm32_encoder_info; extern struct obs_encoder_info alac_encoder_info; extern struct obs_encoder_info flac_encoder_info; +#ifdef ENABLE_FFMPEG_NVENC extern struct obs_encoder_info h264_nvenc_encoder_info; #ifdef ENABLE_HEVC extern struct obs_encoder_info hevc_nvenc_encoder_info; #endif +#endif extern struct obs_encoder_info svt_av1_encoder_info; extern struct obs_encoder_info aom_av1_encoder_info; @@ -59,11 +55,10 @@ extern struct obs_encoder_info hevc_vaapi_encoder_tex_info; #endif #endif -#ifndef __APPLE__ +#ifdef ENABLE_FFMPEG_NVENC static const char *nvenc_check_name = "nvenc_check"; -#if defined(_WIN32) || defined(__linux__) static const int blacklisted_adapters[] = { 0x1298, // GK208M [GeForce GT 720M] 0x1140, // GF117M [GeForce 610M/710M/810M/820M / GT 620M/625M/630M/720M] @@ -127,9 +122,8 @@ static bool is_blacklisted(const int device_id) return false; } -#endif -#if defined(_WIN32) +#ifdef _WIN32 typedef HRESULT(WINAPI *create_dxgi_proc)(const IID *, IDXGIFactory1 **); static bool nvenc_device_available(void) @@ -243,10 +237,6 @@ static bool nvenc_device_available(void) } #endif -#ifdef OBS_NVENC_AVAILABLE -extern bool load_nvenc_lib(void); -#endif - static bool nvenc_codec_exists(const char *name, const char *fallback) { const AVCodec *nvenc = avcodec_find_encoder_by_name(name); @@ -256,7 +246,7 @@ static bool nvenc_codec_exists(const char *name, const char *fallback) return nvenc != NULL; } -static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1) +static bool nvenc_supported(bool *out_h264, bool *out_hevc) { profile_start(nvenc_check_name); @@ -267,13 +257,18 @@ static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1) const bool hevc = false; #endif - bool av1 = false; - bool success = h264 || hevc; if (success) { -#ifdef OBS_NVENC_AVAILABLE - success = nvenc_device_available() && load_nvenc_lib(); - av1 = success && (get_nvenc_ver() >= ((12 << 4) | 0)); +#ifdef _WIN32 + success = nvenc_device_available(); +#elif defined(__linux__) + success = nvenc_device_available(); + if (success) { + void *const lib = os_dlopen("libnvidia-encode.so.1"); + success = lib != NULL; + if (success) + os_dlclose(lib); + } #else void *const lib = os_dlopen("libnvidia-encode.so.1"); success = lib != NULL; @@ -284,7 +279,6 @@ static bool nvenc_supported(bool *out_h264, bool *out_hevc, bool *out_av1) if (success) { *out_h264 = h264; *out_hevc = hevc; - *out_av1 = av1; } } @@ -334,11 +328,6 @@ static bool hevc_vaapi_supported(void) #endif #endif -#ifdef OBS_NVENC_AVAILABLE -extern void obs_nvenc_load(bool h264, bool hevc, bool av1); -extern void obs_nvenc_unload(void); -#endif - #ifdef _WIN32 extern void amf_load(void); extern void amf_unload(void); @@ -375,16 +364,12 @@ bool obs_module_load(void) obs_register_encoder(&pcm32_encoder_info); obs_register_encoder(&alac_encoder_info); obs_register_encoder(&flac_encoder_info); -#ifndef __APPLE__ +#ifdef ENABLE_FFMPEG_NVENC bool h264 = false; bool hevc = false; - bool av1 = false; - if (nvenc_supported(&h264, &hevc, &av1)) { + if (nvenc_supported(&h264, &hevc)) { blog(LOG_INFO, "NVENC supported"); -#ifdef OBS_NVENC_AVAILABLE - obs_nvenc_load(h264, hevc, av1); -#endif if (h264) obs_register_encoder(&h264_nvenc_encoder_info); #ifdef ENABLE_HEVC @@ -447,7 +432,4 @@ void obs_module_unload(void) #ifdef _WIN32 amf_unload(); #endif -#ifdef OBS_NVENC_AVAILABLE - obs_nvenc_unload(); -#endif } diff --git a/plugins/obs-ffmpeg/obs-nvenc-helpers.c b/plugins/obs-ffmpeg/obs-nvenc-helpers.c deleted file mode 100644 index e5b3f369a0e03b..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc-helpers.c +++ /dev/null @@ -1,489 +0,0 @@ -#include "obs-nvenc.h" -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -static void *nvenc_lib = NULL; -static void *cuda_lib = NULL; -static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; -NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER}; -NV_CREATE_INSTANCE_FUNC nv_create_instance = NULL; -CudaFunctions *cu = NULL; - -#define error(format, ...) blog(LOG_ERROR, "[obs-nvenc] " format, ##__VA_ARGS__) - -bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...) -{ - UNUSED_PARAMETER(session); - - struct dstr message = {0}; - struct dstr error_message = {0}; - - va_list args; - va_start(args, format); - dstr_vprintf(&message, format, args); - va_end(args); - - dstr_printf(&error_message, "NVENC Error: %s", message.array); - obs_encoder_set_last_error(encoder, error_message.array); - error("%s", error_message.array); - - dstr_free(&error_message); - dstr_free(&message); - - return true; -} - -bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err, - const char *func, const char *call) -{ - struct dstr error_message = {0}; - const char *nvenc_error = NULL; - - if (err == NV_ENC_SUCCESS) - return false; - - if (session) { - nvenc_error = nv.nvEncGetLastErrorString(session); - if (nvenc_error) { - // Some NVENC errors begin with :: which looks - // odd to users. Strip it off. - while (*nvenc_error == ':') - nvenc_error++; - } - } - - switch (err) { - case NV_ENC_ERR_OUT_OF_MEMORY: - obs_encoder_set_last_error( - encoder, obs_module_text("NVENC.TooManySessions")); - break; - - case NV_ENC_ERR_NO_ENCODE_DEVICE: - case NV_ENC_ERR_UNSUPPORTED_DEVICE: - obs_encoder_set_last_error( - encoder, obs_module_text("NVENC.UnsupportedDevice")); - break; - - case NV_ENC_ERR_INVALID_VERSION: - obs_encoder_set_last_error( - encoder, obs_module_text("NVENC.OutdatedDriver")); - break; - - default: - if (nvenc_error && *nvenc_error) { - dstr_printf(&error_message, "NVENC Error: %s (%s)", - nvenc_error, nv_error_name(err)); - } else { - - dstr_printf(&error_message, - "NVENC Error: %s: %s failed: %d (%s)", func, - call, (int)err, nv_error_name(err)); - } - obs_encoder_set_last_error(encoder, error_message.array); - dstr_free(&error_message); - break; - } - - if (nvenc_error && *nvenc_error) { - error("%s: %s failed: %d (%s): %s", func, call, (int)err, - nv_error_name(err), nvenc_error); - } else { - error("%s: %s failed: %d (%s)", func, call, (int)err, - nv_error_name(err)); - } - return true; -} - -#define NV_FAILED(e, x) nv_failed2(e, NULL, x, __FUNCTION__, #x) - -bool load_nvenc_lib(void) -{ -#ifdef _WIN32 - nvenc_lib = os_dlopen("nvEncodeAPI64.dll"); -#else - nvenc_lib = os_dlopen("libnvidia-encode.so.1"); -#endif - return nvenc_lib != NULL; -} - -static void *load_nv_func(const char *func) -{ - void *func_ptr = os_dlsym(nvenc_lib, func); - if (!func_ptr) { - error("Could not load function: %s", func); - } - return func_ptr; -} - -bool load_cuda_lib(void) -{ -#ifdef _WIN32 - cuda_lib = os_dlopen("nvcuda.dll"); -#else - cuda_lib = os_dlopen("libcuda.so.1"); -#endif - return cuda_lib != NULL; -} - -static void *load_cuda_func(const char *func) -{ - void *func_ptr = os_dlsym(cuda_lib, func); - if (!func_ptr) { - error("Could not load function: %s", func); - } - return func_ptr; -} - -typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *); - -uint32_t get_nvenc_ver(void) -{ - static NV_MAX_VER_FUNC nv_max_ver = NULL; - static bool failed = false; - static uint32_t ver = 0; - - if (!failed && ver) - return ver; - - if (!nv_max_ver) { - if (failed) - return 0; - - nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func( - "NvEncodeAPIGetMaxSupportedVersion"); - if (!nv_max_ver) { - failed = true; - return 0; - } - } - - if (nv_max_ver(&ver) != NV_ENC_SUCCESS) { - return 0; - } - return ver; -} - -const char *nv_error_name(NVENCSTATUS err) -{ -#define RETURN_CASE(x) \ - case x: \ - return #x - - switch (err) { - RETURN_CASE(NV_ENC_SUCCESS); - RETURN_CASE(NV_ENC_ERR_NO_ENCODE_DEVICE); - RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_DEVICE); - RETURN_CASE(NV_ENC_ERR_INVALID_ENCODERDEVICE); - RETURN_CASE(NV_ENC_ERR_INVALID_DEVICE); - RETURN_CASE(NV_ENC_ERR_DEVICE_NOT_EXIST); - RETURN_CASE(NV_ENC_ERR_INVALID_PTR); - RETURN_CASE(NV_ENC_ERR_INVALID_EVENT); - RETURN_CASE(NV_ENC_ERR_INVALID_PARAM); - RETURN_CASE(NV_ENC_ERR_INVALID_CALL); - RETURN_CASE(NV_ENC_ERR_OUT_OF_MEMORY); - RETURN_CASE(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); - RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_PARAM); - RETURN_CASE(NV_ENC_ERR_LOCK_BUSY); - RETURN_CASE(NV_ENC_ERR_NOT_ENOUGH_BUFFER); - RETURN_CASE(NV_ENC_ERR_INVALID_VERSION); - RETURN_CASE(NV_ENC_ERR_MAP_FAILED); - RETURN_CASE(NV_ENC_ERR_NEED_MORE_INPUT); - RETURN_CASE(NV_ENC_ERR_ENCODER_BUSY); - RETURN_CASE(NV_ENC_ERR_EVENT_NOT_REGISTERD); - RETURN_CASE(NV_ENC_ERR_GENERIC); - RETURN_CASE(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); - RETURN_CASE(NV_ENC_ERR_UNIMPLEMENTED); - RETURN_CASE(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); - RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); - RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_MAPPED); - } -#undef RETURN_CASE - - return "Unknown Error"; -} - -static inline bool init_nvenc_internal(obs_encoder_t *encoder) -{ - static bool initialized = false; - static bool success = false; - - if (initialized) - return success; - initialized = true; - - uint32_t ver = get_nvenc_ver(); - if (ver == 0) { - obs_encoder_set_last_error( - encoder, - "Missing NvEncodeAPIGetMaxSupportedVersion, check " - "your video card drivers are up to date."); - return false; - } - - uint32_t supported_ver = (NVENC_COMPAT_MAJOR_VER << 4) | - NVENC_COMPAT_MINOR_VER; - if (supported_ver > ver) { - obs_encoder_set_last_error( - encoder, obs_module_text("NVENC.OutdatedDriver")); - - error("Current driver version does not support this NVENC " - "version, please upgrade your driver"); - return false; - } - - nv_create_instance = (NV_CREATE_INSTANCE_FUNC)load_nv_func( - "NvEncodeAPICreateInstance"); - if (!nv_create_instance) { - obs_encoder_set_last_error( - encoder, "Missing NvEncodeAPICreateInstance, check " - "your video card drivers are up to date."); - return false; - } - - if (NV_FAILED(encoder, nv_create_instance(&nv))) { - return false; - } - - success = true; - return true; -} - -typedef struct cuda_function { - ptrdiff_t offset; - const char *name; -} cuda_function; - -static const cuda_function cuda_functions[] = { - {offsetof(CudaFunctions, cuInit), "cuInit"}, - - {offsetof(CudaFunctions, cuDeviceGetCount), "cuDeviceGetCount"}, - {offsetof(CudaFunctions, cuDeviceGet), "cuDeviceGet"}, - {offsetof(CudaFunctions, cuDeviceGetAttribute), "cuDeviceGetAttribute"}, - - {offsetof(CudaFunctions, cuCtxCreate), "cuCtxCreate_v2"}, - {offsetof(CudaFunctions, cuCtxDestroy), "cuCtxDestroy_v2"}, - {offsetof(CudaFunctions, cuCtxPushCurrent), "cuCtxPushCurrent_v2"}, - {offsetof(CudaFunctions, cuCtxPopCurrent), "cuCtxPopCurrent_v2"}, - - {offsetof(CudaFunctions, cuArray3DCreate), "cuArray3DCreate_v2"}, - {offsetof(CudaFunctions, cuArrayDestroy), "cuArrayDestroy"}, - {offsetof(CudaFunctions, cuMemcpy2D), "cuMemcpy2D_v2"}, - - {offsetof(CudaFunctions, cuGetErrorName), "cuGetErrorName"}, - {offsetof(CudaFunctions, cuGetErrorString), "cuGetErrorString"}, - - {offsetof(CudaFunctions, cuMemHostRegister), "cuMemHostRegister_v2"}, - {offsetof(CudaFunctions, cuMemHostUnregister), "cuMemHostUnregister"}, - -#ifndef _WIN32 - {offsetof(CudaFunctions, cuGLGetDevices), "cuGLGetDevices_v2"}, - {offsetof(CudaFunctions, cuGraphicsGLRegisterImage), - "cuGraphicsGLRegisterImage"}, - {offsetof(CudaFunctions, cuGraphicsUnregisterResource), - "cuGraphicsUnregisterResource"}, - {offsetof(CudaFunctions, cuGraphicsMapResources), - "cuGraphicsMapResources"}, - {offsetof(CudaFunctions, cuGraphicsUnmapResources), - "cuGraphicsUnmapResources"}, - {offsetof(CudaFunctions, cuGraphicsSubResourceGetMappedArray), - "cuGraphicsSubResourceGetMappedArray"}, -#endif -}; - -static const size_t num_cuda_funcs = - sizeof(cuda_functions) / sizeof(cuda_function); - -static bool init_cuda_internal(obs_encoder_t *encoder) -{ - static bool initialized = false; - static bool success = false; - - if (initialized) - return success; - initialized = true; - - if (!load_cuda_lib()) { - obs_encoder_set_last_error(encoder, - "Loading CUDA library failed."); - return false; - } - - cu = bzalloc(sizeof(CudaFunctions)); - - for (size_t idx = 0; idx < num_cuda_funcs; idx++) { - const cuda_function func = cuda_functions[idx]; - void *fptr = load_cuda_func(func.name); - - if (!fptr) { - error("Failed to find CUDA function: %s", func.name); - obs_encoder_set_last_error( - encoder, "Loading CUDA functions failed."); - return false; - } - - *(uintptr_t *)((uintptr_t)cu + func.offset) = (uintptr_t)fptr; - } - - success = true; - return true; -} - -bool cuda_get_error_desc(CUresult res, const char **name, const char **desc) -{ - if (cu->cuGetErrorName(res, name) != CUDA_SUCCESS || - cu->cuGetErrorString(res, desc) != CUDA_SUCCESS) - return false; - - return true; -} - -bool init_nvenc(obs_encoder_t *encoder) -{ - bool success; - - pthread_mutex_lock(&init_mutex); - success = init_nvenc_internal(encoder); - pthread_mutex_unlock(&init_mutex); - - return success; -} - -bool init_cuda(obs_encoder_t *encoder) -{ - bool success; - - pthread_mutex_lock(&init_mutex); - success = init_cuda_internal(encoder); - pthread_mutex_unlock(&init_mutex); - - return success; -} - -extern struct obs_encoder_info h264_nvenc_info; -#ifdef ENABLE_HEVC -extern struct obs_encoder_info hevc_nvenc_info; -#endif -extern struct obs_encoder_info av1_nvenc_info; - -extern struct obs_encoder_info h264_nvenc_soft_info; -#ifdef ENABLE_HEVC -extern struct obs_encoder_info hevc_nvenc_soft_info; -#endif -extern struct obs_encoder_info av1_nvenc_soft_info; - -#ifdef _WIN32 -static bool enum_luids(void *param, uint32_t idx, uint64_t luid) -{ - struct dstr *cmd = param; - dstr_catf(cmd, " %llX", luid); - UNUSED_PARAMETER(idx); - return true; -} - -static bool av1_supported(void) -{ - char *test_exe = os_get_executable_path_ptr("obs-nvenc-test.exe"); - struct dstr cmd = {0}; - struct dstr caps_str = {0}; - bool av1_supported = false; - config_t *config = NULL; - - dstr_init_move_array(&cmd, test_exe); - dstr_insert_ch(&cmd, 0, '\"'); - dstr_cat(&cmd, "\""); - - enum_graphics_device_luids(enum_luids, &cmd); - - os_process_pipe_t *pp = os_process_pipe_create(cmd.array, "r"); - if (!pp) { - blog(LOG_WARNING, "[NVENC] Failed to launch the NVENC " - "test process I guess"); - goto fail; - } - - for (;;) { - char data[2048]; - size_t len = - os_process_pipe_read(pp, (uint8_t *)data, sizeof(data)); - if (!len) - break; - - dstr_ncat(&caps_str, data, len); - } - - os_process_pipe_destroy(pp); - - if (dstr_is_empty(&caps_str)) { - blog(LOG_WARNING, - "[NVENC] Seems the NVENC test subprocess crashed. " - "Better there than here I guess. Let's just " - "skip NVENC AV1 detection then I suppose."); - goto fail; - } - - if (config_open_string(&config, caps_str.array) != 0) { - blog(LOG_WARNING, "[NVENC] Failed to open config string"); - goto fail; - } - - const char *error = config_get_string(config, "error", "string"); - if (error) { - blog(LOG_WARNING, "[NVENC] AV1 test process failed: %s", error); - goto fail; - } - - uint32_t adapter_count = (uint32_t)config_num_sections(config); - bool avc_supported = false; - bool hevc_supported = false; - - /* for now, just check AV1 support on device 0 */ - av1_supported = config_get_bool(config, "0", "supports_av1"); - -fail: - if (config) - config_close(config); - dstr_free(&caps_str); - dstr_free(&cmd); - - return av1_supported; -} -#else -bool av1_supported() -{ - return get_nvenc_ver() >= ((12 << 4) | 0); -} -#endif - -void obs_nvenc_load(bool h264, bool hevc, bool av1) -{ - pthread_mutex_init(&init_mutex, NULL); - if (h264) { - obs_register_encoder(&h264_nvenc_info); - obs_register_encoder(&h264_nvenc_soft_info); - } -#ifdef ENABLE_HEVC - if (hevc) { - obs_register_encoder(&hevc_nvenc_info); - obs_register_encoder(&hevc_nvenc_soft_info); - } -#endif - if (av1 && av1_supported()) { - obs_register_encoder(&av1_nvenc_info); - obs_register_encoder(&av1_nvenc_soft_info); - } else { - blog(LOG_WARNING, "[NVENC] AV1 is not supported"); - } -} - -void obs_nvenc_unload(void) -{ - bfree(cu); - pthread_mutex_destroy(&init_mutex); -} diff --git a/plugins/obs-ffmpeg/obs-nvenc-test/CMakeLists.txt b/plugins/obs-ffmpeg/obs-nvenc-test/CMakeLists.txt deleted file mode 100644 index 351a2e055b913b..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc-test/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.24...3.25) - -legacy_check() - -find_package(FFnvcodec 12 REQUIRED) - -add_executable(obs-nvenc-test) - -target_sources(obs-nvenc-test PRIVATE obs-nvenc-test.c) -target_link_libraries(obs-nvenc-test OBS::obs-nvenc-version FFnvcodec::FFnvcodec d3d11 dxgi dxguid) - -# cmake-format: off -set_target_properties_obs(obs-nvenc-test PROPERTIES FOLDER plugins/obs-ffmpeg) -# cmake-format: on diff --git a/plugins/obs-ffmpeg/obs-nvenc-test/cmake/legacy.cmake b/plugins/obs-ffmpeg/obs-nvenc-test/cmake/legacy.cmake deleted file mode 100644 index b48460bf812767..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc-test/cmake/legacy.cmake +++ /dev/null @@ -1,13 +0,0 @@ -project(obs-nvenc-test) - -add_executable(obs-nvenc-test) - -find_package(FFnvcodec 12 REQUIRED) - -target_sources(obs-nvenc-test PRIVATE obs-nvenc-test.c ../obs-nvenc-ver.h) -target_compile_definitions(obs-nvenc-test PRIVATE OBS_LEGACY) -target_link_libraries(obs-nvenc-test d3d11 dxgi dxguid FFnvcodec::FFnvcodec) - -set_target_properties(obs-nvenc-test PROPERTIES FOLDER "plugins/obs-ffmpeg") - -setup_binary_target(obs-nvenc-test) diff --git a/plugins/obs-ffmpeg/obs-nvenc-test/obs-nvenc-test.c b/plugins/obs-ffmpeg/obs-nvenc-test/obs-nvenc-test.c deleted file mode 100644 index 7d8e75183198e5..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc-test/obs-nvenc-test.c +++ /dev/null @@ -1,238 +0,0 @@ -#include -#include -#include - -#include -#ifdef OBS_LEGACY -#include "../obs-nvenc-ver.h" -#else -#include -#endif - -#include -#include -#include - -__declspec(dllexport) DWORD NvOptimusEnablement = 1; -NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER}; -static void *nvenc_lib = NULL; -static bool av1_supported = false; - -#define NVIDIA_VENDOR_ID 0x10DE - -struct nvenc_info { - bool is_nvidia; - bool supports_av1; -}; - -#define MAX_CAPS 10 -static uint32_t luid_count = 0; -static uint64_t luid_order[MAX_CAPS] = {0}; -static struct nvenc_info adapter_info[MAX_CAPS] = {0}; - -bool load_nvenc_lib(void) -{ - const char *const file = (sizeof(void *) == 8) ? "nvEncodeAPI64.dll" - : "nvEncodeAPI.dll"; - nvenc_lib = LoadLibraryA(file); - return nvenc_lib != NULL; -} - -static inline void *load_nv_func(const char *func) -{ - void *func_ptr = (void *)GetProcAddress(nvenc_lib, func); - return func_ptr; -} - -static inline uint32_t get_adapter_idx(uint32_t adapter_idx, LUID luid) -{ - for (uint32_t i = 0; i < luid_count; i++) { - if (luid_order[i] == *(uint64_t *)&luid) { - return i; - } - } - - return adapter_idx; -} - -static bool get_adapter_caps(IDXGIFactory *factory, uint32_t adapter_idx) -{ - struct nvenc_info *caps; - IDXGIAdapter *adapter = NULL; - ID3D11Device *device = NULL; - ID3D11DeviceContext *context = NULL; - GUID *guids = NULL; - void *session = NULL; - HRESULT hr; - - if (adapter_idx == MAX_CAPS) - return false; - - hr = factory->lpVtbl->EnumAdapters(factory, adapter_idx, &adapter); - if (FAILED(hr)) - return false; - - DXGI_ADAPTER_DESC desc; - adapter->lpVtbl->GetDesc(adapter, &desc); - - caps = &adapter_info[get_adapter_idx(adapter_idx, desc.AdapterLuid)]; - if (desc.VendorId != NVIDIA_VENDOR_ID) - return true; - - caps->is_nvidia = true; - - hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, - 0, D3D11_SDK_VERSION, &device, NULL, &context); - if (FAILED(hr)) - goto finish; - - /* ---------------------------------------------------------------- */ - - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; - params.device = device; - params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; - params.apiVersion = NVENCAPI_VERSION; - - NVENCSTATUS stat = nv.nvEncOpenEncodeSessionEx(¶ms, &session); - if (stat != NV_ENC_SUCCESS) - goto finish; - - uint32_t guid_count = 0; - if (nv.nvEncGetEncodeGUIDCount(session, &guid_count) != NV_ENC_SUCCESS) - goto finish; - - guids = malloc(guid_count * sizeof(GUID)); - stat = nv.nvEncGetEncodeGUIDs(session, guids, guid_count, &guid_count); - if (stat != NV_ENC_SUCCESS) - goto finish; - - for (uint32_t i = 0; i < guid_count; i++) { - GUID *guid = &guids[i]; - - if (memcmp(guid, &NV_ENC_CODEC_AV1_GUID, sizeof(GUID)) == 0) { - caps->supports_av1 = true; - break; - } - } - -finish: - if (guids) - free(guids); - if (session) - nv.nvEncDestroyEncoder(session); - if (context) - context->lpVtbl->Release(context); - if (device) - device->lpVtbl->Release(device); - if (adapter) - adapter->lpVtbl->Release(adapter); - return true; -} - -typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *); -typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)( - NV_ENCODE_API_FUNCTION_LIST *); - -static inline uint32_t get_nvenc_ver(void) -{ - NV_MAX_VER_FUNC nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func( - "NvEncodeAPIGetMaxSupportedVersion"); - if (!nv_max_ver) { - return 0; - } - - uint32_t ver = 0; - if (nv_max_ver(&ver) != NV_ENC_SUCCESS) { - return 0; - } - return ver; -} - -static inline bool init_nvenc_internal(void) -{ - if (!load_nvenc_lib()) - return false; - - uint32_t ver = get_nvenc_ver(); - if (ver == 0) - return false; - - uint32_t supported_ver = (NVENC_COMPAT_MAJOR_VER << 4) | - NVENC_COMPAT_MINOR_VER; - if (supported_ver > ver) - return false; - - NV_CREATE_INSTANCE_FUNC nv_create_instance = - (NV_CREATE_INSTANCE_FUNC)load_nv_func( - "NvEncodeAPICreateInstance"); - if (!nv_create_instance) - return false; - - return nv_create_instance(&nv) == NV_ENC_SUCCESS; -} - -DWORD WINAPI TimeoutThread(LPVOID param) -{ - HANDLE hMainThread = (HANDLE)param; - - DWORD ret = WaitForSingleObject(hMainThread, 2500); - if (ret == WAIT_TIMEOUT) - TerminateProcess(GetCurrentProcess(), STATUS_TIMEOUT); - - CloseHandle(hMainThread); - return 0; -} - -int main(int argc, char *argv[]) -{ - IDXGIFactory *factory = NULL; - HRESULT hr; - - HANDLE hMainThread; - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &hMainThread, 0, FALSE, - DUPLICATE_SAME_ACCESS); - DWORD threadId; - HANDLE hThread; - hThread = - CreateThread(NULL, 0, TimeoutThread, hMainThread, 0, &threadId); - CloseHandle(hThread); - - /* --------------------------------------------------------- */ - /* try initializing nvenc, I guess */ - - if (!init_nvenc_internal()) - return 0; - - /* --------------------------------------------------------- */ - /* parse expected LUID order */ - - luid_count = argc - 1; - for (int i = 1; i < argc; i++) { - luid_order[i - 1] = strtoull(argv[i], NULL, 16); - } - - /* --------------------------------------------------------- */ - /* obtain adapter compatibility information */ - - hr = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&factory); - if (FAILED(hr)) - return 0; - - uint32_t idx = 0; - while (get_adapter_caps(factory, idx++)) - ; - - for (uint32_t i = 0; i < idx; i++) { - struct nvenc_info caps = adapter_info[i]; - - printf("[%u]\n", i); - printf("is_nvidia=%s\n", caps.is_nvidia ? "true" : "false"); - printf("supports_av1=%s\n", - caps.supports_av1 ? "true" : "false"); - } - - factory->lpVtbl->Release(factory); - return 0; -} diff --git a/plugins/obs-ffmpeg/obs-nvenc-ver.h b/plugins/obs-ffmpeg/obs-nvenc-ver.h deleted file mode 100644 index fcd57606556125..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc-ver.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#define NVENC_COMPAT_MAJOR_VER 11 -#define NVENC_COMPAT_MINOR_VER 1 - -#define NVENC_COMPAT_VER \ - (NVENC_COMPAT_MAJOR_VER | (NVENC_COMPAT_MINOR_VER << 24)) diff --git a/plugins/obs-ffmpeg/obs-nvenc.c b/plugins/obs-ffmpeg/obs-nvenc.c deleted file mode 100644 index 818b8ca894294a..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc.c +++ /dev/null @@ -1,2485 +0,0 @@ -#include "obs-nvenc.h" - -#include -#include -#include -#include -#include - -#include - -#ifdef _WIN32 -#define INITGUID -#include -#include -#include -#else -#include -#endif - -/* ========================================================================= */ -/* a hack of the ages: nvenc backward compatibility */ - -#define CONFIGURED_NVENC_MAJOR 12 -#define CONFIGURED_NVENC_MINOR 1 - -/* we cannot guarantee structures haven't changed, so purposely break on - * version change to force the programmer to update or remove backward - * compatibility NVENC code. */ -#if CONFIGURED_NVENC_MAJOR != NVENCAPI_MAJOR_VERSION || \ - CONFIGURED_NVENC_MINOR < NVENCAPI_MINOR_VERSION -#error NVENC version changed, update or remove NVENC compatibility code -#endif - -#undef NVENCAPI_STRUCT_VERSION -#define NVENCAPI_STRUCT_VERSION(ver) \ - ((uint32_t)(enc->needs_compat_ver ? NVENC_COMPAT_VER \ - : NVENCAPI_VERSION) | \ - ((ver) << 16) | (0x7 << 28)) - -#define NV_ENC_CONFIG_COMPAT_VER (NVENCAPI_STRUCT_VERSION(7) | (1 << 31)) -#define NV_ENC_INITIALIZE_PARAMS_COMPAT_VER \ - (NVENCAPI_STRUCT_VERSION(5) | (1 << 31)) -#define NV_ENC_PIC_PARAMS_COMPAT_VER (NVENCAPI_STRUCT_VERSION(4) | (1 << 31)) -#define NV_ENC_LOCK_BITSTREAM_COMPAT_VER NVENCAPI_STRUCT_VERSION(1) -#define NV_ENC_REGISTER_RESOURCE_COMPAT_VER NVENCAPI_STRUCT_VERSION(3) - -#define COMPATIBILITY_VERSION \ - (NVENC_COMPAT_MAJOR_VER << 4 | NVENC_COMPAT_MINOR_VER) - -/* ========================================================================= */ - -#define EXTRA_BUFFERS 5 - -#define do_log(level, format, ...) \ - blog(level, "[obs-nvenc: '%s'] " format, \ - obs_encoder_get_name(enc->encoder), ##__VA_ARGS__) - -#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__) -#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) -#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) -#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) - -#define error_hr(msg) error("%s: %s: 0x%08lX", __FUNCTION__, msg, (uint32_t)hr); - -#ifndef _WIN32 -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -struct nv_bitstream; -struct nv_texture; - -struct handle_tex { -#ifdef _WIN32 - uint32_t handle; - ID3D11Texture2D *tex; - IDXGIKeyedMutex *km; -#else - GLuint tex_id; - CUgraphicsResource res_y; - CUgraphicsResource res_uv; -#endif -}; - -/* ------------------------------------------------------------------------- */ -/* Main Implementation Structure */ - -enum codec_type { - CODEC_H264, - CODEC_HEVC, - CODEC_AV1, -}; - -static const char *get_codec_name(enum codec_type type) -{ - switch (type) { - case CODEC_H264: - return "H264"; - case CODEC_HEVC: - return "HEVC"; - case CODEC_AV1: - return "AV1"; - } - - return "Unknown"; -} - -struct nvenc_data { - obs_encoder_t *encoder; - enum codec_type codec; - GUID codec_guid; - - void *session; - NV_ENC_INITIALIZE_PARAMS params; - NV_ENC_CONFIG config; - int rc_lookahead; - uint32_t buf_count; - int output_delay; - int buffers_queued; - size_t next_bitstream; - size_t cur_bitstream; - bool encode_started; - bool first_packet; - bool can_change_bitrate; - bool needs_compat_ver; - bool fallback; - int32_t bframes; - - DARRAY(struct handle_tex) input_textures; - DARRAY(struct nv_bitstream) bitstreams; - DARRAY(struct nv_cuda_surface) surfaces; - NV_ENC_BUFFER_FORMAT surface_format; - struct deque dts_list; - - DARRAY(uint8_t) packet_data; - int64_t packet_pts; - bool packet_keyframe; - -#ifdef _WIN32 - DARRAY(struct nv_texture) textures; - ID3D11Device *device; - ID3D11DeviceContext *context; -#endif - - uint32_t cx; - uint32_t cy; - enum video_format in_format; - - uint8_t *header; - size_t header_size; - - uint8_t *sei; - size_t sei_size; - - int8_t *roi_map; - size_t roi_map_size; - uint32_t roi_increment; - - CUcontext cu_ctx; -}; - -/* ------------------------------------------------------------------------- */ -/* Bitstream Buffer */ - -struct nv_bitstream { - void *ptr; -}; - -#define NV_FAIL(format, ...) nv_fail(enc->encoder, format, ##__VA_ARGS__) -#define NV_FAILED(x) nv_failed(enc->encoder, x, __FUNCTION__, #x) - -static bool nv_bitstream_init(struct nvenc_data *enc, struct nv_bitstream *bs) -{ - NV_ENC_CREATE_BITSTREAM_BUFFER buf = { - NV_ENC_CREATE_BITSTREAM_BUFFER_VER}; - - if (NV_FAILED(nv.nvEncCreateBitstreamBuffer(enc->session, &buf))) { - return false; - } - - bs->ptr = buf.bitstreamBuffer; - return true; -} - -static void nv_bitstream_free(struct nvenc_data *enc, struct nv_bitstream *bs) -{ - if (bs->ptr) { - nv.nvEncDestroyBitstreamBuffer(enc->session, bs->ptr); - } -} - -/* ------------------------------------------------------------------------- */ -/* Texture Resource */ - -#ifdef _WIN32 -struct nv_texture { - void *res; - ID3D11Texture2D *tex; - void *mapped_res; -}; - -static bool nv_texture_init(struct nvenc_data *enc, struct nv_texture *nvtex) -{ - const bool p010 = obs_p010_tex_active(); - - D3D11_TEXTURE2D_DESC desc = {0}; - desc.Width = enc->cx; - desc.Height = enc->cy; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = p010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12; - desc.SampleDesc.Count = 1; - desc.BindFlags = D3D11_BIND_RENDER_TARGET; - - ID3D11Device *const device = enc->device; - ID3D11Texture2D *tex; - HRESULT hr = device->lpVtbl->CreateTexture2D(device, &desc, NULL, &tex); - if (FAILED(hr)) { - error_hr("Failed to create texture"); - return false; - } - - tex->lpVtbl->SetEvictionPriority(tex, DXGI_RESOURCE_PRIORITY_MAXIMUM); - - uint32_t struct_ver = enc->needs_compat_ver - ? NV_ENC_REGISTER_RESOURCE_COMPAT_VER - : NV_ENC_REGISTER_RESOURCE_VER; - - NV_ENC_REGISTER_RESOURCE res = {struct_ver}; - res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; - res.resourceToRegister = tex; - res.width = enc->cx; - res.height = enc->cy; - res.bufferFormat = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT - : NV_ENC_BUFFER_FORMAT_NV12; - - if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) { - tex->lpVtbl->Release(tex); - return false; - } - - nvtex->res = res.registeredResource; - nvtex->tex = tex; - nvtex->mapped_res = NULL; - return true; -} - -static void nv_texture_free(struct nvenc_data *enc, struct nv_texture *nvtex) -{ - - if (nvtex->res) { - if (nvtex->mapped_res) { - nv.nvEncUnmapInputResource(enc->session, - nvtex->mapped_res); - } - nv.nvEncUnregisterResource(enc->session, nvtex->res); - nvtex->tex->lpVtbl->Release(nvtex->tex); - } -} -#endif - -/* ------------------------------------------------------------------------- */ -/* CUDA Stuff */ - -/* CUDA error handling */ - -static inline bool cuda_error_check(struct nvenc_data *enc, CUresult res, - const char *func, const char *call) -{ - if (res == CUDA_SUCCESS) - return true; - - struct dstr message = {0}; - - const char *name, *desc; - if (cuda_get_error_desc(res, &name, &desc)) { - dstr_printf(&message, - "%s: CUDA call \"%s\" failed with %s (%d): %s", - func, call, name, res, desc); - } else { - dstr_printf(&message, "%s: CUDA call \"%s\" failed with %d", - func, call, res); - } - - error("%s", message.array); - obs_encoder_set_last_error(enc->encoder, message.array); - - dstr_free(&message); - return false; -} - -#define CU_FAILED(call) \ - if (!cuda_error_check(enc, call, __FUNCTION__, #call)) \ - return false; - -#define CU_CHECK(call) \ - if (!cuda_error_check(enc, call, __FUNCTION__, #call)) { \ - success = false; \ - goto unmap; \ - } - -/* CUDA Surfaces */ - -struct nv_cuda_surface { - CUarray tex; - NV_ENC_REGISTERED_PTR res; - NV_ENC_INPUT_PTR *mapped_res; -}; - -/* Missing from ffmpeg nvcodec headers, required for CUDA arrays to be usable in NVENC */ -static const int CUDA_ARRAY3D_SURFACE_LDST = 0x02; - -static bool nv_cuda_surface_init(struct nvenc_data *enc, - struct nv_cuda_surface *nvsurf) -{ - const bool p010 = obs_p010_tex_active(); - CUDA_ARRAY3D_DESCRIPTOR desc; - desc.Width = enc->cx; - desc.Height = enc->cy; - desc.Depth = 0; - desc.Flags = CUDA_ARRAY3D_SURFACE_LDST; - desc.NumChannels = 1; - - if (!enc->fallback) { - desc.Format = p010 ? CU_AD_FORMAT_UNSIGNED_INT16 - : CU_AD_FORMAT_UNSIGNED_INT8; - desc.Height = enc->cy + enc->cy / 2; - } else { - switch (enc->surface_format) { - case NV_ENC_BUFFER_FORMAT_NV12: - desc.Format = CU_AD_FORMAT_UNSIGNED_INT8; - // Additional half-height plane for UV data - desc.Height += enc->cy / 2; - break; - case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: - desc.Format = CU_AD_FORMAT_UNSIGNED_INT16; - desc.Height += enc->cy / 2; - desc.NumChannels = 2; // number of bytes per element - break; - case NV_ENC_BUFFER_FORMAT_YUV444: - desc.Format = CU_AD_FORMAT_UNSIGNED_INT8; - desc.Height *= 3; // 3 full-size planes - break; - default: - error("Unknown input format: %d", enc->surface_format); - return false; - } - } - - CU_FAILED(cu->cuArray3DCreate(&nvsurf->tex, &desc)) - - NV_ENC_REGISTER_RESOURCE res = {0}; - res.version = enc->needs_compat_ver - ? NV_ENC_REGISTER_RESOURCE_COMPAT_VER - : NV_ENC_REGISTER_RESOURCE_VER; - res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY; - res.resourceToRegister = (void *)nvsurf->tex; - res.width = enc->cx; - res.height = enc->cy; - res.pitch = (uint32_t)(desc.Width * desc.NumChannels); - if (!enc->fallback) { - res.bufferFormat = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT - : NV_ENC_BUFFER_FORMAT_NV12; - } else { - res.bufferFormat = enc->surface_format; - } - - if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) { - return false; - } - - nvsurf->res = res.registeredResource; - nvsurf->mapped_res = NULL; - return true; -} - -static void nv_cuda_surface_free(struct nvenc_data *enc, - struct nv_cuda_surface *nvsurf) -{ - - if (nvsurf->res) { - if (nvsurf->mapped_res) { - nv.nvEncUnmapInputResource(enc->session, - nvsurf->mapped_res); - } - nv.nvEncUnregisterResource(enc->session, nvsurf->res); - cu->cuArrayDestroy(nvsurf->tex); - } -} - -/* ------------------------------------------------------------------------- */ -/* Implementation */ - -static const char *h264_nvenc_get_name(void *type_data) -{ - UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC H.264"; -} - -static const char *h264_nvenc_soft_get_name(void *type_data) -{ - UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC H.264 (Fallback)"; -} - -#ifdef ENABLE_HEVC -static const char *hevc_nvenc_get_name(void *type_data) -{ - UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC HEVC"; -} - -static const char *hevc_nvenc_soft_get_name(void *type_data) -{ - UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC HEVC (Fallback)"; -} -#endif - -static const char *av1_nvenc_get_name(void *type_data) -{ - UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC AV1"; -} - -static const char *av1_nvenc_soft_get_name(void *type_data) -{ - UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC AV1 (Fallback)"; -} - -static inline int nv_get_cap(struct nvenc_data *enc, NV_ENC_CAPS cap) -{ - if (!enc->session) - return 0; - - NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER}; - int v; - - param.capsToQuery = cap; - nv.nvEncGetEncodeCaps(enc->session, enc->codec_guid, ¶m, &v); - return v; -} - -static bool nvenc_update(void *data, obs_data_t *settings) -{ - struct nvenc_data *enc = data; - - /* Only support reconfiguration of CBR bitrate */ - if (enc->can_change_bitrate) { - int bitrate = (int)obs_data_get_int(settings, "bitrate"); - int max_bitrate = - (int)obs_data_get_int(settings, "max_bitrate"); - bool vbr = (enc->config.rcParams.rateControlMode == - NV_ENC_PARAMS_RC_VBR); - - enc->config.rcParams.averageBitRate = bitrate * 1000; - enc->config.rcParams.maxBitRate = vbr ? max_bitrate * 1000 - : bitrate * 1000; - - NV_ENC_RECONFIGURE_PARAMS params = {0}; - params.version = NV_ENC_RECONFIGURE_PARAMS_VER; - params.reInitEncodeParams = enc->params; - params.resetEncoder = 1; - params.forceIDR = 1; - - if (NV_FAILED(nv.nvEncReconfigureEncoder(enc->session, - ¶ms))) { - return false; - } - } - - return true; -} - -#ifdef _WIN32 -static HANDLE get_lib(struct nvenc_data *enc, const char *lib) -{ - HMODULE mod = GetModuleHandleA(lib); - if (mod) - return mod; - - mod = LoadLibraryA(lib); - if (!mod) - error("Failed to load %s", lib); - return mod; -} - -typedef HRESULT(WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **); - -static bool init_d3d11(struct nvenc_data *enc, obs_data_t *settings) -{ - HMODULE dxgi = get_lib(enc, "DXGI.dll"); - HMODULE d3d11 = get_lib(enc, "D3D11.dll"); - CREATEDXGIFACTORY1PROC create_dxgi; - PFN_D3D11_CREATE_DEVICE create_device; - IDXGIFactory1 *factory; - IDXGIAdapter *adapter; - ID3D11Device *device; - ID3D11DeviceContext *context; - HRESULT hr; - - if (!dxgi || !d3d11) { - return false; - } - - create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress( - dxgi, "CreateDXGIFactory1"); - create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress( - d3d11, "D3D11CreateDevice"); - - if (!create_dxgi || !create_device) { - error("Failed to load D3D11/DXGI procedures"); - return false; - } - - hr = create_dxgi(&IID_IDXGIFactory1, &factory); - if (FAILED(hr)) { - error_hr("CreateDXGIFactory1 failed"); - return false; - } - - hr = factory->lpVtbl->EnumAdapters(factory, 0, &adapter); - factory->lpVtbl->Release(factory); - if (FAILED(hr)) { - error_hr("EnumAdapters failed"); - return false; - } - - hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, - D3D11_SDK_VERSION, &device, NULL, &context); - adapter->lpVtbl->Release(adapter); - if (FAILED(hr)) { - error_hr("D3D11CreateDevice failed"); - return false; - } - - enc->device = device; - enc->context = context; - return true; -} -#endif - -static bool init_session(struct nvenc_data *enc) -{ - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; - params.apiVersion = enc->needs_compat_ver ? NVENC_COMPAT_VER - : NVENCAPI_VERSION; -#ifdef _WIN32 - if (enc->fallback) { - params.device = enc->cu_ctx; - params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; - } else { - params.device = enc->device; - params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; - } -#else - params.device = enc->cu_ctx; - params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; -#endif - - if (NV_FAILED(nv.nvEncOpenEncodeSessionEx(¶ms, &enc->session))) { - return false; - } - return true; -} - -static void initialize_params(struct nvenc_data *enc, const GUID *nv_preset, - NV_ENC_TUNING_INFO nv_tuning, uint32_t width, - uint32_t height, uint32_t fps_num, - uint32_t fps_den) -{ - int darWidth, darHeight; - av_reduce(&darWidth, &darHeight, width, height, 1024 * 1024); - - NV_ENC_INITIALIZE_PARAMS *params = &enc->params; - memset(params, 0, sizeof(*params)); - params->version = enc->needs_compat_ver - ? NV_ENC_INITIALIZE_PARAMS_COMPAT_VER - : NV_ENC_INITIALIZE_PARAMS_VER; - params->encodeGUID = enc->codec_guid; - params->presetGUID = *nv_preset; - params->encodeWidth = width; - params->encodeHeight = height; - params->darWidth = enc->codec == CODEC_AV1 ? width : (uint32_t)darWidth; - params->darHeight = enc->codec == CODEC_AV1 ? height - : (uint32_t)darHeight; - params->frameRateNum = fps_num; - params->frameRateDen = fps_den; - params->enableEncodeAsync = 0; - params->enablePTD = 1; - params->encodeConfig = &enc->config; - params->tuningInfo = nv_tuning; -} - -static inline GUID get_nv_preset2(const char *preset2) -{ - if (astrcmpi(preset2, "p1") == 0) { - return NV_ENC_PRESET_P1_GUID; - } else if (astrcmpi(preset2, "p2") == 0) { - return NV_ENC_PRESET_P2_GUID; - } else if (astrcmpi(preset2, "p3") == 0) { - return NV_ENC_PRESET_P3_GUID; - } else if (astrcmpi(preset2, "p4") == 0) { - return NV_ENC_PRESET_P4_GUID; - } else if (astrcmpi(preset2, "p6") == 0) { - return NV_ENC_PRESET_P6_GUID; - } else if (astrcmpi(preset2, "p7") == 0) { - return NV_ENC_PRESET_P7_GUID; - } else { - return NV_ENC_PRESET_P5_GUID; - } -} - -static inline NV_ENC_TUNING_INFO get_nv_tuning(const char *tuning) -{ - if (astrcmpi(tuning, "ll") == 0) { - return NV_ENC_TUNING_INFO_LOW_LATENCY; - } else if (astrcmpi(tuning, "ull") == 0) { - return NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY; - } else { - return NV_ENC_TUNING_INFO_HIGH_QUALITY; - } -} - -static inline NV_ENC_MULTI_PASS get_nv_multipass(const char *multipass) -{ - if (astrcmpi(multipass, "qres") == 0) { - return NV_ENC_TWO_PASS_QUARTER_RESOLUTION; - } else if (astrcmpi(multipass, "fullres") == 0) { - return NV_ENC_TWO_PASS_FULL_RESOLUTION; - } else { - return NV_ENC_MULTI_PASS_DISABLED; - } -} - -static bool is_10_bit(const struct nvenc_data *enc) -{ - return enc->fallback ? enc->in_format == VIDEO_FORMAT_P010 - : obs_p010_tex_active(); -} - -static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings, - int bf, bool compatibility, bool *lossless) -{ - const char *rc = obs_data_get_string(settings, "rate_control"); - int bitrate = (int)obs_data_get_int(settings, "bitrate"); - int max_bitrate = (int)obs_data_get_int(settings, "max_bitrate"); - int cqp = (int)obs_data_get_int(settings, "cqp"); - int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); - const char *preset = obs_data_get_string(settings, "preset"); - const char *preset2 = obs_data_get_string(settings, "preset2"); - const char *tuning = obs_data_get_string(settings, "tune"); - const char *multipass = obs_data_get_string(settings, "multipass"); - const char *profile = obs_data_get_string(settings, "profile"); - bool lookahead = obs_data_get_bool(settings, "lookahead"); - bool vbr = astrcmpi(rc, "VBR") == 0; - bool psycho_aq = !compatibility && - obs_data_get_bool(settings, "psycho_aq"); - bool disable_scenecut = obs_data_get_bool(settings, "disable_scenecut"); - NVENCSTATUS err; - - video_t *video = obs_encoder_video(enc->encoder); - const struct video_output_info *voi = video_output_get_info(video); - - enc->cx = obs_encoder_get_width(enc->encoder); - enc->cy = obs_encoder_get_height(enc->encoder); - - /* -------------------------- */ - /* get preset */ - - GUID nv_preset = get_nv_preset2(preset2); - NV_ENC_TUNING_INFO nv_tuning = get_nv_tuning(tuning); - NV_ENC_MULTI_PASS nv_multipass = compatibility - ? NV_ENC_MULTI_PASS_DISABLED - : get_nv_multipass(multipass); - - if (obs_data_has_user_value(settings, "preset") && - !obs_data_has_user_value(settings, "preset2") && - enc->codec == CODEC_H264) { - if (astrcmpi(preset, "mq") == 0) { - nv_preset = NV_ENC_PRESET_P5_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_TWO_PASS_QUARTER_RESOLUTION; - - } else if (astrcmpi(preset, "hq") == 0) { - nv_preset = NV_ENC_PRESET_P5_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "default") == 0) { - nv_preset = NV_ENC_PRESET_P3_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "hp") == 0) { - nv_preset = NV_ENC_PRESET_P1_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "ll") == 0) { - nv_preset = NV_ENC_PRESET_P3_GUID; - nv_tuning = NV_ENC_TUNING_INFO_LOW_LATENCY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "llhq") == 0) { - nv_preset = NV_ENC_PRESET_P4_GUID; - nv_tuning = NV_ENC_TUNING_INFO_LOW_LATENCY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "llhp") == 0) { - nv_preset = NV_ENC_PRESET_P2_GUID; - nv_tuning = NV_ENC_TUNING_INFO_LOW_LATENCY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - } - } else if (obs_data_has_user_value(settings, "preset") && - !obs_data_has_user_value(settings, "preset2") && - enc->codec == CODEC_HEVC) { - if (astrcmpi(preset, "mq") == 0) { - nv_preset = NV_ENC_PRESET_P6_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_TWO_PASS_QUARTER_RESOLUTION; - - } else if (astrcmpi(preset, "hq") == 0) { - nv_preset = NV_ENC_PRESET_P6_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "default") == 0) { - nv_preset = NV_ENC_PRESET_P5_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "hp") == 0) { - nv_preset = NV_ENC_PRESET_P1_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "ll") == 0) { - nv_preset = NV_ENC_PRESET_P3_GUID; - nv_tuning = NV_ENC_TUNING_INFO_LOW_LATENCY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "llhq") == 0) { - nv_preset = NV_ENC_PRESET_P4_GUID; - nv_tuning = NV_ENC_TUNING_INFO_LOW_LATENCY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - - } else if (astrcmpi(preset, "llhp") == 0) { - nv_preset = NV_ENC_PRESET_P2_GUID; - nv_tuning = NV_ENC_TUNING_INFO_LOW_LATENCY; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - } - } - - const bool rc_lossless = astrcmpi(rc, "lossless") == 0; - *lossless = rc_lossless; - if (rc_lossless) { - *lossless = - nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE); - if (*lossless) { - nv_tuning = NV_ENC_TUNING_INFO_LOSSLESS; - nv_multipass = NV_ENC_MULTI_PASS_DISABLED; - } else { - warn("lossless encode is not supported, ignoring"); - nv_preset = NV_ENC_PRESET_P5_GUID; - nv_tuning = NV_ENC_TUNING_INFO_HIGH_QUALITY; - nv_multipass = NV_ENC_TWO_PASS_QUARTER_RESOLUTION; - } - } - - /* -------------------------- */ - /* get preset default config */ - - uint32_t config_ver = enc->needs_compat_ver ? NV_ENC_CONFIG_COMPAT_VER - : NV_ENC_CONFIG_VER; - - NV_ENC_PRESET_CONFIG preset_config = {NV_ENC_PRESET_CONFIG_VER, - {config_ver}}; - - err = nv.nvEncGetEncodePresetConfigEx(enc->session, enc->codec_guid, - nv_preset, nv_tuning, - &preset_config); - if (nv_failed(enc->encoder, err, __FUNCTION__, - "nvEncGetEncodePresetConfig")) { - return false; - } - - /* -------------------------- */ - /* main configuration */ - - enc->config = preset_config.presetCfg; - - uint32_t gop_size = - (keyint_sec) ? keyint_sec * voi->fps_num / voi->fps_den : 250; - - NV_ENC_CONFIG *config = &enc->config; - - initialize_params(enc, &nv_preset, nv_tuning, voi->width, voi->height, - voi->fps_num, voi->fps_den); - - config->gopLength = gop_size; - config->frameIntervalP = 1 + bf; - - enc->bframes = bf; - - /* lookahead */ - const bool use_profile_lookahead = config->rcParams.enableLookahead; - lookahead = nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_LOOKAHEAD) && - (lookahead || use_profile_lookahead); - if (lookahead) { - enc->rc_lookahead = use_profile_lookahead - ? config->rcParams.lookaheadDepth - : 8; - } - - int buf_count = max(4, config->frameIntervalP * 2 * 2); - if (lookahead) { - buf_count = max(buf_count, config->frameIntervalP + - enc->rc_lookahead + - EXTRA_BUFFERS); - } - - buf_count = min(64, buf_count); - enc->buf_count = buf_count; - - const int output_delay = buf_count - 1; - enc->output_delay = output_delay; - - if (lookahead) { - const int lkd_bound = output_delay - config->frameIntervalP - 4; - if (lkd_bound >= 0) { - config->rcParams.enableLookahead = 1; - config->rcParams.lookaheadDepth = - max(enc->rc_lookahead, lkd_bound); - config->rcParams.disableIadapt = 0; - config->rcParams.disableBadapt = 0; - } else { - lookahead = false; - } - } - - enc->config.rcParams.disableIadapt = disable_scenecut; - - /* psycho aq */ - if (!compatibility) { - if (nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ)) { - config->rcParams.enableAQ = psycho_aq; - config->rcParams.aqStrength = 8; - config->rcParams.enableTemporalAQ = psycho_aq; - } else { - warn("Ignoring Psycho Visual Tuning request since GPU is not capable"); - } - } - - /* -------------------------- */ - /* rate control */ - - enc->can_change_bitrate = - nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE); - - config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR; - - if (astrcmpi(rc, "cqp") == 0 || rc_lossless) { - if (*lossless) - cqp = 0; - - int cqp_val = enc->codec == CODEC_AV1 ? cqp * 4 : cqp; - - config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; - config->rcParams.constQP.qpInterP = cqp_val; - config->rcParams.constQP.qpInterB = cqp_val; - config->rcParams.constQP.qpIntra = cqp_val; - enc->can_change_bitrate = false; - - bitrate = 0; - max_bitrate = 0; - - } else if (astrcmpi(rc, "vbr") != 0) { /* CBR by default */ - config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; - } - - config->rcParams.averageBitRate = bitrate * 1000; - config->rcParams.maxBitRate = vbr ? max_bitrate * 1000 : bitrate * 1000; - config->rcParams.vbvBufferSize = bitrate * 1000; - config->rcParams.multiPass = nv_multipass; - config->rcParams.qpMapMode = NV_ENC_QP_MAP_DELTA; - - /* -------------------------- */ - /* initialize */ - - info("settings:\n" - "\tcodec: %s\n" - "\trate_control: %s\n" - "\tbitrate: %d\n" - "\tcqp: %d\n" - "\tkeyint: %d\n" - "\tpreset: %s\n" - "\ttuning: %s\n" - "\tmultipass: %s\n" - "\tprofile: %s\n" - "\twidth: %d\n" - "\theight: %d\n" - "\tb-frames: %d\n" - "\tlookahead: %s\n" - "\tpsycho_aq: %s\n", - get_codec_name(enc->codec), rc, bitrate, cqp, gop_size, preset2, - tuning, multipass, profile, enc->cx, enc->cy, bf, - lookahead ? "true" : "false", psycho_aq ? "true" : "false"); - - return true; -} - -static bool init_encoder_h264(struct nvenc_data *enc, obs_data_t *settings, - int bf, bool compatibility) -{ - const char *rc = obs_data_get_string(settings, "rate_control"); - int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); - const char *profile = obs_data_get_string(settings, "profile"); - bool lossless; - - if (!init_encoder_base(enc, settings, bf, compatibility, &lossless)) { - return false; - } - - NV_ENC_CONFIG *config = &enc->config; - NV_ENC_CONFIG_H264 *h264_config = &config->encodeCodecConfig.h264Config; - NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui_params = - &h264_config->h264VUIParameters; - - video_t *video = obs_encoder_video(enc->encoder); - const struct video_output_info *voi = video_output_get_info(video); - uint32_t gop_size = - (keyint_sec) ? keyint_sec * voi->fps_num / voi->fps_den : 250; - - h264_config->idrPeriod = gop_size; - - bool repeat_headers = obs_data_get_bool(settings, "repeat_headers"); - if (repeat_headers) { - h264_config->repeatSPSPPS = 1; - h264_config->disableSPSPPS = 0; - h264_config->outputAUD = 1; - } - - h264_config->sliceMode = 3; - h264_config->sliceModeData = 1; - - h264_config->useBFramesAsRef = NV_ENC_BFRAME_REF_MODE_DISABLED; - - /* Enable CBR padding */ - if (config->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR) - h264_config->enableFillerDataInsertion = 1; - - vui_params->videoSignalTypePresentFlag = 1; - vui_params->videoFullRangeFlag = (voi->range == VIDEO_RANGE_FULL); - vui_params->colourDescriptionPresentFlag = 1; - - switch (voi->colorspace) { - case VIDEO_CS_601: - vui_params->colourPrimaries = 6; - vui_params->transferCharacteristics = 6; - vui_params->colourMatrix = 6; - break; - case VIDEO_CS_DEFAULT: - case VIDEO_CS_709: - vui_params->colourPrimaries = 1; - vui_params->transferCharacteristics = 1; - vui_params->colourMatrix = 1; - break; - case VIDEO_CS_SRGB: - vui_params->colourPrimaries = 1; - vui_params->transferCharacteristics = 13; - vui_params->colourMatrix = 1; - break; - default: - break; - } - - if (astrcmpi(rc, "lossless") == 0) { - h264_config->qpPrimeYZeroTransformBypassFlag = 1; - } else if (astrcmpi(rc, "vbr") != 0) { /* CBR */ - h264_config->outputBufferingPeriodSEI = 1; - } - - h264_config->outputPictureTimingSEI = 1; - - /* -------------------------- */ - /* profile */ - - if (enc->in_format == VIDEO_FORMAT_I444) { - config->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID; - h264_config->chromaFormatIDC = 3; - } else if (astrcmpi(profile, "main") == 0) { - config->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID; - } else if (astrcmpi(profile, "baseline") == 0) { - config->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; - } else if (!lossless) { - config->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; - } - - if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, &enc->params))) { - return false; - } - - return true; -} - -static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings, - int bf, bool compatibility) -{ - const char *rc = obs_data_get_string(settings, "rate_control"); - int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); - const char *profile = obs_data_get_string(settings, "profile"); - bool lossless; - - if (!init_encoder_base(enc, settings, bf, compatibility, &lossless)) { - return false; - } - - NV_ENC_CONFIG *config = &enc->config; - NV_ENC_CONFIG_HEVC *hevc_config = &config->encodeCodecConfig.hevcConfig; - NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui_params = - &hevc_config->hevcVUIParameters; - - video_t *video = obs_encoder_video(enc->encoder); - const struct video_output_info *voi = video_output_get_info(video); - uint32_t gop_size = - (keyint_sec) ? keyint_sec * voi->fps_num / voi->fps_den : 250; - - hevc_config->idrPeriod = gop_size; - - bool repeat_headers = obs_data_get_bool(settings, "repeat_headers"); - if (repeat_headers) { - hevc_config->repeatSPSPPS = 1; - hevc_config->disableSPSPPS = 0; - hevc_config->outputAUD = 1; - } - - hevc_config->sliceMode = 3; - hevc_config->sliceModeData = 1; - - hevc_config->useBFramesAsRef = NV_ENC_BFRAME_REF_MODE_DISABLED; - - /* Enable CBR padding */ - if (config->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR) - hevc_config->enableFillerDataInsertion = 1; - - vui_params->videoSignalTypePresentFlag = 1; - vui_params->videoFullRangeFlag = (voi->range == VIDEO_RANGE_FULL); - vui_params->colourDescriptionPresentFlag = 1; - - switch (voi->colorspace) { - case VIDEO_CS_601: - vui_params->colourPrimaries = 6; - vui_params->transferCharacteristics = 6; - vui_params->colourMatrix = 6; - break; - case VIDEO_CS_DEFAULT: - case VIDEO_CS_709: - vui_params->colourPrimaries = 1; - vui_params->transferCharacteristics = 1; - vui_params->colourMatrix = 1; - break; - case VIDEO_CS_SRGB: - vui_params->colourPrimaries = 1; - vui_params->transferCharacteristics = 13; - vui_params->colourMatrix = 1; - break; - case VIDEO_CS_2100_PQ: - vui_params->colourPrimaries = 9; - vui_params->transferCharacteristics = 16; - vui_params->colourMatrix = 9; - vui_params->chromaSampleLocationFlag = 1; - vui_params->chromaSampleLocationTop = 2; - vui_params->chromaSampleLocationBot = 2; - break; - case VIDEO_CS_2100_HLG: - vui_params->colourPrimaries = 9; - vui_params->transferCharacteristics = 18; - vui_params->colourMatrix = 9; - vui_params->chromaSampleLocationFlag = 1; - vui_params->chromaSampleLocationTop = 2; - vui_params->chromaSampleLocationBot = 2; - } - - hevc_config->pixelBitDepthMinus8 = is_10_bit(enc) ? 2 : 0; - - if (astrcmpi(rc, "cbr") == 0) { - hevc_config->outputBufferingPeriodSEI = 1; - } - - hevc_config->outputPictureTimingSEI = 1; - - /* -------------------------- */ - /* profile */ - - if (enc->in_format == VIDEO_FORMAT_I444) { - config->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID; - hevc_config->chromaFormatIDC = 3; - } else if (astrcmpi(profile, "main10") == 0) { - config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; - } else if (is_10_bit(enc)) { - blog(LOG_WARNING, "[obs-nvenc] Forcing main10 for P010"); - config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; - } else { - config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID; - } - - if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, &enc->params))) { - return false; - } - - return true; -} - -static bool init_encoder_av1(struct nvenc_data *enc, obs_data_t *settings, - int bf, bool compatibility) -{ - int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec"); - bool lossless; - - if (!init_encoder_base(enc, settings, bf, compatibility, &lossless)) { - return false; - } - - NV_ENC_CONFIG *config = &enc->config; - NV_ENC_CONFIG_AV1 *av1_config = &config->encodeCodecConfig.av1Config; - - video_t *video = obs_encoder_video(enc->encoder); - const struct video_output_info *voi = video_output_get_info(video); - uint32_t gop_size = - (keyint_sec) ? keyint_sec * voi->fps_num / voi->fps_den : 250; - - av1_config->idrPeriod = gop_size; - - av1_config->useBFramesAsRef = NV_ENC_BFRAME_REF_MODE_DISABLED; - - av1_config->colorRange = (voi->range == VIDEO_RANGE_FULL); - - /* Enable CBR padding */ - if (config->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR) - av1_config->enableBitstreamPadding = 1; - -#define PIXELCOUNT_4K (3840 * 2160) - - /* If size is 4K+, set tiles to 2 uniform columns. */ - if ((voi->width * voi->height) >= PIXELCOUNT_4K) { - av1_config->enableCustomTileConfig = 0; - av1_config->numTileColumns = 2; - } - - switch (voi->colorspace) { - case VIDEO_CS_601: - av1_config->colorPrimaries = 6; - av1_config->transferCharacteristics = 6; - av1_config->matrixCoefficients = 6; - break; - case VIDEO_CS_DEFAULT: - case VIDEO_CS_709: - av1_config->colorPrimaries = 1; - av1_config->transferCharacteristics = 1; - av1_config->matrixCoefficients = 1; - break; - case VIDEO_CS_SRGB: - av1_config->colorPrimaries = 1; - av1_config->transferCharacteristics = 13; - av1_config->matrixCoefficients = 1; - break; - case VIDEO_CS_2100_PQ: - av1_config->colorPrimaries = 9; - av1_config->transferCharacteristics = 16; - av1_config->matrixCoefficients = 9; - break; - case VIDEO_CS_2100_HLG: - av1_config->colorPrimaries = 9; - av1_config->transferCharacteristics = 18; - av1_config->matrixCoefficients = 9; - } - - /* -------------------------- */ - /* profile */ - - config->profileGUID = NV_ENC_AV1_PROFILE_MAIN_GUID; - av1_config->tier = NV_ENC_TIER_AV1_0; - - av1_config->level = NV_ENC_LEVEL_AV1_AUTOSELECT; - av1_config->chromaFormatIDC = 1; - av1_config->pixelBitDepthMinus8 = is_10_bit(enc) ? 2 : 0; - av1_config->inputPixelBitDepthMinus8 = av1_config->pixelBitDepthMinus8; - av1_config->numFwdRefs = 1; - av1_config->numBwdRefs = 1; - av1_config->repeatSeqHdr = 1; - - if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, &enc->params))) { - return false; - } - - return true; -} - -static bool init_bitstreams(struct nvenc_data *enc) -{ - da_reserve(enc->bitstreams, enc->buf_count); - for (uint32_t i = 0; i < enc->buf_count; i++) { - struct nv_bitstream bitstream; - if (!nv_bitstream_init(enc, &bitstream)) { - return false; - } - - da_push_back(enc->bitstreams, &bitstream); - } - - return true; -} - -#ifdef _WIN32 -static bool init_textures(struct nvenc_data *enc) -{ - da_reserve(enc->textures, enc->buf_count); - for (uint32_t i = 0; i < enc->buf_count; i++) { - struct nv_texture texture; - if (!nv_texture_init(enc, &texture)) { - return false; - } - - da_push_back(enc->textures, &texture); - } - - return true; -} -#endif - -static bool init_cuda_surfaces(struct nvenc_data *enc) -{ - switch (enc->in_format) { - case VIDEO_FORMAT_P010: - enc->surface_format = NV_ENC_BUFFER_FORMAT_YUV420_10BIT; - break; - case VIDEO_FORMAT_I444: - enc->surface_format = NV_ENC_BUFFER_FORMAT_YUV444; - break; - default: - enc->surface_format = NV_ENC_BUFFER_FORMAT_NV12; - } - - da_reserve(enc->surfaces, enc->buf_count); - - CU_FAILED(cu->cuCtxPushCurrent(enc->cu_ctx)) - for (uint32_t i = 0; i < enc->buf_count; i++) { - struct nv_cuda_surface buf; - if (!nv_cuda_surface_init(enc, &buf)) { - return false; - } - - da_push_back(enc->surfaces, &buf); - } - CU_FAILED(cu->cuCtxPopCurrent(NULL)) - - return true; -} - -static bool init_cuda_ctx(struct nvenc_data *enc, obs_data_t *settings, - const bool texture) -{ - int count, gpu; - CUdevice device; - bool cuda_override; - - /* Allow CUDA device override for texture encoders (experimental) */ - if (obs_data_has_user_value(settings, "cuda_device")) { - gpu = (int)obs_data_get_int(settings, "cuda_device"); - cuda_override = true; - } else { - gpu = (int)obs_data_get_int(settings, "gpu"); - cuda_override = false; - } - - CU_FAILED(cu->cuInit(0)) - CU_FAILED(cu->cuDeviceGetCount(&count)) - if (!count) { - NV_FAIL("No CUDA devices found"); - return false; - } -#ifdef _WIN32 - CU_FAILED(cu->cuDeviceGet(&device, gpu)) -#else - if (!texture || cuda_override) { - CU_FAILED(cu->cuDeviceGet(&device, gpu)) - } else { - unsigned int ctx_count = 0; - CUdevice devices[2]; - - obs_enter_graphics(); - CUresult res = cu->cuGLGetDevices(&ctx_count, devices, 2, - CU_GL_DEVICE_LIST_ALL); - obs_leave_graphics(); - - if (res != CUDA_SUCCESS || !ctx_count) { - /* CUDA_ERROR_INVALID_GRAPHICS_CONTEXT should be treated - * as non-fatal fallback (probably running on iGPU). */ - if (res == 219) { - info("Not running on NVIDIA GPU, falling back to non-texture encoder"); - } else { - const char *name, *desc; - if (cuda_get_error_desc(res, &name, &desc)) { - error("Failed to get a CUDA device for the current OpenGL context: %s: %s", - name, desc); - } else { - error("Failed to get a CUDA device for the current OpenGL context: %d", - res); - } - } - return false; - } - - /* Documentation indicates this should only ever happen with SLI, i.e. never for OBS. */ - if (ctx_count > 1) { - warn("Got more than one CUDA devices for OpenGL context, this is untested."); - } - - device = devices[0]; - debug("Loading up CUDA on device %u", device); - } -#endif - CU_FAILED(cu->cuCtxCreate(&enc->cu_ctx, 0, device)) - CU_FAILED(cu->cuCtxPopCurrent(NULL)) - - return true; -} - -static enum video_format get_preferred_format(enum video_format format) -{ - switch (format) { - case VIDEO_FORMAT_I010: - case VIDEO_FORMAT_P010: - return VIDEO_FORMAT_P010; - case VIDEO_FORMAT_RGBA: - case VIDEO_FORMAT_BGRA: - case VIDEO_FORMAT_BGRX: - case VIDEO_FORMAT_I444: - return VIDEO_FORMAT_I444; - default: - return VIDEO_FORMAT_NV12; - } -} - -static void nvenc_destroy(void *data); - -static bool init_specific_encoder(struct nvenc_data *enc, obs_data_t *settings, - int bf, bool compatibility) -{ - switch (enc->codec) { - case CODEC_HEVC: - return init_encoder_hevc(enc, settings, bf, compatibility); - case CODEC_H264: - return init_encoder_h264(enc, settings, bf, compatibility); - case CODEC_AV1: - return init_encoder_av1(enc, settings, bf, compatibility); - } - - return false; -} - -static bool init_encoder(struct nvenc_data *enc, enum codec_type codec, - obs_data_t *settings, obs_encoder_t *encoder) -{ - UNUSED_PARAMETER(codec); - UNUSED_PARAMETER(encoder); - - int bf = (int)obs_data_get_int(settings, "bf"); - const bool support_10bit = - nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_10BIT_ENCODE); - const bool support_444 = - nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_YUV444_ENCODE); - const int bf_max = nv_get_cap(enc, NV_ENC_CAPS_NUM_MAX_BFRAMES); - - video_t *video = obs_encoder_video(enc->encoder); - const struct video_output_info *voi = video_output_get_info(video); - enum video_format pref_format = - obs_encoder_get_preferred_video_format(enc->encoder); - if (pref_format == VIDEO_FORMAT_NONE) - pref_format = voi->format; - - enc->in_format = get_preferred_format(pref_format); - - if (enc->in_format == VIDEO_FORMAT_I444 && !support_444) { - NV_FAIL(obs_module_text("NVENC.444Unsupported")); - return false; - } - - if (is_10_bit(enc) && !support_10bit) { - NV_FAIL(obs_module_text("NVENC.10bitUnsupported")); - return false; - } - - switch (voi->format) { - case VIDEO_FORMAT_I010: - case VIDEO_FORMAT_P010: - break; - default: - switch (voi->colorspace) { - case VIDEO_CS_2100_PQ: - case VIDEO_CS_2100_HLG: - NV_FAIL(obs_module_text("NVENC.8bitUnsupportedHdr")); - return false; - default: - break; - } - } - - if (bf > bf_max) { - blog(LOG_WARNING, - "[obs-nvenc] Max B-frames setting (%d) is more than encoder supports (%d).\n" - "Setting B-frames to %d", - bf, bf_max, bf_max); - bf = bf_max; - } - - if (!init_specific_encoder(enc, settings, bf, false)) { - blog(LOG_WARNING, "[obs-nvenc] init_specific_encoder failed, " - "trying again with compatibility options"); - - nv.nvEncDestroyEncoder(enc->session); - enc->session = NULL; - - if (!init_session(enc)) { - return false; - } - /* try without multipass and psycho aq */ - if (!init_specific_encoder(enc, settings, bf, true)) { - return false; - } - } - - return true; -} - -static void *nvenc_create_internal(enum codec_type codec, obs_data_t *settings, - obs_encoder_t *encoder, bool texture) -{ - struct nvenc_data *enc = bzalloc(sizeof(*enc)); - enc->encoder = encoder; - enc->codec = codec; - enc->first_packet = true; - enc->fallback = !texture; - - if (get_nvenc_ver() == COMPATIBILITY_VERSION) { - enc->needs_compat_ver = true; - } - NV_ENCODE_API_FUNCTION_LIST init = {NV_ENCODE_API_FUNCTION_LIST_VER}; - - switch (enc->codec) { - case CODEC_H264: - enc->codec_guid = NV_ENC_CODEC_H264_GUID; - break; - case CODEC_HEVC: - enc->codec_guid = NV_ENC_CODEC_HEVC_GUID; - break; - case CODEC_AV1: - enc->codec_guid = NV_ENC_CODEC_AV1_GUID; - break; - } - - if (!init_nvenc(encoder)) { - goto fail; - } - if ( -#ifdef _WIN32 - !texture && -#endif - !init_cuda(encoder)) { - goto fail; - } - if (NV_FAILED(nv_create_instance(&init))) { - goto fail; - } -#ifdef _WIN32 - if (texture && !init_d3d11(enc, settings)) { - goto fail; - } -#endif - if ( -#ifdef _WIN32 - !texture && -#endif - !init_cuda_ctx(enc, settings, texture)) { - goto fail; - } - if (!init_session(enc)) { - goto fail; - } - if (!init_encoder(enc, codec, settings, encoder)) { - goto fail; - } - if (!init_bitstreams(enc)) { - goto fail; - } -#ifdef _WIN32 - if (texture && !init_textures(enc)) { - goto fail; - } -#endif - if ( -#ifdef _WIN32 - !texture && -#endif - !init_cuda_surfaces(enc)) { - goto fail; - } - enc->codec = codec; - - if (enc->cu_ctx) - cu->cuCtxPopCurrent(NULL); - - return enc; - -fail: - nvenc_destroy(enc); - return NULL; -} - -static void *nvenc_create_base(enum codec_type codec, obs_data_t *settings, - obs_encoder_t *encoder, bool texture) -{ - /* this encoder requires shared textures, this cannot be used on a - * gpu other than the one OBS is currently running on. */ - const int gpu = (int)obs_data_get_int(settings, "gpu"); - if (gpu != 0 && texture) { - blog(LOG_INFO, - "[obs-nvenc] different GPU selected by user, falling back " - "to non-texture encoder"); - goto reroute; - } - - if (obs_encoder_scaling_enabled(encoder)) { - if (obs_encoder_gpu_scaling_enabled(encoder)) { - blog(LOG_INFO, "[obs-nvenc] GPU scaling enabled"); - } else if (texture) { - blog(LOG_INFO, - "[obs-nvenc] CPU scaling enabled, falling back to" - " non-texture encoder"); - goto reroute; - } - } - - if (texture && !obs_p010_tex_active() && !obs_nv12_tex_active()) { - blog(LOG_INFO, - "[obs-nvenc] nv12/p010 not active, falling back to " - "non-texture encoder"); - goto reroute; - } - - struct nvenc_data *enc = - nvenc_create_internal(codec, settings, encoder, texture); - - if (enc) { - return enc; - } - -reroute: - if (!texture) { - blog(LOG_ERROR, - "Already in fallback encoder, can't fall back further!"); - return NULL; - } - - switch (codec) { - case CODEC_H264: - return obs_encoder_create_rerouted(encoder, - "obs_nvenc_h264_cuda"); - case CODEC_HEVC: - return obs_encoder_create_rerouted(encoder, - "obs_nvenc_hevc_cuda"); - case CODEC_AV1: - return obs_encoder_create_rerouted(encoder, - "obs_nvenc_av1_cuda"); - } - - return NULL; -} - -static void *h264_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) -{ - return nvenc_create_base(CODEC_H264, settings, encoder, true); -} - -#ifdef ENABLE_HEVC -static void *hevc_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) -{ - return nvenc_create_base(CODEC_HEVC, settings, encoder, true); -} -#endif - -static void *av1_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) -{ - return nvenc_create_base(CODEC_AV1, settings, encoder, true); -} - -static void *h264_nvenc_soft_create(obs_data_t *settings, - obs_encoder_t *encoder) -{ - return nvenc_create_base(CODEC_H264, settings, encoder, false); -} - -#ifdef ENABLE_HEVC -static void *hevc_nvenc_soft_create(obs_data_t *settings, - obs_encoder_t *encoder) -{ - return nvenc_create_base(CODEC_HEVC, settings, encoder, false); -} -#endif - -static void *av1_nvenc_soft_create(obs_data_t *settings, obs_encoder_t *encoder) -{ - return nvenc_create_base(CODEC_AV1, settings, encoder, false); -} - -static bool get_encoded_packet(struct nvenc_data *enc, bool finalize); - -static void nvenc_destroy(void *data) -{ - struct nvenc_data *enc = data; - - if (enc->cu_ctx) - cu->cuCtxPushCurrent(enc->cu_ctx); - - if (enc->encode_started) { - uint32_t struct_ver = enc->needs_compat_ver - ? NV_ENC_PIC_PARAMS_COMPAT_VER - : NV_ENC_PIC_PARAMS_VER; - NV_ENC_PIC_PARAMS params = {struct_ver}; - params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; - nv.nvEncEncodePicture(enc->session, ¶ms); - get_encoded_packet(enc, true); - } -#ifdef _WIN32 - for (size_t i = 0; i < enc->textures.num; i++) { - nv_texture_free(enc, &enc->textures.array[i]); - } -#endif - for (size_t i = 0; i < enc->surfaces.num; i++) { - nv_cuda_surface_free(enc, &enc->surfaces.array[i]); - } - for (size_t i = 0; i < enc->bitstreams.num; i++) { - nv_bitstream_free(enc, &enc->bitstreams.array[i]); - } - if (enc->session) { - nv.nvEncDestroyEncoder(enc->session); - } -#ifdef _WIN32 - for (size_t i = 0; i < enc->input_textures.num; i++) { - ID3D11Texture2D *tex = enc->input_textures.array[i].tex; - IDXGIKeyedMutex *km = enc->input_textures.array[i].km; - tex->lpVtbl->Release(tex); - km->lpVtbl->Release(km); - } - if (enc->context) { - enc->context->lpVtbl->Release(enc->context); - } - if (enc->device) { - enc->device->lpVtbl->Release(enc->device); - } -#else - for (size_t i = 0; i < enc->input_textures.num; i++) { - CUgraphicsResource res_y = enc->input_textures.array[i].res_y; - CUgraphicsResource res_uv = enc->input_textures.array[i].res_uv; - cu->cuGraphicsUnregisterResource(res_y); - cu->cuGraphicsUnregisterResource(res_uv); - } -#endif - if (enc->cu_ctx) { - cu->cuCtxPopCurrent(NULL); - cu->cuCtxDestroy(enc->cu_ctx); - } - - bfree(enc->header); - bfree(enc->sei); - deque_free(&enc->dts_list); - da_free(enc->surfaces); - da_free(enc->input_textures); - da_free(enc->bitstreams); -#ifdef _WIN32 - da_free(enc->textures); -#endif - da_free(enc->packet_data); - bfree(enc->roi_map); - bfree(enc); -} - -#ifdef _WIN32 -static ID3D11Texture2D *get_tex_from_handle(struct nvenc_data *enc, - uint32_t handle, - IDXGIKeyedMutex **km_out) -{ - ID3D11Device *device = enc->device; - IDXGIKeyedMutex *km; - ID3D11Texture2D *input_tex; - HRESULT hr; - - for (size_t i = 0; i < enc->input_textures.num; i++) { - struct handle_tex *ht = &enc->input_textures.array[i]; - if (ht->handle == handle) { - *km_out = ht->km; - return ht->tex; - } - } - - hr = device->lpVtbl->OpenSharedResource(device, - (HANDLE)(uintptr_t)handle, - &IID_ID3D11Texture2D, - &input_tex); - if (FAILED(hr)) { - error_hr("OpenSharedResource failed"); - return NULL; - } - - hr = input_tex->lpVtbl->QueryInterface(input_tex, &IID_IDXGIKeyedMutex, - &km); - if (FAILED(hr)) { - error_hr("QueryInterface(IDXGIKeyedMutex) failed"); - input_tex->lpVtbl->Release(input_tex); - return NULL; - } - - input_tex->lpVtbl->SetEvictionPriority(input_tex, - DXGI_RESOURCE_PRIORITY_MAXIMUM); - - *km_out = km; - - struct handle_tex new_ht = {handle, input_tex, km}; - da_push_back(enc->input_textures, &new_ht); - return input_tex; -} -#endif - -static bool get_encoded_packet(struct nvenc_data *enc, bool finalize) -{ - void *s = enc->session; - - da_resize(enc->packet_data, 0); - - if (!enc->buffers_queued) - return true; - if (!finalize && enc->buffers_queued < enc->output_delay) - return true; - - size_t count = finalize ? enc->buffers_queued : 1; - - for (size_t i = 0; i < count; i++) { - size_t cur_bs_idx = enc->cur_bitstream; - struct nv_bitstream *bs = &enc->bitstreams.array[cur_bs_idx]; -#ifdef _WIN32 - struct nv_texture *nvtex = - enc->fallback ? NULL : &enc->textures.array[cur_bs_idx]; - struct nv_cuda_surface *surf = - enc->fallback ? &enc->surfaces.array[cur_bs_idx] : NULL; -#else - struct nv_cuda_surface *surf = &enc->surfaces.array[cur_bs_idx]; -#endif - - /* ---------------- */ - - uint32_t struct_ver = enc->needs_compat_ver - ? NV_ENC_LOCK_BITSTREAM_COMPAT_VER - : NV_ENC_LOCK_BITSTREAM_VER; - - NV_ENC_LOCK_BITSTREAM lock = {struct_ver}; - lock.outputBitstream = bs->ptr; - lock.doNotWait = false; - - if (NV_FAILED(nv.nvEncLockBitstream(s, &lock))) { - return false; - } - - if (enc->first_packet) { - NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = {0}; - uint8_t buf[256]; - uint32_t size = 0; - - payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER; - payload.spsppsBuffer = buf; - payload.inBufferSize = sizeof(buf); - payload.outSPSPPSPayloadSize = &size; - - nv.nvEncGetSequenceParams(s, &payload); - enc->header = bmemdup(buf, size); - enc->header_size = size; - enc->first_packet = false; - } - - da_copy_array(enc->packet_data, lock.bitstreamBufferPtr, - lock.bitstreamSizeInBytes); - - enc->packet_pts = (int64_t)lock.outputTimeStamp; - enc->packet_keyframe = lock.pictureType == NV_ENC_PIC_TYPE_IDR; - - if (NV_FAILED(nv.nvEncUnlockBitstream(s, bs->ptr))) { - return false; - } - - /* ---------------- */ -#ifdef _WIN32 - if (nvtex && nvtex->mapped_res) { - NVENCSTATUS err; - err = nv.nvEncUnmapInputResource(s, nvtex->mapped_res); - if (nv_failed(enc->encoder, err, __FUNCTION__, - "unmap")) { - return false; - } - nvtex->mapped_res = NULL; - } -#endif - /* ---------------- */ - - if (surf && surf->mapped_res) { - NVENCSTATUS err; - err = nv.nvEncUnmapInputResource(s, surf->mapped_res); - if (nv_failed(enc->encoder, err, __FUNCTION__, - "unmap")) { - return false; - } - surf->mapped_res = NULL; - } - - /* ---------------- */ - - if (++enc->cur_bitstream == enc->buf_count) - enc->cur_bitstream = 0; - - enc->buffers_queued--; - } - - return true; -} - -struct roi_params { - uint32_t mb_width; - uint32_t mb_height; - uint32_t mb_size; - bool av1; - int8_t *map; -}; - -static void roi_cb(void *param, struct obs_encoder_roi *roi) -{ - const struct roi_params *rp = param; - - int8_t qp_val; - /* AV1 has a larger QP range than HEVC/H.264 */ - if (rp->av1) { - qp_val = (int8_t)(-128.0f * roi->priority); - } else { - qp_val = (int8_t)(-51.0f * roi->priority); - } - - const uint32_t roi_left = roi->left / rp->mb_size; - const uint32_t roi_top = roi->top / rp->mb_size; - const uint32_t roi_right = (roi->right - 1) / rp->mb_size; - const uint32_t roi_bottom = (roi->bottom - 1) / rp->mb_size; - - for (uint32_t mb_y = 0; mb_y < rp->mb_height; mb_y++) { - if (mb_y < roi_top || mb_y > roi_bottom) - continue; - - for (uint32_t mb_x = 0; mb_x < rp->mb_width; mb_x++) { - if (mb_x < roi_left || mb_x > roi_right) - continue; - - rp->map[mb_y * rp->mb_width + mb_x] = qp_val; - } - } -} - -static void add_roi(struct nvenc_data *enc, NV_ENC_PIC_PARAMS *params) -{ - const uint32_t increment = obs_encoder_get_roi_increment(enc->encoder); - - if (enc->roi_map && enc->roi_increment == increment) { - params->qpDeltaMap = enc->roi_map; - params->qpDeltaMapSize = (uint32_t)enc->roi_map_size; - return; - } - - uint32_t mb_size = 0; - switch (enc->codec) { - case CODEC_H264: - /* H.264 is always 16x16 */ - mb_size = 16; - break; - case CODEC_HEVC: - /* HEVC can be 16x16, 32x32, or 64x64, but NVENC is always 32x32 */ - mb_size = 32; - break; - case CODEC_AV1: - /* AV1 can be 64x64 or 128x128, but NVENC is always 64x64 */ - mb_size = 64; - break; - } - - const uint32_t mb_width = (enc->cx + mb_size - 1) / mb_size; - const uint32_t mb_height = (enc->cy + mb_size - 1) / mb_size; - const size_t map_size = mb_width * mb_height * sizeof(int8_t); - - if (map_size != enc->roi_map_size) { - enc->roi_map = brealloc(enc->roi_map, map_size); - enc->roi_map_size = map_size; - } - - memset(enc->roi_map, 0, enc->roi_map_size); - - struct roi_params par = { - .mb_width = mb_width, - .mb_height = mb_height, - .mb_size = mb_size, - .av1 = enc->codec == CODEC_AV1, - .map = enc->roi_map, - }; - - obs_encoder_enum_roi(enc->encoder, roi_cb, &par); - - enc->roi_increment = increment; - params->qpDeltaMap = enc->roi_map; - params->qpDeltaMapSize = (uint32_t)map_size; -} - -static bool nvenc_encode_shared(struct nvenc_data *enc, struct nv_bitstream *bs, - void *pic, int64_t pts, - struct encoder_packet *packet, - bool *received_packet) -{ - NV_ENC_PIC_PARAMS params = {0}; - params.version = enc->needs_compat_ver ? NV_ENC_PIC_PARAMS_COMPAT_VER - : NV_ENC_PIC_PARAMS_VER; - params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; - params.inputBuffer = pic; - params.inputTimeStamp = (uint64_t)pts; - params.inputWidth = enc->cx; - params.inputHeight = enc->cy; - params.inputPitch = enc->cx; - params.outputBitstream = bs->ptr; - - if (enc->fallback) { - params.bufferFmt = enc->surface_format; - } else { - params.bufferFmt = obs_p010_tex_active() - ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT - : NV_ENC_BUFFER_FORMAT_NV12; - } - - /* Add ROI map if enabled */ - if (obs_encoder_has_roi(enc->encoder)) - add_roi(enc, ¶ms); - - NVENCSTATUS err = nv.nvEncEncodePicture(enc->session, ¶ms); - if (err != NV_ENC_SUCCESS && err != NV_ENC_ERR_NEED_MORE_INPUT) { - nv_failed(enc->encoder, err, __FUNCTION__, - "nvEncEncodePicture"); - return false; - } - - enc->encode_started = true; - enc->buffers_queued++; - - if (++enc->next_bitstream == enc->buf_count) { - enc->next_bitstream = 0; - } - - /* ------------------------------------ */ - /* check for encoded packet and parse */ - - if (!get_encoded_packet(enc, false)) { - return false; - } - - /* ------------------------------------ */ - /* output encoded packet */ - - if (enc->packet_data.num) { - int64_t dts; - deque_pop_front(&enc->dts_list, &dts, sizeof(dts)); - - /* subtract bframe delay from dts for H.264 and HEVC */ - if (enc->codec != CODEC_AV1) - dts -= (int64_t)enc->bframes * packet->timebase_num; - - *received_packet = true; - packet->data = enc->packet_data.array; - packet->size = enc->packet_data.num; - packet->type = OBS_ENCODER_VIDEO; - packet->pts = enc->packet_pts; - packet->dts = dts; - packet->keyframe = enc->packet_keyframe; - } else { - *received_packet = false; - } - - return true; -} - -#ifdef _WIN32 -static bool nvenc_encode_tex(void *data, uint32_t handle, int64_t pts, - uint64_t lock_key, uint64_t *next_key, - struct encoder_packet *packet, - bool *received_packet) -{ - struct nvenc_data *enc = data; - ID3D11DeviceContext *context = enc->context; - ID3D11Texture2D *input_tex; - ID3D11Texture2D *output_tex; - IDXGIKeyedMutex *km; - struct nv_texture *nvtex; - struct nv_bitstream *bs; - - if (handle == GS_INVALID_HANDLE) { - error("Encode failed: bad texture handle"); - *next_key = lock_key; - return false; - } - - bs = &enc->bitstreams.array[enc->next_bitstream]; - nvtex = &enc->textures.array[enc->next_bitstream]; - - input_tex = get_tex_from_handle(enc, handle, &km); - output_tex = nvtex->tex; - - if (!input_tex) { - *next_key = lock_key; - return false; - } - - deque_push_back(&enc->dts_list, &pts, sizeof(pts)); - - /* ------------------------------------ */ - /* copy to output tex */ - - km->lpVtbl->AcquireSync(km, lock_key, INFINITE); - - context->lpVtbl->CopyResource(context, (ID3D11Resource *)output_tex, - (ID3D11Resource *)input_tex); - - km->lpVtbl->ReleaseSync(km, *next_key); - - /* ------------------------------------ */ - /* map output tex so nvenc can use it */ - - NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; - map.registeredResource = nvtex->res; - if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) { - return false; - } - - nvtex->mapped_res = map.mappedResource; - - /* ------------------------------------ */ - /* do actual encode call */ - - return nvenc_encode_shared(enc, bs, nvtex->mapped_res, pts, packet, - received_packet); -} - -#else - -static inline bool get_res_for_tex_ids(struct nvenc_data *enc, GLuint tex_id_y, - GLuint tex_id_uv, - CUgraphicsResource *tex_y, - CUgraphicsResource *tex_uv) -{ - bool success = true; - - for (size_t idx = 0; idx < enc->input_textures.num; idx++) { - struct handle_tex *ht = &enc->input_textures.array[idx]; - if (ht->tex_id != tex_id_y) - continue; - - *tex_y = ht->res_y; - *tex_uv = ht->res_uv; - return success; - } - - CU_CHECK(cu->cuGraphicsGLRegisterImage( - tex_y, tex_id_y, GL_TEXTURE_2D, - CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY)) - CU_CHECK(cu->cuGraphicsGLRegisterImage( - tex_uv, tex_id_uv, GL_TEXTURE_2D, - CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY)) - - struct handle_tex ht = {tex_id_y, *tex_y, *tex_uv}; - da_push_back(enc->input_textures, &ht); - -unmap: - if (!success) { - cu->cuGraphicsUnregisterResource(*tex_y); - cu->cuGraphicsUnregisterResource(*tex_uv); - } - - return success; -} - -static inline bool copy_tex_cuda(struct nvenc_data *enc, const bool p010, - GLuint tex[2], struct nv_cuda_surface *surf) -{ - bool success = true; - CUgraphicsResource mapped_tex[2] = {0}; - CUarray mapped_cuda; - - if (!get_res_for_tex_ids(enc, tex[0], tex[1], &mapped_tex[0], - &mapped_tex[1])) - return false; - - CU_CHECK(cu->cuGraphicsMapResources(2, mapped_tex, 0)) - - CUDA_MEMCPY2D m = {0}; - m.dstMemoryType = CU_MEMORYTYPE_ARRAY; - m.srcMemoryType = CU_MEMORYTYPE_ARRAY; - m.dstArray = surf->tex; - m.WidthInBytes = p010 ? enc->cx * 2 : enc->cx; - m.Height = enc->cy; - - // Map and copy Y texture - CU_CHECK(cu->cuGraphicsSubResourceGetMappedArray(&mapped_cuda, - mapped_tex[0], 0, 0)); - m.srcArray = mapped_cuda; - CU_CHECK(cu->cuMemcpy2D(&m)) - - // Map and copy UV texture - CU_CHECK(cu->cuGraphicsSubResourceGetMappedArray(&mapped_cuda, - mapped_tex[1], 0, 0)) - m.srcArray = mapped_cuda; - m.dstY += enc->cy; - m.Height = enc->cy / 2; - - CU_CHECK(cu->cuMemcpy2D(&m)) - -unmap: - cu->cuGraphicsUnmapResources(2, mapped_tex, 0); - - return success; -} - -static bool nvenc_encode_tex2(void *data, struct encoder_texture *tex, - int64_t pts, uint64_t lock_key, - uint64_t *next_key, struct encoder_packet *packet, - bool *received_packet) -{ - struct nvenc_data *enc = data; - struct nv_cuda_surface *surf; - struct nv_bitstream *bs; - const bool p010 = obs_p010_tex_active(); - GLuint input_tex[2]; - - if (tex == NULL || tex->tex[0] == NULL) { - error("Encode failed: bad texture handle"); - *next_key = lock_key; - return false; - } - - bs = &enc->bitstreams.array[enc->next_bitstream]; - surf = &enc->surfaces.array[enc->next_bitstream]; - - deque_push_back(&enc->dts_list, &pts, sizeof(pts)); - - /* ------------------------------------ */ - /* copy to CUDA data */ - - CU_FAILED(cu->cuCtxPushCurrent(enc->cu_ctx)) - obs_enter_graphics(); - input_tex[0] = *(GLuint *)gs_texture_get_obj(tex->tex[0]); - input_tex[1] = *(GLuint *)gs_texture_get_obj(tex->tex[1]); - - bool success = copy_tex_cuda(enc, p010, input_tex, surf); - - obs_leave_graphics(); - CU_FAILED(cu->cuCtxPopCurrent(NULL)) - - if (!success) - return false; - - /* ------------------------------------ */ - /* map output tex so nvenc can use it */ - - NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; - map.registeredResource = surf->res; - map.mappedBufferFmt = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT - : NV_ENC_BUFFER_FORMAT_NV12; - - if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) - return false; - - surf->mapped_res = map.mappedResource; - - /* ------------------------------------ */ - /* do actual encode call */ - - return nvenc_encode_shared(enc, bs, surf->mapped_res, pts, packet, - received_packet); -} -#endif - -static inline bool nvenc_copy_frame(struct nvenc_data *enc, - struct encoder_frame *frame, - struct nv_cuda_surface *surf) -{ - bool success = true; - size_t height = enc->cy; - size_t width = enc->cx; - CUDA_MEMCPY2D m = {0}; - - m.srcMemoryType = CU_MEMORYTYPE_HOST; - m.dstMemoryType = CU_MEMORYTYPE_ARRAY; - m.dstArray = surf->tex; - m.WidthInBytes = width; - m.Height = height; - - CU_FAILED(cu->cuCtxPushCurrent(enc->cu_ctx)) - - if (enc->surface_format == NV_ENC_BUFFER_FORMAT_NV12) { - /* Page-locks the host memory so that it can be DMAd directly - * rather than CUDA doing an internal copy to page-locked - * memory before actually DMA-ing to the GPU. */ - CU_CHECK(cu->cuMemHostRegister(frame->data[0], - frame->linesize[0] * height, 0)) - CU_CHECK(cu->cuMemHostRegister( - frame->data[1], frame->linesize[1] * height / 2, 0)) - - m.srcPitch = frame->linesize[0]; - m.srcHost = frame->data[0]; - CU_FAILED(cu->cuMemcpy2D(&m)) - - m.srcPitch = frame->linesize[1]; - m.srcHost = frame->data[1]; - m.dstY += height; - m.Height /= 2; - CU_FAILED(cu->cuMemcpy2D(&m)) - } else if (enc->surface_format == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) { - CU_CHECK(cu->cuMemHostRegister(frame->data[0], - frame->linesize[0] * height, 0)) - CU_CHECK(cu->cuMemHostRegister( - frame->data[1], frame->linesize[1] * height / 2, 0)) - - // P010 lines are double the size (16 bit per pixel) - m.WidthInBytes *= 2; - - m.srcPitch = frame->linesize[0]; - m.srcHost = frame->data[0]; - CU_FAILED(cu->cuMemcpy2D(&m)) - - m.srcPitch = frame->linesize[1]; - m.srcHost = frame->data[1]; - m.dstY += height; - m.Height /= 2; - CU_FAILED(cu->cuMemcpy2D(&m)) - } else { // I444 - CU_CHECK(cu->cuMemHostRegister(frame->data[0], - frame->linesize[0] * height, 0)) - CU_CHECK(cu->cuMemHostRegister(frame->data[1], - frame->linesize[1] * height, 0)) - CU_CHECK(cu->cuMemHostRegister(frame->data[2], - frame->linesize[2] * height, 0)) - - m.srcPitch = frame->linesize[0]; - m.srcHost = frame->data[0]; - CU_FAILED(cu->cuMemcpy2D(&m)) - - m.srcPitch = frame->linesize[1]; - m.srcHost = frame->data[1]; - m.dstY += height; - CU_FAILED(cu->cuMemcpy2D(&m)) - - m.srcPitch = frame->linesize[2]; - m.srcHost = frame->data[2]; - m.dstY += height; - CU_FAILED(cu->cuMemcpy2D(&m)) - } - -unmap: - if (frame->data[0]) - cu->cuMemHostUnregister(frame->data[0]); - if (frame->data[1]) - cu->cuMemHostUnregister(frame->data[1]); - if (frame->data[2]) - cu->cuMemHostUnregister(frame->data[2]); - - CU_FAILED(cu->cuCtxPopCurrent(NULL)) - - return success; -} - -static bool nvenc_encode_soft(void *data, struct encoder_frame *frame, - struct encoder_packet *packet, - bool *received_packet) -{ - struct nvenc_data *enc = data; - struct nv_cuda_surface *surf; - struct nv_bitstream *bs; - - bs = &enc->bitstreams.array[enc->next_bitstream]; - surf = &enc->surfaces.array[enc->next_bitstream]; - - deque_push_back(&enc->dts_list, &frame->pts, sizeof(frame->pts)); - - /* ------------------------------------ */ - /* copy to CUDA surface */ - - if (!nvenc_copy_frame(enc, frame, surf)) - return false; - - /* ------------------------------------ */ - /* map output tex so nvenc can use it */ - - NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; - map.registeredResource = surf->res; - map.mappedBufferFmt = enc->surface_format; - - if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) - return false; - - surf->mapped_res = map.mappedResource; - - /* ------------------------------------ */ - /* do actual encode call */ - - return nvenc_encode_shared(enc, bs, surf->mapped_res, frame->pts, - packet, received_packet); -} - -static void nvenc_soft_video_info(void *data, struct video_scale_info *info) -{ - struct nvenc_data *enc = data; - info->format = enc->in_format; -} - -extern void h264_nvenc_defaults(obs_data_t *settings); -extern obs_properties_t *h264_nvenc_properties(void *unused); -#ifdef ENABLE_HEVC -extern void hevc_nvenc_defaults(obs_data_t *settings); -extern obs_properties_t *hevc_nvenc_properties(void *unused); -#endif -extern obs_properties_t *av1_nvenc_properties(void *unused); -extern void av1_nvenc_defaults(obs_data_t *settings); - -static bool nvenc_extra_data(void *data, uint8_t **header, size_t *size) -{ - struct nvenc_data *enc = data; - - if (!enc->header) { - return false; - } - - *header = enc->header; - *size = enc->header_size; - return true; -} - -static bool nvenc_sei_data(void *data, uint8_t **sei, size_t *size) -{ - struct nvenc_data *enc = data; - - if (!enc->sei) { - return false; - } - - *sei = enc->sei; - *size = enc->sei_size; - return true; -} - -struct obs_encoder_info h264_nvenc_info = { - .id = "jim_nvenc", - .codec = "h264", - .type = OBS_ENCODER_VIDEO, - .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | - OBS_ENCODER_CAP_ROI, - .get_name = h264_nvenc_get_name, - .create = h264_nvenc_create, - .destroy = nvenc_destroy, - .update = nvenc_update, -#ifdef _WIN32 - .encode_texture = nvenc_encode_tex, -#else - .encode_texture2 = nvenc_encode_tex2, -#endif - .get_defaults = h264_nvenc_defaults, - .get_properties = h264_nvenc_properties, - .get_extra_data = nvenc_extra_data, - .get_sei_data = nvenc_sei_data, -}; - -#ifdef ENABLE_HEVC -struct obs_encoder_info hevc_nvenc_info = { - .id = "jim_hevc_nvenc", - .codec = "hevc", - .type = OBS_ENCODER_VIDEO, - .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | - OBS_ENCODER_CAP_ROI, - .get_name = hevc_nvenc_get_name, - .create = hevc_nvenc_create, - .destroy = nvenc_destroy, - .update = nvenc_update, -#ifdef _WIN32 - .encode_texture = nvenc_encode_tex, -#else - .encode_texture2 = nvenc_encode_tex2, -#endif - .get_defaults = hevc_nvenc_defaults, - .get_properties = hevc_nvenc_properties, - .get_extra_data = nvenc_extra_data, - .get_sei_data = nvenc_sei_data, -}; -#endif - -struct obs_encoder_info av1_nvenc_info = { - .id = "jim_av1_nvenc", - .codec = "av1", - .type = OBS_ENCODER_VIDEO, - .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | - OBS_ENCODER_CAP_ROI, - .get_name = av1_nvenc_get_name, - .create = av1_nvenc_create, - .destroy = nvenc_destroy, - .update = nvenc_update, -#ifdef _WIN32 - .encode_texture = nvenc_encode_tex, -#else - .encode_texture2 = nvenc_encode_tex2, -#endif - .get_defaults = av1_nvenc_defaults, - .get_properties = av1_nvenc_properties, - .get_extra_data = nvenc_extra_data, -}; - -struct obs_encoder_info h264_nvenc_soft_info = { - .id = "obs_nvenc_h264_cuda", - .codec = "h264", - .type = OBS_ENCODER_VIDEO, - .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | - OBS_ENCODER_CAP_INTERNAL, - .get_name = h264_nvenc_soft_get_name, - .create = h264_nvenc_soft_create, - .destroy = nvenc_destroy, - .update = nvenc_update, - .encode = nvenc_encode_soft, - .get_defaults = h264_nvenc_defaults, - .get_properties = h264_nvenc_properties, - .get_extra_data = nvenc_extra_data, - .get_sei_data = nvenc_sei_data, - .get_video_info = nvenc_soft_video_info, -}; - -#ifdef ENABLE_HEVC -struct obs_encoder_info hevc_nvenc_soft_info = { - .id = "obs_nvenc_hevc_cuda", - .codec = "hevc", - .type = OBS_ENCODER_VIDEO, - .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | - OBS_ENCODER_CAP_INTERNAL, - .get_name = hevc_nvenc_soft_get_name, - .create = hevc_nvenc_soft_create, - .destroy = nvenc_destroy, - .update = nvenc_update, - .encode = nvenc_encode_soft, - .get_defaults = hevc_nvenc_defaults, - .get_properties = hevc_nvenc_properties, - .get_extra_data = nvenc_extra_data, - .get_sei_data = nvenc_sei_data, - .get_video_info = nvenc_soft_video_info, -}; -#endif - -struct obs_encoder_info av1_nvenc_soft_info = { - .id = "obs_nvenc_av1_cuda", - .codec = "av1", - .type = OBS_ENCODER_VIDEO, - .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | - OBS_ENCODER_CAP_INTERNAL, - .get_name = av1_nvenc_soft_get_name, - .create = av1_nvenc_soft_create, - .destroy = nvenc_destroy, - .update = nvenc_update, - .encode = nvenc_encode_soft, - .get_defaults = av1_nvenc_defaults, - .get_properties = av1_nvenc_properties, - .get_extra_data = nvenc_extra_data, - .get_video_info = nvenc_soft_video_info, -}; diff --git a/plugins/obs-ffmpeg/obs-nvenc.h b/plugins/obs-ffmpeg/obs-nvenc.h deleted file mode 100644 index 3c0f949f099c9d..00000000000000 --- a/plugins/obs-ffmpeg/obs-nvenc.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#include -#include -#include - -#include "obs-nvenc-ver.h" - -/* Missing from FFmpeg headers */ -typedef CUresult CUDAAPI tcuMemHostRegister(void *p, size_t bytesize, - unsigned int Flags); -typedef CUresult CUDAAPI tcuMemHostUnregister(void *p); - -typedef struct CudaFunctions { - tcuInit *cuInit; - - tcuDeviceGetCount *cuDeviceGetCount; - tcuDeviceGet *cuDeviceGet; - tcuDeviceGetAttribute *cuDeviceGetAttribute; - - tcuCtxCreate_v2 *cuCtxCreate; - tcuCtxDestroy_v2 *cuCtxDestroy; - tcuCtxPushCurrent_v2 *cuCtxPushCurrent; - tcuCtxPopCurrent_v2 *cuCtxPopCurrent; - - tcuArray3DCreate *cuArray3DCreate; - tcuArrayDestroy *cuArrayDestroy; - tcuMemcpy2D_v2 *cuMemcpy2D; - - tcuGetErrorName *cuGetErrorName; - tcuGetErrorString *cuGetErrorString; - - tcuMemHostRegister *cuMemHostRegister; - tcuMemHostUnregister *cuMemHostUnregister; - -#ifndef _WIN32 - tcuGLGetDevices_v2 *cuGLGetDevices; - tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage; - tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource; - tcuGraphicsMapResources *cuGraphicsMapResources; - tcuGraphicsUnmapResources *cuGraphicsUnmapResources; - tcuGraphicsSubResourceGetMappedArray - *cuGraphicsSubResourceGetMappedArray; -#endif -} CudaFunctions; - -typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)( - NV_ENCODE_API_FUNCTION_LIST *); - -extern const char *nv_error_name(NVENCSTATUS err); -extern NV_ENCODE_API_FUNCTION_LIST nv; -extern NV_CREATE_INSTANCE_FUNC nv_create_instance; -extern CudaFunctions *cu; -extern uint32_t get_nvenc_ver(void); -extern bool init_nvenc(obs_encoder_t *encoder); -extern bool init_cuda(obs_encoder_t *encoder); -bool cuda_get_error_desc(CUresult res, const char **name, const char **desc); -bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...); -bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err, - const char *func, const char *call); - -#define nv_fail(encoder, format, ...) \ - nv_fail2(encoder, enc->session, format, ##__VA_ARGS__) - -#define nv_failed(encoder, err, func, call) \ - nv_failed2(encoder, enc->session, err, func, call) From 911a49070beb15beeca967ed2b781decf631f73b Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 11 Mar 2024 10:00:17 +0100 Subject: [PATCH 0375/1073] obs-nvenc: Add new NVENC plugin --- plugins/CMakeLists.txt | 1 + plugins/obs-nvenc/CMakeLists.txt | 58 + .../obs-nvenc/cmake/windows/obs-module.rc.in | 24 + plugins/obs-nvenc/cuda-helpers.c | 172 ++ plugins/obs-nvenc/cuda-helpers.h | 66 + plugins/obs-nvenc/data/locale/en-US.ini | 71 + plugins/obs-nvenc/nvenc-compat.c | 457 ++++++ plugins/obs-nvenc/nvenc-cuda.c | 345 ++++ plugins/obs-nvenc/nvenc-d3d11.c | 278 ++++ plugins/obs-nvenc/nvenc-helpers.c | 409 +++++ plugins/obs-nvenc/nvenc-helpers.h | 89 + plugins/obs-nvenc/nvenc-internal.h | 211 +++ plugins/obs-nvenc/nvenc-opengl.c | 158 ++ plugins/obs-nvenc/nvenc-opts-parser.c | 220 +++ plugins/obs-nvenc/nvenc-properties.c | 324 ++++ plugins/obs-nvenc/nvenc.c | 1444 +++++++++++++++++ .../obs-nvenc/obs-nvenc-test/CMakeLists.txt | 12 + .../obs-nvenc-test/obs-nvenc-test.cpp | 532 ++++++ plugins/obs-nvenc/obs-nvenc.c | 30 + plugins/obs-nvenc/obs-nvenc.h | 11 + 20 files changed, 4912 insertions(+) create mode 100644 plugins/obs-nvenc/CMakeLists.txt create mode 100644 plugins/obs-nvenc/cmake/windows/obs-module.rc.in create mode 100644 plugins/obs-nvenc/cuda-helpers.c create mode 100644 plugins/obs-nvenc/cuda-helpers.h create mode 100644 plugins/obs-nvenc/data/locale/en-US.ini create mode 100644 plugins/obs-nvenc/nvenc-compat.c create mode 100644 plugins/obs-nvenc/nvenc-cuda.c create mode 100644 plugins/obs-nvenc/nvenc-d3d11.c create mode 100644 plugins/obs-nvenc/nvenc-helpers.c create mode 100644 plugins/obs-nvenc/nvenc-helpers.h create mode 100644 plugins/obs-nvenc/nvenc-internal.h create mode 100644 plugins/obs-nvenc/nvenc-opengl.c create mode 100644 plugins/obs-nvenc/nvenc-opts-parser.c create mode 100644 plugins/obs-nvenc/nvenc-properties.c create mode 100644 plugins/obs-nvenc/nvenc.c create mode 100644 plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt create mode 100644 plugins/obs-nvenc/obs-nvenc-test/obs-nvenc-test.cpp create mode 100644 plugins/obs-nvenc/obs-nvenc.c create mode 100644 plugins/obs-nvenc/obs-nvenc.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 68cb1024970cab..632a40be4604f2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -62,6 +62,7 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) add_obs_plugin(obs-ffmpeg) add_obs_plugin(obs-filters) add_obs_plugin(obs-libfdk) + add_obs_plugin(obs-nvenc PLATFORMS WINDOWS LINUX) add_obs_plugin(obs-outputs) add_obs_plugin( obs-qsv11 diff --git a/plugins/obs-nvenc/CMakeLists.txt b/plugins/obs-nvenc/CMakeLists.txt new file mode 100644 index 00000000000000..301dd7edfc8476 --- /dev/null +++ b/plugins/obs-nvenc/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +option(ENABLE_NVENC "Build NVIDIA Hardware Encoder Plugin" ON) +option(ENABLE_NVENC_FFMPEG_IDS "Register FFmpeg encoder IDs" ON) +mark_as_advanced(ENABLE_NVENC_FFMPEG_IDS) + +if(NOT ENABLE_NVENC) + target_disable_feature(obs-nvenc "NVIDIA Hardware Encoder") + target_disable(obs-nvenc) + return() +endif() + +if(NOT TARGET OBS::opts-parser) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") +endif() + +if(OS_LINUX AND NOT TARGET OBS::glad) + add_subdirectory("${CMAKE_SOURCE_DIR}/deps/glad" "${CMAKE_BINARY_DIR}/deps/glad") +endif() + +find_package(FFnvcodec 12 REQUIRED) + +add_library(obs-nvenc MODULE) +add_library(OBS::nvenc ALIAS obs-nvenc) + +add_subdirectory(obs-nvenc-test) + +target_sources( + obs-nvenc + PRIVATE # cmake-format: sortable + $<$:nvenc-opengl.c> + $<$:nvenc-d3d11.c> + cuda-helpers.c + cuda-helpers.h + nvenc-compat.c + nvenc-cuda.c + nvenc-helpers.c + nvenc-helpers.h + nvenc-internal.h + nvenc-opts-parser.c + nvenc-properties.c + nvenc.c + obs-nvenc.c + obs-nvenc.h) + +target_link_libraries(obs-nvenc PRIVATE OBS::libobs OBS::opts-parser FFnvcodec::FFnvcodec + $<$:OBS::glad>) + +target_compile_definitions(obs-nvenc PRIVATE $<$:REGISTER_FFMPEG_IDS>) + +if(OS_WINDOWS) + configure_file(cmake/windows/obs-module.rc.in obs-nvenc.rc) + target_sources(obs-nvenc PRIVATE obs-nvenc.rc) +endif() + +# cmake-format: off +set_target_properties_obs(obs-nvenc PROPERTIES FOLDER plugins/obs-nvenc PREFIX "") +# cmake-format: on diff --git a/plugins/obs-nvenc/cmake/windows/obs-module.rc.in b/plugins/obs-nvenc/cmake/windows/obs-module.rc.in new file mode 100644 index 00000000000000..e0c73d52f79837 --- /dev/null +++ b/plugins/obs-nvenc/cmake/windows/obs-module.rc.in @@ -0,0 +1,24 @@ +1 VERSIONINFO +FILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "${OBS_COMPANY_NAME}" + VALUE "FileDescription", "OBS NVENC module" + VALUE "FileVersion", "${OBS_VERSION_CANONICAL}" + VALUE "ProductName", "${OBS_PRODUCT_NAME}" + VALUE "ProductVersion", "${OBS_VERSION_CANONICAL}" + VALUE "Comments", "${OBS_COMMENTS}" + VALUE "LegalCopyright", "${OBS_LEGAL_COPYRIGHT}" + VALUE "InternalName", "obs-nvenc" + VALUE "OriginalFilename", "obs-nvenc" + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04B0 + END +END diff --git a/plugins/obs-nvenc/cuda-helpers.c b/plugins/obs-nvenc/cuda-helpers.c new file mode 100644 index 00000000000000..430a4d5d82c65d --- /dev/null +++ b/plugins/obs-nvenc/cuda-helpers.c @@ -0,0 +1,172 @@ +#include "obs-nvenc.h" + +#include "nvenc-internal.h" +#include "cuda-helpers.h" + +#include +#include +#include +#include +#include + +static void *cuda_lib = NULL; +static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; +CudaFunctions *cu = NULL; + +bool load_cuda_lib(void) +{ +#ifdef _WIN32 + cuda_lib = os_dlopen("nvcuda.dll"); +#else + cuda_lib = os_dlopen("libcuda.so.1"); +#endif + return cuda_lib != NULL; +} + +static void *load_cuda_func(const char *func) +{ + void *func_ptr = os_dlsym(cuda_lib, func); + if (!func_ptr) { + blog(LOG_ERROR, "[obs-nvenc] Could not load function: %s", + func); + } + return func_ptr; +} + +typedef struct cuda_function { + ptrdiff_t offset; + const char *name; +} cuda_function; + +static const cuda_function cuda_functions[] = { + {offsetof(CudaFunctions, cuInit), "cuInit"}, + + {offsetof(CudaFunctions, cuDeviceGetCount), "cuDeviceGetCount"}, + {offsetof(CudaFunctions, cuDeviceGet), "cuDeviceGet"}, + {offsetof(CudaFunctions, cuDeviceGetAttribute), "cuDeviceGetAttribute"}, + + {offsetof(CudaFunctions, cuCtxCreate), "cuCtxCreate_v2"}, + {offsetof(CudaFunctions, cuCtxDestroy), "cuCtxDestroy_v2"}, + {offsetof(CudaFunctions, cuCtxPushCurrent), "cuCtxPushCurrent_v2"}, + {offsetof(CudaFunctions, cuCtxPopCurrent), "cuCtxPopCurrent_v2"}, + + {offsetof(CudaFunctions, cuArray3DCreate), "cuArray3DCreate_v2"}, + {offsetof(CudaFunctions, cuArrayDestroy), "cuArrayDestroy"}, + {offsetof(CudaFunctions, cuMemcpy2D), "cuMemcpy2D_v2"}, + + {offsetof(CudaFunctions, cuGetErrorName), "cuGetErrorName"}, + {offsetof(CudaFunctions, cuGetErrorString), "cuGetErrorString"}, + + {offsetof(CudaFunctions, cuMemHostRegister), "cuMemHostRegister_v2"}, + {offsetof(CudaFunctions, cuMemHostUnregister), "cuMemHostUnregister"}, + +#ifndef _WIN32 + {offsetof(CudaFunctions, cuGLGetDevices), "cuGLGetDevices_v2"}, + {offsetof(CudaFunctions, cuGraphicsGLRegisterImage), + "cuGraphicsGLRegisterImage"}, + {offsetof(CudaFunctions, cuGraphicsUnregisterResource), + "cuGraphicsUnregisterResource"}, + {offsetof(CudaFunctions, cuGraphicsMapResources), + "cuGraphicsMapResources"}, + {offsetof(CudaFunctions, cuGraphicsUnmapResources), + "cuGraphicsUnmapResources"}, + {offsetof(CudaFunctions, cuGraphicsSubResourceGetMappedArray), + "cuGraphicsSubResourceGetMappedArray"}, +#endif +}; + +static const size_t num_cuda_funcs = + sizeof(cuda_functions) / sizeof(cuda_function); + +static bool init_cuda_internal(obs_encoder_t *encoder) +{ + static bool initialized = false; + static bool success = false; + + if (initialized) + return success; + initialized = true; + + if (!load_cuda_lib()) { + obs_encoder_set_last_error(encoder, + "Loading CUDA library failed."); + return false; + } + + cu = bzalloc(sizeof(CudaFunctions)); + + for (size_t idx = 0; idx < num_cuda_funcs; idx++) { + const cuda_function func = cuda_functions[idx]; + void *fptr = load_cuda_func(func.name); + + if (!fptr) { + blog(LOG_ERROR, + "[obs-nvenc] Failed to find CUDA function: %s", + func.name); + obs_encoder_set_last_error( + encoder, "Loading CUDA functions failed."); + return false; + } + + *(uintptr_t *)((uintptr_t)cu + func.offset) = (uintptr_t)fptr; + } + + success = true; + return true; +} + +bool cuda_get_error_desc(CUresult res, const char **name, const char **desc) +{ + if (cu->cuGetErrorName(res, name) != CUDA_SUCCESS || + cu->cuGetErrorString(res, desc) != CUDA_SUCCESS) + return false; + + return true; +} + +bool cuda_error_check(struct nvenc_data *enc, CUresult res, const char *func, + const char *call) +{ + if (res == CUDA_SUCCESS) + return true; + + struct dstr message = {0}; + + const char *name, *desc; + if (cuda_get_error_desc(res, &name, &desc)) { + dstr_printf(&message, + "%s: CUDA call \"%s\" failed with %s (%d): %s", + func, call, name, res, desc); + } else { + dstr_printf(&message, "%s: CUDA call \"%s\" failed with %d", + func, call, res); + } + + error("%s", message.array); + obs_encoder_set_last_error(enc->encoder, message.array); + + dstr_free(&message); + return false; +} + +bool init_cuda(obs_encoder_t *encoder) +{ + bool success; + + pthread_mutex_lock(&init_mutex); + success = init_cuda_internal(encoder); + pthread_mutex_unlock(&init_mutex); + + return success; +} + +void obs_cuda_load(void) +{ + pthread_mutex_init(&init_mutex, NULL); +} + +void obs_cuda_unload(void) +{ + bfree(cu); + pthread_mutex_destroy(&init_mutex); +} diff --git a/plugins/obs-nvenc/cuda-helpers.h b/plugins/obs-nvenc/cuda-helpers.h new file mode 100644 index 00000000000000..be76261fef6335 --- /dev/null +++ b/plugins/obs-nvenc/cuda-helpers.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include + +/* Missing from FFmpeg headers */ +typedef CUresult CUDAAPI tcuMemHostRegister(void *p, size_t bytesize, + unsigned int Flags); +typedef CUresult CUDAAPI tcuMemHostUnregister(void *p); + +#define CUDA_ERROR_INVALID_GRAPHICS_CONTEXT 219 +#define CUDA_ARRAY3D_SURFACE_LDST 0x02 + +typedef struct CudaFunctions { + tcuInit *cuInit; + + tcuDeviceGetCount *cuDeviceGetCount; + tcuDeviceGet *cuDeviceGet; + tcuDeviceGetAttribute *cuDeviceGetAttribute; + + tcuCtxCreate_v2 *cuCtxCreate; + tcuCtxDestroy_v2 *cuCtxDestroy; + tcuCtxPushCurrent_v2 *cuCtxPushCurrent; + tcuCtxPopCurrent_v2 *cuCtxPopCurrent; + + tcuArray3DCreate *cuArray3DCreate; + tcuArrayDestroy *cuArrayDestroy; + tcuMemcpy2D_v2 *cuMemcpy2D; + + tcuGetErrorName *cuGetErrorName; + tcuGetErrorString *cuGetErrorString; + + tcuMemHostRegister *cuMemHostRegister; + tcuMemHostUnregister *cuMemHostUnregister; + +#ifndef _WIN32 + tcuGLGetDevices_v2 *cuGLGetDevices; + tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage; + tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource; + tcuGraphicsMapResources *cuGraphicsMapResources; + tcuGraphicsUnmapResources *cuGraphicsUnmapResources; + tcuGraphicsSubResourceGetMappedArray + *cuGraphicsSubResourceGetMappedArray; +#endif +} CudaFunctions; + +extern CudaFunctions *cu; + +bool init_cuda(obs_encoder_t *encoder); +bool cuda_get_error_desc(CUresult res, const char **name, const char **desc); + +struct nvenc_data; +bool cuda_error_check(struct nvenc_data *enc, CUresult res, const char *func, + const char *call); + +/* CUDA error handling */ +#define CU_FAILED(call) \ + if (!cuda_error_check(enc, call, __FUNCTION__, #call)) \ + return false; + +#define CU_CHECK(call) \ + if (!cuda_error_check(enc, call, __FUNCTION__, #call)) { \ + success = false; \ + goto unmap; \ + } diff --git a/plugins/obs-nvenc/data/locale/en-US.ini b/plugins/obs-nvenc/data/locale/en-US.ini new file mode 100644 index 00000000000000..4d1418758fe5f8 --- /dev/null +++ b/plugins/obs-nvenc/data/locale/en-US.ini @@ -0,0 +1,71 @@ +RateControl="Rate Control" +CBR="Constant Bitrate" +VBR="Variable Bitrate" +CQVBR="Variable Bitrate with Target Quality" +CQP="Constant QP" +Lossless="Lossless" + +Bitrate="Bitrate" +MaxBitrate="Maximum Bitrate (0 = Level Limit)" +TargetQuality="Target Quality" + +KeyframeIntervalSec="Keyframe interval (seconds, 0 = auto)" + +LookAhead="Look-ahead" +LookAhead.ToolTip="Enables Lookahead.\n\nIf enabled, it will increase visual quality by determining a better bitrate distribution through analysis of future frames,\nat the cost of increased GPU utilization and latency." + +AdaptiveQuantization="Adaptive Quantization" +AdaptiveQuantization.ToolTip="Enables Temporal/Spatial Adaptive Quantization which optimizes the use of bitrate for increased perceived visual quality,\nespecially in situations with high motion, at the cost of increased GPU utilization.\n\nFormerly known as \"Psycho-Visual Tuning\"." + +Preset="Preset" +Preset.p1="P1: Fastest (Lowest Quality)" +Preset.p2="P2: Faster (Lower Quality)" +Preset.p3="P3: Fast (Low Quality)" +Preset.p4="P4: Medium (Medium Quality)" +Preset.p5="P5: Slow (Good Quality)" +Preset.p6="P6: Slower (Better Quality)" +Preset.p7="P7: Slowest (Best Quality)" + +Tuning.uhq="Ultra High Quality (slow, not recommended)" +Tuning.hq="High Quality" +Tuning.ll="Low Latency" +Tuning.ull="Ultra Low Latency" + +Multipass="Multipass Mode" +Multipass.disabled="Single Pass" +Multipass.qres="Two Passes (Quarter Resolution)" +Multipass.fullres="Two Passes (Full Resolution)" + +BFrames="B-Frames" +BFrameRefMode="B-Frame as Reference" +BframeRefMode.Disabled="Disabled" +BframeRefMode.Each="Each" +BframeRefMode.Middle="Middle b-frame only" + +SplitEncode="Split Encode" +SplitEncode.Auto="Auto" +SplitEncode.Disabled="Disabled" +SplitEncode.Enabled="Two-way split" +SplitEncode.ThreeWay="Three-way split" + +Opts="Custom Encoder Options" +Opts.TT="Space-separated list of options to apply to the rate control and codec settings,\nbased their names in the nvEncodeAPI header.\ne.g. \"lookaheadDepth=16 aqStrength=4\"" + +Error="Failed to open NVENC codec: %1" +GenericError="Try installing the latest NVIDIA driver and closing other recording software that might be using NVENC such as NVIDIA ShadowPlay or Windows Game DVR." +BadGPUIndex="You have selected GPU %1 in your output encoder settings. Set this back to 0 and try again." +OutdatedDriver="The installed NVIDIA driver does not support this NVENC version, try updating the driver." +UnsupportedDevice="NVENC Error: Unsupported device. Check that your video card supports NVENC and try updating the driver." +TooManySessions="NVENC Error: Too many concurrent sessions. Try closing other recording software that might be using NVENC such as NVIDIA ShadowPlay or Windows Game DVR." +CheckDrivers="Try installing the latest NVIDIA driver." + +8bitUnsupportedHdr="OBS does not support 8-bit output of Rec. 2100." +I010Unsupported="NVENC does not support I010. Use P010 instead." +10bitUnsupported="Cannot perform 10-bit encode on this encoder." +16bitUnsupported="Cannot perform 16-bit encode on this encoder." +444Unsupported="Cannot perform 4:4:4 encode on this encoder." + +# Legacy strings, to be removed once compat encoders are removed +CQLevel="CQ Level" +PsychoVisualTuning="Psycho Visual Tuning" +PsychoVisualTuning.ToolTip="Enables encoder settings that optimize the use of bitrate for increased perceived visual quality,\nespecially in situations with high motion, at the cost of increased GPU utilization." diff --git a/plugins/obs-nvenc/nvenc-compat.c b/plugins/obs-nvenc/nvenc-compat.c new file mode 100644 index 00000000000000..e8958d3b065900 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-compat.c @@ -0,0 +1,457 @@ +#include "nvenc-helpers.h" + +#include + +/* + * Compatibility encoder objects for pre-31.0 encoder compatibility. + * + * All they do is update the settings object, and then reroute to one of the + * new encoder implementations. + * + * This should be removed once NVENC settings are migrated directly and + * backwards-compatibility is no longer required. + */ + +/* ------------------------------------------------------------------------- */ +/* Actual redirector implementation. */ + +static void migrate_settings(obs_data_t *settings, enum codec_type codec) +{ + struct encoder_caps *caps = get_encoder_caps(codec); + + const char *preset = obs_data_get_string(settings, "preset2"); + obs_data_set_string(settings, "preset", preset); + + const char *rc = obs_data_get_string(settings, "rate_control"); + /* Old NVENC allowed lossless even if unsupported, + * and just emulated it via CQP 0, do the same here. */ + if (!caps->lossless && strcmp(rc, "lossless") == 0) { + obs_data_set_string(settings, "rate_control", "CQP"); + obs_data_set_int(settings, "cqp", 0); + } + + obs_data_set_bool(settings, "adaptive_quantization", + obs_data_get_bool(settings, "psycho_aq")); + + if (obs_data_has_user_value(settings, "gpu") && + num_encoder_devices() > 1) { + obs_data_set_int(settings, "device", + obs_data_get_int(settings, "gpu")); + } +} + +static void *nvenc_reroute(enum codec_type codec, obs_data_t *settings, + obs_encoder_t *encoder, bool texture) +{ + /* Update settings object to v2 encoder configuration */ + migrate_settings(settings, codec); + + switch (codec) { + case CODEC_H264: + return obs_encoder_create_rerouted( + encoder, + texture ? "obs_nvenc_h264_tex" : "obs_nvenc_h264_soft"); + case CODEC_HEVC: + return obs_encoder_create_rerouted( + encoder, + texture ? "obs_nvenc_hevc_tex" : "obs_nvenc_hevc_soft"); + case CODEC_AV1: + return obs_encoder_create_rerouted( + encoder, + texture ? "obs_nvenc_av1_tex" : "obs_nvenc_av1_soft"); + } + + return NULL; +} + +/* ------------------------------------------------------------------------- */ + +static const char *h264_nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC H.264 (deprecated)"; +} + +#ifdef ENABLE_HEVC +static const char *hevc_nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC HEVC (deprecated)"; +} +#endif + +static const char *av1_nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC AV1 (deprecated)"; +} + +static void *h264_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_reroute(CODEC_H264, settings, encoder, true); +} + +#ifdef ENABLE_HEVC +static void *hevc_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_reroute(CODEC_HEVC, settings, encoder, true); +} +#endif + +static void *av1_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_reroute(CODEC_AV1, settings, encoder, true); +} + +static void *h264_nvenc_soft_create(obs_data_t *settings, + obs_encoder_t *encoder) +{ + return nvenc_reroute(CODEC_H264, settings, encoder, false); +} + +#ifdef ENABLE_HEVC +static void *hevc_nvenc_soft_create(obs_data_t *settings, + obs_encoder_t *encoder) +{ + return nvenc_reroute(CODEC_HEVC, settings, encoder, false); +} +#endif + +static void *av1_nvenc_soft_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_reroute(CODEC_AV1, settings, encoder, false); +} + +static void nvenc_defaults_base(enum codec_type codec, obs_data_t *settings) +{ + /* Defaults from legacy FFmpeg encoder */ + obs_data_set_default_int(settings, "bitrate", 2500); + obs_data_set_default_int(settings, "max_bitrate", 5000); + obs_data_set_default_int(settings, "keyint_sec", 0); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_string(settings, "rate_control", "CBR"); + obs_data_set_default_string(settings, "preset2", "p5"); + obs_data_set_default_string(settings, "multipass", "qres"); + obs_data_set_default_string(settings, "tune", "hq"); + obs_data_set_default_string(settings, "profile", + codec != CODEC_H264 ? "main" : "high"); + obs_data_set_default_bool(settings, "psycho_aq", true); + obs_data_set_default_int(settings, "gpu", 0); + obs_data_set_default_int(settings, "bf", 2); + obs_data_set_default_bool(settings, "repeat_headers", false); +} + +static void h264_nvenc_defaults(obs_data_t *settings) +{ + nvenc_defaults_base(CODEC_H264, settings); +} + +#ifdef ENABLE_HEVC +static void hevc_nvenc_defaults(obs_data_t *settings) +{ + nvenc_defaults_base(CODEC_HEVC, settings); +} +#endif + +static void av1_nvenc_defaults(obs_data_t *settings) +{ + nvenc_defaults_base(CODEC_AV1, settings); +} + +static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, + obs_data_t *settings) +{ + const char *rc = obs_data_get_string(settings, "rate_control"); + bool cqp = astrcmpi(rc, "CQP") == 0; + bool vbr = astrcmpi(rc, "VBR") == 0; + bool lossless = astrcmpi(rc, "lossless") == 0; + + p = obs_properties_get(ppts, "bitrate"); + obs_property_set_visible(p, !cqp && !lossless); + p = obs_properties_get(ppts, "max_bitrate"); + obs_property_set_visible(p, vbr); + p = obs_properties_get(ppts, "cqp"); + obs_property_set_visible(p, cqp); + p = obs_properties_get(ppts, "preset2"); + obs_property_set_visible(p, !lossless); + p = obs_properties_get(ppts, "tune"); + obs_property_set_visible(p, !lossless); + + return true; +} + +static obs_properties_t *nvenc_properties_internal(enum codec_type codec) +{ + obs_properties_t *props = obs_properties_create(); + obs_property_t *p; + + p = obs_properties_add_list(props, "rate_control", + obs_module_text("RateControl"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, "CBR", "CBR"); + obs_property_list_add_string(p, "CQP", "CQP"); + obs_property_list_add_string(p, "VBR", "VBR"); + obs_property_list_add_string(p, obs_module_text("Lossless"), + "lossless"); + + obs_property_set_modified_callback(p, rate_control_modified); + + p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), + 50, 300000, 50); + obs_property_int_set_suffix(p, " Kbps"); + p = obs_properties_add_int(props, "max_bitrate", + obs_module_text("MaxBitrate"), 50, 300000, + 50); + obs_property_int_set_suffix(p, " Kbps"); + + obs_properties_add_int(props, "cqp", obs_module_text("CQLevel"), 1, + codec == CODEC_AV1 ? 63 : 51, 1); + + p = obs_properties_add_int(props, "keyint_sec", + obs_module_text("KeyframeIntervalSec"), 0, + 10, 1); + obs_property_int_set_suffix(p, " s"); + + p = obs_properties_add_list(props, "preset2", obs_module_text("Preset"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_preset(val) \ + obs_property_list_add_string(p, obs_module_text("Preset." val), val) + + add_preset("p1"); + add_preset("p2"); + add_preset("p3"); + add_preset("p4"); + add_preset("p5"); + add_preset("p6"); + add_preset("p7"); +#undef add_preset + + p = obs_properties_add_list(props, "tune", obs_module_text("Tuning"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_tune(val) \ + obs_property_list_add_string(p, obs_module_text("Tuning." val), val) + add_tune("hq"); + add_tune("ll"); + add_tune("ull"); +#undef add_tune + + p = obs_properties_add_list(props, "multipass", + obs_module_text("Multipass"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_multipass(val) \ + obs_property_list_add_string(p, obs_module_text("Multipass." val), val) + add_multipass("disabled"); + add_multipass("qres"); + add_multipass("fullres"); +#undef add_multipass + + p = obs_properties_add_list(props, "profile", + obs_module_text("Profile"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_profile(val) obs_property_list_add_string(p, val, val) + if (codec == CODEC_HEVC) { + add_profile("main10"); + add_profile("main"); + } else if (codec == CODEC_AV1) { + add_profile("main"); + } else { + add_profile("high"); + add_profile("main"); + add_profile("baseline"); + } +#undef add_profile + + p = obs_properties_add_bool(props, "lookahead", + obs_module_text("LookAhead")); + obs_property_set_long_description(p, + obs_module_text("LookAhead.ToolTip")); + p = obs_properties_add_bool(props, "repeat_headers", "repeat_headers"); + obs_property_set_visible(p, false); + + p = obs_properties_add_bool(props, "psycho_aq", + obs_module_text("PsychoVisualTuning")); + obs_property_set_long_description( + p, obs_module_text("PsychoVisualTuning.ToolTip")); + + obs_properties_add_int(props, "gpu", obs_module_text("GPU"), 0, 8, 1); + + obs_properties_add_int(props, "bf", obs_module_text("BFrames"), 0, 4, + 1); + + return props; +} + +static obs_properties_t *h264_nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(CODEC_H264); +} + +#ifdef ENABLE_HEVC +static obs_properties_t *hevc_nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(CODEC_HEVC); +} +#endif + +static obs_properties_t *av1_nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(CODEC_AV1); +} + +/* ------------------------------------------------------------------------- */ +/* Stubs for required - but unused - functions. */ + +static void fake_nvenc_destroy(void *p) +{ + UNUSED_PARAMETER(p); +} + +static bool fake_encode(void *data, struct encoder_frame *frame, + struct encoder_packet *packet, bool *received_packet) +{ + UNUSED_PARAMETER(data); + UNUSED_PARAMETER(frame); + UNUSED_PARAMETER(packet); + UNUSED_PARAMETER(received_packet); + + return true; +} + +static bool fake_encode_tex2(void *data, struct encoder_texture *texture, + int64_t pts, uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, + bool *received_packet) +{ + UNUSED_PARAMETER(data); + UNUSED_PARAMETER(texture); + UNUSED_PARAMETER(pts); + UNUSED_PARAMETER(lock_key); + UNUSED_PARAMETER(next_key); + UNUSED_PARAMETER(packet); + UNUSED_PARAMETER(received_packet); + + return true; +} + +struct obs_encoder_info compat_h264_nvenc_info = { + .id = "jim_nvenc", + .codec = "h264", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | + OBS_ENCODER_CAP_ROI | OBS_ENCODER_CAP_DEPRECATED, + .get_name = h264_nvenc_get_name, + .create = h264_nvenc_create, + .destroy = fake_nvenc_destroy, + .encode_texture2 = fake_encode_tex2, + .get_defaults = h264_nvenc_defaults, + .get_properties = h264_nvenc_properties, +}; + +#ifdef ENABLE_HEVC +struct obs_encoder_info compat_hevc_nvenc_info = { + .id = "jim_hevc_nvenc", + .codec = "hevc", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | + OBS_ENCODER_CAP_ROI | OBS_ENCODER_CAP_DEPRECATED, + .get_name = hevc_nvenc_get_name, + .create = hevc_nvenc_create, + .destroy = fake_nvenc_destroy, + .encode_texture2 = fake_encode_tex2, + .get_defaults = hevc_nvenc_defaults, + .get_properties = hevc_nvenc_properties, +}; +#endif + +struct obs_encoder_info compat_av1_nvenc_info = { + .id = "jim_av1_nvenc", + .codec = "av1", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | + OBS_ENCODER_CAP_ROI | OBS_ENCODER_CAP_DEPRECATED, + .get_name = av1_nvenc_get_name, + .create = av1_nvenc_create, + .destroy = fake_nvenc_destroy, + .encode_texture2 = fake_encode_tex2, + .get_defaults = av1_nvenc_defaults, + .get_properties = av1_nvenc_properties, +}; + +struct obs_encoder_info compat_h264_nvenc_soft_info = { + .id = "obs_nvenc_h264_cuda", + .codec = "h264", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | + OBS_ENCODER_CAP_DEPRECATED, + .get_name = h264_nvenc_get_name, + .create = h264_nvenc_soft_create, + .destroy = fake_nvenc_destroy, + .encode = fake_encode, + .get_defaults = h264_nvenc_defaults, + .get_properties = h264_nvenc_properties, +}; + +#ifdef ENABLE_HEVC +struct obs_encoder_info compat_hevc_nvenc_soft_info = { + .id = "obs_nvenc_hevc_cuda", + .codec = "hevc", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | + OBS_ENCODER_CAP_DEPRECATED, + .get_name = hevc_nvenc_get_name, + .create = hevc_nvenc_soft_create, + .destroy = fake_nvenc_destroy, + .encode = fake_encode, + .get_defaults = hevc_nvenc_defaults, + .get_properties = hevc_nvenc_properties, +}; +#endif + +struct obs_encoder_info compat_av1_nvenc_soft_info = { + .id = "obs_nvenc_av1_cuda", + .codec = "av1", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | + OBS_ENCODER_CAP_DEPRECATED, + .get_name = av1_nvenc_get_name, + .create = av1_nvenc_soft_create, + .destroy = fake_nvenc_destroy, + .encode = fake_encode, + .get_defaults = av1_nvenc_defaults, + .get_properties = av1_nvenc_properties, +}; + +void register_compat_encoders(void) +{ + obs_register_encoder(&compat_h264_nvenc_info); + obs_register_encoder(&compat_h264_nvenc_soft_info); +#ifdef ENABLE_HEVC + obs_register_encoder(&compat_hevc_nvenc_info); + obs_register_encoder(&compat_hevc_nvenc_soft_info); +#endif + if (is_codec_supported(CODEC_AV1)) { + obs_register_encoder(&compat_av1_nvenc_info); + obs_register_encoder(&compat_av1_nvenc_soft_info); + } + +#ifdef REGISTER_FFMPEG_IDS + compat_h264_nvenc_soft_info.id = "ffmpeg_nvenc"; + obs_register_encoder(&compat_h264_nvenc_soft_info); +#ifdef ENABLE_HEVC + compat_hevc_nvenc_soft_info.id = "ffmpeg_hevc_nvenc"; + obs_register_encoder(&compat_hevc_nvenc_soft_info); +#endif +#endif +} diff --git a/plugins/obs-nvenc/nvenc-cuda.c b/plugins/obs-nvenc/nvenc-cuda.c new file mode 100644 index 00000000000000..5cdbca45e165c8 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-cuda.c @@ -0,0 +1,345 @@ +#include "nvenc-internal.h" +#include "nvenc-helpers.h" + +/* + * NVENC implementation using CUDA context and arrays + */ + +/* ------------------------------------------------------------------------- */ +/* CUDA Context management */ + +bool cuda_ctx_init(struct nvenc_data *enc, obs_data_t *settings, + const bool texture) +{ +#ifdef _WIN32 + if (texture) + return true; +#endif + + int count; + CUdevice device; + + int gpu = (int)obs_data_get_int(settings, "device"); +#ifndef _WIN32 + /* CUDA can do fairly efficient cross-GPU OpenGL mappings, allow it as + * a hidden option for experimentation. */ + bool force_cuda_tex = obs_data_get_bool(settings, "force_cuda_tex"); +#endif + + if (gpu == -1) + gpu = 0; + + CU_FAILED(cu->cuInit(0)) + CU_FAILED(cu->cuDeviceGetCount(&count)) + if (!count) { + NV_FAIL("No CUDA devices found"); + return false; + } +#ifdef _WIN32 + CU_FAILED(cu->cuDeviceGet(&device, gpu)) +#else + if (!texture || force_cuda_tex) { + CU_FAILED(cu->cuDeviceGet(&device, gpu)) + } else { + unsigned int ctx_count = 0; + CUdevice devices[2]; + + obs_enter_graphics(); + CUresult res = cu->cuGLGetDevices(&ctx_count, devices, 2, + CU_GL_DEVICE_LIST_ALL); + obs_leave_graphics(); + + if (res != CUDA_SUCCESS || !ctx_count) { + /* Probably running on iGPU, should just fall back to + * non-texture encoder. */ + if (res == CUDA_ERROR_INVALID_GRAPHICS_CONTEXT) { + info("Not running on NVIDIA GPU, falling back " + "to non-texture encoder"); + } else { + const char *name, *desc; + if (cuda_get_error_desc(res, &name, &desc)) { + error("Failed to get a CUDA device for " + "the current OpenGL context: " + "%s: %s", + name, desc); + } else { + error("Failed to get a CUDA device for " + "the current OpenGL context: %d", + res); + } + } + return false; + } + + /* Documentation indicates this should only ever happen with + * SLI, i.e. never for OBS. */ + if (ctx_count > 1) { + warn("Got more than one CUDA devices for OpenGL context," + " this is untested."); + } + + device = devices[0]; + debug("Loading up CUDA on device %u", device); + } +#endif + CU_FAILED(cu->cuCtxCreate(&enc->cu_ctx, 0, device)) + CU_FAILED(cu->cuCtxPopCurrent(NULL)) + + return true; +} + +void cuda_ctx_free(struct nvenc_data *enc) +{ + if (enc->cu_ctx) { + cu->cuCtxPopCurrent(NULL); + cu->cuCtxDestroy(enc->cu_ctx); + } +} + +/* ------------------------------------------------------------------------- */ +/* CUDA Surface management */ + +static bool cuda_surface_init(struct nvenc_data *enc, + struct nv_cuda_surface *nvsurf) +{ + const bool p010 = obs_p010_tex_active(); + CUDA_ARRAY3D_DESCRIPTOR desc; + desc.Width = enc->cx; + desc.Height = enc->cy; + desc.Depth = 0; + desc.Flags = CUDA_ARRAY3D_SURFACE_LDST; + desc.NumChannels = 1; + + if (!enc->non_texture) { + desc.Format = p010 ? CU_AD_FORMAT_UNSIGNED_INT16 + : CU_AD_FORMAT_UNSIGNED_INT8; + desc.Height = enc->cy + enc->cy / 2; + } else { + switch (enc->surface_format) { + case NV_ENC_BUFFER_FORMAT_NV12: + desc.Format = CU_AD_FORMAT_UNSIGNED_INT8; + // Additional half-height plane for UV data + desc.Height += enc->cy / 2; + break; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + desc.Format = CU_AD_FORMAT_UNSIGNED_INT16; + desc.Height += enc->cy / 2; + desc.NumChannels = 2; // number of bytes per element + break; + case NV_ENC_BUFFER_FORMAT_YUV444: + desc.Format = CU_AD_FORMAT_UNSIGNED_INT8; + desc.Height *= 3; // 3 full-size planes + break; + default: + error("Unknown input format: %d", enc->surface_format); + return false; + } + } + + CU_FAILED(cu->cuArray3DCreate(&nvsurf->tex, &desc)) + + NV_ENC_REGISTER_RESOURCE res = {0}; + res.version = NV_ENC_REGISTER_RESOURCE_VER; + res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY; + res.resourceToRegister = (void *)nvsurf->tex; + res.width = enc->cx; + res.height = enc->cy; + res.pitch = (uint32_t)(desc.Width * desc.NumChannels); + if (!enc->non_texture) { + res.bufferFormat = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT + : NV_ENC_BUFFER_FORMAT_NV12; + } else { + res.bufferFormat = enc->surface_format; + } + + if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) { + return false; + } + + nvsurf->res = res.registeredResource; + nvsurf->mapped_res = NULL; + return true; +} + +bool cuda_init_surfaces(struct nvenc_data *enc) +{ + switch (enc->in_format) { + case VIDEO_FORMAT_P010: + enc->surface_format = NV_ENC_BUFFER_FORMAT_YUV420_10BIT; + break; + case VIDEO_FORMAT_I444: + enc->surface_format = NV_ENC_BUFFER_FORMAT_YUV444; + break; + default: + enc->surface_format = NV_ENC_BUFFER_FORMAT_NV12; + } + + da_reserve(enc->surfaces, enc->buf_count); + + CU_FAILED(cu->cuCtxPushCurrent(enc->cu_ctx)) + for (uint32_t i = 0; i < enc->buf_count; i++) { + struct nv_cuda_surface buf; + if (!cuda_surface_init(enc, &buf)) { + return false; + } + + da_push_back(enc->surfaces, &buf); + } + CU_FAILED(cu->cuCtxPopCurrent(NULL)) + + return true; +} + +static void cuda_surface_free(struct nvenc_data *enc, + struct nv_cuda_surface *nvsurf) +{ + if (nvsurf->res) { + if (nvsurf->mapped_res) { + nv.nvEncUnmapInputResource(enc->session, + nvsurf->mapped_res); + } + nv.nvEncUnregisterResource(enc->session, nvsurf->res); + cu->cuArrayDestroy(nvsurf->tex); + } +} + +void cuda_free_surfaces(struct nvenc_data *enc) +{ + if (!enc->cu_ctx) + return; + + cu->cuCtxPushCurrent(enc->cu_ctx); + for (size_t i = 0; i < enc->surfaces.num; i++) { + cuda_surface_free(enc, &enc->surfaces.array[i]); + } + cu->cuCtxPopCurrent(NULL); +} + +/* ------------------------------------------------------------------------- */ +/* Actual encoding stuff */ + +static inline bool copy_frame(struct nvenc_data *enc, + struct encoder_frame *frame, + struct nv_cuda_surface *surf) +{ + bool success = true; + size_t height = enc->cy; + size_t width = enc->cx; + CUDA_MEMCPY2D m = {0}; + + m.srcMemoryType = CU_MEMORYTYPE_HOST; + m.dstMemoryType = CU_MEMORYTYPE_ARRAY; + m.dstArray = surf->tex; + m.WidthInBytes = width; + m.Height = height; + + CU_FAILED(cu->cuCtxPushCurrent(enc->cu_ctx)) + + if (enc->surface_format == NV_ENC_BUFFER_FORMAT_NV12) { + /* Page-locks the host memory so that it can be DMAd directly + * rather than CUDA doing an internal copy to page-locked + * memory before actually DMA-ing to the GPU. */ + CU_CHECK(cu->cuMemHostRegister(frame->data[0], + frame->linesize[0] * height, 0)) + CU_CHECK(cu->cuMemHostRegister( + frame->data[1], frame->linesize[1] * height / 2, 0)) + + m.srcPitch = frame->linesize[0]; + m.srcHost = frame->data[0]; + CU_FAILED(cu->cuMemcpy2D(&m)) + + m.srcPitch = frame->linesize[1]; + m.srcHost = frame->data[1]; + m.dstY += height; + m.Height /= 2; + CU_FAILED(cu->cuMemcpy2D(&m)) + } else if (enc->surface_format == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) { + CU_CHECK(cu->cuMemHostRegister(frame->data[0], + frame->linesize[0] * height, 0)) + CU_CHECK(cu->cuMemHostRegister( + frame->data[1], frame->linesize[1] * height / 2, 0)) + + // P010 lines are double the size (16 bit per pixel) + m.WidthInBytes *= 2; + + m.srcPitch = frame->linesize[0]; + m.srcHost = frame->data[0]; + CU_FAILED(cu->cuMemcpy2D(&m)) + + m.srcPitch = frame->linesize[1]; + m.srcHost = frame->data[1]; + m.dstY += height; + m.Height /= 2; + CU_FAILED(cu->cuMemcpy2D(&m)) + } else { // I444 + CU_CHECK(cu->cuMemHostRegister(frame->data[0], + frame->linesize[0] * height, 0)) + CU_CHECK(cu->cuMemHostRegister(frame->data[1], + frame->linesize[1] * height, 0)) + CU_CHECK(cu->cuMemHostRegister(frame->data[2], + frame->linesize[2] * height, 0)) + + m.srcPitch = frame->linesize[0]; + m.srcHost = frame->data[0]; + CU_FAILED(cu->cuMemcpy2D(&m)) + + m.srcPitch = frame->linesize[1]; + m.srcHost = frame->data[1]; + m.dstY += height; + CU_FAILED(cu->cuMemcpy2D(&m)) + + m.srcPitch = frame->linesize[2]; + m.srcHost = frame->data[2]; + m.dstY += height; + CU_FAILED(cu->cuMemcpy2D(&m)) + } + +unmap: + if (frame->data[0]) + cu->cuMemHostUnregister(frame->data[0]); + if (frame->data[1]) + cu->cuMemHostUnregister(frame->data[1]); + if (frame->data[2]) + cu->cuMemHostUnregister(frame->data[2]); + + CU_FAILED(cu->cuCtxPopCurrent(NULL)) + + return success; +} + +bool cuda_encode(void *data, struct encoder_frame *frame, + struct encoder_packet *packet, bool *received_packet) +{ + struct nvenc_data *enc = data; + struct nv_cuda_surface *surf; + struct nv_bitstream *bs; + + bs = &enc->bitstreams.array[enc->next_bitstream]; + surf = &enc->surfaces.array[enc->next_bitstream]; + + deque_push_back(&enc->dts_list, &frame->pts, sizeof(frame->pts)); + + /* ------------------------------------ */ + /* copy to CUDA surface */ + + if (!copy_frame(enc, frame, surf)) + return false; + + /* ------------------------------------ */ + /* map output tex so nvenc can use it */ + + NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; + map.registeredResource = surf->res; + map.mappedBufferFmt = enc->surface_format; + + if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) + return false; + + surf->mapped_res = map.mappedResource; + + /* ------------------------------------ */ + /* do actual encode call */ + + return nvenc_encode_base(enc, bs, surf->mapped_res, frame->pts, packet, + received_packet); +} diff --git a/plugins/obs-nvenc/nvenc-d3d11.c b/plugins/obs-nvenc/nvenc-d3d11.c new file mode 100644 index 00000000000000..3ebe230140bced --- /dev/null +++ b/plugins/obs-nvenc/nvenc-d3d11.c @@ -0,0 +1,278 @@ +#include "nvenc-internal.h" +#include "nvenc-helpers.h" + +/* + * NVENC implementation using Direct3D 11 context and textures + */ + +/* ------------------------------------------------------------------------- */ +/* D3D11 Context/Device management */ + +static HANDLE get_lib(struct nvenc_data *enc, const char *lib) +{ + HMODULE mod = GetModuleHandleA(lib); + if (mod) + return mod; + + mod = LoadLibraryA(lib); + if (!mod) + error("Failed to load %s", lib); + return mod; +} + +typedef HRESULT(WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **); + +bool d3d11_init(struct nvenc_data *enc, obs_data_t *settings) +{ + HMODULE dxgi = get_lib(enc, "DXGI.dll"); + HMODULE d3d11 = get_lib(enc, "D3D11.dll"); + CREATEDXGIFACTORY1PROC create_dxgi; + PFN_D3D11_CREATE_DEVICE create_device; + IDXGIFactory1 *factory; + IDXGIAdapter *adapter; + ID3D11Device *device; + ID3D11DeviceContext *context; + HRESULT hr; + + if (!dxgi || !d3d11) { + return false; + } + + create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress( + dxgi, "CreateDXGIFactory1"); + create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress( + d3d11, "D3D11CreateDevice"); + + if (!create_dxgi || !create_device) { + error("Failed to load D3D11/DXGI procedures"); + return false; + } + + hr = create_dxgi(&IID_IDXGIFactory1, &factory); + if (FAILED(hr)) { + error_hr("CreateDXGIFactory1 failed"); + return false; + } + + hr = factory->lpVtbl->EnumAdapters(factory, 0, &adapter); + factory->lpVtbl->Release(factory); + if (FAILED(hr)) { + error_hr("EnumAdapters failed"); + return false; + } + + hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, + D3D11_SDK_VERSION, &device, NULL, &context); + adapter->lpVtbl->Release(adapter); + if (FAILED(hr)) { + error_hr("D3D11CreateDevice failed"); + return false; + } + + enc->device = device; + enc->context = context; + return true; +} + +void d3d11_free(struct nvenc_data *enc) +{ + for (size_t i = 0; i < enc->input_textures.num; i++) { + ID3D11Texture2D *tex = enc->input_textures.array[i].tex; + IDXGIKeyedMutex *km = enc->input_textures.array[i].km; + tex->lpVtbl->Release(tex); + km->lpVtbl->Release(km); + } + if (enc->context) { + enc->context->lpVtbl->Release(enc->context); + } + if (enc->device) { + enc->device->lpVtbl->Release(enc->device); + } +} + +/* ------------------------------------------------------------------------- */ +/* D3D11 Surface management */ + +static bool d3d11_texture_init(struct nvenc_data *enc, struct nv_texture *nvtex) +{ + const bool p010 = obs_p010_tex_active(); + + D3D11_TEXTURE2D_DESC desc = {0}; + desc.Width = enc->cx; + desc.Height = enc->cy; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = p010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12; + desc.SampleDesc.Count = 1; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + + ID3D11Device *const device = enc->device; + ID3D11Texture2D *tex; + HRESULT hr = device->lpVtbl->CreateTexture2D(device, &desc, NULL, &tex); + if (FAILED(hr)) { + error_hr("Failed to create texture"); + return false; + } + + tex->lpVtbl->SetEvictionPriority(tex, DXGI_RESOURCE_PRIORITY_MAXIMUM); + + NV_ENC_REGISTER_RESOURCE res = {NV_ENC_REGISTER_RESOURCE_VER}; + res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; + res.resourceToRegister = tex; + res.width = enc->cx; + res.height = enc->cy; + res.bufferFormat = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT + : NV_ENC_BUFFER_FORMAT_NV12; + + if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) { + tex->lpVtbl->Release(tex); + return false; + } + + nvtex->res = res.registeredResource; + nvtex->tex = tex; + nvtex->mapped_res = NULL; + return true; +} + +bool d3d11_init_textures(struct nvenc_data *enc) +{ + //blog(LOG_DEBUG, "buf count: %d", enc->buf_count); + da_reserve(enc->textures, enc->buf_count); + for (uint32_t i = 0; i < enc->buf_count; i++) { + struct nv_texture texture; + if (!d3d11_texture_init(enc, &texture)) { + return false; + } + + da_push_back(enc->textures, &texture); + } + + return true; +} + +static void d3d11_texture_free(struct nvenc_data *enc, struct nv_texture *nvtex) +{ + + if (nvtex->res) { + if (nvtex->mapped_res) { + nv.nvEncUnmapInputResource(enc->session, + nvtex->mapped_res); + } + nv.nvEncUnregisterResource(enc->session, nvtex->res); + nvtex->tex->lpVtbl->Release(nvtex->tex); + } +} + +void d3d11_free_textures(struct nvenc_data *enc) +{ + for (size_t i = 0; i < enc->textures.num; i++) { + d3d11_texture_free(enc, &enc->textures.array[i]); + } +} + +/* ------------------------------------------------------------------------- */ +/* Actual encoding stuff */ + +static ID3D11Texture2D *get_tex_from_handle(struct nvenc_data *enc, + uint32_t handle, + IDXGIKeyedMutex **km_out) +{ + ID3D11Device *device = enc->device; + IDXGIKeyedMutex *km; + ID3D11Texture2D *input_tex; + HRESULT hr; + + for (size_t i = 0; i < enc->input_textures.num; i++) { + struct handle_tex *ht = &enc->input_textures.array[i]; + if (ht->handle == handle) { + *km_out = ht->km; + return ht->tex; + } + } + + hr = device->lpVtbl->OpenSharedResource(device, + (HANDLE)(uintptr_t)handle, + &IID_ID3D11Texture2D, + &input_tex); + if (FAILED(hr)) { + error_hr("OpenSharedResource failed"); + return NULL; + } + + hr = input_tex->lpVtbl->QueryInterface(input_tex, &IID_IDXGIKeyedMutex, + &km); + if (FAILED(hr)) { + error_hr("QueryInterface(IDXGIKeyedMutex) failed"); + input_tex->lpVtbl->Release(input_tex); + return NULL; + } + + input_tex->lpVtbl->SetEvictionPriority(input_tex, + DXGI_RESOURCE_PRIORITY_MAXIMUM); + + *km_out = km; + + struct handle_tex new_ht = {handle, input_tex, km}; + da_push_back(enc->input_textures, &new_ht); + return input_tex; +} + +bool d3d11_encode(void *data, struct encoder_texture *texture, int64_t pts, + uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, bool *received_packet) +{ + struct nvenc_data *enc = data; + ID3D11DeviceContext *context = enc->context; + ID3D11Texture2D *input_tex; + ID3D11Texture2D *output_tex; + IDXGIKeyedMutex *km; + struct nv_texture *nvtex; + struct nv_bitstream *bs; + + if (texture->handle == GS_INVALID_HANDLE) { + error("Encode failed: bad texture handle"); + *next_key = lock_key; + return false; + } + + bs = &enc->bitstreams.array[enc->next_bitstream]; + nvtex = &enc->textures.array[enc->next_bitstream]; + + input_tex = get_tex_from_handle(enc, texture->handle, &km); + output_tex = nvtex->tex; + + if (!input_tex) { + *next_key = lock_key; + return false; + } + + deque_push_back(&enc->dts_list, &pts, sizeof(pts)); + + /* ------------------------------------ */ + /* copy to output tex */ + + km->lpVtbl->AcquireSync(km, lock_key, INFINITE); + + context->lpVtbl->CopyResource(context, (ID3D11Resource *)output_tex, + (ID3D11Resource *)input_tex); + + km->lpVtbl->ReleaseSync(km, *next_key); + + /* ------------------------------------ */ + /* map output tex so nvenc can use it */ + + NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; + map.registeredResource = nvtex->res; + if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) { + return false; + } + + nvtex->mapped_res = map.mappedResource; + + /* ------------------------------------ */ + /* do actual encode call */ + + return nvenc_encode_base(enc, bs, nvtex->mapped_res, pts, packet, + received_packet); +} diff --git a/plugins/obs-nvenc/nvenc-helpers.c b/plugins/obs-nvenc/nvenc-helpers.c new file mode 100644 index 00000000000000..a07d6ff274585e --- /dev/null +++ b/plugins/obs-nvenc/nvenc-helpers.c @@ -0,0 +1,409 @@ +#include "obs-nvenc.h" +#include "nvenc-helpers.h" + +#include +#include +#include +#include +#include + +static void *nvenc_lib = NULL; +static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; +NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER}; +NV_CREATE_INSTANCE_FUNC nv_create_instance = NULL; + +/* Will be populated with results from obs-nvenc-test */ +static struct encoder_caps encoder_capabilities[3]; +static bool codec_supported[3]; +static int num_devices; +static int driver_version_major; +static int driver_version_minor; + +#define error(format, ...) blog(LOG_ERROR, "[obs-nvenc] " format, ##__VA_ARGS__) + +bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...) +{ + UNUSED_PARAMETER(session); + + struct dstr message = {0}; + struct dstr error_message = {0}; + + va_list args; + va_start(args, format); + dstr_vprintf(&message, format, args); + va_end(args); + + dstr_printf(&error_message, "NVENC Error: %s", message.array); + obs_encoder_set_last_error(encoder, error_message.array); + error("%s", error_message.array); + + dstr_free(&error_message); + dstr_free(&message); + + return true; +} + +bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err, + const char *func, const char *call) +{ + struct dstr error_message = {0}; + const char *nvenc_error = NULL; + + if (err == NV_ENC_SUCCESS) + return false; + + if (session) { + nvenc_error = nv.nvEncGetLastErrorString(session); + if (nvenc_error) { + // Some NVENC errors begin with :: which looks + // odd to users. Strip it off. + while (*nvenc_error == ':') + nvenc_error++; + } + } + + switch (err) { + case NV_ENC_ERR_OUT_OF_MEMORY: + obs_encoder_set_last_error(encoder, + obs_module_text("TooManySessions")); + break; + + case NV_ENC_ERR_NO_ENCODE_DEVICE: + case NV_ENC_ERR_UNSUPPORTED_DEVICE: + obs_encoder_set_last_error( + encoder, obs_module_text("UnsupportedDevice")); + break; + + case NV_ENC_ERR_INVALID_VERSION: + obs_encoder_set_last_error(encoder, + obs_module_text("OutdatedDriver")); + break; + + default: + if (nvenc_error && *nvenc_error) { + dstr_printf(&error_message, "NVENC Error: %s (%s)", + nvenc_error, nv_error_name(err)); + } else { + + dstr_printf(&error_message, + "NVENC Error: %s: %s failed: %d (%s)", func, + call, (int)err, nv_error_name(err)); + } + obs_encoder_set_last_error(encoder, error_message.array); + dstr_free(&error_message); + break; + } + + if (nvenc_error && *nvenc_error) { + error("%s: %s failed: %d (%s): %s", func, call, (int)err, + nv_error_name(err), nvenc_error); + } else { + error("%s: %s failed: %d (%s)", func, call, (int)err, + nv_error_name(err)); + } + return true; +} + +#define NV_FAILED(e, x) nv_failed2(e, NULL, x, __FUNCTION__, #x) + +bool load_nvenc_lib(void) +{ +#ifdef _WIN32 + nvenc_lib = os_dlopen("nvEncodeAPI64.dll"); +#else + nvenc_lib = os_dlopen("libnvidia-encode.so.1"); +#endif + return nvenc_lib != NULL; +} + +static void *load_nv_func(const char *func) +{ + void *func_ptr = os_dlsym(nvenc_lib, func); + if (!func_ptr) { + error("Could not load function: %s", func); + } + return func_ptr; +} + +typedef NVENCSTATUS(NVENCAPI *NV_MAX_VER_FUNC)(uint32_t *); + +static uint32_t get_nvenc_ver(void) +{ + static NV_MAX_VER_FUNC nv_max_ver = NULL; + static bool failed = false; + static uint32_t ver = 0; + + if (!failed && ver) + return ver; + + if (!nv_max_ver) { + if (failed) + return 0; + + nv_max_ver = (NV_MAX_VER_FUNC)load_nv_func( + "NvEncodeAPIGetMaxSupportedVersion"); + if (!nv_max_ver) { + failed = true; + return 0; + } + } + + if (nv_max_ver(&ver) != NV_ENC_SUCCESS) { + return 0; + } + return ver; +} + +const char *nv_error_name(NVENCSTATUS err) +{ +#define RETURN_CASE(x) \ + case x: \ + return #x + + switch (err) { + RETURN_CASE(NV_ENC_SUCCESS); + RETURN_CASE(NV_ENC_ERR_NO_ENCODE_DEVICE); + RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_DEVICE); + RETURN_CASE(NV_ENC_ERR_INVALID_ENCODERDEVICE); + RETURN_CASE(NV_ENC_ERR_INVALID_DEVICE); + RETURN_CASE(NV_ENC_ERR_DEVICE_NOT_EXIST); + RETURN_CASE(NV_ENC_ERR_INVALID_PTR); + RETURN_CASE(NV_ENC_ERR_INVALID_EVENT); + RETURN_CASE(NV_ENC_ERR_INVALID_PARAM); + RETURN_CASE(NV_ENC_ERR_INVALID_CALL); + RETURN_CASE(NV_ENC_ERR_OUT_OF_MEMORY); + RETURN_CASE(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + RETURN_CASE(NV_ENC_ERR_UNSUPPORTED_PARAM); + RETURN_CASE(NV_ENC_ERR_LOCK_BUSY); + RETURN_CASE(NV_ENC_ERR_NOT_ENOUGH_BUFFER); + RETURN_CASE(NV_ENC_ERR_INVALID_VERSION); + RETURN_CASE(NV_ENC_ERR_MAP_FAILED); + RETURN_CASE(NV_ENC_ERR_NEED_MORE_INPUT); + RETURN_CASE(NV_ENC_ERR_ENCODER_BUSY); + RETURN_CASE(NV_ENC_ERR_EVENT_NOT_REGISTERD); + RETURN_CASE(NV_ENC_ERR_GENERIC); + RETURN_CASE(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); + RETURN_CASE(NV_ENC_ERR_UNIMPLEMENTED); + RETURN_CASE(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); + RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); + RETURN_CASE(NV_ENC_ERR_RESOURCE_NOT_MAPPED); + } +#undef RETURN_CASE + + return "Unknown Error"; +} + +static inline bool init_nvenc_internal(obs_encoder_t *encoder) +{ + static bool initialized = false; + static bool success = false; + + if (initialized) + return success; + initialized = true; + + uint32_t ver = get_nvenc_ver(); + if (ver == 0) { + obs_encoder_set_last_error( + encoder, + "Missing NvEncodeAPIGetMaxSupportedVersion, check " + "your video card drivers are up to date."); + return false; + } + + if (ver < NVCODEC_CONFIGURED_VERSION) { + obs_encoder_set_last_error(encoder, + obs_module_text("OutdatedDriver")); + + error("Current driver version does not support this NVENC " + "version, please upgrade your driver"); + return false; + } + + nv_create_instance = (NV_CREATE_INSTANCE_FUNC)load_nv_func( + "NvEncodeAPICreateInstance"); + if (!nv_create_instance) { + obs_encoder_set_last_error( + encoder, "Missing NvEncodeAPICreateInstance, check " + "your video card drivers are up to date."); + return false; + } + + if (NV_FAILED(encoder, nv_create_instance(&nv))) { + return false; + } + + success = true; + return true; +} + +bool init_nvenc(obs_encoder_t *encoder) +{ + bool success; + + pthread_mutex_lock(&init_mutex); + success = init_nvenc_internal(encoder); + pthread_mutex_unlock(&init_mutex); + + return success; +} + +struct encoder_caps *get_encoder_caps(enum codec_type codec) +{ + struct encoder_caps *caps = &encoder_capabilities[codec]; + return caps; +} + +int num_encoder_devices(void) +{ + return num_devices; +} + +bool is_codec_supported(enum codec_type codec) +{ + return codec_supported[codec]; +} + +bool has_broken_split_encoding(void) +{ + /* CBR padding and tearing artifacts with split encoding are fixed in + * driver versions 555+, previous ones should be considered broken. */ + return driver_version_major < 555; +} + +static void read_codec_caps(config_t *config, enum codec_type codec, + const char *section) +{ + struct encoder_caps *caps = &encoder_capabilities[codec]; + + codec_supported[codec] = + config_get_bool(config, section, "codec_supported"); + if (!codec_supported[codec]) + return; + + caps->bframes = (int)config_get_int(config, section, "bframes"); + caps->bref_modes = (int)config_get_int(config, section, "bref"); + caps->engines = (int)config_get_int(config, section, "engines"); + caps->max_width = (int)config_get_int(config, section, "max_width"); + caps->max_height = (int)config_get_int(config, section, "max_height"); + caps->temporal_filter = + (int)config_get_int(config, section, "temporal_filter"); + caps->lookahead_level = + (int)config_get_int(config, section, "lookahead_level"); + + caps->dyn_bitrate = config_get_bool(config, section, "dynamic_bitrate"); + caps->lookahead = config_get_bool(config, section, "lookahead"); + caps->lossless = config_get_bool(config, section, "lossless"); + caps->temporal_aq = config_get_bool(config, section, "temporal_aq"); + caps->ten_bit = config_get_bool(config, section, "10bit"); + caps->four_four_four = config_get_bool(config, section, "yuv_444"); +} + +static bool nvenc_check(void) +{ +#ifdef _WIN32 + char *test_exe = os_get_executable_path_ptr("obs-nvenc-test.exe"); +#else + char *test_exe = os_get_executable_path_ptr("obs-nvenc-test"); +#endif + os_process_args_t *args; + struct dstr caps_str = {0}; + config_t *config = NULL; + + args = os_process_args_create(test_exe); + + os_process_pipe_t *pp = os_process_pipe_create2(args, "r"); + if (!pp) { + blog(LOG_WARNING, "[NVENC] Failed to launch the NVENC " + "test process I guess"); + goto fail; + } + + for (;;) { + char data[2048]; + size_t len = + os_process_pipe_read(pp, (uint8_t *)data, sizeof(data)); + if (!len) + break; + + dstr_ncat(&caps_str, data, len); + } + + os_process_pipe_destroy(pp); + + if (dstr_is_empty(&caps_str)) { + blog(LOG_WARNING, + "[NVENC] Seems the NVENC test subprocess crashed. " + "Better there than here I guess. "); + goto fail; + } + + if (config_open_string(&config, caps_str.array) != 0) { + blog(LOG_WARNING, "[NVENC] Failed to open config string"); + goto fail; + } + + bool success = config_get_bool(config, "general", "nvenc_supported"); + if (!success) { + const char *error = + config_get_string(config, "general", "reason"); + blog(LOG_WARNING, "[NVENC] Test process failed: %s", + error ? error : "unknown"); + goto fail; + } + + num_devices = (int)config_get_int(config, "general", "nvenc_devices"); + read_codec_caps(config, CODEC_H264, "h264"); + read_codec_caps(config, CODEC_HEVC, "hevc"); + read_codec_caps(config, CODEC_AV1, "av1"); + + const char *nvenc_ver = + config_get_string(config, "general", "nvenc_ver"); + const char *cuda_ver = config_get_string(config, "general", "cuda_ver"); + const char *driver_ver = + config_get_string(config, "general", "driver_ver"); + /* Parse out major/minor for some brokenness checks */ + sscanf(driver_ver, "%d.%d", &driver_version_major, + &driver_version_minor); + + blog(LOG_INFO, + "[obs-nvenc] NVENC version: %d.%d (compiled) / %s (driver), " + "CUDA driver version: %s, AV1 supported: %s", + NVCODEC_CONFIGURED_VERSION >> 4, NVCODEC_CONFIGURED_VERSION & 0xf, + nvenc_ver, cuda_ver, + codec_supported[CODEC_AV1] ? "true" : "false"); + +fail: + if (config) + config_close(config); + + bfree(test_exe); + dstr_free(&caps_str); + os_process_args_destroy(args); + + return true; +} + +static const char *nvenc_check_name = "nvenc_check"; +bool nvenc_supported(void) +{ + bool success; + + profile_start(nvenc_check_name); + success = load_nvenc_lib() && nvenc_check(); + profile_end(nvenc_check_name); + + return success; +} + +void obs_nvenc_load(void) +{ + pthread_mutex_init(&init_mutex, NULL); + register_encoders(); + register_compat_encoders(); +} + +void obs_nvenc_unload(void) +{ + pthread_mutex_destroy(&init_mutex); +} diff --git a/plugins/obs-nvenc/nvenc-helpers.h b/plugins/obs-nvenc/nvenc-helpers.h new file mode 100644 index 00000000000000..ab3cd3bca969c8 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-helpers.h @@ -0,0 +1,89 @@ +#pragma once + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include + +#define NVCODEC_CONFIGURED_VERSION \ + ((NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION) + +#if NVENCAPI_MAJOR_VERSION > 12 || NVENCAPI_MINOR_VERSION >= 1 +#define NVENC_12_1_OR_LATER +#endif + +#if NVENCAPI_MAJOR_VERSION > 12 || NVENCAPI_MINOR_VERSION >= 2 +#define NVENC_12_2_OR_LATER +#endif + +enum codec_type { + CODEC_H264, + CODEC_HEVC, + CODEC_AV1, +}; + +static const char *get_codec_name(enum codec_type type) +{ + switch (type) { + case CODEC_H264: + return "H264"; + case CODEC_HEVC: + return "HEVC"; + case CODEC_AV1: + return "AV1"; + } + + return "Unknown"; +} + +struct encoder_caps { + int bframes; + int bref_modes; + int engines; + + int max_width; + int max_height; + + /* These don't seem to work correctly, thanks NVIDIA. */ + int temporal_filter; + int lookahead_level; + + bool dyn_bitrate; + bool lookahead; + bool lossless; + bool temporal_aq; + + /* Yeah... */ + bool ten_bit; + bool four_four_four; +}; + +typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)( + NV_ENCODE_API_FUNCTION_LIST *); + +extern NV_ENCODE_API_FUNCTION_LIST nv; +extern NV_CREATE_INSTANCE_FUNC nv_create_instance; + +const char *nv_error_name(NVENCSTATUS err); + +bool init_nvenc(obs_encoder_t *encoder); +bool nv_fail2(obs_encoder_t *encoder, void *session, const char *format, ...); +bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err, + const char *func, const char *call); + +struct encoder_caps *get_encoder_caps(enum codec_type codec); +int num_encoder_devices(void); +bool is_codec_supported(enum codec_type codec); +bool has_broken_split_encoding(void); + +void register_encoders(void); +void register_compat_encoders(void); + +#define nv_fail(encoder, format, ...) \ + nv_fail2(encoder, enc->session, format, ##__VA_ARGS__) + +#define nv_failed(encoder, err, func, call) \ + nv_failed2(encoder, enc->session, err, func, call) diff --git a/plugins/obs-nvenc/nvenc-internal.h b/plugins/obs-nvenc/nvenc-internal.h new file mode 100644 index 00000000000000..efaaef9d199d61 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-internal.h @@ -0,0 +1,211 @@ +#pragma once + +#include "cuda-helpers.h" +#include "nvenc-helpers.h" + +#include +#include + +#ifdef _WIN32 +#define INITGUID +#include +#include +#include +#else +#include +#endif + +#define do_log(level, format, ...) \ + blog(level, "[obs-nvenc: '%s'] " format, \ + obs_encoder_get_name(enc->encoder), ##__VA_ARGS__) + +#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__) +#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) +#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) +#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) + +#define error_hr(msg) error("%s: %s: 0x%08lX", __FUNCTION__, msg, (uint32_t)hr); + +#define NV_FAIL(format, ...) nv_fail(enc->encoder, format, ##__VA_ARGS__) +#define NV_FAILED(x) nv_failed(enc->encoder, x, __FUNCTION__, #x) + +/* ------------------------------------------------------------------------- */ +/* Main Implementation Structure */ + +struct nvenc_properties { + int64_t bitrate; + int64_t max_bitrate; + int64_t keyint_sec; + int64_t cqp; + int64_t device; + int64_t bf; + int64_t bframe_ref_mode; + int64_t split_encode; + int64_t target_quality; + + const char *rate_control; + const char *preset; + const char *profile; + const char *tune; + const char *multipass; + const char *opts_str; + + bool adaptive_quantization; + bool lookahead; + bool disable_scenecut; + bool repeat_headers; + bool force_cuda_tex; + + struct obs_options opts; + obs_data_t *data; +}; + +struct nvenc_data { + obs_encoder_t *encoder; + enum codec_type codec; + GUID codec_guid; + + void *session; + NV_ENC_INITIALIZE_PARAMS params; + NV_ENC_CONFIG config; + uint32_t buf_count; + int output_delay; + int buffers_queued; + size_t next_bitstream; + size_t cur_bitstream; + bool encode_started; + bool first_packet; + bool can_change_bitrate; + bool non_texture; + + DARRAY(struct handle_tex) input_textures; + DARRAY(struct nv_bitstream) bitstreams; + DARRAY(struct nv_cuda_surface) surfaces; + NV_ENC_BUFFER_FORMAT surface_format; + struct deque dts_list; + + DARRAY(uint8_t) packet_data; + int64_t packet_pts; + bool packet_keyframe; + +#ifdef _WIN32 + DARRAY(struct nv_texture) textures; + ID3D11Device *device; + ID3D11DeviceContext *context; +#endif + + uint32_t cx; + uint32_t cy; + enum video_format in_format; + + uint8_t *header; + size_t header_size; + + uint8_t *sei; + size_t sei_size; + + int8_t *roi_map; + size_t roi_map_size; + uint32_t roi_increment; + + struct nvenc_properties props; + + CUcontext cu_ctx; +}; + +/* ------------------------------------------------------------------------- */ +/* Resource data structures */ + +/* Input texture handles */ +struct handle_tex { +#ifdef _WIN32 + uint32_t handle; + ID3D11Texture2D *tex; + IDXGIKeyedMutex *km; +#else + GLuint tex_id; + /* CUDA mappings */ + CUgraphicsResource res_y; + CUgraphicsResource res_uv; +#endif +}; + +/* Bitstream buffer */ +struct nv_bitstream { + void *ptr; +}; + +/** Mapped resources **/ +/* CUDA Arrays */ +struct nv_cuda_surface { + CUarray tex; + NV_ENC_REGISTERED_PTR res; + NV_ENC_INPUT_PTR *mapped_res; +}; + +#ifdef _WIN32 +/* DX11 textures */ +struct nv_texture { + void *res; + ID3D11Texture2D *tex; + void *mapped_res; +}; +#endif + +/* ------------------------------------------------------------------------- */ +/* Shared functions */ + +bool nvenc_encode_base(struct nvenc_data *enc, struct nv_bitstream *bs, + void *pic, int64_t pts, struct encoder_packet *packet, + bool *received_packet); + +/* ------------------------------------------------------------------------- */ +/* Backend-specific functions */ + +#ifdef _WIN32 +/** D3D11 **/ +bool d3d11_init(struct nvenc_data *enc, obs_data_t *settings); +void d3d11_free(struct nvenc_data *enc); + +bool d3d11_init_textures(struct nvenc_data *enc); +void d3d11_free_textures(struct nvenc_data *enc); + +bool d3d11_encode(void *data, struct encoder_texture *texture, int64_t pts, + uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, bool *received_packet); +#endif + +/** CUDA **/ +bool cuda_ctx_init(struct nvenc_data *enc, obs_data_t *settings, bool texture); +void cuda_ctx_free(struct nvenc_data *enc); + +bool cuda_init_surfaces(struct nvenc_data *enc); +void cuda_free_surfaces(struct nvenc_data *enc); + +bool cuda_encode(void *data, struct encoder_frame *frame, + struct encoder_packet *packet, bool *received_packet); + +#ifndef _WIN32 +/** CUDA OpenGL **/ +void cuda_opengl_free(struct nvenc_data *enc); +bool cuda_opengl_encode(void *data, struct encoder_texture *tex, int64_t pts, + uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, bool *received_packet); +#endif + +/* ------------------------------------------------------------------------- */ +/* Properties crap */ + +void nvenc_properties_read(struct nvenc_properties *enc, obs_data_t *settings); + +void h264_nvenc_defaults(obs_data_t *settings); +void hevc_nvenc_defaults(obs_data_t *settings); +void av1_nvenc_defaults(obs_data_t *settings); + +obs_properties_t *h264_nvenc_properties(void *); +obs_properties_t *hevc_nvenc_properties(void *); +obs_properties_t *av1_nvenc_properties(void *); + +/* Custom argument parsing */ +void apply_user_args(struct nvenc_data *enc); +bool get_user_arg_int(struct nvenc_data *enc, const char *name, int *val); diff --git a/plugins/obs-nvenc/nvenc-opengl.c b/plugins/obs-nvenc/nvenc-opengl.c new file mode 100644 index 00000000000000..c8b10d3f3e75f9 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-opengl.c @@ -0,0 +1,158 @@ +#include "nvenc-internal.h" +#include "nvenc-helpers.h" + +/* + * NVENC implementation using CUDA context and OpenGL textures + */ + +void cuda_opengl_free(struct nvenc_data *enc) +{ + if (!enc->cu_ctx) + return; + + cu->cuCtxPushCurrent(enc->cu_ctx); + for (size_t i = 0; i < enc->input_textures.num; i++) { + CUgraphicsResource res_y = enc->input_textures.array[i].res_y; + CUgraphicsResource res_uv = enc->input_textures.array[i].res_uv; + cu->cuGraphicsUnregisterResource(res_y); + cu->cuGraphicsUnregisterResource(res_uv); + } + cu->cuCtxPopCurrent(NULL); +} + +/* ------------------------------------------------------------------------- */ +/* Actual encoding stuff */ + +static inline bool get_res_for_tex_ids(struct nvenc_data *enc, GLuint tex_id_y, + GLuint tex_id_uv, + CUgraphicsResource *tex_y, + CUgraphicsResource *tex_uv) +{ + bool success = true; + + for (size_t idx = 0; idx < enc->input_textures.num; idx++) { + struct handle_tex *ht = &enc->input_textures.array[idx]; + if (ht->tex_id != tex_id_y) + continue; + + *tex_y = ht->res_y; + *tex_uv = ht->res_uv; + return success; + } + + CU_CHECK(cu->cuGraphicsGLRegisterImage( + tex_y, tex_id_y, GL_TEXTURE_2D, + CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY)) + CU_CHECK(cu->cuGraphicsGLRegisterImage( + tex_uv, tex_id_uv, GL_TEXTURE_2D, + CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY)) + + struct handle_tex ht = {tex_id_y, *tex_y, *tex_uv}; + da_push_back(enc->input_textures, &ht); + +unmap: + if (!success) { + cu->cuGraphicsUnregisterResource(*tex_y); + cu->cuGraphicsUnregisterResource(*tex_uv); + } + + return success; +} + +static inline bool copy_tex_data(struct nvenc_data *enc, const bool p010, + GLuint tex[2], struct nv_cuda_surface *surf) +{ + bool success = true; + CUgraphicsResource mapped_tex[2] = {0}; + CUarray mapped_cuda; + + if (!get_res_for_tex_ids(enc, tex[0], tex[1], &mapped_tex[0], + &mapped_tex[1])) + return false; + + CU_CHECK(cu->cuGraphicsMapResources(2, mapped_tex, 0)) + + CUDA_MEMCPY2D m = {0}; + m.dstMemoryType = CU_MEMORYTYPE_ARRAY; + m.srcMemoryType = CU_MEMORYTYPE_ARRAY; + m.dstArray = surf->tex; + m.WidthInBytes = p010 ? enc->cx * 2 : enc->cx; + m.Height = enc->cy; + + // Map and copy Y texture + CU_CHECK(cu->cuGraphicsSubResourceGetMappedArray(&mapped_cuda, + mapped_tex[0], 0, 0)); + m.srcArray = mapped_cuda; + CU_CHECK(cu->cuMemcpy2D(&m)) + + // Map and copy UV texture + CU_CHECK(cu->cuGraphicsSubResourceGetMappedArray(&mapped_cuda, + mapped_tex[1], 0, 0)) + m.srcArray = mapped_cuda; + m.dstY += enc->cy; + m.Height = enc->cy / 2; + + CU_CHECK(cu->cuMemcpy2D(&m)) + +unmap: + cu->cuGraphicsUnmapResources(2, mapped_tex, 0); + + return success; +} + +bool cuda_opengl_encode(void *data, struct encoder_texture *tex, int64_t pts, + uint64_t lock_key, uint64_t *next_key, + struct encoder_packet *packet, bool *received_packet) +{ + struct nvenc_data *enc = data; + struct nv_cuda_surface *surf; + struct nv_bitstream *bs; + const bool p010 = obs_p010_tex_active(); + GLuint input_tex[2]; + + if (tex == NULL || tex->tex[0] == NULL) { + error("Encode failed: bad texture handle"); + *next_key = lock_key; + return false; + } + + bs = &enc->bitstreams.array[enc->next_bitstream]; + surf = &enc->surfaces.array[enc->next_bitstream]; + + deque_push_back(&enc->dts_list, &pts, sizeof(pts)); + + /* ------------------------------------ */ + /* copy to CUDA data */ + + CU_FAILED(cu->cuCtxPushCurrent(enc->cu_ctx)) + obs_enter_graphics(); + input_tex[0] = *(GLuint *)gs_texture_get_obj(tex->tex[0]); + input_tex[1] = *(GLuint *)gs_texture_get_obj(tex->tex[1]); + + bool success = copy_tex_data(enc, p010, input_tex, surf); + + obs_leave_graphics(); + CU_FAILED(cu->cuCtxPopCurrent(NULL)) + + if (!success) + return false; + + /* ------------------------------------ */ + /* map output tex so nvenc can use it */ + + NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; + map.registeredResource = surf->res; + map.mappedBufferFmt = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT + : NV_ENC_BUFFER_FORMAT_NV12; + + if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) + return false; + + surf->mapped_res = map.mappedResource; + + /* ------------------------------------ */ + /* do actual encode call */ + + return nvenc_encode_base(enc, bs, surf->mapped_res, pts, packet, + received_packet); +} diff --git a/plugins/obs-nvenc/nvenc-opts-parser.c b/plugins/obs-nvenc/nvenc-opts-parser.c new file mode 100644 index 00000000000000..ae43bcd14f46f6 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-opts-parser.c @@ -0,0 +1,220 @@ +#include "nvenc-internal.h" + +#include + +/* NVIDIA uses bitfields for a variety of options. As it is not possible to + * use offsetof() or similar with those we resort to macros here to avoid too + * much boilerplate. */ + +#define APPLY_BIT_OPT(opt_name, bits) \ + if (strcmp(opt->name, #opt_name) == 0) { \ + uint32_t old_val = nv_conf->opt_name; \ + nv_conf->opt_name = strtol(opt->value, NULL, 10); \ + blog(LOG_DEBUG, "[obs-nvenc] Changing: \"%s\": %u -> %u", \ + #opt_name, old_val, nv_conf->opt_name); \ + return true; \ + } + +#define APPLY_INT_OPT(opt_name, type) \ + if (strcmp(opt->name, #opt_name) == 0) { \ + blog(LOG_DEBUG, "[obs-nvenc] Changing \"%s\": %d -> %s (%s)", \ + #opt_name, nv_conf->opt_name, opt->value, #type); \ + nv_conf->opt_name = (type)strtol(opt->value, NULL, 10); \ + return true; \ + } + +static void parse_qp_opt(const char *name, const char *val, NV_ENC_QP *qp_opt) +{ + /* QP options can be passed in either as a single value to apply to all + * or as three values separated by ":". */ + int32_t p, b, i; + + if (sscanf(val, "%d:%d:%d", &p, &b, &i) != 3) { + p = b = i = atoi(val); + } + + blog(LOG_DEBUG, + "[obs-nvenc] Applying custom %s = %d / %d / %d (P / B / I)", name, + p, b, i); + + /* Values should be treated as int32_t but are passed in as uint32_t + * for legacy reasons, see comment in nvEncodeAPI.h */ + qp_opt->qpInterP = (uint32_t)p; + qp_opt->qpInterB = (uint32_t)b; + qp_opt->qpIntra = (uint32_t)i; +} + +#define APPLY_QP_OPT(opt_name) \ + if (strcmp(opt->name, #opt_name) == 0) { \ + parse_qp_opt(#opt_name, opt->value, &nv_conf->opt_name); \ + return true; \ + } + +static bool apply_rc_opt(const struct obs_option *opt, + NV_ENC_RC_PARAMS *nv_conf) +{ + APPLY_QP_OPT(constQP) + APPLY_QP_OPT(minQP) + APPLY_QP_OPT(maxQP) + APPLY_QP_OPT(initialRCQP) + + APPLY_INT_OPT(averageBitRate, uint32_t) + APPLY_INT_OPT(maxBitRate, uint32_t) + APPLY_INT_OPT(vbvBufferSize, uint32_t) + APPLY_INT_OPT(vbvInitialDelay, uint32_t) + + APPLY_INT_OPT(targetQuality, uint8_t) + APPLY_INT_OPT(targetQualityLSB, uint8_t) + + APPLY_INT_OPT(cbQPIndexOffset, int8_t) + APPLY_INT_OPT(crQPIndexOffset, int8_t) + + APPLY_BIT_OPT(enableMinQP, 1) + APPLY_BIT_OPT(enableMaxQP, 1) + APPLY_BIT_OPT(enableInitialRCQP, 1) + APPLY_BIT_OPT(enableAQ, 1) + APPLY_BIT_OPT(enableLookahead, 1) + APPLY_BIT_OPT(disableIadapt, 1) + APPLY_BIT_OPT(disableBadapt, 1) + APPLY_BIT_OPT(enableTemporalAQ, 1) + APPLY_BIT_OPT(aqStrength, 4) + +#ifdef NVENC_12_2_OR_LATER + APPLY_INT_OPT(lookaheadLevel, NV_ENC_LOOKAHEAD_LEVEL) +#endif + + /* Macros above will return true if succesfully evaluated. + * Otherwise, return false if option unknown/unsupported. */ + return false; +} + +static bool apply_conf_opt(const struct obs_option *opt, NV_ENC_CONFIG *nv_conf) +{ + APPLY_INT_OPT(gopLength, uint32_t) + APPLY_INT_OPT(frameIntervalP, int32_t) + + return false; +} + +static void parse_level_opt(const char *val, uint32_t *level, bool hevc) +{ + /* Support for passing level both as raw value (e.g. "42") + * and human-readable format (e.g. "4.2"). */ + uint32_t int_val = 0; + + if (strstr(val, ".") != NULL) { + uint32_t high_val, low_val; + if (sscanf(val, "%u.%u", &high_val, &low_val) == 2) { + int_val = high_val * 10 + low_val; + } + } else { + int_val = strtol(val, NULL, 10); + } + + if (!int_val) + return; + + if (hevc) + int_val *= 3; + + blog(LOG_DEBUG, "[obs-nvenc] Applying custom level = %s (%u)", val, + int_val); + *level = int_val; +} + +static bool apply_h264_opt(struct obs_option *opt, NV_ENC_CONFIG_H264 *nv_conf) +{ + if (strcmp(opt->name, "level") == 0) { + parse_level_opt(opt->value, &nv_conf->level, false); + return true; + } + + APPLY_INT_OPT(idrPeriod, uint32_t) + APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE) + + APPLY_BIT_OPT(enableFillerDataInsertion, 1) + + return false; +} + +static bool apply_hevc_opt(struct obs_option *opt, NV_ENC_CONFIG_HEVC *nv_conf) +{ + if (strcmp(opt->name, "level") == 0) { + parse_level_opt(opt->value, &nv_conf->level, true); + return true; + } + + APPLY_INT_OPT(tier, uint32_t) + APPLY_INT_OPT(idrPeriod, uint32_t) + APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE) +#ifdef NVENC_12_2_OR_LATER + APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL) +#endif + + APPLY_BIT_OPT(enableFillerDataInsertion, 1) + + return false; +} + +static bool apply_av1_opt(struct obs_option *opt, NV_ENC_CONFIG_AV1 *nv_conf) +{ + APPLY_INT_OPT(level, uint32_t) + APPLY_INT_OPT(tier, uint32_t) + APPLY_INT_OPT(numTileColumns, uint32_t) + APPLY_INT_OPT(numTileRows, uint32_t) + APPLY_INT_OPT(idrPeriod, uint32_t) + APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE) + + APPLY_BIT_OPT(enableBitstreamPadding, 1) + + return false; +} + +static bool apply_codec_opt(enum codec_type codec, struct obs_option *opt, + NV_ENC_CODEC_CONFIG *enc_config) +{ + if (codec == CODEC_H264) + return apply_h264_opt(opt, &enc_config->h264Config); + if (codec == CODEC_HEVC) + return apply_hevc_opt(opt, &enc_config->hevcConfig); + if (codec == CODEC_AV1) + return apply_av1_opt(opt, &enc_config->av1Config); + + return false; +} + +void apply_user_args(struct nvenc_data *enc) +{ + for (size_t idx = 0; idx < enc->props.opts.count; idx++) { + struct obs_option *opt = &enc->props.opts.options[idx]; + + /* Special options handled elsewhere */ + if (strcmp(opt->name, "lookaheadDepth") == 0 || + strcmp(opt->name, "keyint") == 0) + continue; + + if (apply_rc_opt(opt, &enc->config.rcParams)) + continue; + if (apply_conf_opt(opt, &enc->config)) + continue; + if (apply_codec_opt(enc->codec, opt, + &enc->config.encodeCodecConfig)) + continue; + + warn("Unknown custom option: \"%s\"", opt->name); + } +} + +bool get_user_arg_int(struct nvenc_data *enc, const char *name, int *val) +{ + for (size_t idx = 0; idx < enc->props.opts.count; idx++) { + struct obs_option *opt = &enc->props.opts.options[idx]; + if (strcmp(opt->name, name) != 0) + continue; + + *val = strtol(opt->value, NULL, 10); + return true; + } + + return false; +} diff --git a/plugins/obs-nvenc/nvenc-properties.c b/plugins/obs-nvenc/nvenc-properties.c new file mode 100644 index 00000000000000..ff67cdba13cba9 --- /dev/null +++ b/plugins/obs-nvenc/nvenc-properties.c @@ -0,0 +1,324 @@ +#include "nvenc-internal.h" + +void nvenc_properties_read(struct nvenc_properties *props, obs_data_t *settings) +{ + props->bitrate = obs_data_get_int(settings, "bitrate"); + props->max_bitrate = obs_data_get_int(settings, "max_bitrate"); + props->keyint_sec = obs_data_get_int(settings, "keyint_sec"); + props->cqp = obs_data_get_int(settings, "cqp"); + props->device = obs_data_get_int(settings, "device"); + props->bf = obs_data_get_int(settings, "bf"); + props->bframe_ref_mode = obs_data_get_int(settings, "bframe_ref_mode"); + props->split_encode = obs_data_get_int(settings, "split_encode"); + props->target_quality = obs_data_get_int(settings, "target_quality"); + + props->rate_control = obs_data_get_string(settings, "rate_control"); + props->preset = obs_data_get_string(settings, "preset"); + props->profile = obs_data_get_string(settings, "profile"); + props->tune = obs_data_get_string(settings, "tune"); + props->multipass = obs_data_get_string(settings, "multipass"); + + props->adaptive_quantization = + obs_data_get_bool(settings, "adaptive_quantization"); + props->lookahead = obs_data_get_bool(settings, "lookahead"); + props->disable_scenecut = + obs_data_get_bool(settings, "disable_scenecut"); + props->repeat_headers = obs_data_get_bool(settings, "repeat_headers"); + props->force_cuda_tex = obs_data_get_bool(settings, "force_cuda_tex"); + + if (obs_data_has_user_value(settings, "opts")) { + props->opts_str = obs_data_get_string(settings, "opts"); + props->opts = obs_parse_options(props->opts_str); + } + + /* Retain settings object until destroyed since we use its strings. */ + obs_data_addref(settings); + props->data = settings; +} + +static void nvenc_defaults_base(enum codec_type codec, obs_data_t *settings) +{ + struct encoder_caps *caps = get_encoder_caps(codec); + + obs_data_set_default_int(settings, "bitrate", 10000); + obs_data_set_default_int(settings, "max_bitrate", 10000); + obs_data_set_default_int(settings, "target_quality", 20); + obs_data_set_default_int(settings, "keyint_sec", 0); + obs_data_set_default_int(settings, "cqp", 20); + obs_data_set_default_int(settings, "device", -1); + obs_data_set_default_int(settings, "bf", caps->bframes > 0 ? 2 : 0); + + obs_data_set_default_string(settings, "rate_control", "cbr"); + obs_data_set_default_string(settings, "preset", "p5"); + obs_data_set_default_string(settings, "multipass", "qres"); + obs_data_set_default_string(settings, "tune", "hq"); + obs_data_set_default_string(settings, "profile", + codec != CODEC_H264 ? "main" : "high"); + + obs_data_set_default_bool(settings, "adaptive_quantization", true); + obs_data_set_default_bool(settings, "lookahead", caps->lookahead); + + /* Hidden options */ + obs_data_set_default_bool(settings, "repeat_headers", false); + obs_data_set_default_bool(settings, "force_cuda_tex", false); + obs_data_set_default_bool(settings, "disable_scenecut", false); +} + +void h264_nvenc_defaults(obs_data_t *settings) +{ + nvenc_defaults_base(CODEC_H264, settings); +} + +#ifdef ENABLE_HEVC +void hevc_nvenc_defaults(obs_data_t *settings) +{ + nvenc_defaults_base(CODEC_HEVC, settings); +} +#endif + +void av1_nvenc_defaults(obs_data_t *settings) +{ + nvenc_defaults_base(CODEC_AV1, settings); +} + +static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, + obs_data_t *settings) +{ + const char *rc = obs_data_get_string(settings, "rate_control"); + bool cqp = strcmp(rc, "CQP") == 0; + bool vbr = strcmp(rc, "VBR") == 0; + bool cqvbr = strcmp(rc, "CQVBR") == 0; + bool lossless = strcmp(rc, "lossless") == 0; + + p = obs_properties_get(ppts, "bitrate"); + obs_property_set_visible(p, !cqp && !lossless && !cqvbr); + p = obs_properties_get(ppts, "max_bitrate"); + obs_property_set_visible(p, vbr || cqvbr); + p = obs_properties_get(ppts, "target_quality"); + obs_property_set_visible(p, cqvbr); + p = obs_properties_get(ppts, "cqp"); + obs_property_set_visible(p, cqp); + p = obs_properties_get(ppts, "preset"); + obs_property_set_visible(p, !lossless); + p = obs_properties_get(ppts, "tune"); + obs_property_set_visible(p, !lossless); + p = obs_properties_get(ppts, "adaptive_quantization"); + obs_property_set_visible(p, !lossless); + + return true; +} + +obs_properties_t *nvenc_properties_internal(enum codec_type codec) +{ + obs_properties_t *props = obs_properties_create(); + obs_property_t *p; + + struct encoder_caps *caps = get_encoder_caps(codec); + + p = obs_properties_add_list(props, "rate_control", + obs_module_text("RateControl"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, obs_module_text("CBR"), "CBR"); + obs_property_list_add_string(p, obs_module_text("CQP"), "CQP"); + obs_property_list_add_string(p, obs_module_text("VBR"), "VBR"); + obs_property_list_add_string(p, obs_module_text("CQVBR"), "CQVBR"); + if (caps->lossless) { + obs_property_list_add_string(p, obs_module_text("Lossless"), + "lossless"); + } + + obs_property_set_modified_callback(p, rate_control_modified); + + p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), + 50, UINT32_MAX / 1000, 50); + obs_property_int_set_suffix(p, " Kbps"); + + obs_properties_add_int(props, "target_quality", + obs_module_text("TargetQuality"), 1, 51, 1); + + p = obs_properties_add_int(props, "max_bitrate", + obs_module_text("MaxBitrate"), 0, + UINT32_MAX / 1000, 50); + obs_property_int_set_suffix(p, " Kbps"); + + /* AV1 uses 0-255 instead of 0-51 for QP, and most implementations just + * multiply the value by 4 to keep the range smaller. */ + obs_properties_add_int(props, "cqp", obs_module_text("CQP"), 1, + codec == CODEC_AV1 ? 63 : 51, 1); + + p = obs_properties_add_int(props, "keyint_sec", + obs_module_text("KeyframeIntervalSec"), 0, + 10, 1); + obs_property_int_set_suffix(p, " s"); + + p = obs_properties_add_list(props, "preset", obs_module_text("Preset"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_preset(val) \ + obs_property_list_add_string(p, obs_module_text("Preset." val), val) + + add_preset("p1"); + add_preset("p2"); + add_preset("p3"); + add_preset("p4"); + add_preset("p5"); + add_preset("p6"); + add_preset("p7"); +#undef add_preset + + p = obs_properties_add_list(props, "tune", obs_module_text("Tuning"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_tune(val) \ + obs_property_list_add_string(p, obs_module_text("Tuning." val), val) +#ifdef NVENC_12_2_OR_LATER + if (codec == CODEC_HEVC) + add_tune("uhq"); +#endif + add_tune("hq"); + add_tune("ll"); + add_tune("ull"); +#undef add_tune + + p = obs_properties_add_list(props, "multipass", + obs_module_text("Multipass"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_multipass(val) \ + obs_property_list_add_string(p, obs_module_text("Multipass." val), val) + add_multipass("disabled"); + add_multipass("qres"); + add_multipass("fullres"); +#undef add_multipass + + p = obs_properties_add_list(props, "profile", + obs_module_text("Profile"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + +#define add_profile(val) obs_property_list_add_string(p, val, val) + if (codec == CODEC_HEVC) { + if (caps->ten_bit) + add_profile("main10"); + add_profile("main"); + } else if (codec == CODEC_AV1) { + add_profile("main"); + } else { + add_profile("high"); + add_profile("main"); + add_profile("baseline"); + } +#undef add_profile + + p = obs_properties_add_bool(props, "lookahead", + obs_module_text("LookAhead")); + obs_property_set_long_description(p, + obs_module_text("LookAhead.ToolTip")); + + p = obs_properties_add_bool(props, "adaptive_quantization", + obs_module_text("AdaptiveQuantization")); + obs_property_set_long_description( + p, obs_module_text("AdaptiveQuantization.ToolTip")); + + if (num_encoder_devices() > 1) { + obs_properties_add_int(props, "device", obs_module_text("GPU"), + -1, num_encoder_devices(), 1); + } + + if (caps->bframes > 0) { + obs_properties_add_int(props, "bf", obs_module_text("BFrames"), + 0, caps->bframes, 1); + } + + /* H.264 supports this, but seems to cause issues with some decoders, + * so restrict it to the custom options field for now. */ + if (caps->bref_modes && codec != CODEC_H264) { + p = obs_properties_add_list(props, "bframe_ref_mode", + obs_module_text("BFrameRefMode"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int( + p, obs_module_text("BframeRefMode.Disabled"), + NV_ENC_BFRAME_REF_MODE_DISABLED); + + if (caps->bref_modes & 1) { + obs_property_list_add_int( + p, obs_module_text("BframeRefMode.Each"), + NV_ENC_BFRAME_REF_MODE_EACH); + } + if (caps->bref_modes & 2) { + obs_property_list_add_int( + p, obs_module_text("BframeRefMode.Middle"), + NV_ENC_BFRAME_REF_MODE_MIDDLE); + } + } + +#ifdef NVENC_12_1_OR_LATER + /* Some older GPUs such as the 1080 Ti have 2 NVENC chips, but do not + * support split encoding. Therefore, we check for AV1 support here to + * make sure this option is only presented on 40-series and later. */ + if (is_codec_supported(CODEC_AV1) && caps->engines > 1 && + !has_broken_split_encoding() && + (codec == CODEC_HEVC || codec == CODEC_AV1)) { + p = obs_properties_add_list(props, "split_encode", + obs_module_text("SplitEncode"), + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + + obs_property_list_add_int(p, + obs_module_text("SplitEncode.Auto"), + NV_ENC_SPLIT_AUTO_MODE); + obs_property_list_add_int( + p, obs_module_text("SplitEncode.Disabled"), + NV_ENC_SPLIT_DISABLE_MODE); + obs_property_list_add_int( + p, obs_module_text("SplitEncode.Enabled"), + NV_ENC_SPLIT_TWO_FORCED_MODE); + if (caps->engines > 2) { + obs_property_list_add_int( + p, obs_module_text("SplitEncode.ThreeWay"), + NV_ENC_SPLIT_THREE_FORCED_MODE); + } + } +#endif + + p = obs_properties_add_text(props, "opts", obs_module_text("Opts"), + OBS_TEXT_DEFAULT); + obs_property_set_long_description(p, obs_module_text("Opts.TT")); + + /* Invisible properties */ + p = obs_properties_add_bool(props, "repeat_headers", "repeat_headers"); + obs_property_set_visible(p, false); + p = obs_properties_add_bool(props, "force_cuda_tex", "force_cuda_tex"); + obs_property_set_visible(p, false); + p = obs_properties_add_bool(props, "disable_scenecut", + "disable_scenecut"); + obs_property_set_visible(p, false); + + return props; +} + +obs_properties_t *h264_nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(CODEC_H264); +} + +#ifdef ENABLE_HEVC +obs_properties_t *hevc_nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(CODEC_HEVC); +} +#endif + +obs_properties_t *av1_nvenc_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + return nvenc_properties_internal(CODEC_AV1); +} diff --git a/plugins/obs-nvenc/nvenc.c b/plugins/obs-nvenc/nvenc.c new file mode 100644 index 00000000000000..9bd27adced56bc --- /dev/null +++ b/plugins/obs-nvenc/nvenc.c @@ -0,0 +1,1444 @@ +#include "nvenc-internal.h" + +#include +#include + +/* ========================================================================= */ + +#define EXTRA_BUFFERS 5 + +#ifndef _WIN32 +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/* ------------------------------------------------------------------------- */ +/* Bitstream Buffer */ + +static bool nv_bitstream_init(struct nvenc_data *enc, struct nv_bitstream *bs) +{ + NV_ENC_CREATE_BITSTREAM_BUFFER buf = { + NV_ENC_CREATE_BITSTREAM_BUFFER_VER}; + + if (NV_FAILED(nv.nvEncCreateBitstreamBuffer(enc->session, &buf))) { + return false; + } + + bs->ptr = buf.bitstreamBuffer; + return true; +} + +static void nv_bitstream_free(struct nvenc_data *enc, struct nv_bitstream *bs) +{ + if (bs->ptr) { + nv.nvEncDestroyBitstreamBuffer(enc->session, bs->ptr); + } +} + +/* ------------------------------------------------------------------------- */ +/* Implementation */ + +static const char *h264_nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC H.264"; +} + +static const char *h264_nvenc_soft_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC H.264 (Fallback)"; +} + +#ifdef ENABLE_HEVC +static const char *hevc_nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC HEVC"; +} + +static const char *hevc_nvenc_soft_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC HEVC (Fallback)"; +} +#endif + +static const char *av1_nvenc_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC AV1"; +} + +static const char *av1_nvenc_soft_get_name(void *type_data) +{ + UNUSED_PARAMETER(type_data); + return "NVIDIA NVENC AV1 (Fallback)"; +} + +static inline int nv_get_cap(struct nvenc_data *enc, NV_ENC_CAPS cap) +{ + if (!enc->session) + return 0; + + NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER}; + int v; + + param.capsToQuery = cap; + nv.nvEncGetEncodeCaps(enc->session, enc->codec_guid, ¶m, &v); + return v; +} + +static bool nvenc_update(void *data, obs_data_t *settings) +{ + struct nvenc_data *enc = data; + + /* Only support reconfiguration of CBR bitrate */ + if (enc->can_change_bitrate) { + enc->props.bitrate = obs_data_get_int(settings, "bitrate"); + enc->props.max_bitrate = + obs_data_get_int(settings, "max_bitrate"); + + bool vbr = (enc->config.rcParams.rateControlMode == + NV_ENC_PARAMS_RC_VBR); + enc->config.rcParams.averageBitRate = + (uint32_t)enc->props.bitrate * 1000; + enc->config.rcParams.maxBitRate = + vbr ? (uint32_t)enc->props.max_bitrate * 1000 + : (uint32_t)enc->props.bitrate * 1000; + + NV_ENC_RECONFIGURE_PARAMS params = {0}; + params.version = NV_ENC_RECONFIGURE_PARAMS_VER; + params.reInitEncodeParams = enc->params; + params.resetEncoder = 1; + params.forceIDR = 1; + + if (NV_FAILED(nv.nvEncReconfigureEncoder(enc->session, + ¶ms))) { + return false; + } + } + + return true; +} + +static bool init_session(struct nvenc_data *enc) +{ + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = { + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; + params.apiVersion = NVENCAPI_VERSION; +#ifdef _WIN32 + if (enc->non_texture) { + params.device = enc->cu_ctx; + params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + } else { + params.device = enc->device; + params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX; + } +#else + params.device = enc->cu_ctx; + params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; +#endif + + if (NV_FAILED(nv.nvEncOpenEncodeSessionEx(¶ms, &enc->session))) { + return false; + } + return true; +} + +static void initialize_params(struct nvenc_data *enc, const GUID *nv_preset, + NV_ENC_TUNING_INFO nv_tuning, uint32_t width, + uint32_t height, uint32_t fps_num, + uint32_t fps_den) +{ + NV_ENC_INITIALIZE_PARAMS *params = &enc->params; + memset(params, 0, sizeof(*params)); + params->version = NV_ENC_INITIALIZE_PARAMS_VER; + params->encodeGUID = enc->codec_guid; + params->presetGUID = *nv_preset; + params->encodeWidth = width; + params->encodeHeight = height; + params->darWidth = width; + params->darHeight = height; + params->frameRateNum = fps_num; + params->frameRateDen = fps_den; + params->enableEncodeAsync = 0; + params->enablePTD = 1; + params->encodeConfig = &enc->config; + params->tuningInfo = nv_tuning; +#ifdef NVENC_12_1_OR_LATER + params->splitEncodeMode = + (NV_ENC_SPLIT_ENCODE_MODE)enc->props.split_encode; +#endif +} + +static inline GUID get_nv_preset(const char *preset2) +{ + if (astrcmpi(preset2, "p1") == 0) { + return NV_ENC_PRESET_P1_GUID; + } else if (astrcmpi(preset2, "p2") == 0) { + return NV_ENC_PRESET_P2_GUID; + } else if (astrcmpi(preset2, "p3") == 0) { + return NV_ENC_PRESET_P3_GUID; + } else if (astrcmpi(preset2, "p4") == 0) { + return NV_ENC_PRESET_P4_GUID; + } else if (astrcmpi(preset2, "p6") == 0) { + return NV_ENC_PRESET_P6_GUID; + } else if (astrcmpi(preset2, "p7") == 0) { + return NV_ENC_PRESET_P7_GUID; + } else { + return NV_ENC_PRESET_P5_GUID; + } +} + +static inline NV_ENC_TUNING_INFO get_nv_tuning(const char *tuning) +{ + if (astrcmpi(tuning, "ll") == 0) { + return NV_ENC_TUNING_INFO_LOW_LATENCY; + } else if (astrcmpi(tuning, "ull") == 0) { + return NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY; +#ifdef NVENC_12_2_OR_LATER + } else if (astrcmpi(tuning, "uhq") == 0) { + return NV_ENC_TUNING_INFO_ULTRA_HIGH_QUALITY; +#endif + } else { + return NV_ENC_TUNING_INFO_HIGH_QUALITY; + } +} + +static inline NV_ENC_MULTI_PASS get_nv_multipass(const char *multipass) +{ + if (astrcmpi(multipass, "qres") == 0) { + return NV_ENC_TWO_PASS_QUARTER_RESOLUTION; + } else if (astrcmpi(multipass, "fullres") == 0) { + return NV_ENC_TWO_PASS_FULL_RESOLUTION; + } else { + return NV_ENC_MULTI_PASS_DISABLED; + } +} + +static bool is_10_bit(const struct nvenc_data *enc) +{ + return enc->non_texture ? enc->in_format == VIDEO_FORMAT_P010 + : obs_p010_tex_active(); +} + +static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings) +{ + UNUSED_PARAMETER(settings); + + int bitrate = (int)enc->props.bitrate; + int max_bitrate = (int)enc->props.max_bitrate; + int rc_lookahead = 0; + + bool cqvbr = astrcmpi(enc->props.rate_control, "CQVBR") == 0; + bool vbr = cqvbr || astrcmpi(enc->props.rate_control, "VBR") == 0; + bool lossless = strcmp(enc->props.rate_control, "lossless") == 0; + + NVENCSTATUS err; + + video_t *video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + + enc->cx = obs_encoder_get_width(enc->encoder); + enc->cy = obs_encoder_get_height(enc->encoder); + + /* -------------------------- */ + /* get preset */ + + GUID nv_preset = get_nv_preset(enc->props.preset); + NV_ENC_TUNING_INFO nv_tuning = get_nv_tuning(enc->props.tune); + NV_ENC_MULTI_PASS nv_multipass = get_nv_multipass(enc->props.multipass); + + if (lossless) { + nv_tuning = NV_ENC_TUNING_INFO_LOSSLESS; + nv_multipass = NV_ENC_MULTI_PASS_DISABLED; + enc->props.adaptive_quantization = false; + enc->props.cqp = 0; + } + + /* -------------------------- */ + /* get preset default config */ + + NV_ENC_PRESET_CONFIG preset_config = {0}; + preset_config.version = NV_ENC_PRESET_CONFIG_VER; + preset_config.presetCfg.version = NV_ENC_CONFIG_VER; + + err = nv.nvEncGetEncodePresetConfigEx(enc->session, enc->codec_guid, + nv_preset, nv_tuning, + &preset_config); + if (nv_failed(enc->encoder, err, __FUNCTION__, + "nvEncGetEncodePresetConfig")) { + return false; + } + + /* -------------------------- */ + /* main configuration */ + + enc->config = preset_config.presetCfg; + + int keyint = (int)enc->props.keyint_sec * voi->fps_num / voi->fps_den; + get_user_arg_int(enc, "keyint", &keyint); + + uint32_t gop_size = keyint > 0 ? keyint : 250; + + NV_ENC_CONFIG *config = &enc->config; + + initialize_params(enc, &nv_preset, nv_tuning, voi->width, voi->height, + voi->fps_num, voi->fps_den); + + config->gopLength = gop_size; + config->frameIntervalP = gop_size == 1 ? 0 : (int32_t)enc->props.bf + 1; + + /* lookahead */ + + const bool use_profile_lookahead = config->rcParams.enableLookahead; + bool lookahead = nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_LOOKAHEAD) && + (enc->props.lookahead || use_profile_lookahead); + + if (lookahead) { + rc_lookahead = use_profile_lookahead + ? config->rcParams.lookaheadDepth + : 8; + + /* Due to the additional calculations required to handle lookahead, + * get the user override here (if any). */ + get_user_arg_int(enc, "lookaheadDepth", &rc_lookahead); + } + + int buf_count = max(4, config->frameIntervalP * 2 * 2); + if (lookahead) { + buf_count = + max(buf_count, config->frameIntervalP + rc_lookahead + + EXTRA_BUFFERS); + } + + buf_count = min(64, buf_count); + enc->buf_count = buf_count; + + const int output_delay = buf_count - 1; + enc->output_delay = output_delay; + + if (lookahead) { + const int lkd_bound = output_delay - config->frameIntervalP - 4; + if (lkd_bound >= 0) { + config->rcParams.enableLookahead = 1; + config->rcParams.lookaheadDepth = + min(rc_lookahead, lkd_bound); + config->rcParams.disableIadapt = 0; + config->rcParams.disableBadapt = 0; + } else { + lookahead = false; + } + } + + enc->config.rcParams.disableIadapt = enc->props.disable_scenecut; + + /* psycho aq */ + + if (enc->props.adaptive_quantization) { + config->rcParams.enableAQ = 1; + config->rcParams.aqStrength = 8; + config->rcParams.enableTemporalAQ = + nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ); + } + + /* -------------------------- */ + /* rate control */ + + enc->can_change_bitrate = + nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE); + + config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR; + config->rcParams.averageBitRate = bitrate * 1000; + config->rcParams.maxBitRate = vbr ? max_bitrate * 1000 : bitrate * 1000; + config->rcParams.vbvBufferSize = bitrate * 1000; + + if (strcmp(enc->props.rate_control, "CQP") == 0 || lossless) { + int cqp_val = enc->codec == CODEC_AV1 ? (int)enc->props.cqp * 4 + : (int)enc->props.cqp; + + config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + config->rcParams.constQP.qpInterP = cqp_val; + config->rcParams.constQP.qpInterB = cqp_val; + config->rcParams.constQP.qpIntra = cqp_val; + enc->can_change_bitrate = false; + + bitrate = 0; + max_bitrate = 0; + + } else if (!vbr) { /* CBR by default */ + config->rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; + } else if (cqvbr) { + config->rcParams.targetQuality = + (uint8_t)enc->props.target_quality; + config->rcParams.averageBitRate = 0; + config->rcParams.vbvBufferSize = 0; + } + + config->rcParams.multiPass = nv_multipass; + config->rcParams.qpMapMode = NV_ENC_QP_MAP_DELTA; + + /* -------------------------- */ + /* initialize */ + + info("settings:\n" + "\tcodec: %s\n" + "\trate_control: %s\n" + "\tbitrate: %d\n" + "\tmax_bitrate: %d\n" + "\tcq/cqp: %ld\n" + "\tkeyint: %d\n" + "\tpreset: %s\n" + "\ttuning: %s\n" + "\tmultipass: %s\n" + "\tprofile: %s\n" + "\twidth: %d\n" + "\theight: %d\n" + "\tb-frames: %ld\n" + "\tb-ref-mode: %ld\n" + "\tlookahead: %s (%d)\n" + "\taq: %s\n" + "\tsplit encode: %ld\n" + "\tuser opts: %s\n", + get_codec_name(enc->codec), enc->props.rate_control, bitrate, + max_bitrate, vbr ? enc->props.target_quality : enc->props.cqp, + gop_size, enc->props.preset, enc->props.tune, enc->props.multipass, + enc->props.profile, enc->cx, enc->cy, enc->props.bf, + enc->props.bframe_ref_mode, lookahead ? "true" : "false", + rc_lookahead, enc->props.adaptive_quantization ? "true" : "false", + enc->props.split_encode, enc->props.opts_str); + + return true; +} + +static bool init_encoder_h264(struct nvenc_data *enc, obs_data_t *settings) +{ + bool lossless = strcmp(enc->props.rate_control, "lossless") == 0; + + if (!init_encoder_base(enc, settings)) { + return false; + } + + NV_ENC_CONFIG *config = &enc->config; + NV_ENC_CONFIG_H264 *h264_config = &config->encodeCodecConfig.h264Config; + NV_ENC_CONFIG_H264_VUI_PARAMETERS *vui_params = + &h264_config->h264VUIParameters; + + video_t *video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + + if (enc->props.repeat_headers) { + h264_config->repeatSPSPPS = 1; + h264_config->disableSPSPPS = 0; + h264_config->outputAUD = 1; + } + + h264_config->idrPeriod = config->gopLength; + + h264_config->sliceMode = 3; + h264_config->sliceModeData = 1; + + h264_config->useBFramesAsRef = + (NV_ENC_BFRAME_REF_MODE)enc->props.bframe_ref_mode; + + /* Enable CBR padding */ + if (config->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR) + h264_config->enableFillerDataInsertion = 1; + + vui_params->videoSignalTypePresentFlag = 1; + vui_params->videoFullRangeFlag = (voi->range == VIDEO_RANGE_FULL); + vui_params->colourDescriptionPresentFlag = 1; + + switch (voi->colorspace) { + case VIDEO_CS_601: + vui_params->colourPrimaries = 6; + vui_params->transferCharacteristics = 6; + vui_params->colourMatrix = 6; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 1; + vui_params->colourMatrix = 1; + break; + case VIDEO_CS_SRGB: + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 13; + vui_params->colourMatrix = 1; + break; + default: + break; + } + + if (lossless) { + h264_config->qpPrimeYZeroTransformBypassFlag = 1; + } else if (strcmp(enc->props.rate_control, "CBR") == 0) { /* CBR */ + h264_config->outputBufferingPeriodSEI = 1; + } + + h264_config->outputPictureTimingSEI = 1; + + /* -------------------------- */ + /* profile */ + + if (enc->in_format == VIDEO_FORMAT_I444) { + config->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID; + h264_config->chromaFormatIDC = 3; + } else if (astrcmpi(enc->props.profile, "main") == 0) { + config->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID; + } else if (astrcmpi(enc->props.profile, "baseline") == 0) { + config->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; + } else if (!lossless) { + config->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; + } + + apply_user_args(enc); + + if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, &enc->params))) { + return false; + } + + return true; +} + +static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings) +{ + if (!init_encoder_base(enc, settings)) { + return false; + } + + NV_ENC_CONFIG *config = &enc->config; + NV_ENC_CONFIG_HEVC *hevc_config = &config->encodeCodecConfig.hevcConfig; + NV_ENC_CONFIG_HEVC_VUI_PARAMETERS *vui_params = + &hevc_config->hevcVUIParameters; + + video_t *video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + + if (enc->props.repeat_headers) { + hevc_config->repeatSPSPPS = 1; + hevc_config->disableSPSPPS = 0; + hevc_config->outputAUD = 1; + } + + hevc_config->idrPeriod = config->gopLength; + + hevc_config->sliceMode = 3; + hevc_config->sliceModeData = 1; + + hevc_config->useBFramesAsRef = + (NV_ENC_BFRAME_REF_MODE)enc->props.bframe_ref_mode; + + /* Enable CBR padding */ + if (config->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR) + hevc_config->enableFillerDataInsertion = 1; + + vui_params->videoSignalTypePresentFlag = 1; + vui_params->videoFullRangeFlag = (voi->range == VIDEO_RANGE_FULL); + vui_params->colourDescriptionPresentFlag = 1; + + switch (voi->colorspace) { + case VIDEO_CS_601: + vui_params->colourPrimaries = 6; + vui_params->transferCharacteristics = 6; + vui_params->colourMatrix = 6; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 1; + vui_params->colourMatrix = 1; + break; + case VIDEO_CS_SRGB: + vui_params->colourPrimaries = 1; + vui_params->transferCharacteristics = 13; + vui_params->colourMatrix = 1; + break; + case VIDEO_CS_2100_PQ: + vui_params->colourPrimaries = 9; + vui_params->transferCharacteristics = 16; + vui_params->colourMatrix = 9; + vui_params->chromaSampleLocationFlag = 1; + vui_params->chromaSampleLocationTop = 2; + vui_params->chromaSampleLocationBot = 2; + break; + case VIDEO_CS_2100_HLG: + vui_params->colourPrimaries = 9; + vui_params->transferCharacteristics = 18; + vui_params->colourMatrix = 9; + vui_params->chromaSampleLocationFlag = 1; + vui_params->chromaSampleLocationTop = 2; + vui_params->chromaSampleLocationBot = 2; + } + + if (astrcmpi(enc->props.rate_control, "cbr") == 0) { + hevc_config->outputBufferingPeriodSEI = 1; + } + + hevc_config->outputPictureTimingSEI = 1; + + /* -------------------------- */ + /* profile */ + + bool profile_is_10bpc = false; + + if (enc->in_format == VIDEO_FORMAT_I444) { + config->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID; + hevc_config->chromaFormatIDC = 3; + } else if (astrcmpi(enc->props.profile, "main10") == 0) { + config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; + profile_is_10bpc = true; + } else if (is_10_bit(enc)) { + blog(LOG_WARNING, "[obs-nvenc] Forcing main10 for P010"); + config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; + profile_is_10bpc = true; + } else { + config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID; + } + +#ifndef NVENC_12_2_OR_LATER + hevc_config->pixelBitDepthMinus8 = is_10_bit(enc) ? 2 : 0; +#else + hevc_config->inputBitDepth = is_10_bit(enc) ? NV_ENC_BIT_DEPTH_10 + : NV_ENC_BIT_DEPTH_8; + hevc_config->outputBitDepth = profile_is_10bpc ? NV_ENC_BIT_DEPTH_10 + : NV_ENC_BIT_DEPTH_8; +#endif + + apply_user_args(enc); + + if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, &enc->params))) { + return false; + } + + return true; +} + +static bool init_encoder_av1(struct nvenc_data *enc, obs_data_t *settings) +{ + if (!init_encoder_base(enc, settings)) { + return false; + } + + NV_ENC_CONFIG *config = &enc->config; + NV_ENC_CONFIG_AV1 *av1_config = &config->encodeCodecConfig.av1Config; + + video_t *video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + + av1_config->idrPeriod = config->gopLength; + + av1_config->useBFramesAsRef = + (NV_ENC_BFRAME_REF_MODE)enc->props.bframe_ref_mode; + + av1_config->colorRange = (voi->range == VIDEO_RANGE_FULL); + + /* Enable CBR padding */ + if (config->rcParams.rateControlMode == NV_ENC_PARAMS_RC_CBR) + av1_config->enableBitstreamPadding = 1; + +#define PIXELCOUNT_4K (3840 * 2160) + + /* If size is 4K+, set tiles to 2 uniform columns. */ + if ((voi->width * voi->height) >= PIXELCOUNT_4K) + av1_config->numTileColumns = 2; + + switch (voi->colorspace) { + case VIDEO_CS_601: + av1_config->colorPrimaries = 6; + av1_config->transferCharacteristics = 6; + av1_config->matrixCoefficients = 6; + break; + case VIDEO_CS_DEFAULT: + case VIDEO_CS_709: + av1_config->colorPrimaries = 1; + av1_config->transferCharacteristics = 1; + av1_config->matrixCoefficients = 1; + break; + case VIDEO_CS_SRGB: + av1_config->colorPrimaries = 1; + av1_config->transferCharacteristics = 13; + av1_config->matrixCoefficients = 1; + break; + case VIDEO_CS_2100_PQ: + av1_config->colorPrimaries = 9; + av1_config->transferCharacteristics = 16; + av1_config->matrixCoefficients = 9; + break; + case VIDEO_CS_2100_HLG: + av1_config->colorPrimaries = 9; + av1_config->transferCharacteristics = 18; + av1_config->matrixCoefficients = 9; + } + + /* -------------------------- */ + /* profile */ + + config->profileGUID = NV_ENC_AV1_PROFILE_MAIN_GUID; + av1_config->tier = NV_ENC_TIER_AV1_0; + + av1_config->level = NV_ENC_LEVEL_AV1_AUTOSELECT; + av1_config->chromaFormatIDC = 1; +#ifndef NVENC_12_2_OR_LATER + av1_config->pixelBitDepthMinus8 = is_10_bit(enc) ? 2 : 0; + av1_config->inputPixelBitDepthMinus8 = av1_config->pixelBitDepthMinus8; +#else + av1_config->inputBitDepth = is_10_bit(enc) ? NV_ENC_BIT_DEPTH_10 + : NV_ENC_BIT_DEPTH_8; + av1_config->outputBitDepth = av1_config->inputBitDepth; +#endif + av1_config->numFwdRefs = 1; + av1_config->numBwdRefs = 1; + av1_config->repeatSeqHdr = 1; + + apply_user_args(enc); + + if (NV_FAILED(nv.nvEncInitializeEncoder(enc->session, &enc->params))) { + return false; + } + + return true; +} + +static bool init_bitstreams(struct nvenc_data *enc) +{ + da_reserve(enc->bitstreams, enc->buf_count); + for (uint32_t i = 0; i < enc->buf_count; i++) { + struct nv_bitstream bitstream; + if (!nv_bitstream_init(enc, &bitstream)) { + return false; + } + + da_push_back(enc->bitstreams, &bitstream); + } + + return true; +} + +static enum video_format get_preferred_format(enum video_format format) +{ + switch (format) { + case VIDEO_FORMAT_I010: + case VIDEO_FORMAT_P010: + return VIDEO_FORMAT_P010; + case VIDEO_FORMAT_RGBA: + case VIDEO_FORMAT_BGRA: + case VIDEO_FORMAT_BGRX: + case VIDEO_FORMAT_I444: + return VIDEO_FORMAT_I444; + default: + return VIDEO_FORMAT_NV12; + } +} + +static void nvenc_destroy(void *data); + +static bool init_encoder(struct nvenc_data *enc, enum codec_type codec, + obs_data_t *settings, obs_encoder_t *encoder) +{ + UNUSED_PARAMETER(codec); + UNUSED_PARAMETER(encoder); + + const bool support_10bit = + nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_10BIT_ENCODE); + const bool support_444 = + nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_YUV444_ENCODE); + + video_t *video = obs_encoder_video(enc->encoder); + const struct video_output_info *voi = video_output_get_info(video); + enum video_format pref_format = + obs_encoder_get_preferred_video_format(enc->encoder); + if (pref_format == VIDEO_FORMAT_NONE) + pref_format = voi->format; + + enc->in_format = get_preferred_format(pref_format); + + if (enc->in_format == VIDEO_FORMAT_I444 && !support_444) { + NV_FAIL(obs_module_text("NVENC.444Unsupported")); + return false; + } + + if (is_10_bit(enc) && !support_10bit) { + NV_FAIL(obs_module_text("10bitUnsupported")); + return false; + } + + switch (voi->format) { + case VIDEO_FORMAT_I010: + case VIDEO_FORMAT_P010: + break; + default: + switch (voi->colorspace) { + case VIDEO_CS_2100_PQ: + case VIDEO_CS_2100_HLG: + NV_FAIL(obs_module_text("8bitUnsupportedHdr")); + return false; + default: + break; + } + } + + switch (enc->codec) { + case CODEC_HEVC: + return init_encoder_hevc(enc, settings); + case CODEC_H264: + return init_encoder_h264(enc, settings); + case CODEC_AV1: + return init_encoder_av1(enc, settings); + } + + return false; +} + +static void *nvenc_create_internal(enum codec_type codec, obs_data_t *settings, + obs_encoder_t *encoder, bool texture) +{ + struct nvenc_data *enc = bzalloc(sizeof(*enc)); + enc->encoder = encoder; + enc->codec = codec; + enc->first_packet = true; + enc->non_texture = !texture; + + nvenc_properties_read(&enc->props, settings); + + NV_ENCODE_API_FUNCTION_LIST init = {NV_ENCODE_API_FUNCTION_LIST_VER}; + + switch (enc->codec) { + case CODEC_H264: + enc->codec_guid = NV_ENC_CODEC_H264_GUID; + break; + case CODEC_HEVC: + enc->codec_guid = NV_ENC_CODEC_HEVC_GUID; + break; + case CODEC_AV1: + enc->codec_guid = NV_ENC_CODEC_AV1_GUID; + break; + } + + if (!init_nvenc(encoder)) + goto fail; + +#ifdef _WIN32 + if (texture ? !d3d11_init(enc, settings) : !init_cuda(encoder)) + goto fail; +#else + if (!init_cuda(encoder)) + goto fail; +#endif + + if (NV_FAILED(nv_create_instance(&init))) + goto fail; + + if (!cuda_ctx_init(enc, settings, texture)) + goto fail; + + if (!init_session(enc)) { + goto fail; + } + if (!init_encoder(enc, codec, settings, encoder)) { + goto fail; + } + if (!init_bitstreams(enc)) { + goto fail; + } + +#ifdef _WIN32 + if (texture ? !d3d11_init_textures(enc) : !cuda_init_surfaces(enc)) + goto fail; +#else + if (!cuda_init_surfaces(enc)) + goto fail; +#endif + + enc->codec = codec; + + return enc; + +fail: + nvenc_destroy(enc); + return NULL; +} + +static void *nvenc_create_base(enum codec_type codec, obs_data_t *settings, + obs_encoder_t *encoder, bool texture) +{ + /* This encoder requires shared textures, this cannot be used on a + * gpu other than the one OBS is currently running on. + * + * 2024 Amendment: On Linux when using CUDA<->OpenGL interop we can + * in fact use shared textures even when using a different GPU, this + * will still copy data through the CPU, but much more efficiently than + * our native non-texture encoder. For now allow this via a hidden + * option as it may cause issues for people. + */ + const int gpu = (int)obs_data_get_int(settings, "device"); +#ifndef _WIN32 + const bool force_tex = obs_data_get_bool(settings, "force_cuda_tex"); +#else + const bool force_tex = false; +#endif + + if (gpu != -1 && texture && !force_tex) { + blog(LOG_INFO, + "[obs-nvenc] different GPU selected by user, falling back " + "to non-texture encoder"); + goto reroute; + } + + if (obs_encoder_scaling_enabled(encoder)) { + if (obs_encoder_gpu_scaling_enabled(encoder)) { + blog(LOG_INFO, "[obs-nvenc] GPU scaling enabled"); + } else if (texture) { + blog(LOG_INFO, + "[obs-nvenc] CPU scaling enabled, falling back to" + " non-texture encoder"); + goto reroute; + } + } + + if (texture && !obs_p010_tex_active() && !obs_nv12_tex_active()) { + blog(LOG_INFO, + "[obs-nvenc] nv12/p010 not active, falling back to " + "non-texture encoder"); + goto reroute; + } + + struct nvenc_data *enc = + nvenc_create_internal(codec, settings, encoder, texture); + + if (enc) { + return enc; + } + +reroute: + if (!texture) { + blog(LOG_ERROR, + "Already in non_texture encoder, can't fall back further!"); + return NULL; + } + + switch (codec) { + case CODEC_H264: + return obs_encoder_create_rerouted(encoder, + "obs_nvenc_h264_soft"); + case CODEC_HEVC: + return obs_encoder_create_rerouted(encoder, + "obs_nvenc_hevc_soft"); + case CODEC_AV1: + return obs_encoder_create_rerouted(encoder, + "obs_nvenc_av1_soft"); + } + + return NULL; +} + +static void *h264_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_create_base(CODEC_H264, settings, encoder, true); +} + +#ifdef ENABLE_HEVC +static void *hevc_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_create_base(CODEC_HEVC, settings, encoder, true); +} +#endif + +static void *av1_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_create_base(CODEC_AV1, settings, encoder, true); +} + +static void *h264_nvenc_soft_create(obs_data_t *settings, + obs_encoder_t *encoder) +{ + return nvenc_create_base(CODEC_H264, settings, encoder, false); +} + +#ifdef ENABLE_HEVC +static void *hevc_nvenc_soft_create(obs_data_t *settings, + obs_encoder_t *encoder) +{ + return nvenc_create_base(CODEC_HEVC, settings, encoder, false); +} +#endif + +static void *av1_nvenc_soft_create(obs_data_t *settings, obs_encoder_t *encoder) +{ + return nvenc_create_base(CODEC_AV1, settings, encoder, false); +} + +static bool get_encoded_packet(struct nvenc_data *enc, bool finalize); + +static void nvenc_destroy(void *data) +{ + struct nvenc_data *enc = data; + + if (enc->encode_started) { + NV_ENC_PIC_PARAMS params = {NV_ENC_PIC_PARAMS_VER}; + params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; + nv.nvEncEncodePicture(enc->session, ¶ms); + get_encoded_packet(enc, true); + } + + for (size_t i = 0; i < enc->bitstreams.num; i++) { + nv_bitstream_free(enc, &enc->bitstreams.array[i]); + } + if (enc->session) + nv.nvEncDestroyEncoder(enc->session); + +#ifdef _WIN32 + d3d11_free_textures(enc); + d3d11_free(enc); +#else + cuda_opengl_free(enc); +#endif + cuda_free_surfaces(enc); + cuda_ctx_free(enc); + + bfree(enc->header); + bfree(enc->sei); + bfree(enc->roi_map); + + deque_free(&enc->dts_list); + + da_free(enc->surfaces); + da_free(enc->input_textures); + da_free(enc->bitstreams); +#ifdef _WIN32 + da_free(enc->textures); +#endif + da_free(enc->packet_data); + + obs_free_options(enc->props.opts); + obs_data_release(enc->props.data); + + bfree(enc); +} + +static bool get_encoded_packet(struct nvenc_data *enc, bool finalize) +{ + void *s = enc->session; + + da_resize(enc->packet_data, 0); + + if (!enc->buffers_queued) + return true; + if (!finalize && enc->buffers_queued < enc->output_delay) + return true; + + size_t count = finalize ? enc->buffers_queued : 1; + + for (size_t i = 0; i < count; i++) { + size_t cur_bs_idx = enc->cur_bitstream; + struct nv_bitstream *bs = &enc->bitstreams.array[cur_bs_idx]; +#ifdef _WIN32 + struct nv_texture *nvtex = + enc->non_texture ? NULL + : &enc->textures.array[cur_bs_idx]; + struct nv_cuda_surface *surf = + enc->non_texture ? &enc->surfaces.array[cur_bs_idx] + : NULL; +#else + struct nv_cuda_surface *surf = &enc->surfaces.array[cur_bs_idx]; +#endif + + /* ---------------- */ + + NV_ENC_LOCK_BITSTREAM lock = {NV_ENC_LOCK_BITSTREAM_VER}; + lock.outputBitstream = bs->ptr; + lock.doNotWait = false; + + if (NV_FAILED(nv.nvEncLockBitstream(s, &lock))) { + return false; + } + + if (enc->first_packet) { + NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = {0}; + uint8_t buf[256]; + uint32_t size = 0; + + payload.version = NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER; + payload.spsppsBuffer = buf; + payload.inBufferSize = sizeof(buf); + payload.outSPSPPSPayloadSize = &size; + + nv.nvEncGetSequenceParams(s, &payload); + enc->header = bmemdup(buf, size); + enc->header_size = size; + enc->first_packet = false; + } + + da_copy_array(enc->packet_data, lock.bitstreamBufferPtr, + lock.bitstreamSizeInBytes); + + enc->packet_pts = (int64_t)lock.outputTimeStamp; + enc->packet_keyframe = lock.pictureType == NV_ENC_PIC_TYPE_IDR; + + if (NV_FAILED(nv.nvEncUnlockBitstream(s, bs->ptr))) { + return false; + } + + /* ---------------- */ +#ifdef _WIN32 + if (nvtex && nvtex->mapped_res) { + NVENCSTATUS err; + err = nv.nvEncUnmapInputResource(s, nvtex->mapped_res); + if (nv_failed(enc->encoder, err, __FUNCTION__, + "unmap")) { + return false; + } + nvtex->mapped_res = NULL; + } +#endif + /* ---------------- */ + + if (surf && surf->mapped_res) { + NVENCSTATUS err; + err = nv.nvEncUnmapInputResource(s, surf->mapped_res); + if (nv_failed(enc->encoder, err, __FUNCTION__, + "unmap")) { + return false; + } + surf->mapped_res = NULL; + } + + /* ---------------- */ + + if (++enc->cur_bitstream == enc->buf_count) + enc->cur_bitstream = 0; + + enc->buffers_queued--; + } + + return true; +} + +struct roi_params { + uint32_t mb_width; + uint32_t mb_height; + uint32_t mb_size; + bool av1; + int8_t *map; +}; + +static void roi_cb(void *param, struct obs_encoder_roi *roi) +{ + const struct roi_params *rp = param; + + int8_t qp_val; + /* AV1 has a larger QP range than HEVC/H.264 */ + if (rp->av1) { + qp_val = (int8_t)(-128.0f * roi->priority); + } else { + qp_val = (int8_t)(-51.0f * roi->priority); + } + + const uint32_t roi_left = roi->left / rp->mb_size; + const uint32_t roi_top = roi->top / rp->mb_size; + const uint32_t roi_right = (roi->right - 1) / rp->mb_size; + const uint32_t roi_bottom = (roi->bottom - 1) / rp->mb_size; + + for (uint32_t mb_y = 0; mb_y < rp->mb_height; mb_y++) { + if (mb_y < roi_top || mb_y > roi_bottom) + continue; + + for (uint32_t mb_x = 0; mb_x < rp->mb_width; mb_x++) { + if (mb_x < roi_left || mb_x > roi_right) + continue; + + rp->map[mb_y * rp->mb_width + mb_x] = qp_val; + } + } +} + +static void add_roi(struct nvenc_data *enc, NV_ENC_PIC_PARAMS *params) +{ + const uint32_t increment = obs_encoder_get_roi_increment(enc->encoder); + + if (enc->roi_map && enc->roi_increment == increment) { + params->qpDeltaMap = enc->roi_map; + params->qpDeltaMapSize = (uint32_t)enc->roi_map_size; + return; + } + + uint32_t mb_size = 0; + switch (enc->codec) { + case CODEC_H264: + /* H.264 is always 16x16 */ + mb_size = 16; + break; + case CODEC_HEVC: + /* HEVC can be 16x16, 32x32, or 64x64, but NVENC is always 32x32 */ + mb_size = 32; + break; + case CODEC_AV1: + /* AV1 can be 64x64 or 128x128, but NVENC is always 64x64 */ + mb_size = 64; + break; + } + + const uint32_t mb_width = (enc->cx + mb_size - 1) / mb_size; + const uint32_t mb_height = (enc->cy + mb_size - 1) / mb_size; + const size_t map_size = mb_width * mb_height * sizeof(int8_t); + + if (map_size != enc->roi_map_size) { + enc->roi_map = brealloc(enc->roi_map, map_size); + enc->roi_map_size = map_size; + } + + memset(enc->roi_map, 0, enc->roi_map_size); + + struct roi_params par = { + .mb_width = mb_width, + .mb_height = mb_height, + .mb_size = mb_size, + .av1 = enc->codec == CODEC_AV1, + .map = enc->roi_map, + }; + + obs_encoder_enum_roi(enc->encoder, roi_cb, &par); + + enc->roi_increment = increment; + params->qpDeltaMap = enc->roi_map; + params->qpDeltaMapSize = (uint32_t)map_size; +} + +bool nvenc_encode_base(struct nvenc_data *enc, struct nv_bitstream *bs, + void *pic, int64_t pts, struct encoder_packet *packet, + bool *received_packet) +{ + NV_ENC_PIC_PARAMS params = {0}; + params.version = NV_ENC_PIC_PARAMS_VER; + params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + params.inputBuffer = pic; + params.inputTimeStamp = (uint64_t)pts; + params.inputWidth = enc->cx; + params.inputHeight = enc->cy; + params.inputPitch = enc->cx; + params.outputBitstream = bs->ptr; + params.frameIdx = (uint32_t)pts; + + if (enc->non_texture) { + params.bufferFmt = enc->surface_format; + } else { + params.bufferFmt = obs_p010_tex_active() + ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT + : NV_ENC_BUFFER_FORMAT_NV12; + } + + /* Add ROI map if enabled */ + if (obs_encoder_has_roi(enc->encoder)) + add_roi(enc, ¶ms); + + NVENCSTATUS err = nv.nvEncEncodePicture(enc->session, ¶ms); + if (err != NV_ENC_SUCCESS && err != NV_ENC_ERR_NEED_MORE_INPUT) { + nv_failed(enc->encoder, err, __FUNCTION__, + "nvEncEncodePicture"); + return false; + } + + enc->encode_started = true; + enc->buffers_queued++; + + if (++enc->next_bitstream == enc->buf_count) { + enc->next_bitstream = 0; + } + + /* ------------------------------------ */ + /* check for encoded packet and parse */ + + if (!get_encoded_packet(enc, false)) { + return false; + } + + /* ------------------------------------ */ + /* output encoded packet */ + + if (enc->packet_data.num) { + int64_t dts; + deque_pop_front(&enc->dts_list, &dts, sizeof(dts)); + + /* subtract bframe delay from dts for H.264/HEVC */ + if (enc->codec != CODEC_AV1) + dts -= enc->props.bf * packet->timebase_num; + + *received_packet = true; + packet->data = enc->packet_data.array; + packet->size = enc->packet_data.num; + packet->type = OBS_ENCODER_VIDEO; + packet->pts = enc->packet_pts; + packet->dts = dts; + packet->keyframe = enc->packet_keyframe; + } else { + *received_packet = false; + } + + return true; +} + +static void nvenc_soft_video_info(void *data, struct video_scale_info *info) +{ + struct nvenc_data *enc = data; + info->format = enc->in_format; +} + +static bool nvenc_extra_data(void *data, uint8_t **header, size_t *size) +{ + struct nvenc_data *enc = data; + + if (!enc->header) { + return false; + } + + *header = enc->header; + *size = enc->header_size; + return true; +} + +static bool nvenc_sei_data(void *data, uint8_t **sei, size_t *size) +{ + struct nvenc_data *enc = data; + + if (!enc->sei) { + return false; + } + + *sei = enc->sei; + *size = enc->sei_size; + return true; +} + +struct obs_encoder_info h264_nvenc_info = { + .id = "obs_nvenc_h264_tex", + .codec = "h264", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | + OBS_ENCODER_CAP_ROI, + .get_name = h264_nvenc_get_name, + .create = h264_nvenc_create, + .destroy = nvenc_destroy, + .update = nvenc_update, +#ifdef _WIN32 + .encode_texture2 = d3d11_encode, +#else + .encode_texture2 = cuda_opengl_encode, +#endif + .get_defaults = h264_nvenc_defaults, + .get_properties = h264_nvenc_properties, + .get_extra_data = nvenc_extra_data, + .get_sei_data = nvenc_sei_data, +}; + +#ifdef ENABLE_HEVC +struct obs_encoder_info hevc_nvenc_info = { + .id = "obs_nvenc_hevc_tex", + .codec = "hevc", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | + OBS_ENCODER_CAP_ROI, + .get_name = hevc_nvenc_get_name, + .create = hevc_nvenc_create, + .destroy = nvenc_destroy, + .update = nvenc_update, +#ifdef _WIN32 + .encode_texture2 = d3d11_encode, +#else + .encode_texture2 = cuda_opengl_encode, +#endif + .get_defaults = hevc_nvenc_defaults, + .get_properties = hevc_nvenc_properties, + .get_extra_data = nvenc_extra_data, + .get_sei_data = nvenc_sei_data, +}; +#endif + +struct obs_encoder_info av1_nvenc_info = { + .id = "obs_nvenc_av1_tex", + .codec = "av1", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_PASS_TEXTURE | OBS_ENCODER_CAP_DYN_BITRATE | + OBS_ENCODER_CAP_ROI, + .get_name = av1_nvenc_get_name, + .create = av1_nvenc_create, + .destroy = nvenc_destroy, + .update = nvenc_update, +#ifdef _WIN32 + .encode_texture2 = d3d11_encode, +#else + .encode_texture2 = cuda_opengl_encode, +#endif + .get_defaults = av1_nvenc_defaults, + .get_properties = av1_nvenc_properties, + .get_extra_data = nvenc_extra_data, +}; + +struct obs_encoder_info h264_nvenc_soft_info = { + .id = "obs_nvenc_h264_soft", + .codec = "h264", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | + OBS_ENCODER_CAP_INTERNAL, + .get_name = h264_nvenc_soft_get_name, + .create = h264_nvenc_soft_create, + .destroy = nvenc_destroy, + .update = nvenc_update, + .encode = cuda_encode, + .get_defaults = h264_nvenc_defaults, + .get_properties = h264_nvenc_properties, + .get_extra_data = nvenc_extra_data, + .get_sei_data = nvenc_sei_data, + .get_video_info = nvenc_soft_video_info, +}; + +#ifdef ENABLE_HEVC +struct obs_encoder_info hevc_nvenc_soft_info = { + .id = "obs_nvenc_hevc_soft", + .codec = "hevc", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | + OBS_ENCODER_CAP_INTERNAL, + .get_name = hevc_nvenc_soft_get_name, + .create = hevc_nvenc_soft_create, + .destroy = nvenc_destroy, + .update = nvenc_update, + .encode = cuda_encode, + .get_defaults = hevc_nvenc_defaults, + .get_properties = hevc_nvenc_properties, + .get_extra_data = nvenc_extra_data, + .get_sei_data = nvenc_sei_data, + .get_video_info = nvenc_soft_video_info, +}; +#endif + +struct obs_encoder_info av1_nvenc_soft_info = { + .id = "obs_nvenc_av1_soft", + .codec = "av1", + .type = OBS_ENCODER_VIDEO, + .caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI | + OBS_ENCODER_CAP_INTERNAL, + .get_name = av1_nvenc_soft_get_name, + .create = av1_nvenc_soft_create, + .destroy = nvenc_destroy, + .update = nvenc_update, + .encode = cuda_encode, + .get_defaults = av1_nvenc_defaults, + .get_properties = av1_nvenc_properties, + .get_extra_data = nvenc_extra_data, + .get_video_info = nvenc_soft_video_info, +}; + +void register_encoders(void) +{ + obs_register_encoder(&h264_nvenc_info); + obs_register_encoder(&h264_nvenc_soft_info); +#ifdef ENABLE_HEVC + obs_register_encoder(&hevc_nvenc_info); + obs_register_encoder(&hevc_nvenc_soft_info); +#endif + if (is_codec_supported(CODEC_AV1)) { + obs_register_encoder(&av1_nvenc_info); + obs_register_encoder(&av1_nvenc_soft_info); + } +} diff --git a/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt b/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt new file mode 100644 index 00000000000000..db51d25e1ed068 --- /dev/null +++ b/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.24...3.25) + +find_package(FFnvcodec 12 REQUIRED) + +add_executable(obs-nvenc-test) + +target_sources(obs-nvenc-test PRIVATE obs-nvenc-test.cpp) +target_link_libraries(obs-nvenc-test FFnvcodec::FFnvcodec) + +# cmake-format: off +set_target_properties_obs(obs-nvenc-test PROPERTIES FOLDER plugins/obs-nvenc) +# cmake-format: on diff --git a/plugins/obs-nvenc/obs-nvenc-test/obs-nvenc-test.cpp b/plugins/obs-nvenc/obs-nvenc-test/obs-nvenc-test.cpp new file mode 100644 index 00000000000000..91cfbb9cf3a61a --- /dev/null +++ b/plugins/obs-nvenc/obs-nvenc-test/obs-nvenc-test.cpp @@ -0,0 +1,532 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Utility to check for NVENC support and capabilities. + * Will check all GPUs and return INI-formatted results based on highest capability of all devices. + */ + +using namespace std; +using namespace std::chrono_literals; + +static CudaFunctions *cu = nullptr; +static NvencFunctions *nvenc = nullptr; + +NV_ENCODE_API_FUNCTION_LIST nv = {NV_ENCODE_API_FUNCTION_LIST_VER}; +static constexpr uint32_t NVENC_CONFIGURED_VERSION = + (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + +/* NVML stuff */ +#define NVML_SUCCESS 0 +#define NVML_DEVICE_UUID_V2_BUFFER_SIZE 96 +#define NVML_DEVICE_NAME_V2_BUFFER_SIZE 96 +#define NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE 80 + +typedef int nvmlReturn_t; +typedef struct nvmlDevice *nvmlDevice_t; + +typedef enum nvmlEncoderType { + NVML_ENCODER_QUERY_H264, + NVML_ENCODER_QUERY_HEVC, + NVML_ENCODER_QUERY_AV1, + NVML_ENCODER_QUERY_UNKNOWN +} nvmlEncoderType_t; + +typedef nvmlReturn_t (*NVML_GET_DRIVER_VER_FUNC)(char *, unsigned int); +typedef nvmlReturn_t (*NVML_INIT_V2)(); +typedef nvmlReturn_t (*NVML_SHUTDOWN)(); +typedef nvmlReturn_t (*NVML_GET_HANDLE_BY_BUS_ID)(const char *, nvmlDevice_t *); +typedef nvmlReturn_t (*NVML_GET_DEVICE_UUID)(nvmlDevice_t, char *, unsigned); +typedef nvmlReturn_t (*NVML_GET_DEVICE_NAME)(nvmlDevice_t, char *, unsigned); +typedef nvmlReturn_t (*NVML_GET_DEVICE_PCIE_GEN)(nvmlDevice_t, unsigned *); +typedef nvmlReturn_t (*NVML_GET_DEVICE_PCIE_WIDTH)(nvmlDevice_t, unsigned *); +typedef nvmlReturn_t (*NVML_GET_DEVICE_NAME)(nvmlDevice_t, char *, unsigned); +typedef nvmlReturn_t (*NVML_GET_ENCODER_SESSIONS)(nvmlDevice_t, unsigned *, + void *); +typedef nvmlReturn_t (*NVML_GET_ENCODER_CAPACITY)(nvmlDevice_t, nvmlEncoderType, + unsigned *); +typedef nvmlReturn_t (*NVML_GET_ENCODER_UTILISATION)(nvmlDevice_t, unsigned *, + unsigned *); +/* List of capabilities to be queried per codec */ +static const vector> capabilities = { + {NV_ENC_CAPS_NUM_MAX_BFRAMES, "bframes"}, + {NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, "lossless"}, + {NV_ENC_CAPS_SUPPORT_LOOKAHEAD, "lookahead"}, + {NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, "temporal_aq"}, + {NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE, "dynamic_bitrate"}, + {NV_ENC_CAPS_SUPPORT_10BIT_ENCODE, "10bit"}, + {NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, "bref"}, + {NV_ENC_CAPS_NUM_ENCODER_ENGINES, "engines"}, + {NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, "yuv_444"}, + {NV_ENC_CAPS_WIDTH_MAX, "max_width"}, + {NV_ENC_CAPS_HEIGHT_MAX, "max_height"}, +#if NVENCAPI_MAJOR_VERSION > 12 || NVENCAPI_MINOR_VERSION >= 2 + /* SDK 12.2+ features */ + {NV_ENC_CAPS_SUPPORT_TEMPORAL_FILTER, "temporal_filter"}, + {NV_ENC_CAPS_SUPPORT_LOOKAHEAD_LEVEL, "lookahead_level"}, +#endif +}; + +static const vector> codecs = { + {"h264", NV_ENC_CODEC_H264_GUID}, + {"hevc", NV_ENC_CODEC_HEVC_GUID}, + {"av1", NV_ENC_CODEC_AV1_GUID}}; + +typedef unordered_map> codec_caps_map; + +struct device_info { + string pci_id; + string nvml_uuid; + string cuda_uuid; + string name; + + uint32_t pcie_gen; + uint32_t pcie_width; + + uint32_t encoder_sessions; + uint32_t utilisation; + uint32_t sample_period; + uint32_t capacity_h264; + uint32_t capacity_hevc; + uint32_t capacity_av1; + + codec_caps_map caps; +}; + +/* RAII wrappers to make my life a little easier. */ +struct NVML { + NVML_INIT_V2 init; + NVML_SHUTDOWN shutdown; + NVML_GET_DRIVER_VER_FUNC getDriverVersion; + NVML_GET_HANDLE_BY_BUS_ID getDeviceHandleByPCIBusId; + NVML_GET_DEVICE_UUID getDeviceUUID; + NVML_GET_DEVICE_NAME getDeviceName; + NVML_GET_DEVICE_PCIE_GEN getDevicePCIeGen; + NVML_GET_DEVICE_PCIE_WIDTH getDevicePCIeWidth; + NVML_GET_ENCODER_SESSIONS getEncoderSessions; + NVML_GET_ENCODER_CAPACITY getEncoderCapacity; + NVML_GET_ENCODER_UTILISATION getEncoderUtilisation; + + NVML() = default; + + ~NVML() + { + if (initialised && shutdown) + shutdown(); + } + + bool Init() + { + if (!load_nvml_lib()) { + printf("reason=nvml_lib\n"); + return false; + } + + init = (NVML_INIT_V2)load_nvml_func("nvmlInit_v2"); + shutdown = (NVML_SHUTDOWN)load_nvml_func("nvmlShutdown"); + getDriverVersion = (NVML_GET_DRIVER_VER_FUNC)load_nvml_func( + "nvmlSystemGetDriverVersion"); + getDeviceHandleByPCIBusId = + (NVML_GET_HANDLE_BY_BUS_ID)load_nvml_func( + "nvmlDeviceGetHandleByPciBusId_v2"); + getDeviceUUID = (NVML_GET_DEVICE_UUID)load_nvml_func( + "nvmlDeviceGetUUID"); + getDeviceName = (NVML_GET_DEVICE_NAME)load_nvml_func( + "nvmlDeviceGetName"); + getDevicePCIeGen = (NVML_GET_DEVICE_PCIE_GEN)load_nvml_func( + "nvmlDeviceGetCurrPcieLinkGeneration"); + getDevicePCIeWidth = (NVML_GET_DEVICE_PCIE_WIDTH)load_nvml_func( + "nvmlDeviceGetCurrPcieLinkWidth"); + getEncoderSessions = (NVML_GET_ENCODER_SESSIONS)load_nvml_func( + "nvmlDeviceGetEncoderSessions"); + getEncoderCapacity = (NVML_GET_ENCODER_CAPACITY)load_nvml_func( + "nvmlDeviceGetEncoderCapacity"); + getEncoderUtilisation = + (NVML_GET_ENCODER_UTILISATION)load_nvml_func( + "nvmlDeviceGetEncoderUtilization"); + + if (!init || !shutdown || !getDriverVersion || + !getDeviceHandleByPCIBusId || !getDeviceUUID || + !getDeviceName || !getDevicePCIeGen || + !getDevicePCIeWidth || !getEncoderSessions || + !getEncoderCapacity || !getEncoderUtilisation) { + return false; + } + + nvmlReturn_t res = init(); + if (res != 0) { + printf("reason=nvml_init_%d\n", res); + return false; + } + + initialised = true; + return true; + } + +private: + bool initialised = false; + static inline void *nvml_lib = nullptr; + + bool load_nvml_lib() + { +#ifdef _WIN32 + nvml_lib = LoadLibraryA("nvml.dll"); +#else + nvml_lib = dlopen("libnvidia-ml.so.1", RTLD_LAZY); +#endif + return nvml_lib != nullptr; + } + + static void *load_nvml_func(const char *func) + { +#ifdef _WIN32 + void *func_ptr = + (void *)GetProcAddress((HMODULE)nvml_lib, func); +#else + void *func_ptr = dlsym(nvml_lib, func); +#endif + return func_ptr; + } +}; + +struct CUDACtx { + CUcontext ctx; + + CUDACtx() = default; + + ~CUDACtx() { cu->cuCtxDestroy(ctx); } + + bool Init(int adapter_idx) + { + CUdevice dev; + if (cu->cuDeviceGet(&dev, adapter_idx) != CUDA_SUCCESS) + return false; + + return cu->cuCtxCreate(&ctx, 0, dev) == CUDA_SUCCESS; + } + + string GetPCIBusId() + { + CUdevice dev; + string bus_id; + bus_id.resize(16); + + cu->cuCtxGetDevice(&dev); + cu->cuDeviceGetPCIBusId(bus_id.data(), (int)bus_id.capacity(), + dev); + return bus_id; + } + + string GetUUID() + { + CUdevice dev; + CUuuid uuid; + string uuid_str; + + cu->cuCtxGetDevice(&dev); + cu->cuDeviceGetUuid_v2(&uuid, dev); + + uuid_str.resize(32); + for (size_t idx = 0; idx < 16; idx++) { + sprintf(uuid_str.data() + idx * 2, "%02x", + uuid.bytes[idx] & 0xFF); + } + + return uuid_str; + } +}; + +struct NVSession { + void *ptr = nullptr; + + NVSession() = default; + + ~NVSession() { nv.nvEncDestroyEncoder(ptr); } + + bool OpenSession(const CUDACtx &ctx) + { + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params = {}; + params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER; + params.apiVersion = NVENCAPI_VERSION; + params.device = ctx.ctx; + params.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + + return nv.nvEncOpenEncodeSessionEx(¶ms, &ptr) == + NV_ENC_SUCCESS; + } +}; + +static bool init_nvenc() +{ + if (nvenc_load_functions(&nvenc, nullptr)) { + printf("reason=nvenc_lib\n"); + return false; + } + + NVENCSTATUS res = nvenc->NvEncodeAPICreateInstance(&nv); + if (res != NV_ENC_SUCCESS) { + printf("reason=nvenc_init_%d\n", res); + return false; + } + + return true; +} + +static bool init_cuda() +{ + if (cuda_load_functions(&cu, nullptr)) { + printf("reason=cuda_lib\n"); + return false; + } + + CUresult res = cu->cuInit(0); + if (res != CUDA_SUCCESS) { + printf("reason=cuda_init_%d\n", res); + return false; + } + + return true; +} + +static bool get_adapter_caps(int adapter_idx, codec_caps_map &caps, + device_info &device_info, NVML &nvml) +{ + CUDACtx cudaCtx; + NVSession nvSession; + + if (!cudaCtx.Init(adapter_idx)) + return false; + + device_info.pci_id = cudaCtx.GetPCIBusId(); + device_info.cuda_uuid = cudaCtx.GetUUID(); + + nvmlDevice_t dev; + if (nvml.getDeviceHandleByPCIBusId(device_info.pci_id.data(), &dev) == + NVML_SUCCESS) { + char uuid[NVML_DEVICE_UUID_V2_BUFFER_SIZE]; + nvml.getDeviceUUID(dev, uuid, sizeof(uuid)); + device_info.nvml_uuid = uuid; + + char name[NVML_DEVICE_NAME_V2_BUFFER_SIZE]; + nvml.getDeviceName(dev, name, sizeof(name)); + device_info.name = name; + + nvml.getDevicePCIeGen(dev, &device_info.pcie_gen); + nvml.getDevicePCIeWidth(dev, &device_info.pcie_width); + nvml.getEncoderSessions(dev, &device_info.encoder_sessions, + nullptr); + nvml.getEncoderUtilisation(dev, &device_info.utilisation, + &device_info.sample_period); + nvml.getEncoderCapacity(dev, NVML_ENCODER_QUERY_H264, + &device_info.capacity_h264); + nvml.getEncoderCapacity(dev, NVML_ENCODER_QUERY_HEVC, + &device_info.capacity_hevc); + nvml.getEncoderCapacity(dev, NVML_ENCODER_QUERY_AV1, + &device_info.capacity_av1); + } + + if (!nvSession.OpenSession(cudaCtx)) + return false; + + uint32_t guid_count = 0; + if (nv.nvEncGetEncodeGUIDCount(nvSession.ptr, &guid_count) != + NV_ENC_SUCCESS) + return false; + + vector guids; + guids.resize(guid_count); + NVENCSTATUS stat = nv.nvEncGetEncodeGUIDs(nvSession.ptr, guids.data(), + guid_count, &guid_count); + if (stat != NV_ENC_SUCCESS) + return false; + + NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER}; + + for (uint32_t i = 0; i < guid_count; i++) { + GUID *guid = &guids[i]; + + std::string codec_name = "unknown"; + for (const auto &[name, codec_guid] : codecs) { + if (memcmp(&codec_guid, guid, sizeof(GUID)) == 0) { + codec_name = name; + break; + } + } + + caps[codec_name]["codec_supported"] = 1; + device_info.caps[codec_name]["codec_supported"] = 1; + + for (const auto &[cap, name] : capabilities) { + int v; + param.capsToQuery = cap; + if (nv.nvEncGetEncodeCaps(nvSession.ptr, *guid, ¶m, + &v) != NV_ENC_SUCCESS) + continue; + + device_info.caps[codec_name][name] = v; + if (v > caps[codec_name][name]) + caps[codec_name][name] = v; + } + } + + return true; +} + +bool nvenc_checks(codec_caps_map &caps, vector &device_infos) +{ + /* NVENC API init */ + if (!init_nvenc()) + return false; + + /* CUDA init */ + if (!init_cuda()) + return false; + + NVML nvml; + if (!nvml.Init()) + return false; + + /* --------------------------------------------------------- */ + /* obtain adapter compatibility information */ + + uint32_t nvenc_ver; + int cuda_driver_ver; + int cuda_devices = 0; + int nvenc_devices = 0; + char driver_ver[NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE]; + + /* NVIDIA driver version */ + if (nvml.getDriverVersion(driver_ver, sizeof(driver_ver)) == + NVML_SUCCESS) { + printf("driver_ver=%s\n", driver_ver); + } else { + // Treat this as a non-fatal failure + printf("driver_ver=0.0\n"); + } + + /* CUDA driver version and devices */ + if (cu->cuDriverGetVersion(&cuda_driver_ver) == CUDA_SUCCESS) { + printf("cuda_ver=%d.%d\n", cuda_driver_ver / 1000, + cuda_driver_ver % 1000); + } else { + printf("reason=no_cuda_version\n"); + return false; + } + + if (cu->cuDeviceGetCount(&cuda_devices) == CUDA_SUCCESS && + cuda_devices) { + printf("cuda_devices=%d\n", cuda_devices); + } else { + printf("reason=no_devices\n"); + return false; + } + + /* NVENC API version */ + if (nvenc->NvEncodeAPIGetMaxSupportedVersion(&nvenc_ver) == + NV_ENC_SUCCESS) { + printf("nvenc_ver=%d.%d\n", nvenc_ver >> 4, nvenc_ver & 0xf); + } else { + printf("reason=no_nvenc_version\n"); + return false; + } + + if (nvenc_ver < NVENC_CONFIGURED_VERSION) { + printf("reason=outdated_driver\n"); + return false; + } + + device_infos.resize(cuda_devices); + for (int idx = 0; idx < cuda_devices; idx++) { + if (get_adapter_caps(idx, caps, device_infos[idx], nvml)) + nvenc_devices++; + } + + printf("nvenc_devices=%d\n", nvenc_devices); + if (!nvenc_devices) { + printf("reason=no_supported_devices\n"); + return false; + } + + return true; +} + +int check_thread() +{ + int ret = 0; + codec_caps_map caps; + vector device_infos; + + caps["h264"]["codec_supported"] = 0; + caps["hevc"]["codec_supported"] = 0; + caps["av1"]["codec_supported"] = 0; + + printf("[general]\n"); + + if (nvenc_checks(caps, device_infos)) { + printf("nvenc_supported=true\n"); + } else { + printf("nvenc_supported=false\n"); + ret = 1; + } + + /* Global capabilities, based on highest supported across all devices */ + for (const auto &[codec, codec_caps] : caps) { + printf("\n[%s]\n", codec.c_str()); + + for (const auto &[name, value] : codec_caps) { + printf("%s=%d\n", name.c_str(), value); + } + } + + /* Per-device info (mostly for debugging) */ + for (size_t idx = 0; idx < device_infos.size(); idx++) { + const auto &info = device_infos[idx]; + + printf("\n[device.%zu]\n" + "pci_id=%s\n" + "nvml_uuid=%s\n" + "cuda_uuid=%s\n" + "name=%s\n" + "pcie_link_width=%d\n" + "pcie_link_gen=%d\n" + "encoder_sessions=%u\n" + "utilisation=%u\n" + "sample_period=%u\n" + "capacity_h264=%u\n" + "capacity_hevc=%u\n" + "capacity_av1=%u\n", + idx, info.pci_id.c_str(), info.nvml_uuid.c_str(), + info.cuda_uuid.c_str(), info.name.c_str(), + info.pcie_width, info.pcie_gen, info.encoder_sessions, + info.utilisation, info.sample_period, info.capacity_h264, + info.capacity_hevc, info.capacity_av1); + + for (const auto &[codec, codec_caps] : info.caps) { + printf("\n[device.%zu.%s]\n", idx, codec.c_str()); + + for (const auto &[name, value] : codec_caps) { + printf("%s=%d\n", name.c_str(), value); + } + } + } + + return ret; +} + +int main(int, char **) +{ + future f = async(launch::async, check_thread); + future_status status = f.wait_for(2.5s); + + if (status == future_status::timeout) + exit(1); + + return f.get(); +} diff --git a/plugins/obs-nvenc/obs-nvenc.c b/plugins/obs-nvenc/obs-nvenc.c new file mode 100644 index 00000000000000..3e3735e56ff1a2 --- /dev/null +++ b/plugins/obs-nvenc/obs-nvenc.c @@ -0,0 +1,30 @@ +#include + +#include "obs-nvenc.h" + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("obs-nvenc", "en-US") + +MODULE_EXPORT const char *obs_module_description(void) +{ + return "NVIDIA Encoder (NVENC) Plugin"; +} + +bool obs_module_load(void) +{ + if (!nvenc_supported()) { + blog(LOG_INFO, "NVENC not supported"); + return false; + } + + obs_nvenc_load(); + obs_cuda_load(); + + return true; +} + +void obs_module_unload(void) +{ + obs_cuda_unload(); + obs_nvenc_unload(); +} diff --git a/plugins/obs-nvenc/obs-nvenc.h b/plugins/obs-nvenc/obs-nvenc.h new file mode 100644 index 00000000000000..6ee96fae4b426d --- /dev/null +++ b/plugins/obs-nvenc/obs-nvenc.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +bool nvenc_supported(void); + +void obs_nvenc_load(void); +void obs_nvenc_unload(void); + +void obs_cuda_load(void); +void obs_cuda_unload(void); From 6e435dbe2ebd35bddfb7135172be2c4a01d83f52 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 13 Apr 2024 11:36:22 +0200 Subject: [PATCH 0376/1073] cmake: Update ubuntu preset with NVENC changes --- CMakePresets.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index 3869d53a327b01..1349681c13758e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -73,7 +73,8 @@ "CMAKE_INSTALL_LIBDIR": "lib/CMAKE_SYSTEM_PROCESSOR-linux-gnu", "OBS_CMAKE_VERSION": {"type": "STRING", "value": "3.0.0"}, "ENABLE_AJA": false, - "ENABLE_NATIVE_NVENC": false, + "ENABLE_NVENC": false, + "ENABLE_FFMPEG_NVENC": true, "ENABLE_VLC": true, "ENABLE_WAYLAND": true, "ENABLE_WEBRTC": false From feba2bcbf9d63147f327336c82f6cf876b0f7dd0 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 12 Apr 2024 05:09:15 +0200 Subject: [PATCH 0377/1073] UI: Use new NVENC encoder ids --- UI/window-basic-main-outputs.cpp | 14 ++++++++------ UI/window-basic-settings-stream.cpp | 7 ++++--- UI/window-basic-settings.cpp | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 033c5f3ca913db..b1edff50b9c695 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -609,15 +609,17 @@ const char *get_simple_output_encoder(const char *encoder) } else if (strcmp(encoder, SIMPLE_ENCODER_AMD_AV1) == 0) { return "av1_texture_amf"; } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) { - return EncoderAvailable("jim_nvenc") ? "jim_nvenc" - : "ffmpeg_nvenc"; + return EncoderAvailable("obs_nvenc_h264_tex") + ? "obs_nvenc_h264_tex" + : "ffmpeg_nvenc"; #ifdef ENABLE_HEVC } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC_HEVC) == 0) { - return EncoderAvailable("jim_hevc_nvenc") ? "jim_hevc_nvenc" - : "ffmpeg_hevc_nvenc"; + return EncoderAvailable("obs_nvenc_hevc_tex") + ? "obs_nvenc_hevc_tex" + : "ffmpeg_hevc_nvenc"; #endif } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC_AV1) == 0) { - return "jim_av1_nvenc"; + return "obs_nvenc_av1_tex"; } else if (strcmp(encoder, SIMPLE_ENCODER_APPLE_H264) == 0) { return "com.apple.videotoolbox.videoencoder.ave.avc"; #ifdef ENABLE_HEVC @@ -1848,7 +1850,7 @@ void AdvancedOutput::UpdateStreamSettings() blog(LOG_WARNING, "User is ignoring service settings."); } - if (dynBitrate && astrcmpi(streamEncoder, "jim_nvenc") == 0) + if (dynBitrate && strstr(streamEncoder, "nvenc") != nullptr) obs_data_set_bool(settings, "lookahead", false); video_t *video = obs_get_video(); diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 0c34fdac86ee2a..f005faf8f7dbe5 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -1606,8 +1606,9 @@ bool OBSBasicSettings::ServiceAndACodecCompatible() /* we really need a way to find fallbacks in a less hardcoded way. maybe. */ static QString get_adv_fallback(const QString &enc) { - if (enc == "jim_hevc_nvenc" || enc == "jim_av1_nvenc") - return "jim_nvenc"; + if (enc == "obs_nvenc_hevc_tex" || enc == "obs_nvenc_av1_tex" || + enc == "jim_hevc_nvenc" || enc == "jim_av1_nvenc") + return "obs_nvenc_h264_tex"; if (enc == "h265_texture_amf" || enc == "av1_texture_amf") return "h264_texture_amf"; if (enc == "com.apple.videotoolbox.videoencoder.ave.hevc") @@ -1863,7 +1864,7 @@ void OBSBasicSettings::ResetEncoders(bool streamOnly) ui->simpleOutStrEncoder->addItem( ENCODER_STR("Hardware.NVENC.H264"), QString(SIMPLE_ENCODER_NVENC)); - if (service_supports_encoder(vcodecs, "jim_av1_nvenc")) + if (service_supports_encoder(vcodecs, "obs_nvenc_av1_tex")) ui->simpleOutStrEncoder->addItem( ENCODER_STR("Hardware.NVENC.AV1"), QString(SIMPLE_ENCODER_NVENC_AV1)); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 1f5812b42cf5db..787a5c047a4eba 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -5354,7 +5354,7 @@ void OBSBasicSettings::FillSimpleRecordingValues() ui->simpleOutRecEncoder->addItem( ENCODER_STR("Hardware.NVENC.H264"), QString(SIMPLE_ENCODER_NVENC)); - if (EncoderAvailable("jim_av1_nvenc")) + if (EncoderAvailable("obs_nvenc_av1_tex")) ui->simpleOutRecEncoder->addItem( ENCODER_STR("Hardware.NVENC.AV1"), QString(SIMPLE_ENCODER_NVENC_AV1)); From 1451554fb8f05d9a92452b5d8bd4dfa18fb56578 Mon Sep 17 00:00:00 2001 From: pkv Date: Wed, 10 Apr 2024 11:31:31 +0200 Subject: [PATCH 0378/1073] obs-ffmpeg: Fix SRT listener bug Fixes #10504. There was a bug in FFmpeg implementation which was hidden by a bug in libsrt; it was fixed in a recent commit [1]. When we ported FFmpeg libsrt.c to obs, we brought the said bug along. When starting an SRT stream in listener mode, if no connection is made by a client, there were two issues: - 1) obs was stuck into a connecting loop, - 2) the socket was not closed when exiting OBS. This fixes the issue so that SRT is displaying that a stream started when in listener mode even if NO client is connected. This is the correct behaviour for a listener. The stream now closes properly. [1] https://git.videolan.org/?p=ffmpeg.git;a=commit;h=87677c2195e86b126c3438439a05d0a46ae5bb50 Signed-off-by: pkv --- plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c | 44 ++++++++++++-------------- plugins/obs-ffmpeg/obs-ffmpeg-output.h | 2 ++ plugins/obs-ffmpeg/obs-ffmpeg-srt.h | 23 ++++++++------ 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c b/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c index 69ec8a7c25ef4a..5139acc83e3d50 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c @@ -63,16 +63,14 @@ void ffmpeg_mpegts_log_error(int log_level, struct ffmpeg_data *data, blog(log_level, "%s", out); } -static bool is_rist(struct ffmpeg_output *stream) +static bool is_rist(const char *url) { - return !strncmp(stream->ff_data.config.url, RIST_PROTO, - sizeof(RIST_PROTO) - 1); + return !strncmp(url, RIST_PROTO, sizeof(RIST_PROTO) - 1); } -static bool is_srt(struct ffmpeg_output *stream) +static bool is_srt(const char *url) { - return !strncmp(stream->ff_data.config.url, SRT_PROTO, - sizeof(SRT_PROTO) - 1); + return !strncmp(url, SRT_PROTO, sizeof(SRT_PROTO) - 1); } static bool proto_is_allowed(struct ffmpeg_output *stream) @@ -467,8 +465,8 @@ static inline int open_output_file(struct ffmpeg_output *stream, struct ffmpeg_data *data) { int ret; - bool rist = is_rist(stream); - bool srt = is_srt(stream); + bool rist = data->config.is_rist; + bool srt = data->config.is_srt; bool allowed_proto = proto_is_allowed(stream); AVDictionary *dict = NULL; @@ -591,10 +589,7 @@ static void close_audio(struct ffmpeg_data *data) static void close_mpegts_url(struct ffmpeg_output *stream, bool is_rist) { int err = 0; - AVIOContext *s = stream->s; - if (!s) - return; - URLContext *h = s->opaque; + URLContext *h = stream->h; if (!h) return; /* can happen when opening the url fails */ @@ -608,10 +603,14 @@ static void close_mpegts_url(struct ffmpeg_output *stream, bool is_rist) av_freep(h); /* close custom avio_context for srt or rist */ - avio_flush(stream->s); - stream->s->opaque = NULL; - av_freep(&stream->s->buffer); - avio_context_free(&stream->s); + AVIOContext *s = stream->s; + if (!s) + return; + + avio_flush(s); + s->opaque = NULL; + av_freep(&s->buffer); + avio_context_free(&s); if (err) info("[ffmpeg mpegts muxer]: Error closing URL %s", @@ -632,8 +631,8 @@ void ffmpeg_mpegts_data_free(struct ffmpeg_output *stream, } if (data->output) { - if (is_rist(stream) || is_srt(stream)) { - close_mpegts_url(stream, is_rist(stream)); + if (data->config.is_rist || data->config.is_srt) { + close_mpegts_url(stream, data->config.is_rist); } else { avio_close(data->output->pb); } @@ -757,11 +756,10 @@ static void ffmpeg_mpegts_destroy(void *data) struct ffmpeg_output *output = data; if (output) { + ffmpeg_mpegts_full_stop(output); if (output->connecting) pthread_join(output->start_thread, NULL); - ffmpeg_mpegts_full_stop(output); - pthread_mutex_destroy(&output->write_mutex); os_sem_destroy(output->write_sem); os_event_destroy(output->stop_event); @@ -910,7 +908,8 @@ static bool set_config(struct ffmpeg_output *stream) service, OBS_SERVICE_CONNECT_INFO_ENCRYPT_PASSPHRASE); config.format_name = "mpegts"; config.format_mime_type = "video/M2PT"; - + config.is_rist = is_rist(config.url); + config.is_srt = is_srt(config.url); /* 2. video settings */ // 2.a) set width & height @@ -1110,6 +1109,7 @@ static void ffmpeg_mpegts_full_stop(void *data) obs_output_end_data_capture(output->output); ffmpeg_mpegts_deactivate(output); } + ffmpeg_mpegts_data_free(output, &output->ff_data); } static void ffmpeg_mpegts_stop(void *data, uint64_t ts) @@ -1144,8 +1144,6 @@ static void ffmpeg_mpegts_deactivate(struct ffmpeg_output *output) da_free(output->packets); pthread_mutex_unlock(&output->write_mutex); - - ffmpeg_mpegts_data_free(output, &output->ff_data); } static uint64_t ffmpeg_mpegts_total_bytes(void *data) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.h b/plugins/obs-ffmpeg/obs-ffmpeg-output.h index 7b898f9a21d9d8..04715d53cac7c6 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.h @@ -43,6 +43,8 @@ struct ffmpeg_cfg { const char *password; const char *stream_id; const char *encrypt_passphrase; + bool is_srt; + bool is_rist; }; struct ffmpeg_audio_info { diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-srt.h b/plugins/obs-ffmpeg/obs-ffmpeg-srt.h index 274ec17953545d..d6d6c9cd4c191c 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-srt.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-srt.h @@ -150,7 +150,7 @@ static int libsrt_network_wait_fd(URLContext *h, int eid, int write) int ret, len = 1, errlen = 1; SRTSOCKET ready[1]; SRTSOCKET error[1]; - + SRTContext *s = (SRTContext *)h->priv_data; if (write) { ret = srt_epoll_wait(eid, error, &errlen, ready, &len, POLLING_TIME, 0, 0, 0, 0); @@ -158,7 +158,7 @@ static int libsrt_network_wait_fd(URLContext *h, int eid, int write) ret = srt_epoll_wait(eid, ready, &len, error, &errlen, POLLING_TIME, 0, 0, 0, 0); } - if (len == 1 && errlen == 1) { + if (len == 1 && errlen == 1 && s->mode == SRT_MODE_CALLER) { /* Socket reported in wsock AND rsock signifies an error. */ int reason = srt_getrejectreason(*ready); @@ -232,7 +232,7 @@ static int libsrt_listen(int eid, SRTSOCKET fd, const struct sockaddr *addr, if (srt_listen(fd, 1)) return libsrt_neterrno(h); - ret = libsrt_network_wait_fd_timeout(h, eid, 1, timeout, + ret = libsrt_network_wait_fd_timeout(h, eid, 0, timeout, &h->interrupt_callback); if (ret < 0) return ret; @@ -462,7 +462,7 @@ static int libsrt_setup(URLContext *h, const char *uri) char hostname[1024], proto[1024], path[1024]; char portstr[10]; int64_t open_timeout = 0; - int eid, write_eid; + int eid; struct sockaddr_in la; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), @@ -552,20 +552,23 @@ static int libsrt_setup(URLContext *h, const char *uri) blog(LOG_DEBUG, "[obs-ffmpeg mpegts muxer / libsrt]: libsrt_socket_nonblock failed"); - ret = write_eid = libsrt_epoll_create(h, fd, 1); - if (ret < 0) - goto fail1; if (s->mode == SRT_MODE_LISTENER) { + int read_eid = ret = libsrt_epoll_create(h, fd, 0); + if (ret < 0) + goto fail1; // multi-client - ret = libsrt_listen(write_eid, fd, cur_ai->ai_addr, + ret = libsrt_listen(read_eid, fd, cur_ai->ai_addr, (socklen_t)cur_ai->ai_addrlen, h, s->listen_timeout); - srt_epoll_release(write_eid); + srt_epoll_release(read_eid); if (ret < 0) goto fail1; srt_close(fd); fd = ret; } else { + int write_eid = ret = libsrt_epoll_create(h, fd, 1); + if (ret < 0) + goto fail1; if (s->mode == SRT_MODE_RENDEZVOUS) { if (srt_bind(fd, (struct sockaddr *)&la, sizeof(struct sockaddr_in))) { @@ -878,6 +881,8 @@ static int libsrt_write(URLContext *h, const uint8_t *buf, int size) static int libsrt_close(URLContext *h) { SRTContext *s = (SRTContext *)h->priv_data; + if (!s) + return 0; if (s->streamid) av_freep(&s->streamid); if (s->passphrase) From 66ff8cb6d37a0b6eb5624b41797d77e3dc60559d Mon Sep 17 00:00:00 2001 From: jcm <6864788+jcm93@users.noreply.github.com> Date: Thu, 8 Aug 2024 17:39:23 -0500 Subject: [PATCH 0379/1073] libobs: Always explicitly check modifiers in macOS hotkey event handler --- libobs/obs-cocoa.m | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libobs/obs-cocoa.m b/libobs/obs-cocoa.m index 6094b12427736b..fc93f8eeca4c88 100644 --- a/libobs/obs-cocoa.m +++ b/libobs/obs-cocoa.m @@ -668,14 +668,13 @@ static bool log_layout_name(TISInputSourceRef tis) static void handle_monitor_event(obs_hotkeys_platform_t *plat, NSEvent *event) { - if (event.type == NSEventTypeFlagsChanged) { - NSEventModifierFlags flags = event.modifierFlags; - plat->is_key_down[OBS_KEY_CAPSLOCK] = !!(flags & NSEventModifierFlagCapsLock); - plat->is_key_down[OBS_KEY_SHIFT] = !!(flags & NSEventModifierFlagShift); - plat->is_key_down[OBS_KEY_ALT] = !!(flags & NSEventModifierFlagOption); - plat->is_key_down[OBS_KEY_META] = !!(flags & NSEventModifierFlagCommand); - plat->is_key_down[OBS_KEY_CONTROL] = !!(flags & NSEventModifierFlagControl); - } else if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { + NSEventModifierFlags flags = event.modifierFlags; + plat->is_key_down[OBS_KEY_CAPSLOCK] = !!(flags & NSEventModifierFlagCapsLock); + plat->is_key_down[OBS_KEY_SHIFT] = !!(flags & NSEventModifierFlagShift); + plat->is_key_down[OBS_KEY_ALT] = !!(flags & NSEventModifierFlagOption); + plat->is_key_down[OBS_KEY_META] = !!(flags & NSEventModifierFlagCommand); + plat->is_key_down[OBS_KEY_CONTROL] = !!(flags & NSEventModifierFlagControl); + if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { plat->is_key_down[obs_key_from_virtual_key(event.keyCode)] = (event.type == NSEventTypeKeyDown); } } From 83528fba2c461dbd9c6c079f8572498a0aefd183 Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 10 Aug 2024 01:34:45 +0200 Subject: [PATCH 0380/1073] obs-nvenc: Improve logging for custom options --- plugins/obs-nvenc/nvenc-opts-parser.c | 73 ++++++++++++++------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/plugins/obs-nvenc/nvenc-opts-parser.c b/plugins/obs-nvenc/nvenc-opts-parser.c index ae43bcd14f46f6..6b8ad420761268 100644 --- a/plugins/obs-nvenc/nvenc-opts-parser.c +++ b/plugins/obs-nvenc/nvenc-opts-parser.c @@ -1,26 +1,31 @@ #include "nvenc-internal.h" #include +#include /* NVIDIA uses bitfields for a variety of options. As it is not possible to * use offsetof() or similar with those we resort to macros here to avoid too * much boilerplate. */ -#define APPLY_BIT_OPT(opt_name, bits) \ - if (strcmp(opt->name, #opt_name) == 0) { \ - uint32_t old_val = nv_conf->opt_name; \ - nv_conf->opt_name = strtol(opt->value, NULL, 10); \ - blog(LOG_DEBUG, "[obs-nvenc] Changing: \"%s\": %u -> %u", \ - #opt_name, old_val, nv_conf->opt_name); \ - return true; \ +#define APPLY_BIT_OPT(opt_name, bits) \ + if (strcmp(opt->name, #opt_name) == 0) { \ + uint32_t old_val = nv_conf->opt_name; \ + nv_conf->opt_name = strtol(opt->value, NULL, 10); \ + blog(LOG_DEBUG, \ + "[obs-nvenc] Changing parameter: \"%s\": %u -> %u (%d bit(s))", \ + #opt_name, old_val, nv_conf->opt_name, bits); \ + return true; \ } -#define APPLY_INT_OPT(opt_name, type) \ - if (strcmp(opt->name, #opt_name) == 0) { \ - blog(LOG_DEBUG, "[obs-nvenc] Changing \"%s\": %d -> %s (%s)", \ - #opt_name, nv_conf->opt_name, opt->value, #type); \ - nv_conf->opt_name = (type)strtol(opt->value, NULL, 10); \ - return true; \ +#define APPLY_INT_OPT(opt_name, type, format) \ + if (strcmp(opt->name, #opt_name) == 0) { \ + type old_val = nv_conf->opt_name; \ + nv_conf->opt_name = (type)strtol(opt->value, NULL, 10); \ + blog(LOG_DEBUG, \ + "[obs-nvenc] Changing parameter: \"%s\": %" format \ + " -> %" format " (%s)", \ + #opt_name, old_val, nv_conf->opt_name, #type); \ + return true; \ } static void parse_qp_opt(const char *name, const char *val, NV_ENC_QP *qp_opt) @@ -58,16 +63,16 @@ static bool apply_rc_opt(const struct obs_option *opt, APPLY_QP_OPT(maxQP) APPLY_QP_OPT(initialRCQP) - APPLY_INT_OPT(averageBitRate, uint32_t) - APPLY_INT_OPT(maxBitRate, uint32_t) - APPLY_INT_OPT(vbvBufferSize, uint32_t) - APPLY_INT_OPT(vbvInitialDelay, uint32_t) + APPLY_INT_OPT(averageBitRate, uint32_t, PRIu32) + APPLY_INT_OPT(maxBitRate, uint32_t, PRIu32) + APPLY_INT_OPT(vbvBufferSize, uint32_t, PRIu32) + APPLY_INT_OPT(vbvInitialDelay, uint32_t, PRIu32) - APPLY_INT_OPT(targetQuality, uint8_t) - APPLY_INT_OPT(targetQualityLSB, uint8_t) + APPLY_INT_OPT(targetQuality, uint8_t, PRIu8) + APPLY_INT_OPT(targetQualityLSB, uint8_t, PRIu8) - APPLY_INT_OPT(cbQPIndexOffset, int8_t) - APPLY_INT_OPT(crQPIndexOffset, int8_t) + APPLY_INT_OPT(cbQPIndexOffset, int8_t, PRIi8) + APPLY_INT_OPT(crQPIndexOffset, int8_t, PRIi8) APPLY_BIT_OPT(enableMinQP, 1) APPLY_BIT_OPT(enableMaxQP, 1) @@ -90,8 +95,8 @@ static bool apply_rc_opt(const struct obs_option *opt, static bool apply_conf_opt(const struct obs_option *opt, NV_ENC_CONFIG *nv_conf) { - APPLY_INT_OPT(gopLength, uint32_t) - APPLY_INT_OPT(frameIntervalP, int32_t) + APPLY_INT_OPT(gopLength, uint32_t, PRIu32) + APPLY_INT_OPT(frameIntervalP, int32_t, PRIi32) return false; } @@ -129,8 +134,8 @@ static bool apply_h264_opt(struct obs_option *opt, NV_ENC_CONFIG_H264 *nv_conf) return true; } - APPLY_INT_OPT(idrPeriod, uint32_t) - APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE) + APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32) + APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32) APPLY_BIT_OPT(enableFillerDataInsertion, 1) @@ -144,9 +149,9 @@ static bool apply_hevc_opt(struct obs_option *opt, NV_ENC_CONFIG_HEVC *nv_conf) return true; } - APPLY_INT_OPT(tier, uint32_t) - APPLY_INT_OPT(idrPeriod, uint32_t) - APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE) + APPLY_INT_OPT(tier, uint32_t, PRIu32) + APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32) + APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32) #ifdef NVENC_12_2_OR_LATER APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL) #endif @@ -158,12 +163,12 @@ static bool apply_hevc_opt(struct obs_option *opt, NV_ENC_CONFIG_HEVC *nv_conf) static bool apply_av1_opt(struct obs_option *opt, NV_ENC_CONFIG_AV1 *nv_conf) { - APPLY_INT_OPT(level, uint32_t) - APPLY_INT_OPT(tier, uint32_t) - APPLY_INT_OPT(numTileColumns, uint32_t) - APPLY_INT_OPT(numTileRows, uint32_t) - APPLY_INT_OPT(idrPeriod, uint32_t) - APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE) + APPLY_INT_OPT(level, uint32_t, PRIu32) + APPLY_INT_OPT(tier, uint32_t, PRIu32) + APPLY_INT_OPT(numTileColumns, uint32_t, PRIu32) + APPLY_INT_OPT(numTileRows, uint32_t, PRIu32) + APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32) + APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32) APPLY_BIT_OPT(enableBitstreamPadding, 1) From 0306effc5f8c14829bfd7599b9513ef98c005767 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 18 Jun 2024 15:41:07 -0500 Subject: [PATCH 0381/1073] UI: Add pragma once to ScreenshotObj header Makes sure header is only included once preventing code clashes. --- UI/screenshot-obj.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/screenshot-obj.hpp b/UI/screenshot-obj.hpp index 3a2603ff42bce4..037b7f84d681d1 100644 --- a/UI/screenshot-obj.hpp +++ b/UI/screenshot-obj.hpp @@ -15,6 +15,8 @@ along with this program. If not, see . ******************************************************************************/ +#pragma once + #include #include #include From 89554112c1384f4bebbff503e70468fa3ba96803 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Tue, 18 Jun 2024 14:18:22 -0500 Subject: [PATCH 0382/1073] UI: Cleanup frontend event handling This adds a function to OBSBasic to call on_event, so every time a event is called, the api variable doesn't have to be checked everytime. --- UI/window-basic-main-profiles.cpp | 31 +++---- UI/window-basic-main-scene-collections.cpp | 27 ++---- UI/window-basic-main-screenshot.cpp | 4 +- UI/window-basic-main-transitions.cpp | 40 +++------ UI/window-basic-main.cpp | 99 ++++++++-------------- UI/window-basic-main.hpp | 2 + 6 files changed, 73 insertions(+), 130 deletions(-) diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index 08b869aebe5e73..7492e51d7e4c48 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -305,8 +305,8 @@ bool OBSBasic::CreateProfile(const std::string &newName, bool create_new, return false; } - if (api && !rename) - api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGING); + if (!rename) + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); config_set_string(App()->GlobalConfig(), "Basic", "Profile", newName.c_str()); @@ -355,9 +355,9 @@ bool OBSBasic::CreateProfile(const std::string &newName, bool create_new, wizard.exec(); } - if (api && !rename) { - api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); - api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); + if (!rename) { + OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED); } return true; } @@ -451,8 +451,7 @@ void OBSBasic::DeleteProfile(const QString &profileName) DeleteProfile(name.c_str(), profileDir); RefreshProfiles(); config_save_safe(App()->GlobalConfig(), "tmp", nullptr); - if (api) - api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); } void OBSBasic::RefreshProfiles() @@ -539,8 +538,7 @@ void OBSBasic::on_actionRenameProfile_triggered() RefreshProfiles(); } - if (api) - api->on_event(OBS_FRONTEND_EVENT_PROFILE_RENAMED); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_RENAMED); } void OBSBasic::on_actionRemoveProfile_triggered(bool skipConfirmation) @@ -589,8 +587,7 @@ void OBSBasic::on_actionRemoveProfile_triggered(bool skipConfirmation) return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); newPath.resize(newPath_len); @@ -626,10 +623,8 @@ void OBSBasic::on_actionRemoveProfile_triggered(bool skipConfirmation) Auth::Load(); - if (api) { - api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); - api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); - } + OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED); if (needsRestart) { QMessageBox::StandardButton button = OBSMessageBox::question( @@ -768,8 +763,7 @@ void OBSBasic::ChangeProfile() return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); path.resize(path_len); @@ -811,8 +805,7 @@ void OBSBasic::ChangeProfile() blog(LOG_INFO, "Switched to profile '%s' (%s)", newName, newDir); blog(LOG_INFO, "------------------------------------------------"); - if (api) - api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED); if (needsRestart) { QMessageBox::StandardButton button = OBSMessageBox::question( diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index 26b5506ae4ee59..b3d6d18815ff61 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -168,8 +168,7 @@ bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname) RefreshSceneCollections(); }; - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); new_collection(file, name); @@ -179,10 +178,8 @@ bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname) UpdateTitleBar(); - if (api) { - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); - } + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); return true; } @@ -294,8 +291,7 @@ void OBSBasic::on_actionRenameSceneCollection_triggered() UpdateTitleBar(); RefreshSceneCollections(); - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED); } void OBSBasic::on_actionRemoveSceneCollection_triggered() @@ -339,8 +335,7 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered() return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); oldFile.insert(0, path); /* os_rename() overwrites if necessary, only the .bak file will remain. */ @@ -360,10 +355,8 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered() UpdateTitleBar(); - if (api) { - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); - } + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); } void OBSBasic::on_actionImportSceneCollection_triggered() @@ -461,8 +454,7 @@ void OBSBasic::ChangeSceneCollection() return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); SaveProjectNow(); @@ -480,6 +472,5 @@ void OBSBasic::ChangeSceneCollection() UpdateTitleBar(); - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); } diff --git a/UI/window-basic-main-screenshot.cpp b/UI/window-basic-main-screenshot.cpp index ba9c4d2bcf430a..54700d0ffa4a7c 100644 --- a/UI/window-basic-main-screenshot.cpp +++ b/UI/window-basic-main-screenshot.cpp @@ -57,9 +57,7 @@ ScreenshotObj::~ScreenshotObj() main->lastScreenshot = path; - if (main->api) - main->api->on_event( - OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN); + main->OnEvent(OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN); } } } diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index ffb28344e39bed..5a6a58a021935d 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -267,10 +267,8 @@ void OBSBasic::TransitionStopped() EnableTransitionWidgets(true); UpdatePreviewProgramIndicators(); - if (api) { - api->on_event(OBS_FRONTEND_EVENT_TRANSITION_STOPPED); - api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED); - } + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_STOPPED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED); swapScene = nullptr; } @@ -371,8 +369,7 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, if (force) { obs_transition_set(transition, source); - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED); } else { int duration = ui->transitionDuration->value(); @@ -450,8 +447,7 @@ void OBSBasic::SetTransition(OBSSource transition) ui->transitionRemove->setEnabled(configurable); ui->transitionProps->setEnabled(configurable); - if (api) - api->on_event(OBS_FRONTEND_EVENT_TRANSITION_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_CHANGED); } OBSSource OBSBasic::GetCurrentTransition() @@ -509,9 +505,7 @@ void OBSBasic::AddTransition(const char *id) CreatePropertiesWindow(source); obs_source_release(source); - if (api) - api->on_event( - OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); ClearQuickTransitionWidgets(); RefreshQuickTransitions(); @@ -566,8 +560,7 @@ void OBSBasic::on_transitionRemove_clicked() ui->transitions->removeItem(idx); - if (api) - api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); ClearQuickTransitionWidgets(); RefreshQuickTransitions(); @@ -607,9 +600,7 @@ void OBSBasic::RenameTransition(OBSSource transition) if (idx != -1) { ui->transitions->setItemText(idx, QT_UTF8(name.c_str())); - if (api) - api->on_event( - OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); ClearQuickTransitionWidgets(); RefreshQuickTransitions(); @@ -643,9 +634,7 @@ void OBSBasic::on_transitionProps_clicked() void OBSBasic::on_transitionDuration_valueChanged() { - if (api) { - api->on_event(OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED); - } + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED); } QuickTransition *OBSBasic::GetQuickTransition(int id) @@ -714,9 +703,7 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force) outputHandler ->UpdateVirtualCamOutputSource(); - if (api) - api->on_event( - OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); break; } } @@ -968,8 +955,7 @@ void OBSBasic::TBarChanged(int value) obs_transition_set_manual_time(transition, (float)value / T_BAR_PRECISION_F); - if (api) - api->on_event(OBS_FRONTEND_EVENT_TBAR_VALUE_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_TBAR_VALUE_CHANGED); } int OBSBasic::GetTbarPosition() @@ -1668,8 +1654,7 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) ui->previewLayout->setAlignment(programOptions, Qt::AlignCenter); - if (api) - api->on_event(OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED); + OnEvent(OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED); blog(LOG_INFO, "Switched to Preview/Program mode"); blog(LOG_INFO, "-----------------------------" @@ -1707,8 +1692,7 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) ui->transitions->setEnabled(true); tBarActive = false; - if (api) - api->on_event(OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED); + OnEvent(OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED); blog(LOG_INFO, "Switched to regular Preview mode"); blog(LOG_INFO, "-----------------------------" diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 22f6517297f8ea..aea1c4d4feb6ee 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -619,10 +619,8 @@ OBSBasic::OBSBasic(QWidget *parent) connect(ui->scenes, &SceneTree::scenesReordered, []() { OBSProjector::UpdateMultiviewProjectors(); }); - connect(App(), &OBSApp::StyleChanged, this, [this]() { - if (api) - api->on_event(OBS_FRONTEND_EVENT_THEME_CHANGED); - }); + connect(App(), &OBSApp::StyleChanged, this, + [this]() { OnEvent(OBS_FRONTEND_EVENT_THEME_CHANGED); }); QActionGroup *actionGroup = new QActionGroup(this); actionGroup->addAction(ui->actionSceneListMode); @@ -1470,10 +1468,8 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file) if (vcamEnabled) outputHandler->UpdateVirtualCamOutputSource(); - if (api) { - api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED); - api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); - } + OnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); } #define SERVICE_PATH "service.json" @@ -2549,8 +2545,7 @@ void OBSBasic::OBSInit() void OBSBasic::OnFirstLoad() { - if (api) - api->on_event(OBS_FRONTEND_EVENT_FINISHED_LOADING); + OnEvent(OBS_FRONTEND_EVENT_FINISHED_LOADING); #ifdef WHATSNEW_ENABLED /* Attempt to load init screen if available */ @@ -3376,8 +3371,7 @@ void OBSBasic::AddScene(OBSSource source) OBSProjector::UpdateMultiviewProjectors(); } - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); } void OBSBasic::RemoveScene(OBSSource source) @@ -3412,8 +3406,7 @@ void OBSBasic::RemoveScene(OBSSource source) OBSProjector::UpdateMultiviewProjectors(); } - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); } static bool select_one(obs_scene_t * /* scene */, obs_sceneitem_t *item, @@ -4505,8 +4498,7 @@ void OBSBasic::RemoveSelectedScene() RemoveSceneAndReleaseNested(source); - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); } void OBSBasic::ReorderSources(OBSScene scene) @@ -5131,8 +5123,7 @@ void OBSBasic::ClearSceneData() obs_enum_scenes(cb, nullptr); obs_enum_sources(cb, nullptr); - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP); undo_s.clear(); @@ -5304,8 +5295,7 @@ void OBSBasic::closeEvent(QCloseEvent *event) ClearExtraBrowserDocks(); #endif - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN); + OnEvent(OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN); disableSaving++; @@ -5313,8 +5303,7 @@ void OBSBasic::closeEvent(QCloseEvent *event) * sources, etc) so that all references are released before shutdown */ ClearSceneData(); - if (api) - api->on_event(OBS_FRONTEND_EVENT_EXIT); + OnEvent(OBS_FRONTEND_EVENT_EXIT); // Destroys the frontend API so plugins can't continue calling it obs_frontend_set_callbacks_internal(nullptr); @@ -5554,8 +5543,7 @@ void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, if (vcamEnabled && vcamConfig.type == VCamOutputType::PreviewOutput) outputHandler->UpdateVirtualCamOutputSource(); - if (api) - api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); UpdateContextBar(); } @@ -6898,8 +6886,7 @@ void OBSBasic::SceneNameEdited(QWidget *editor) ui->scenesDock->addAction(renameScene); - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); } void OBSBasic::OpenFilters(OBSSource source) @@ -7132,8 +7119,7 @@ void OBSBasic::StartStreaming() return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTING); + OnEvent(OBS_FRONTEND_EVENT_STREAMING_STARTING); SaveProject(); @@ -7490,8 +7476,7 @@ void OBSBasic::StreamDelayStopping(int sec) ui->statusbar->StreamDelayStopping(sec); - if (api) - api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); + OnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPING); } void OBSBasic::StreamingStart() @@ -7522,8 +7507,7 @@ void OBSBasic::StreamingStart() } #endif - if (api) - api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTED); + OnEvent(OBS_FRONTEND_EVENT_STREAMING_STARTED); OnActivate(); @@ -7543,8 +7527,7 @@ void OBSBasic::StreamStopping() sysTrayStream->setText(QTStr("Basic.Main.StoppingStreaming")); streamingStopping = true; - if (api) - api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); + OnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPING); } void OBSBasic::StreamingStop(int code, QString last_error) @@ -7605,8 +7588,7 @@ void OBSBasic::StreamingStop(int code, QString last_error) } streamingStopping = false; - if (api) - api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPED); + OnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPED); OnDeactivate(); @@ -7738,8 +7720,7 @@ void OBSBasic::StartRecording() return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING); + OnEvent(OBS_FRONTEND_EVENT_RECORDING_STARTING); SaveProject(); @@ -7754,8 +7735,7 @@ void OBSBasic::RecordStopping() sysTrayRecord->setText(QTStr("Basic.Main.StoppingRecording")); recordingStopping = true; - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING); + OnEvent(OBS_FRONTEND_EVENT_RECORDING_STOPPING); } void OBSBasic::StopRecording() @@ -7777,8 +7757,7 @@ void OBSBasic::RecordingStart() sysTrayRecord->setText(QTStr("Basic.Main.StopRecording")); recordingStopping = false; - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); + OnEvent(OBS_FRONTEND_EVENT_RECORDING_STARTED); if (!diskFullTimer->isActive()) diskFullTimer->start(1000); @@ -7852,8 +7831,7 @@ void OBSBasic::RecordingStop(int code, QString last_error) } } - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPED); + OnEvent(OBS_FRONTEND_EVENT_RECORDING_STOPPED); if (diskFullTimer->isActive()) diskFullTimer->stop(); @@ -7924,8 +7902,7 @@ void OBSBasic::StartReplayBuffer() return; } - if (api) - api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING); + OnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING); SaveProject(); @@ -7947,8 +7924,7 @@ void OBSBasic::ReplayBufferStopping() QTStr("Basic.Main.StoppingReplayBuffer")); replayBufferStopping = true; - if (api) - api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING); + OnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING); } void OBSBasic::StopReplayBuffer() @@ -7976,8 +7952,7 @@ void OBSBasic::ReplayBufferStart() QTStr("Basic.Main.StopReplayBuffer")); replayBufferStopping = false; - if (api) - api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED); + OnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED); OnActivate(); @@ -8016,8 +7991,7 @@ void OBSBasic::ReplayBufferSaved() lastReplay = path; calldata_free(&cd); - if (api) - api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED); + OnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED); AutoRemux(QT_UTF8(path.c_str())); } @@ -8061,8 +8035,7 @@ void OBSBasic::ReplayBufferStop(int code) QSystemTrayIcon::Warning); } - if (api) - api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED); + OnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED); OnDeactivate(); } @@ -8104,8 +8077,7 @@ void OBSBasic::OnVirtualCamStart() if (sysTrayVirtualCam) sysTrayVirtualCam->setText(QTStr("Basic.Main.StopVirtualCam")); - if (api) - api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED); + OnEvent(OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED); OnActivate(); @@ -8122,8 +8094,7 @@ void OBSBasic::OnVirtualCamStop(int) if (sysTrayVirtualCam) sysTrayVirtualCam->setText(QTStr("Basic.Main.StartVirtualCam")); - if (api) - api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED); + OnEvent(OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED); blog(LOG_INFO, VIRTUAL_CAM_STOP); @@ -10751,8 +10722,7 @@ void OBSBasic::PauseRecording() trayIconFile)); } - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_PAUSED); + OnEvent(OBS_FRONTEND_EVENT_RECORDING_PAUSED); if (os_atomic_load_bool(&replaybuf_active)) ShowReplayBufferPauseWarning(); @@ -10789,8 +10759,7 @@ void OBSBasic::UnpauseRecording() trayIconFile)); } - if (api) - api->on_event(OBS_FRONTEND_EVENT_RECORDING_UNPAUSED); + OnEvent(OBS_FRONTEND_EVENT_RECORDING_UNPAUSED); } } @@ -11114,3 +11083,9 @@ float OBSBasic::GetDevicePixelRatio() { return dpi; } + +void OBSBasic::OnEvent(enum obs_frontend_event event) +{ + if (api) + api->on_event(event); +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index eb55d2f051a2a3..de11ea29d06862 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -359,6 +359,8 @@ class OBSBasic : public OBSMainWindow { std::atomic currentScene = nullptr; std::optional> lastOutputResolution; + void OnEvent(enum obs_frontend_event event); + void UpdateMultiviewProjectorMenu(); void DrawBackdrop(float cx, float cy); From dd3ffc1e348e569e5f28124a024d0088e136c14f Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 8 Aug 2024 22:00:07 +0200 Subject: [PATCH 0383/1073] obs-outputs: Defer muxer destruction to task queue --- plugins/obs-outputs/mp4-output.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/obs-outputs/mp4-output.c b/plugins/obs-outputs/mp4-output.c index fc7d2cbac423a2..1ca777f882eaa0 100644 --- a/plugins/obs-outputs/mp4-output.c +++ b/plugins/obs-outputs/mp4-output.c @@ -434,6 +434,12 @@ static void mp4_output_stop(void *data, uint64_t ts) os_atomic_set_bool(&out->stopping, true); } +static void mp4_mux_destroy_task(void *ptr) +{ + struct mp4_mux *muxer = ptr; + mp4_mux_destroy(muxer); +} + static void mp4_output_actual_stop(struct mp4_output *out, int code) { os_atomic_set_bool(&out->active, false); @@ -457,7 +463,8 @@ static void mp4_output_actual_stop(struct mp4_output *out, int code) /* Flush/close output file and destroy muxer */ buffered_file_serializer_free(&out->serializer); - mp4_mux_destroy(out->muxer); + obs_queue_task(OBS_TASK_DESTROY, mp4_mux_destroy_task, out->muxer, + false); out->muxer = NULL; /* Clear chapter data */ From 198581a475b6349e6c9ed5272f273f31bdc7316e Mon Sep 17 00:00:00 2001 From: Rodney Date: Sun, 13 Aug 2023 15:28:46 +0200 Subject: [PATCH 0384/1073] libobs: Add source profiler --- libobs/CMakeLists.txt | 2 + libobs/cmake/legacy.cmake | 4 +- libobs/obs-internal.h | 35 ++ libobs/obs-source.c | 20 ++ libobs/obs-video.c | 6 + libobs/obs.c | 2 + libobs/util/source-profiler.c | 644 ++++++++++++++++++++++++++++++++++ libobs/util/source-profiler.h | 67 ++++ 8 files changed, 779 insertions(+), 1 deletion(-) create mode 100644 libobs/util/source-profiler.c create mode 100644 libobs/util/source-profiler.h diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index fe9445f0f7670a..98092bbbb2c207 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -129,6 +129,8 @@ target_sources( util/profiler.h util/profiler.hpp util/serializer.h + util/source-profiler.c + util/source-profiler.h util/sse-intrin.h util/task.c util/task.h diff --git a/libobs/cmake/legacy.cmake b/libobs/cmake/legacy.cmake index 9ecaf7a843ac03..e6f1a0d65ac39e 100644 --- a/libobs/cmake/legacy.cmake +++ b/libobs/cmake/legacy.cmake @@ -216,7 +216,9 @@ target_sources( util/util_uint128.h util/curl/curl-helper.h util/darray.h - util/util.hpp) + util/util.hpp + util/source-profiler.c + util/source-profiler.h) if(ENABLE_HEVC) target_sources(libobs PRIVATE obs-hevc.c obs-hevc.h) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 6c3ca8aa133157..1e71d956f84fdf 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -823,6 +823,7 @@ struct obs_source { uint32_t async_cache_height; uint32_t async_convert_width[MAX_AV_PLANES]; uint32_t async_convert_height[MAX_AV_PLANES]; + uint64_t async_last_rendered_ts; pthread_mutex_t caption_cb_mutex; DARRAY(struct caption_cb_info) caption_cb_list; @@ -1023,6 +1024,7 @@ extern void obs_source_deactivate(obs_source_t *source, enum view_type type); extern void obs_source_video_tick(obs_source_t *source, float seconds); extern float obs_source_get_target_volume(obs_source_t *source, obs_source_t *target); +extern uint64_t obs_source_get_last_async_ts(const obs_source_t *source); extern void obs_source_audio_render(obs_source_t *source, uint32_t mixers, size_t channels, size_t sample_rate, @@ -1400,3 +1402,36 @@ void obs_service_destroy(obs_service_t *service); void obs_output_remove_encoder_internal(struct obs_output *output, struct obs_encoder *encoder); + +/** Internal Source Profiler functions **/ + +/* Start of frame in graphics loop */ +extern void source_profiler_frame_begin(void); +/* Process data collected during frame */ +extern void source_profiler_frame_collect(void); + +/* Start/end of outputs being rendered (GPU timer begin/end) */ +extern void source_profiler_render_begin(void); +extern void source_profiler_render_end(void); + +/* Reset settings, buffers, and GPU timers when video settings change */ +extern void source_profiler_reset_video(struct obs_video_info *ovi); + +/* Signal that source received an async frame */ +extern void source_profiler_async_frame_received(obs_source_t *source); + +/* Get timestamp for start of tick */ +extern uint64_t source_profiler_source_tick_start(void); +/* Submit start timestamp for source */ +extern void source_profiler_source_tick_end(obs_source_t *source, + uint64_t start); + +/* Obtain GPU timer and start timestamp for render start of a source. */ +extern uint64_t source_profiler_source_render_begin(gs_timer_t **timer); +/* Submit start timestamp and GPU timer after rendering source */ +extern void source_profiler_source_render_end(obs_source_t *source, + uint64_t start, + gs_timer_t *timer); + +/* Remove source from profiler hashmaps */ +extern void source_profiler_remove_source(obs_source_t *source); diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 8207cb07484bca..1c816c3bc7b01e 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -668,6 +668,8 @@ void obs_source_destroy(struct obs_source *source) obs_context_data_remove_name(&source->context, &obs->data.public_sources); + source_profiler_remove_source(source); + /* defer source destroy */ os_task_queue_queue_task(obs->destruction_task_thread, (os_task_t)obs_source_destroy_defer, source); @@ -2579,6 +2581,7 @@ static void obs_source_update_async_video(obs_source_t *source) source->async_update_texture = false; } + source->async_last_rendered_ts = frame->timestamp; obs_source_release_frame(source, frame); } } @@ -2609,6 +2612,10 @@ static void rotate_async_video(obs_source_t *source, long rotation) static inline void obs_source_render_async_video(obs_source_t *source) { if (source->async_textures[0] && source->async_active) { + gs_timer_t *timer = NULL; + const uint64_t start = + source_profiler_source_render_begin(&timer); + const enum gs_color_space source_space = convert_video_space( source->async_format, source->async_trc); @@ -2718,6 +2725,8 @@ static inline void obs_source_render_async_video(obs_source_t *source) gs_technique_end(tech); gs_set_linear_srgb(previous); + + source_profiler_source_render_end(source, start, timer); } } @@ -2786,6 +2795,9 @@ static uint32_t get_base_height(const obs_source_t *source) static void source_render(obs_source_t *source, gs_effect_t *effect) { + gs_timer_t *timer = NULL; + const uint64_t start = source_profiler_source_render_begin(&timer); + void *const data = source->context.data; const enum gs_color_space current_space = gs_get_color_space(); const enum gs_color_space source_space = @@ -2912,6 +2924,7 @@ static void source_render(obs_source_t *source, gs_effect_t *effect) } else { source->info.video_render(data, effect); } + source_profiler_source_render_end(source, start, timer); } void obs_source_default_render(obs_source_t *source) @@ -3697,6 +3710,8 @@ obs_source_output_video_internal(obs_source_t *source, return; } + source_profiler_async_frame_received(source); + struct obs_source_frame *output = cache_video(source, frame); /* ------------------------------------------- */ @@ -6308,3 +6323,8 @@ void obs_source_restore_filters(obs_source_t *source, obs_data_array_t *array) da_free(cur_filters); } + +uint64_t obs_source_get_last_async_ts(const obs_source_t *source) +{ + return source->async_last_rendered_ts; +} diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 1cd566ac8716ed..09b885452fe9b2 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -77,7 +77,9 @@ static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time) for (size_t i = 0; i < data->sources_to_tick.num; i++) { obs_source_t *s = data->sources_to_tick.array[i]; + const uint64_t start = source_profiler_source_tick_start(); obs_source_video_tick(s, seconds); + source_profiler_source_tick_end(s, start); obs_source_release(s); } @@ -1212,6 +1214,7 @@ bool obs_graphics_thread_loop(struct obs_graphics_context *context) update_active_states(); profile_start(context->video_thread_name); + source_profiler_frame_begin(); gs_enter_context(obs->video.graphics); gs_begin_frame(); @@ -1230,6 +1233,7 @@ bool obs_graphics_thread_loop(struct obs_graphics_context *context) } #endif + source_profiler_render_begin(); profile_start(output_frame_name); output_frames(); profile_end(output_frame_name); @@ -1237,11 +1241,13 @@ bool obs_graphics_thread_loop(struct obs_graphics_context *context) profile_start(render_displays_name); render_displays(); profile_end(render_displays_name); + source_profiler_render_end(); execute_graphics_tasks(); frame_time_ns = os_gettime_ns() - frame_start; + source_profiler_frame_collect(); profile_end(context->video_thread_name); profile_reenable_thread(); diff --git a/libobs/obs.c b/libobs/obs.c index 9f8658d5a31388..fad764868995c4 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1591,6 +1591,8 @@ int obs_reset_video(struct obs_video_info *ovi) get_video_format_name(ovi->output_format), yuv ? yuv_format : "None", yuv ? "/" : "", yuv ? yuv_range : ""); + source_profiler_reset_video(ovi); + return obs_init_video(ovi); } diff --git a/libobs/util/source-profiler.c b/libobs/util/source-profiler.c new file mode 100644 index 00000000000000..b4cea545092d4d --- /dev/null +++ b/libobs/util/source-profiler.c @@ -0,0 +1,644 @@ +/****************************************************************************** + Copyright (C) 2023 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "source-profiler.h" + +#include "darray.h" +#include "obs-internal.h" +#include "platform.h" +#include "threading.h" +#include "uthash.h" + +struct frame_sample { + uint64_t tick; + DARRAY(uint64_t) render_cpu; + DARRAY(gs_timer_t *) render_timers; +}; + +/* Buffer frame data collection to give GPU time to finish rendering. + * Set to the same as the rendering buffer (NUM_TEXTURES) */ +#define FRAME_BUFFER_SIZE NUM_TEXTURES + +struct source_samples { + /* the pointer address of the source is the hashtable key */ + uintptr_t key; + + uint8_t frame_idx; + struct frame_sample *frames[FRAME_BUFFER_SIZE]; + + UT_hash_handle hh; +}; + +/* Basic fixed-size circular buffer to hold most recent N uint64_t values + * (older items will be overwritten). */ +struct ucirclebuf { + size_t idx; + size_t capacity; + size_t num; + + uint64_t *array; +}; + +struct profiler_entry { + /* the pointer address of the source is the hashtable key */ + uintptr_t key; + + /* Tick times for last N frames */ + struct ucirclebuf tick; + /* Time of first render pass in a frame, for last N frames */ + struct ucirclebuf render_cpu; + struct ucirclebuf render_gpu; + /* Sum of all render passes in a frame, for last N frames */ + struct ucirclebuf render_cpu_sum; + struct ucirclebuf render_gpu_sum; + /* Timestamps of last N async frame submissions */ + struct ucirclebuf async_frame_ts; + /* Timestamps of last N async frames rendered */ + struct ucirclebuf async_rendered_ts; + + UT_hash_handle hh; +}; + +/* Hashmaps */ +struct source_samples *hm_samples = NULL; +struct profiler_entry *hm_entries = NULL; + +/* GPU timer ranges (only required for DirectX) */ +static uint8_t timer_idx = 0; +static gs_timer_range_t *timer_ranges[FRAME_BUFFER_SIZE] = {0}; + +static uint64_t profiler_samples = 0; +/* Sources can be rendered more than once per frame, to avoid reallocating + * memory in the majority of cases, reserve at least two. */ +static const size_t render_times_reservation = 2; + +pthread_rwlock_t hm_rwlock = PTHREAD_RWLOCK_INITIALIZER; + +static bool enabled = false; +static bool gpu_enabled = false; +/* These can be set from other threads, mark them volatile */ +static volatile bool enable_next = false; +static volatile bool gpu_enable_next = false; + +void ucirclebuf_init(struct ucirclebuf *buf, size_t capacity) +{ + if (!capacity) + return; + + memset(buf, 0, sizeof(struct ucirclebuf)); + buf->capacity = capacity; + buf->array = bmalloc(sizeof(uint64_t) * capacity); +} + +void ucirclebuf_free(struct ucirclebuf *buf) +{ + bfree(buf->array); + memset(buf, 0, sizeof(struct ucirclebuf)); +} + +void ucirclebuf_push(struct ucirclebuf *buf, uint64_t val) +{ + if (buf->num == buf->capacity) { + buf->idx %= buf->capacity; + buf->array[buf->idx++] = val; + return; + } + + buf->array[buf->idx++] = val; + buf->num++; +} + +static struct frame_sample *frame_sample_create(void) +{ + struct frame_sample *smp = bzalloc(sizeof(struct frame_sample)); + da_reserve(smp->render_cpu, render_times_reservation); + da_reserve(smp->render_timers, render_times_reservation); + return smp; +} + +static void frame_sample_destroy(struct frame_sample *sample) +{ + if (sample->render_timers.num) { + gs_enter_context(obs->video.graphics); + for (size_t i = 0; i < sample->render_timers.num; i++) + gs_timer_destroy(sample->render_timers.array[i]); + gs_leave_context(); + } + + da_free(sample->render_cpu); + da_free(sample->render_timers); + bfree(sample); +} + +struct source_samples *source_samples_create(const uintptr_t key) +{ + struct source_samples *smps = bzalloc(sizeof(struct source_samples)); + + smps->key = key; + for (size_t i = 0; i < FRAME_BUFFER_SIZE; i++) + smps->frames[i] = frame_sample_create(); + + return smps; +} + +static void source_samples_destroy(struct source_samples *sample) +{ + for (size_t i = 0; i < FRAME_BUFFER_SIZE; i++) + frame_sample_destroy(sample->frames[i]); + + bfree(sample); +} + +static struct profiler_entry *entry_create(const uintptr_t key) +{ + struct profiler_entry *ent = bzalloc(sizeof(struct profiler_entry)); + ent->key = key; + ucirclebuf_init(&ent->tick, profiler_samples); + ucirclebuf_init(&ent->render_cpu, profiler_samples); + ucirclebuf_init(&ent->render_gpu, profiler_samples); + ucirclebuf_init(&ent->render_cpu_sum, profiler_samples); + ucirclebuf_init(&ent->render_gpu_sum, profiler_samples); + ucirclebuf_init(&ent->async_frame_ts, profiler_samples); + ucirclebuf_init(&ent->async_rendered_ts, profiler_samples); + return ent; +} + +static void entry_destroy(struct profiler_entry *entry) +{ + ucirclebuf_free(&entry->tick); + ucirclebuf_free(&entry->render_cpu); + ucirclebuf_free(&entry->render_gpu); + ucirclebuf_free(&entry->render_cpu_sum); + ucirclebuf_free(&entry->render_gpu_sum); + ucirclebuf_free(&entry->async_frame_ts); + ucirclebuf_free(&entry->async_rendered_ts); + bfree(entry); +} + +static void reset_gpu_timers(void) +{ + gs_enter_context(obs->video.graphics); + for (int i = 0; i < FRAME_BUFFER_SIZE; i++) { + if (timer_ranges[i]) { + gs_timer_range_destroy(timer_ranges[i]); + timer_ranges[i] = NULL; + } + } + gs_leave_context(); +} + +static void profiler_shutdown(void) +{ + struct source_samples *smp, *tmp; + HASH_ITER (hh, hm_samples, smp, tmp) { + HASH_DEL(hm_samples, smp); + source_samples_destroy(smp); + } + + pthread_rwlock_wrlock(&hm_rwlock); + struct profiler_entry *ent, *etmp; + HASH_ITER (hh, hm_entries, ent, etmp) { + HASH_DEL(hm_entries, ent); + entry_destroy(ent); + } + pthread_rwlock_unlock(&hm_rwlock); + + reset_gpu_timers(); +} + +void source_profiler_enable(bool enable) +{ + enable_next = enable; +} + +void source_profiler_gpu_enable(bool enable) +{ + gpu_enable_next = enable && enable_next; +} + +void source_profiler_reset_video(struct obs_video_info *ovi) +{ + double fps = ceil((double)ovi->fps_num / (double)ovi->fps_den); + profiler_samples = (uint64_t)(fps * 5); + + /* This is fine because the video thread won't be running at this point */ + profiler_shutdown(); +} + +void source_profiler_render_begin(void) +{ + if (!gpu_enabled) + return; + + gs_enter_context(obs->video.graphics); + if (!timer_ranges[timer_idx]) + timer_ranges[timer_idx] = gs_timer_range_create(); + + gs_timer_range_begin(timer_ranges[timer_idx]); + gs_leave_context(); +} + +void source_profiler_render_end(void) +{ + if (!gpu_enabled || !timer_ranges[timer_idx]) + return; + + gs_enter_context(obs->video.graphics); + gs_timer_range_end(timer_ranges[timer_idx]); + gs_leave_context(); +} + +void source_profiler_frame_begin(void) +{ + if (!enabled && enable_next) + enabled = true; + + if (!gpu_enabled && enabled && gpu_enable_next) { + gpu_enabled = true; + } else if (gpu_enabled) { + /* Advance timer idx if gpu enabled */ + timer_idx = (timer_idx + 1) % FRAME_BUFFER_SIZE; + } +} + +static inline bool is_async_video_source(const struct obs_source *source) +{ + return (source->info.output_flags & OBS_SOURCE_ASYNC_VIDEO) == + OBS_SOURCE_ASYNC_VIDEO; +} + +static const char *source_profiler_frame_collect_name = + "source_profiler_frame_collect"; +void source_profiler_frame_collect(void) +{ + if (!enabled) + return; + + profile_start(source_profiler_frame_collect_name); + bool gpu_disjoint = false; + bool gpu_ready = false; + uint64_t freq = 0; + + if (gpu_enabled) { + uint8_t timer_range_idx = (timer_idx + 1) % FRAME_BUFFER_SIZE; + + if (timer_ranges[timer_range_idx]) { + gpu_ready = true; + gs_enter_context(obs->video.graphics); + gs_timer_range_get_data(timer_ranges[timer_range_idx], + &gpu_disjoint, &freq); + } + + if (gpu_disjoint) { + blog(LOG_WARNING, + "GPU Timers were disjoint, discarding samples."); + } + } + + pthread_rwlock_wrlock(&hm_rwlock); + + struct source_samples *smps = hm_samples; + while (smps) { + /* processing is delayed by FRAME_BUFFER_SIZE - 1 frames */ + uint8_t frame_idx = (smps->frame_idx + 1) % FRAME_BUFFER_SIZE; + struct frame_sample *smp = smps->frames[frame_idx]; + + if (!smp->tick) { + /* No data yet */ + smps = smps->hh.next; + continue; + } + + struct profiler_entry *ent; + HASH_FIND_PTR(hm_entries, &smps->key, ent); + if (!ent) { + ent = entry_create(smps->key); + HASH_ADD_PTR(hm_entries, key, ent); + } + + ucirclebuf_push(&ent->tick, smp->tick); + + if (smp->render_cpu.num) { + uint64_t sum = 0; + for (size_t idx = 0; idx < smp->render_cpu.num; idx++) { + sum += smp->render_cpu.array[idx]; + } + ucirclebuf_push(&ent->render_cpu, + smp->render_cpu.array[0]); + ucirclebuf_push(&ent->render_cpu_sum, sum); + da_clear(smp->render_cpu); + } + + /* Note that we still check this even if GPU profiling has been + * disabled to destroy leftover timers. */ + if (smp->render_timers.num) { + uint64_t sum = 0, first = 0, ticks = 0; + + for (size_t i = 0; i < smp->render_timers.num; i++) { + gs_timer_t *timer = smp->render_timers.array[i]; + + if (gpu_ready && !gpu_disjoint && + gs_timer_get_data(timer, &ticks)) { + /* Convert ticks to ns */ + sum += util_mul_div64( + ticks, 1000000000ULL, freq); + if (!first) + first = sum; + } + + gs_timer_destroy(timer); + } + + if (first) { + ucirclebuf_push(&ent->render_gpu, first); + ucirclebuf_push(&ent->render_gpu_sum, sum); + } + da_clear(smp->render_timers); + } + + const obs_source_t *src = *(const obs_source_t **)smps->hh.key; + if (is_async_video_source(src)) { + uint64_t ts = obs_source_get_last_async_ts(src); + ucirclebuf_push(&ent->async_rendered_ts, ts); + } + + smps = smps->hh.next; + } + + pthread_rwlock_unlock(&hm_rwlock); + + if (gpu_enabled && gpu_ready) + gs_leave_context(); + + /* Apply updated states for next frame */ + if (!enable_next) { + enabled = gpu_enabled = false; + profiler_shutdown(); + } else if (!gpu_enable_next) { + gpu_enabled = false; + reset_gpu_timers(); + } + + profile_end(source_profiler_frame_collect_name); +} + +void source_profiler_async_frame_received(obs_source_t *source) +{ + if (!enabled) + return; + + uint64_t ts = os_gettime_ns(); + + pthread_rwlock_wrlock(&hm_rwlock); + + struct profiler_entry *ent; + HASH_FIND_PTR(hm_entries, &source, ent); + if (ent) + ucirclebuf_push(&ent->async_frame_ts, ts); + + pthread_rwlock_unlock(&hm_rwlock); +} + +uint64_t source_profiler_source_tick_start(void) +{ + if (!enabled) + return 0; + + return os_gettime_ns(); +} + +void source_profiler_source_tick_end(obs_source_t *source, uint64_t start) +{ + if (!enabled) + return; + + const uint64_t delta = os_gettime_ns() - start; + + struct source_samples *smp = NULL; + HASH_FIND_PTR(hm_samples, &source, smp); + if (!smp) { + smp = source_samples_create((uintptr_t)source); + HASH_ADD_PTR(hm_samples, key, smp); + } else { + /* Advance index here since tick happens first and only once + * at the start of each frame. */ + smp->frame_idx = (smp->frame_idx + 1) % FRAME_BUFFER_SIZE; + } + + smp->frames[smp->frame_idx]->tick = delta; +} + +uint64_t source_profiler_source_render_begin(gs_timer_t **timer) +{ + if (!enabled) + return 0; + + if (gpu_enabled) { + *timer = gs_timer_create(); + gs_timer_begin(*timer); + } else { + *timer = NULL; + } + + return os_gettime_ns(); +} + +void source_profiler_source_render_end(obs_source_t *source, uint64_t start, + gs_timer_t *timer) +{ + if (!enabled) + return; + if (timer) + gs_timer_end(timer); + + const uint64_t delta = os_gettime_ns() - start; + + struct source_samples *smp; + HASH_FIND_PTR(hm_samples, &source, smp); + + if (smp) { + da_push_back(smp->frames[smp->frame_idx]->render_cpu, &delta); + if (timer) { + da_push_back(smp->frames[smp->frame_idx]->render_timers, + &timer); + } + } else if (timer) { + gs_timer_destroy(timer); + } +} + +static void task_delete_source(void *key) +{ + struct source_samples *smp; + HASH_FIND_PTR(hm_samples, &key, smp); + if (smp) { + HASH_DEL(hm_samples, smp); + source_samples_destroy(smp); + } + + pthread_rwlock_rdlock(&hm_rwlock); + struct profiler_entry *ent = NULL; + HASH_FIND_PTR(hm_entries, &key, ent); + if (ent) { + HASH_DEL(hm_entries, ent); + entry_destroy(ent); + } + pthread_rwlock_unlock(&hm_rwlock); +} + +void source_profiler_remove_source(obs_source_t *source) +{ + if (!enabled) + return; + /* Schedule deletion task on graphics thread */ + obs_queue_task(OBS_TASK_GRAPHICS, task_delete_source, source, false); +} + +static inline void calculate_tick(struct profiler_entry *ent, + struct profiler_result *result) +{ + size_t idx = 0; + uint64_t sum = 0; + + for (; idx < ent->tick.num; idx++) { + const uint64_t delta = ent->tick.array[idx]; + if (delta > result->tick_max) + result->tick_max = delta; + + sum += delta; + } + + if (idx) + result->tick_avg = sum / idx; +} + +static inline void calculate_render(struct profiler_entry *ent, + struct profiler_result *result) +{ + size_t idx; + uint64_t sum = 0, sum_sum = 0; + + for (idx = 0; idx < ent->render_cpu.num; idx++) { + const uint64_t delta = ent->render_cpu.array[idx]; + if (delta > result->render_max) + result->render_max = delta; + + sum += delta; + sum_sum += ent->render_cpu_sum.array[idx]; + } + + if (idx) { + result->render_avg = sum / idx; + result->render_sum = sum_sum / idx; + } + + if (!gpu_enabled) + return; + + sum = sum_sum = 0; + for (idx = 0; idx < ent->render_gpu.num; idx++) { + const uint64_t delta = ent->render_gpu.array[idx]; + if (delta > result->render_gpu_max) + result->render_gpu_max = delta; + + sum += delta; + sum_sum += ent->render_gpu_sum.array[idx]; + } + + if (idx) { + result->render_gpu_avg = sum / idx; + result->render_gpu_sum = sum_sum / idx; + } +} + +static inline void calculate_fps(const struct ucirclebuf *frames, double *avg, + uint64_t *best, uint64_t *worst) +{ + uint64_t deltas = 0, delta_sum = 0, best_delta = 0, worst_delta = 0; + + for (size_t idx = 0; idx < frames->num; idx++) { + const uint64_t ts = frames->array[idx]; + if (!ts) + break; + + size_t prev_idx = idx ? idx - 1 : frames->num - 1; + const uint64_t prev_ts = frames->array[prev_idx]; + if (!prev_ts || prev_ts >= ts) + continue; + + uint64_t delta = (ts - prev_ts); + if (delta < best_delta || !best_delta) + best_delta = delta; + if (delta > worst_delta) + worst_delta = delta; + + delta_sum += delta; + deltas++; + } + + if (deltas && delta_sum) { + *avg = 1.0E9 / ((double)delta_sum / (double)deltas); + *best = best_delta; + *worst = worst_delta; + } +} + +bool source_profiler_fill_result(obs_source_t *source, + struct profiler_result *result) +{ + if (!enabled || !result) + return false; + + memset(result, 0, sizeof(struct profiler_result)); + /* No or only stale data available */ + if (!obs_source_enabled(source)) + return true; + + pthread_rwlock_rdlock(&hm_rwlock); + + struct profiler_entry *ent = NULL; + HASH_FIND_PTR(hm_entries, &source, ent); + if (ent) { + calculate_tick(ent, result); + calculate_render(ent, result); + + if (is_async_video_source(source)) { + calculate_fps(&ent->async_frame_ts, + &result->async_input, + &result->async_input_best, + &result->async_input_worst); + calculate_fps(&ent->async_rendered_ts, + &result->async_rendered, + &result->async_rendered_best, + &result->async_rendered_worst); + } + } + + pthread_rwlock_unlock(&hm_rwlock); + + return !!ent; +} + +profiler_result_t *source_profiler_get_result(obs_source_t *source) +{ + profiler_result_t *ret = bmalloc(sizeof(profiler_result_t)); + if (!source_profiler_fill_result(source, ret)) { + bfree(ret); + return NULL; + } + return ret; +} diff --git a/libobs/util/source-profiler.h b/libobs/util/source-profiler.h new file mode 100644 index 00000000000000..956e7be8db46c7 --- /dev/null +++ b/libobs/util/source-profiler.h @@ -0,0 +1,67 @@ +/****************************************************************************** + Copyright (C) 2023 by Dennis Sädtler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include "obs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct profiler_result { + /* Tick times in ns */ + uint64_t tick_avg; + uint64_t tick_max; + + /* Average and max render times for CPU and GPU in ns */ + uint64_t render_avg; + uint64_t render_max; + uint64_t render_gpu_avg; + uint64_t render_gpu_max; + + /* Average of the sum of all render passes in a frame in ns + * (a source can be rendered more than once per frame). */ + uint64_t render_sum; + uint64_t render_gpu_sum; + + /* FPS of submitted async input */ + double async_input; + /* Actually rendered async frames */ + double async_rendered; + + /* Best and worst frame times of input/output in ns */ + uint64_t async_input_best; + uint64_t async_input_worst; + uint64_t async_rendered_best; + uint64_t async_rendered_worst; +} profiler_result_t; + +/* Enable/disable profiler (applied on next frame) */ +EXPORT void source_profiler_enable(bool enable); +/* Enable/disable GPU profiling (applied on next frame) */ +EXPORT void source_profiler_gpu_enable(bool enable); + +/* Get latest profiling results for source (must be freed by user) */ +EXPORT profiler_result_t *source_profiler_get_result(obs_source_t *source); +/* Update existing profiler results object for source */ +EXPORT bool source_profiler_fill_result(obs_source_t *source, + profiler_result_t *result); + +#ifdef __cplusplus +} +#endif From 8adc76f87f56600d44984a1585dc8644b242f0c8 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 15 Aug 2023 23:09:34 +0200 Subject: [PATCH 0385/1073] docs: Add source profiler functions --- .../reference-libobs-util-source-profiler.rst | 91 +++++++++++++++++++ docs/sphinx/reference-libobs-util.rst | 1 + 2 files changed, 92 insertions(+) create mode 100644 docs/sphinx/reference-libobs-util-source-profiler.rst diff --git a/docs/sphinx/reference-libobs-util-source-profiler.rst b/docs/sphinx/reference-libobs-util-source-profiler.rst new file mode 100644 index 00000000000000..baf4df042498a2 --- /dev/null +++ b/docs/sphinx/reference-libobs-util-source-profiler.rst @@ -0,0 +1,91 @@ +Source Profiler +=============== + +The source profiler is used to get information about individual source's performance. + +.. struct:: profiler_result + +.. member:: uint64_t profiler_result.tick_avg + uint64_t profiler_result.render_avg + + Average execution time of this source's tick and render functions within the sampled timeframe (5 seconds). + + Note that a source will be ticked only once per frame, but may be rendered multiple times. + +.. member:: uint64_t profiler_result.tick_max + uint64_t profiler_result.render_max + + Maximum execution time of this source's tick and render functions within the sampled timeframe (5 seconds). + +.. member:: uint64_t profiler_result.render_gpu_avg + uint64_t profiler_result.render_gpu_max + + Average and maximum execution time for GPU rendering to execute within the sampled timeframe (5 seconds). + + Note that GPU timing is not supported on macOS and is of limited accuracy due to variations in GPU load/clock speed. + +.. member:: uint64_t profiler_result.render_sum + uint64_t profiler_result.render_gpu_sum + + Sum of all CPU/GPU render time in a frame, averaged over the sampled timeframe (5 seconds). + + For example, assuming a source with perfect consistency in its render time that gets rendered twice in a frame and a value for :c:member:`profiler_result.render_avg` of `1000000` (1 ms), will have a value for :c:member:`profiler_result.render_sum` of `2000000` (2 ms). + +.. member:: double profiler_result.async_fps + + Framerate calculated from average time delta between async frames submitted via :c:func:`obs_source_output_video2()`. + + Only valid for async sources (e.g. Media Source). + +.. type:: struct profiler_result profiler_result_t + +.. code:: cpp + + #include + + +Source Profiler Functions +--------------------- + +.. function:: void source_profiler_enable(bool enable) + + Enables or disables the source profiler. + The profiler will then start or stop collecting data with the next rendered frame. + + Note that enabling the profiler may have a small performance penalty. + + :param enable: Whether or not to enable the source profiler. + +--------------------- + +.. function:: void source_profiler_gpu_enable(bool enable) + + Enables or disables GPU profiling (not available on macOS). + GPU profiling will start or stop with the next frame OBS is rendering. + + Note that GPU profiling may have a larger performance impact. + + :param enable: Whether or not to enable GPU profiling. + +--------------------- + +.. function:: profiler_result_t *source_profiler_get_result(obs_source_t *source) + + Returns profiling information for the provided `source`. + + Note that result must be freed with :c:func:`bfree()`. + + :param source: Source to get profiling information for + :return: Pointer to `profiler_result_t` with data, `NULL` otherwise. + +--------------------- + +.. function:: bool source_profiler_fill_result(obs_source_t *source, profiler_result_t *result) + + Fill a preexisting `profiler_result_t` object with data for `source`. + + This function exists to avoid having to allocate new memory each time a profiling result is queried. + + :param source: Source to get profiling informatio for + :param result: Result object to fill + :return: *true* if data for the source exists, *false* otherwise diff --git a/docs/sphinx/reference-libobs-util.rst b/docs/sphinx/reference-libobs-util.rst index 344255c117cc7c..235f13e6fdedb0 100644 --- a/docs/sphinx/reference-libobs-util.rst +++ b/docs/sphinx/reference-libobs-util.rst @@ -14,5 +14,6 @@ Platform/Utility API Reference (libobs/util) reference-libobs-util-platform reference-libobs-util-profiler reference-libobs-util-serializers + reference-libobs-util-source-profiler reference-libobs-util-text-lookup reference-libobs-util-threading From 1b25acd18450401b96e1c92767f4c8d039f0b8a2 Mon Sep 17 00:00:00 2001 From: jcm <6864788+jcm93@users.noreply.github.com> Date: Sat, 20 Jul 2024 18:25:22 -0500 Subject: [PATCH 0386/1073] UI: Don't create default desktop audio source on macOS 13+ --- UI/platform-osx.mm | 9 +++++++++ UI/platform.hpp | 1 + UI/window-basic-main.cpp | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/UI/platform-osx.mm b/UI/platform-osx.mm index 75f4adfba71c40..07f1d77b8ceccf 100644 --- a/UI/platform-osx.mm +++ b/UI/platform-osx.mm @@ -133,6 +133,15 @@ void SetAlwaysOnTop(QWidget *window, bool enable) window->show(); } +bool shouldCreateDefaultAudioSource(void) +{ + if (@available(macOS 13, *)) { + return false; + } else { + return true; + } +} + bool SetDisplayAffinitySupported(void) { // Not implemented yet diff --git a/UI/platform.hpp b/UI/platform.hpp index bf45879f9fbbeb..9e9125ad7ad35c 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -104,6 +104,7 @@ void InstallNSApplicationSubclass(); void InstallNSThreadLocks(); void disableColorSpaceConversion(QWidget *window); void SetMacOSDarkMode(bool dark); +bool shouldCreateDefaultAudioSource(); MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, bool prompt_for_permission); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index aea1c4d4feb6ee..04e71bfffedd29 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -997,6 +997,10 @@ void OBSBasic::CreateFirstRunSources() bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource()); bool hasInputAudio = HasAudioDevices(App()->InputAudioSource()); +#ifdef __APPLE__ + hasDesktopAudio = hasDesktopAudio && shouldCreateDefaultAudioSource(); +#endif + if (hasDesktopAudio) ResetAudioDevice(App()->OutputAudioSource(), "default", Str("Basic.DesktopDevice1"), 1); From c7b0c63d01a447224cd6e3d141de30e28a92512d Mon Sep 17 00:00:00 2001 From: tytan652 Date: Mon, 12 Aug 2024 22:10:12 +0200 Subject: [PATCH 0387/1073] obs-nvenc: Fix building with nvcodec 12.2 --- plugins/obs-nvenc/nvenc-opts-parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/obs-nvenc/nvenc-opts-parser.c b/plugins/obs-nvenc/nvenc-opts-parser.c index 6b8ad420761268..397f3dc63ad15a 100644 --- a/plugins/obs-nvenc/nvenc-opts-parser.c +++ b/plugins/obs-nvenc/nvenc-opts-parser.c @@ -85,7 +85,7 @@ static bool apply_rc_opt(const struct obs_option *opt, APPLY_BIT_OPT(aqStrength, 4) #ifdef NVENC_12_2_OR_LATER - APPLY_INT_OPT(lookaheadLevel, NV_ENC_LOOKAHEAD_LEVEL) + APPLY_INT_OPT(lookaheadLevel, NV_ENC_LOOKAHEAD_LEVEL, PRIu32) #endif /* Macros above will return true if succesfully evaluated. @@ -153,7 +153,7 @@ static bool apply_hevc_opt(struct obs_option *opt, NV_ENC_CONFIG_HEVC *nv_conf) APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32) APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32) #ifdef NVENC_12_2_OR_LATER - APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL) + APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32) #endif APPLY_BIT_OPT(enableFillerDataInsertion, 1) From fce534572b18d14e059bb63045213d2506c88b85 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Sun, 11 Aug 2024 04:31:55 +0200 Subject: [PATCH 0388/1073] happy-eyeballs: Reserve array length for all candidates The code currently assumes that the array does not change during execution, this assumption is violated when the darray resizes causing previous pointers to point to invalid memory and cause undefined behavior and crashes. This may need refactoring in the future, this commit simply fixes the issue for now. --- shared/happy-eyeballs/happy-eyeballs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/happy-eyeballs/happy-eyeballs.c b/shared/happy-eyeballs/happy-eyeballs.c index 0ed15376b0b695..7877a9f99985d5 100644 --- a/shared/happy-eyeballs/happy-eyeballs.c +++ b/shared/happy-eyeballs/happy-eyeballs.c @@ -497,6 +497,7 @@ int happy_eyeballs_create(struct happy_eyeballs_ctx **context) ctx->socket_fd = INVALID_SOCKET; da_init(ctx->candidates); + da_reserve(ctx->candidates, HAPPY_EYEBALLS_MAX_ATTEMPTS); /* race_completed_event will be signalled when there is a winner or all * attempts have failed */ From 78ffd99ab1dc524bbfe7e0896875cd390bdf99c0 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Sun, 11 Aug 2024 20:43:41 +0200 Subject: [PATCH 0389/1073] happy-eyeballs: Move happy_eyeballs_destroy to a thread On Windows, shutdown() will not interrupt a blocking connect() call, so happy_eyeballs_destroy could block until the remaining candidates timed out. As happy_eyeballs_destroy is called in the RTMP connect path, this would stall the RTMP connection and cause the winning candidate's socket to be disconnected due to a timeout. --- shared/happy-eyeballs/happy-eyeballs.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/shared/happy-eyeballs/happy-eyeballs.c b/shared/happy-eyeballs/happy-eyeballs.c index 7877a9f99985d5..538226ce5dff1d 100644 --- a/shared/happy-eyeballs/happy-eyeballs.c +++ b/shared/happy-eyeballs/happy-eyeballs.c @@ -650,11 +650,11 @@ int happy_eyeballs_timedwait(struct happy_eyeballs_ctx *context, return status; } -int happy_eyeballs_destroy(struct happy_eyeballs_ctx *context) +static void *destroy_thread(void *param) { - if (context == NULL) - return STATUS_INVALID_ARGUMENT; + struct happy_eyeballs_ctx *context = param; + os_set_thread_name("happy-eyeballs destroy thread"); #ifdef _WIN32 #define SHUT_RDWR SD_BOTH #else @@ -698,6 +698,21 @@ int happy_eyeballs_destroy(struct happy_eyeballs_ctx *context) da_free(context->candidates); free(context); + + return NULL; +} + +int happy_eyeballs_destroy(struct happy_eyeballs_ctx *context) +{ + if (context == NULL) + return STATUS_INVALID_ARGUMENT; + + /* The destroy happens asynchronously in another thread due to the + * connect() call blocking on Windows. */ + pthread_t thread; + pthread_create(&thread, NULL, destroy_thread, context); + pthread_detach(thread); + return STATUS_SUCCESS; } From 1e6c375e95fe982bdeadf671edf1df420dd8d5bc Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Sun, 11 Aug 2024 21:05:48 +0200 Subject: [PATCH 0390/1073] happy-eyeballs: Specify AI_ADDRCONFIG for getaddrinfo This might help to avoid returning IPv6 addresses on systems without IPv6 connectivity. --- shared/happy-eyeballs/happy-eyeballs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/happy-eyeballs/happy-eyeballs.c b/shared/happy-eyeballs/happy-eyeballs.c index 538226ce5dff1d..126bae55a75d96 100644 --- a/shared/happy-eyeballs/happy-eyeballs.c +++ b/shared/happy-eyeballs/happy-eyeballs.c @@ -194,6 +194,7 @@ static int build_addr_list(const char *hostname, int port, hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG; if (context->bind_addr_len == sizeof(struct sockaddr_in)) hints.ai_family = AF_INET; From 5c7b1c3ed350b3ecee5a46ed42a631612364dd88 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 11 Jun 2024 15:21:58 +0200 Subject: [PATCH 0391/1073] CI: Add configuration file for gersemi CMake formatter --- .gersemirc | 11 +++++++++++ .gitignore | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .gersemirc diff --git a/.gersemirc b/.gersemirc new file mode 100644 index 00000000000000..f01baabf528513 --- /dev/null +++ b/.gersemirc @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/master/gersemi/configuration.schema.json + +color: false +definitions: [] +line_length: 120 +indent: 2 +list_expansion: favour-inlining +quiet: false +unsafe: false +workers: 10 +warn_about_unknown_commands: false diff --git a/.gitignore b/.gitignore index 0748807f414e04..7c1082426f33f1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,8 +14,8 @@ !/UI !.cirrus.xml !.clang-format -!.cmake-format.json !.editorconfig +!.gersemirc !.git-blame-ignore-devs !.gitmodules !.gitignore From 19d3e30a3a1a4eeecbe41bed8768e15c71e9d4ce Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 11 Jun 2024 15:22:26 +0200 Subject: [PATCH 0392/1073] CI: Replace cmake-format with gersemi for CMake file format checks --- .cmake-format.json | 54 -------- .github/actions/run-gersemi/action.yaml | 59 +++++++++ .github/workflows/check-format.yaml | 8 +- build-aux/.run-format.zsh | 135 +++++++++++++++----- build-aux/README.md | 6 +- build-aux/{run-cmake-format => run-gersemi} | 0 6 files changed, 172 insertions(+), 90 deletions(-) delete mode 100644 .cmake-format.json create mode 100644 .github/actions/run-gersemi/action.yaml rename build-aux/{run-cmake-format => run-gersemi} (100%) diff --git a/.cmake-format.json b/.cmake-format.json deleted file mode 100644 index 335a7dccdaec61..00000000000000 --- a/.cmake-format.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "format": { - "line_width": 120, - "tab_size": 2, - "enable_sort": true, - "autosort": true - }, - "additional_commands": { - "find_qt": { - "flags": [], - "kwargs": { - "COMPONENTS": "+", - "COMPONENTS_WIN": "+", - "COMPONENTS_MACOS": "+", - "COMPONENTS_LINUX": "+" - } - }, - "set_target_properties_obs": { - "pargs": 1, - "flags": [], - "kwargs": { - "PROPERTIES": { - "kwargs": { - "PREFIX": 1, - "OUTPUT_NAME": 1, - "FOLDER": 1, - "VERSION": 1, - "SOVERSION": 1, - "FRAMEWORK": 1, - "BUNDLE": 1, - "AUTOMOC": 1, - "AUTOUIC": 1, - "AUTORCC": 1, - "AUTOUIC_SEARCH_PATHS": 1, - "BUILD_RPATH": 1, - "INSTALL_RPATH": 1, - "XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC": 1, - "XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION": 1, - "XCODE_ATTRIBUTE_GCC_WARN_SHADOW":1 , - "LIBRARY_OUTPUT_DIRECTORY": 1 - } - } - } - }, - "add_obs_plugin": { - "pargs": 1, - "flags": ["WITH_MESSAGE"], - "kwargs": { - "PLATFORMS": "+", - "ARCHITECTURES": "+" - } - } - } -} diff --git a/.github/actions/run-gersemi/action.yaml b/.github/actions/run-gersemi/action.yaml new file mode 100644 index 00000000000000..5a6818f91a9bd1 --- /dev/null +++ b/.github/actions/run-gersemi/action.yaml @@ -0,0 +1,59 @@ +name: Run gersemi +description: Runs gersemi and checks for any changes introduced by it +inputs: + failCondition: + description: Controls whether failed checks also fail the workflow run + required: false + default: never + workingDirectory: + description: Working directory for checks + required: false + default: ${{ github.workspace }} +runs: + using: composite + steps: + - name: Check Runner Operating System 🏃‍♂️ + if: runner.os == 'Windows' + shell: bash + run: | + : Check Runner Operating System 🏃‍♂️ + echo "::notice::run-gersemi action requires a macOS-based or Linux-based runner." + exit 2 + + - name: Check for Changed Files ✅ + uses: ./.github/actions/check-changes + id: checks + with: + checkGlob: "'*.cmake' '*CMakeLists.txt'" + diffFilter: 'ACM' + + - name: Install Dependencies 🛍️ + if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles) + shell: bash + run: | + : Install Dependencies 🛍️ + echo ::group::Install Dependencies + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH + brew install --quiet zsh + echo ::endgroup:: + + - name: Run gersemi 🎛️ + if: fromJSON(steps.checks.outputs.hasChangedFiles) + id: result + shell: zsh --no-rcs --errexit --pipefail {0} + working-directory: ${{ github.workspace }} + env: + CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }} + run: | + : Run gersemi 🎛️ + if (( ${+RUNNER_DEBUG} )) setopt XTRACE + + print ::group::Install gersemi + brew install --quiet obsproject/tools/gersemi + print ::endgroup:: + + print ::group::Run gersemi + local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/}) + ./build-aux/run-gersemi --fail-${{ inputs.failCondition }} --check ${changes} + print ::endgroup:: diff --git a/.github/workflows/check-format.yaml b/.github/workflows/check-format.yaml index baeca41e6513f3..4ace7677e7c060 100644 --- a/.github/workflows/check-format.yaml +++ b/.github/workflows/check-format.yaml @@ -26,15 +26,15 @@ jobs: with: failCondition: error - cmake-format: + gersemi: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: cmake-format Check 🎛️ - id: cmake-format - uses: ./.github/actions/run-cmake-format + - name: gersemi Check 🎛️ + id: gersemi + uses: ./.github/actions/run-gersemi with: failCondition: error diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh index 6c54433c1de413..11e9e64c2de6f6 100755 --- a/build-aux/.run-format.zsh +++ b/build-aux/.run-format.zsh @@ -60,27 +60,81 @@ invoke_formatter() { local -a format_args=(-style=file -fallback-style=none) if (( _loglevel > 2 )) format_args+=(--verbose) + + check_files() { + local -i num_failures=0 + local -a source_files=($@) + local file + local -a format_args=(-style=file -fallback-style=none) + if (( _loglevel > 2 )) format_args+=(--verbose) + + local -a command=(${formatter} ${format_args}) + + for file (${source_files}) { + if ! ${command} "${file}" | diff -q "${file}" - &> /dev/null; then + log_error "${file} requires formatting changes." + if (( fail_on_error == 2 )) return 2; + num_failures=$(( num_failures + 1 )) + fi + } + if (( num_failures && fail_on_error == 1 )) return 2 + } + + format_files() { + local -a source_files=($@) + + if (( ${#source_files} )) { + local -a format_args=(-style=file -fallback-style=none -i) + if (( _loglevel > 2 )) format_args+=(--verbose) + + "${formatter}" ${format_args} ${source_files} + } + } ;; - cmake) - local formatter=cmake-format - if (( ${+commands[cmake-format]} )) { - local cmake_format_version=$(cmake-format --version) + gersemi) + local formatter=gersemi + if (( ${+commands[gersemi]} )) { + local gersemi_version=($(gersemi --version)) - if ! is-at-least 0.6.13 ${cmake_format_version}; then - log_error "cmake-format is not version 0.6.13 or above (found ${cmake_format_version})." + if ! is-at-least 0.12.0 ${gersemi_version[2]}; then + log_error "gersemi is not version 0.12.0 or above (found ${gersemi_version[2]}." exit 2 fi - } else { - log_error "No viable cmake-format version found (required 0.6.13)" - exit 2 } - if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared|cmake)/**/(CMakeLists.txt|*.cmake)(.N)) + if (( ! #source_files )) source_files=(CMakeLists.txt (libobs|libobs-*|UI|plugins|deps|shared|cmake|test)/**/(CMakeLists.txt|*.cmake)(.N)) source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#(cmake/Modules/*|*/legacy.cmake)}) + + check_files() { + local -i num_failures=0 + local -a source_files=($@) + local file + local -a command=(${formatter} -c --no-cache ${source_files}) + + if (( ${#source_files} )) { + while read -r line; do + local -a line_tokens=(${(z)line}) + file=${line_tokens[1]//*obs-studio\//} - local -a format_args=() - if (( _loglevel > 2 )) format_args+=(--log-level debug) + log_error "${file} requires formatting changes." + + if (( fail_on_error == 2 )) return 2 + num_failures=$(( num_failures + 1 )) + done < <(${command} 2>&1) + + if (( num_failures && fail_on_error == 1 )) return 2 + } + } + + format_files() { + local -a source_files=($@) + + if (( ${#source_files} )) { + "${formatter}" -i ${source_files} + } + } ;; swift) local formatter=swift-format @@ -98,30 +152,53 @@ invoke_formatter() { if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins)/**/*.swift(.N)) - local -a format_args=() + check_files() { + local -i num_failures=0 + local -a source_files=($@) + local file + local -a format_args=() + + local -a command=(${formatter} ${format_args}) + + for file (${source_files}) { + if ! "${command}" "${file}" | diff -q "${file}" - &> /dev/null; then + log_error "${file} requires formatting changes." + if (( fail_on_error == 2 )) return 2; + num_failures=$(( num_failures + 1 )) + fi + } + if (( num_failures && fail_on_error == 1 )) return 2 + } + + format_files() { + local -a source_files=($@) + + if (( ${#source_files} )) { + local -a format_args=(-i) + + "${formatter}" ${format_args} ${source_files} + } + } ;; - *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, cmake-format, and swift-format."; exit 2 ;; + *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, gersemi, and swift-format."; exit 2 ;; } local file local -i num_failures=0 if (( check_only )) { - for file (${source_files}) { - if (( _loglevel > 1 )) log_info "Checking format of ${file}..." - - if ! "${formatter}" ${format_args} "${file}" | diff -q "${file}" - &> /dev/null; then - log_error "${file} requires formatting changes." - - if (( fail_on_error == 2 )) return 2; - num_failures=$(( num_failures + 1 )) - else - if (( _loglevel > 1 )) log_status "${file} requires no formatting changes." - fi + if (( ${+functions[check_files]} )) { + check_files ${source_files} + } else { + log_error "No format check function defined for formatter '${formatter}'" + exit 2 + } + } else { + if (( ${+functions[format_files]} )) { + format_files ${source_files} + } else { + log_error "No format function defined for formatter '${formatter}'" + exit 2 } - if (( fail_on_error && num_failures )) return 2; - } elif (( ${#source_files} )) { - format_args+=(-i) - "${formatter}" ${format_args} ${source_files} } } diff --git a/build-aux/README.md b/build-aux/README.md index fb63c0a03bef10..490b7144ad3d0f 100644 --- a/build-aux/README.md +++ b/build-aux/README.md @@ -23,13 +23,13 @@ Example of use: ./build-aux/run-clang-format ``` -### `run-cmake-format` +### `run-gersemi-format` -This script allows to check the formatting and/or format of the CMake files and requires ZSH and `cmakelang` (`cmake-format`) Python package. +This script allows to check the formatting and/or format of the CMake files and requires ZSH and `gersemi` Python package. Example of use: ```sh -./build-aux/run-cmake-format +./build-aux/run-gersemi ``` ### `run-swift-format` diff --git a/build-aux/run-cmake-format b/build-aux/run-gersemi similarity index 100% rename from build-aux/run-cmake-format rename to build-aux/run-gersemi From b8cfacaec38d31413b0cd82718c9dc1e36beb9af Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 26 Mar 2024 21:28:00 +0100 Subject: [PATCH 0393/1073] Update formatting of CMake files --- CMakeLists.txt | 53 +- UI/CMakeLists.txt | 95 ++- UI/cmake/feature-browserpanels.cmake | 4 +- UI/cmake/feature-importers.cmake | 11 +- UI/cmake/feature-macos-update.cmake | 25 +- UI/cmake/feature-restream.cmake | 4 +- UI/cmake/feature-twitch.cmake | 4 +- UI/cmake/feature-whatsnew.cmake | 17 +- UI/cmake/feature-youtube.cmake | 31 +- UI/cmake/os-linux.cmake | 24 +- UI/cmake/os-macos.cmake | 10 +- UI/cmake/os-windows.cmake | 56 +- UI/cmake/ui-elements.cmake | 115 ++-- UI/cmake/ui-qt.cmake | 99 ++- UI/cmake/ui-windows.cmake | 117 ++-- .../aja-output-ui/CMakeLists.txt | 39 +- .../decklink-captions/CMakeLists.txt | 28 +- .../decklink-output-ui/CMakeLists.txt | 31 +- .../frontend-tools/CMakeLists.txt | 84 +-- UI/obs-frontend-api/CMakeLists.txt | 4 +- UI/win-update/updater/CMakeLists.txt | 41 +- UI/xdg-data/CMakeLists.txt | 24 +- cmake/32bit/projects.cmake | 11 +- cmake/common/bootstrap.cmake | 51 +- cmake/common/buildnumber.cmake | 9 +- cmake/common/buildspec_common.cmake | 65 +- cmake/common/compiler_common.cmake | 106 +-- cmake/common/helpers_common.cmake | 128 ++-- cmake/common/versionconfig.cmake | 26 +- cmake/finders/FindAMF.cmake | 32 +- cmake/finders/FindAsio.cmake | 40 +- cmake/finders/FindCEF.cmake | 113 ++-- cmake/finders/FindDetours.cmake | 42 +- cmake/finders/FindFFmpeg.cmake | 98 +-- cmake/finders/FindFFnvcodec.cmake | 29 +- cmake/finders/FindGio.cmake | 38 +- cmake/finders/FindJack.cmake | 36 +- cmake/finders/FindLibAJANTV2.cmake | 81 +-- cmake/finders/FindLibUUID.cmake | 31 +- cmake/finders/FindLibVLC.cmake | 41 +- cmake/finders/FindLibdrm.cmake | 32 +- cmake/finders/FindLibfdk.cmake | 30 +- cmake/finders/FindLibpci.cmake | 30 +- cmake/finders/FindLibrist.cmake | 36 +- cmake/finders/FindLibrnnoise.cmake | 37 +- cmake/finders/FindLibspeexdsp.cmake | 35 +- cmake/finders/FindLibsrt.cmake | 38 +- cmake/finders/FindLibudev.cmake | 30 +- cmake/finders/FindLibv4l2.cmake | 30 +- cmake/finders/FindLibva.cmake | 51 +- cmake/finders/FindLibx264.cmake | 46 +- cmake/finders/FindLuajit.cmake | 36 +- cmake/finders/FindMbedTLS.cmake | 89 ++- cmake/finders/FindOSS.cmake | 11 +- cmake/finders/FindPipeWire.cmake | 39 +- cmake/finders/FindPulseAudio.cmake | 50 +- cmake/finders/FindSndio.cmake | 30 +- cmake/finders/FindSysinfo.cmake | 22 +- cmake/finders/FindUthash.cmake | 40 +- cmake/finders/FindVPL.cmake | 40 +- cmake/finders/FindWayland.cmake | 38 +- cmake/finders/FindWebsocketpp.cmake | 34 +- cmake/finders/FindX11-xcb.cmake | 32 +- cmake/finders/FindXcb.cmake | 39 +- cmake/finders/FindXkbcommon.cmake | 32 +- cmake/finders/Findjansson.cmake | 36 +- cmake/finders/Findqrcodegencpp.cmake | 45 +- cmake/linux/compilerconfig.cmake | 74 +-- cmake/linux/cpackconfig.cmake | 63 +- cmake/linux/helpers.cmake | 170 ++--- cmake/macos/buildspec.cmake | 7 +- cmake/macos/compilerconfig.cmake | 17 +- cmake/macos/defaults.cmake | 15 +- cmake/macos/helpers.cmake | 182 +++--- cmake/macos/xcode.cmake | 9 +- cmake/windows/FindPython.cmake | 39 +- cmake/windows/compilerconfig.cmake | 58 +- cmake/windows/defaults.cmake | 4 +- cmake/windows/helpers.cmake | 212 +++--- cmake/windows/idlfilehelper.cmake | 8 +- deps/blake2/CMakeLists.txt | 10 +- deps/glad/CMakeLists.txt | 22 +- deps/json11/CMakeLists.txt | 11 +- deps/libcaption/CMakeLists.txt | 54 +- deps/w32-pthreads/CMakeLists.txt | 13 +- libobs-d3d11/CMakeLists.txt | 48 +- libobs-opengl/CMakeLists.txt | 72 +-- libobs-winrt/CMakeLists.txt | 23 +- libobs-winrt/cmake/legacy.cmake | 5 +- libobs/CMakeLists.txt | 607 +++++++++--------- libobs/cmake/legacy.cmake | 509 ++++++++------- libobs/cmake/obs-version.cmake | 5 +- libobs/cmake/os-freebsd.cmake | 58 +- libobs/cmake/os-linux.cmake | 53 +- libobs/cmake/os-macos.cmake | 42 +- libobs/cmake/os-windows.cmake | 63 +- plugins/CMakeLists.txt | 16 +- plugins/aja/CMakeLists.txt | 65 +- plugins/coreaudio-encoder/CMakeLists.txt | 14 +- plugins/decklink/CMakeLists.txt | 81 +-- plugins/image-source/CMakeLists.txt | 2 - plugins/linux-alsa/CMakeLists.txt | 3 +- plugins/linux-capture/CMakeLists.txt | 53 +- plugins/linux-jack/CMakeLists.txt | 3 +- plugins/linux-pipewire/CMakeLists.txt | 25 +- plugins/linux-pulseaudio/CMakeLists.txt | 3 +- plugins/linux-v4l2/CMakeLists.txt | 13 +- plugins/mac-avcapture/CMakeLists.txt | 50 +- plugins/mac-avcapture/legacy/CMakeLists.txt | 19 +- plugins/mac-capture/CMakeLists.txt | 52 +- plugins/mac-syphon/CMakeLists.txt | 17 +- plugins/mac-videotoolbox/CMakeLists.txt | 21 +- .../src/camera-extension/CMakeLists.txt | 19 +- .../src/dal-plugin/CMakeLists.txt | 59 +- .../src/obs-plugin/CMakeLists.txt | 5 +- plugins/nv-filters/CMakeLists.txt | 2 - plugins/obs-ffmpeg/CMakeLists.txt | 99 +-- plugins/obs-ffmpeg/cmake/dependencies.cmake | 21 +- plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt | 6 +- .../obs-ffmpeg/obs-amf-test/CMakeLists.txt | 7 +- plugins/obs-filters/CMakeLists.txt | 47 +- plugins/obs-filters/cmake/rnnoise.cmake | 39 +- plugins/obs-nvenc/CMakeLists.txt | 37 +- .../obs-nvenc/obs-nvenc-test/CMakeLists.txt | 2 - plugins/obs-outputs/CMakeLists.txt | 111 ++-- plugins/obs-qsv11/CMakeLists.txt | 45 +- plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt | 11 +- plugins/obs-text/CMakeLists.txt | 2 - plugins/obs-transitions/CMakeLists.txt | 21 +- plugins/obs-vst/CMakeLists.txt | 52 +- plugins/obs-webrtc/CMakeLists.txt | 7 +- plugins/obs-x264/CMakeLists.txt | 2 - plugins/rtmp-services/CMakeLists.txt | 47 +- plugins/text-freetype2/CMakeLists.txt | 48 +- plugins/vlc-video/CMakeLists.txt | 5 +- plugins/win-capture/CMakeLists.txt | 75 +-- .../get-graphics-offsets/CMakeLists.txt | 13 +- .../cmake/32bit-build.cmake | 7 +- .../get-graphics-offsets/cmake/32bit.cmake | 3 +- .../win-capture/graphics-hook/CMakeLists.txt | 35 +- .../graphics-hook/cmake/32bit-build.cmake | 7 +- .../graphics-hook/cmake/32bit.cmake | 3 +- .../win-capture/inject-helper/CMakeLists.txt | 2 - .../inject-helper/cmake/32bit-build.cmake | 7 +- .../inject-helper/cmake/32bit.cmake | 3 +- plugins/win-dshow/CMakeLists.txt | 21 +- plugins/win-dshow/cmake/libdshowcapture.cmake | 71 +- .../virtualcam-module/CMakeLists.txt | 36 +- .../virtualcam-module/cmake/32bit-build.cmake | 4 +- .../virtualcam-module/cmake/32bit.cmake | 3 +- plugins/win-wasapi/CMakeLists.txt | 8 +- shared/happy-eyeballs/CMakeLists.txt | 5 +- shared/ipc-util/CMakeLists.txt | 8 +- shared/media-playback/CMakeLists.txt | 21 +- shared/obs-scripting/CMakeLists.txt | 27 +- shared/obs-scripting/cmake/lua.cmake | 19 +- shared/obs-scripting/cmake/python.cmake | 34 +- shared/obs-scripting/obslua/CMakeLists.txt | 46 +- shared/obs-scripting/obspython/CMakeLists.txt | 84 ++- shared/opts-parser/CMakeLists.txt | 5 +- shared/properties-view/CMakeLists.txt | 47 +- test/test-input/CMakeLists.txt | 19 +- 162 files changed, 3696 insertions(+), 3402 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8af296a3d94e8..f4cc48c10ebf23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,25 +36,29 @@ endif() message( DEPRECATION - "\n" - "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" - "\n" - "You are using the legacy build system to build OBS Studio. " - "The legacy build system is unsupported and will be removed in the near future." - "\n" - "To migrate to the new build system, familiarize yourself with CMake presets " - "(https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) and create " - "a user preset with your customized build settings, inheriting from one of the default presets." - "\n" - "============ LEGACY BUILD SYSTEM IS DEPRECATED ============") + "\n" + "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" + "\n" + "You are using the legacy build system to build OBS Studio. " + "The legacy build system is unsupported and will be removed in the near future." + "\n" + "To migrate to the new build system, familiarize yourself with CMake presets " + "(https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) and create " + "a user preset with your customized build settings, inheriting from one of the default presets." + "\n" + "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" +) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules") include(VersionConfig) # Prohibit in-source builds if("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") - message(FATAL_ERROR "OBS: You cannot build in a source directory (or any directory with CMakeLists.txt file). " - "Please make a build subdirectory. Feel free to remove CMakeCache.txt and CMakeFiles.") + message( + FATAL_ERROR + "OBS: You cannot build in a source directory (or any directory with CMakeLists.txt file). " + "Please make a build subdirectory. Feel free to remove CMakeCache.txt and CMakeFiles." + ) endif() project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) @@ -68,10 +72,17 @@ include(CompilerConfig) # Allow selection of common build types via UI if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE - "RelWithDebInfo" - CACHE STRING "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel) + set( + CMAKE_BUILD_TYPE + "RelWithDebInfo" + CACHE STRING + "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" + FORCE + ) + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel + ) endif() # Global project options @@ -86,9 +97,11 @@ option(USE_LIBCXX "Use libc++ instead of libstdc++" ${APPLE}) option(BUILD_TESTS "Build test directory (includes test sources and possibly a platform test executable)" OFF) if(OS_WINDOWS) - option(INSTALLER_RUN - "Build a multiarch installer (needs to run independently after both archs have compiled) (Windows)" OFF) - + option( + INSTALLER_RUN + "Build a multiarch installer (needs to run independently after both archs have compiled) (Windows)" + OFF + ) elseif(OS_POSIX) option(LINUX_PORTABLE "Build portable version (Linux)" OFF) option(USE_XDG "Utilize XDG Base Directory Specification (Linux)" ON) diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 5b0c81c996429c..ba640c62b67407 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -25,13 +25,8 @@ add_executable(OBS::studio ALIAS obs-studio) target_link_libraries( obs-studio - PRIVATE CURL::libcurl - FFmpeg::avcodec - FFmpeg::avutil - FFmpeg::avformat - OBS::libobs - OBS::frontend-api - OBS::json11) + PRIVATE CURL::libcurl FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat OBS::libobs OBS::frontend-api OBS::json11 +) include(cmake/ui-qt.cmake) include(cmake/ui-elements.cmake) @@ -40,10 +35,8 @@ include(cmake/feature-importers.cmake) include(cmake/feature-browserpanels.cmake) if(NOT OAUTH_BASE_URL) - # cmake-format: off set(OAUTH_BASE_URL "https://auth.obsproject.com/" CACHE STRING "Default OAuth base URL") mark_as_advanced(OAUTH_BASE_URL) - # cmake-format: on endif() include(cmake/feature-twitch.cmake) include(cmake/feature-restream.cmake) @@ -56,49 +49,51 @@ configure_file(ui-config.h.in ui-config.h) target_sources( obs-studio - PRIVATE # cmake-format: sortable - api-interface.cpp - auth-base.cpp - auth-base.hpp - auth-listener.cpp - auth-listener.hpp - auth-oauth.cpp - auth-oauth.hpp - display-helpers.hpp - ffmpeg-utils.cpp - ffmpeg-utils.hpp - multiview.cpp - multiview.hpp - obf.c - obf.h - obs-app-theming.cpp - obs-app-theming.hpp - obs-app.cpp - obs-app.hpp - obs-proxy-style.cpp - obs-proxy-style.hpp - platform.hpp - qt-display.cpp - qt-display.hpp - ui-config.h - ui-validation.cpp - ui-validation.hpp) + PRIVATE + api-interface.cpp + auth-base.cpp + auth-base.hpp + auth-listener.cpp + auth-listener.hpp + auth-oauth.cpp + auth-oauth.hpp + display-helpers.hpp + ffmpeg-utils.cpp + ffmpeg-utils.hpp + multiview.cpp + multiview.hpp + obf.c + obf.h + obs-app-theming.cpp + obs-app-theming.hpp + obs-app.cpp + obs-app.hpp + obs-proxy-style.cpp + obs-proxy-style.hpp + platform.hpp + qt-display.cpp + qt-display.hpp + ui-config.h + ui-validation.cpp + ui-validation.hpp +) target_sources( obs-studio - PRIVATE # cmake-format: sortable - goliveapi-censoredjson.cpp - goliveapi-censoredjson.hpp - goliveapi-network.cpp - goliveapi-network.hpp - goliveapi-postdata.cpp - goliveapi-postdata.hpp - models/multitrack-video.hpp - multitrack-video-error.cpp - multitrack-video-error.hpp - multitrack-video-output.cpp - multitrack-video-output.hpp - system-info.hpp) + PRIVATE + goliveapi-censoredjson.cpp + goliveapi-censoredjson.hpp + goliveapi-network.cpp + goliveapi-network.hpp + goliveapi-postdata.cpp + goliveapi-postdata.hpp + models/multitrack-video.hpp + multitrack-video-error.cpp + multitrack-video-error.hpp + multitrack-video-output.cpp + multitrack-video-output.hpp + system-info.hpp +) if(OS_WINDOWS) include(cmake/os-windows.cmake) @@ -127,6 +122,4 @@ get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED) list(JOIN obs_module_list "|" SAFE_MODULES) target_compile_definitions(obs-studio PRIVATE "SAFE_MODULES=\"${SAFE_MODULES}\"") -# cmake-format: off set_target_properties_obs(obs-studio PROPERTIES FOLDER frontend OUTPUT_NAME "$,obs64,obs>") -# cmake-format: on diff --git a/UI/cmake/feature-browserpanels.cmake b/UI/cmake/feature-browserpanels.cmake index bce82e4e1c5607..73ef6d2e42b7aa 100644 --- a/UI/cmake/feature-browserpanels.cmake +++ b/UI/cmake/feature-browserpanels.cmake @@ -5,6 +5,6 @@ if(TARGET OBS::browser-panels) target_sources( obs-studio - PRIVATE # cmake-format: sortable - window-dock-browser.cpp window-dock-browser.hpp window-extra-browsers.cpp window-extra-browsers.hpp) + PRIVATE window-dock-browser.cpp window-dock-browser.hpp window-extra-browsers.cpp window-extra-browsers.hpp + ) endif() diff --git a/UI/cmake/feature-importers.cmake b/UI/cmake/feature-importers.cmake index 16ff1635be0580..d11ab27a2a04f3 100644 --- a/UI/cmake/feature-importers.cmake +++ b/UI/cmake/feature-importers.cmake @@ -1,5 +1,10 @@ target_sources( obs-studio - PRIVATE # cmake-format: sortable - importers/classic.cpp importers/importers.cpp importers/importers.hpp importers/sl.cpp importers/studio.cpp - importers/xsplit.cpp) + PRIVATE + importers/classic.cpp + importers/importers.cpp + importers/importers.hpp + importers/sl.cpp + importers/studio.cpp + importers/xsplit.cpp +) diff --git a/UI/cmake/feature-macos-update.cmake b/UI/cmake/feature-macos-update.cmake index 4b7cbf59398042..96c860b87dbdd2 100644 --- a/UI/cmake/feature-macos-update.cmake +++ b/UI/cmake/feature-macos-update.cmake @@ -8,15 +8,18 @@ endif() target_sources( obs-studio - PRIVATE # cmake-format: sortable - update/crypto-helpers-mac.mm - update/crypto-helpers.hpp - update/models/branches.hpp - update/models/whatsnew.hpp - update/shared-update.cpp - update/shared-update.hpp - update/update-helpers.cpp - update/update-helpers.hpp) + PRIVATE + update/crypto-helpers-mac.mm + update/crypto-helpers.hpp + update/models/branches.hpp + update/models/whatsnew.hpp + update/shared-update.cpp + update/shared-update.hpp + update/update-helpers.cpp + update/update-helpers.hpp +) -target_link_libraries(obs-studio PRIVATE "$" nlohmann_json::nlohmann_json - OBS::blake2) +target_link_libraries( + obs-studio + PRIVATE "$" nlohmann_json::nlohmann_json OBS::blake2 +) diff --git a/UI/cmake/feature-restream.cmake b/UI/cmake/feature-restream.cmake index de3a5ac72e66b7..65847a1ab4d501 100644 --- a/UI/cmake/feature-restream.cmake +++ b/UI/cmake/feature-restream.cmake @@ -1,6 +1,4 @@ -if(RESTREAM_CLIENTID - AND RESTREAM_HASH MATCHES "^(0|[a-fA-F0-9]+)$" - AND TARGET OBS::browser-panels) +if(RESTREAM_CLIENTID AND RESTREAM_HASH MATCHES "^(0|[a-fA-F0-9]+)$" AND TARGET OBS::browser-panels) target_sources(obs-studio PRIVATE auth-restream.cpp auth-restream.hpp) target_enable_feature(obs-studio "Restream API connection" RESTREAM_ENABLED) else() diff --git a/UI/cmake/feature-twitch.cmake b/UI/cmake/feature-twitch.cmake index f160fb6cd6fe02..b8415749fdca8e 100644 --- a/UI/cmake/feature-twitch.cmake +++ b/UI/cmake/feature-twitch.cmake @@ -1,6 +1,4 @@ -if(TWITCH_CLIENTID - AND TWITCH_HASH MATCHES "^(0|[a-fA-F0-9]+)$" - AND TARGET OBS::browser-panels) +if(TWITCH_CLIENTID AND TWITCH_HASH MATCHES "^(0|[a-fA-F0-9]+)$" AND TARGET OBS::browser-panels) target_sources(obs-studio PRIVATE auth-twitch.cpp auth-twitch.hpp) target_enable_feature(obs-studio "Twitch API connection" TWITCH_ENABLED) else() diff --git a/UI/cmake/feature-whatsnew.cmake b/UI/cmake/feature-whatsnew.cmake index 487494cddc2639..d7d4d89f031b24 100644 --- a/UI/cmake/feature-whatsnew.cmake +++ b/UI/cmake/feature-whatsnew.cmake @@ -17,14 +17,15 @@ if(ENABLE_WHATSNEW AND TARGET OBS::browser-panels) target_sources( obs-studio - PRIVATE # cmake-format: sortable - update/crypto-helpers-mbedtls.cpp - update/crypto-helpers.hpp - update/models/whatsnew.hpp - update/shared-update.cpp - update/shared-update.hpp - update/update-helpers.cpp - update/update-helpers.hpp) + PRIVATE + update/crypto-helpers-mbedtls.cpp + update/crypto-helpers.hpp + update/models/whatsnew.hpp + update/shared-update.cpp + update/shared-update.hpp + update/update-helpers.cpp + update/update-helpers.hpp + ) endif() target_enable_feature(obs-studio "What's New panel" WHATSNEW_ENABLED) diff --git a/UI/cmake/feature-youtube.cmake b/UI/cmake/feature-youtube.cmake index 97be6ee7377589..4db0e31adb5ea9 100644 --- a/UI/cmake/feature-youtube.cmake +++ b/UI/cmake/feature-youtube.cmake @@ -1,19 +1,22 @@ -if(YOUTUBE_CLIENTID - AND YOUTUBE_SECRET - AND YOUTUBE_CLIENTID_HASH MATCHES "^(0|[a-fA-F0-9]+)$" - AND YOUTUBE_SECRET_HASH MATCHES "^(0|[a-fA-F0-9]+)$" - AND TARGET OBS::browser-panels) +if( + YOUTUBE_CLIENTID + AND YOUTUBE_SECRET + AND YOUTUBE_CLIENTID_HASH MATCHES "^(0|[a-fA-F0-9]+)$" + AND YOUTUBE_SECRET_HASH MATCHES "^(0|[a-fA-F0-9]+)$" + AND TARGET OBS::browser-panels +) target_sources( obs-studio - PRIVATE # cmake-format: sortable - auth-youtube.cpp - auth-youtube.hpp - window-dock-youtube-app.cpp - window-dock-youtube-app.hpp - window-youtube-actions.cpp - window-youtube-actions.hpp - youtube-api-wrappers.cpp - youtube-api-wrappers.hpp) + PRIVATE + auth-youtube.cpp + auth-youtube.hpp + window-dock-youtube-app.cpp + window-dock-youtube-app.hpp + window-youtube-actions.cpp + window-youtube-actions.hpp + youtube-api-wrappers.cpp + youtube-api-wrappers.hpp + ) target_enable_feature(obs-studio "YouTube API connection" YOUTUBE_ENABLED) else() diff --git a/UI/cmake/os-linux.cmake b/UI/cmake/os-linux.cmake index a8c08080f866db..c37e977428e116 100644 --- a/UI/cmake/os-linux.cmake +++ b/UI/cmake/os-linux.cmake @@ -16,7 +16,8 @@ if(NOT DEFINED APPDATA_RELEASE_DATE) COMMAND git log --tags -1 --pretty=%cd --date=short OUTPUT_VARIABLE APPDATA_RELEASE_DATE WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) elseif(EXISTS "${CMAKE_SOURCE_DIR}/cmake/.CMakeBuildNumber") file(TIMESTAMP "${CMAKE_SOURCE_DIR}/cmake/.CMakeBuildNumber" APPDATA_RELEASE_DATE "%Y-%m-%d") else() @@ -30,7 +31,8 @@ if(NOT DEFINED GIT_HASH) COMMAND git rev-parse HEAD OUTPUT_VARIABLE GIT_HASH WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) else() set(GIT_HASH "master") endif() @@ -38,27 +40,33 @@ endif() configure_file(cmake/linux/com.obsproject.Studio.metainfo.xml.in com.obsproject.Studio.metainfo.xml) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml" - DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo") +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" +) install(FILES cmake/linux/com.obsproject.Studio.desktop DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications") install( FILES cmake/linux/icons/obs-logo-128.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" - RENAME com.obsproject.Studio.png) + RENAME com.obsproject.Studio.png +) install( FILES cmake/linux/icons/obs-logo-256.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" - RENAME com.obsproject.Studio.png) + RENAME com.obsproject.Studio.png +) install( FILES cmake/linux/icons/obs-logo-512.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" - RENAME com.obsproject.Studio.png) + RENAME com.obsproject.Studio.png +) install( FILES cmake/linux/icons/obs-logo-scalable.svg DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps" - RENAME com.obsproject.Studio.svg) + RENAME com.obsproject.Studio.svg +) diff --git a/UI/cmake/os-macos.cmake b/UI/cmake/os-macos.cmake index 5003e0b6231e9f..a965f1bffea60f 100644 --- a/UI/cmake/os-macos.cmake +++ b/UI/cmake/os-macos.cmake @@ -11,10 +11,10 @@ if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3) target_compile_options(obs-studio PRIVATE -Wno-error=unqualified-std-cast-call) endif() -# cmake-format: off target_link_libraries( obs-studio - PRIVATE "$" - "$" - "$") -# cmake-format: on + PRIVATE + "$" + "$" + "$" +) diff --git a/UI/cmake/os-windows.cmake b/UI/cmake/os-windows.cmake index 83ced77f09ec4b..56e2f4ac81d2a0 100644 --- a/UI/cmake/os-windows.cmake +++ b/UI/cmake/os-windows.cmake @@ -14,29 +14,32 @@ configure_file(cmake/windows/obs.rc.in obs.rc) target_sources( obs-studio - PRIVATE # cmake-format: sortable - cmake/windows/obs.manifest - obs.rc - platform-windows.cpp - update/crypto-helpers-mbedtls.cpp - update/crypto-helpers.hpp - update/models/branches.hpp - update/models/whatsnew.hpp - update/shared-update.cpp - update/shared-update.hpp - update/update-helpers.cpp - update/update-helpers.hpp - update/update-window.cpp - update/update-window.hpp - update/win-update.cpp - update/win-update.hpp - win-dll-blocklist.c - win-update/updater/manifest.hpp) + PRIVATE + cmake/windows/obs.manifest + obs.rc + platform-windows.cpp + update/crypto-helpers-mbedtls.cpp + update/crypto-helpers.hpp + update/models/branches.hpp + update/models/whatsnew.hpp + update/shared-update.cpp + update/shared-update.hpp + update/update-helpers.cpp + update/update-helpers.hpp + update/update-window.cpp + update/update-window.hpp + update/win-update.cpp + update/win-update.hpp + win-dll-blocklist.c + win-update/updater/manifest.hpp +) target_sources(obs-studio PRIVATE system-info-windows.cpp) -target_link_libraries(obs-studio PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::MbedTLS - nlohmann_json::nlohmann_json Detours::Detours) +target_link_libraries( + obs-studio + PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::MbedTLS nlohmann_json::nlohmann_json Detours::Detours +) target_compile_definitions(obs-studio PRIVATE PSAPI_VERSION=2) @@ -60,14 +63,13 @@ set_source_files_properties(update/win-update.cpp PROPERTIES COMPILE_DEFINITIONS add_subdirectory(win-update/updater) -set_property( - TARGET obs-studio - APPEND - PROPERTY AUTORCC_OPTIONS --format-version 1) +set_property(TARGET obs-studio APPEND PROPERTY AUTORCC_OPTIONS --format-version 1) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT obs-studio) set_target_properties( obs-studio - PROPERTIES WIN32_EXECUTABLE TRUE - VS_DEBUGGER_COMMAND "${CMAKE_BINARY_DIR}/rundir/$/bin/64bit/$" - VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/rundir/$/bin/64bit") + PROPERTIES + WIN32_EXECUTABLE TRUE + VS_DEBUGGER_COMMAND "${CMAKE_BINARY_DIR}/rundir/$/bin/64bit/$" + VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/rundir/$/bin/64bit" +) diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index 73144a77b1eeaa..2a93088aeb0fcc 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -7,65 +7,72 @@ if(NOT TARGET OBS::qt-plain-text-edit) endif() if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") + add_subdirectory( + "${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel" + ) endif() if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") + add_subdirectory( + "${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area" + ) endif() -target_link_libraries(obs-studio PRIVATE OBS::properties-view OBS::qt-plain-text-edit OBS::qt-slider-ignorewheel - OBS::qt-vertical-scroll-area) +target_link_libraries( + obs-studio + PRIVATE OBS::properties-view OBS::qt-plain-text-edit OBS::qt-slider-ignorewheel OBS::qt-vertical-scroll-area +) target_sources( obs-studio - PRIVATE # cmake-format: sortable - absolute-slider.cpp - absolute-slider.hpp - adv-audio-control.cpp - adv-audio-control.hpp - audio-encoders.cpp - audio-encoders.hpp - balance-slider.hpp - basic-controls.cpp - basic-controls.hpp - clickable-label.hpp - context-bar-controls.cpp - context-bar-controls.hpp - focus-list.cpp - focus-list.hpp - horizontal-scroll-area.cpp - horizontal-scroll-area.hpp - hotkey-edit.cpp - hotkey-edit.hpp - item-widget-helpers.cpp - item-widget-helpers.hpp - lineedit-autoresize.cpp - lineedit-autoresize.hpp - log-viewer.cpp - log-viewer.hpp - media-controls.cpp - media-controls.hpp - menu-button.cpp - menu-button.hpp - mute-checkbox.hpp - noncheckable-button.hpp - remote-text.cpp - remote-text.hpp - scene-tree.cpp - scene-tree.hpp - screenshot-obj.hpp - source-label.cpp - source-label.hpp - source-tree.cpp - source-tree.hpp - undo-stack-obs.cpp - undo-stack-obs.hpp - url-push-button.cpp - url-push-button.hpp - visibility-item-widget.cpp - visibility-item-widget.hpp - volume-control.cpp - volume-control.hpp) + PRIVATE + absolute-slider.cpp + absolute-slider.hpp + adv-audio-control.cpp + adv-audio-control.hpp + audio-encoders.cpp + audio-encoders.hpp + balance-slider.hpp + basic-controls.cpp + basic-controls.hpp + clickable-label.hpp + context-bar-controls.cpp + context-bar-controls.hpp + focus-list.cpp + focus-list.hpp + horizontal-scroll-area.cpp + horizontal-scroll-area.hpp + hotkey-edit.cpp + hotkey-edit.hpp + item-widget-helpers.cpp + item-widget-helpers.hpp + lineedit-autoresize.cpp + lineedit-autoresize.hpp + log-viewer.cpp + log-viewer.hpp + media-controls.cpp + media-controls.hpp + menu-button.cpp + menu-button.hpp + mute-checkbox.hpp + noncheckable-button.hpp + remote-text.cpp + remote-text.hpp + scene-tree.cpp + scene-tree.hpp + screenshot-obj.hpp + source-label.cpp + source-label.hpp + source-tree.cpp + source-tree.hpp + undo-stack-obs.cpp + undo-stack-obs.hpp + url-push-button.cpp + url-push-button.hpp + visibility-item-widget.cpp + visibility-item-widget.hpp + volume-control.cpp + volume-control.hpp +) diff --git a/UI/cmake/ui-qt.cmake b/UI/cmake/ui-qt.cmake index d34de0ad9f7f4f..53d53671acaf40 100644 --- a/UI/cmake/ui-qt.cmake +++ b/UI/cmake/ui-qt.cmake @@ -1,10 +1,6 @@ -# cmake-format: off find_package(Qt6 REQUIRED Widgets Network Svg Xml) -# cmake-format: on -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Qt6 REQUIRED Gui DBus) endif() @@ -12,61 +8,58 @@ if(NOT TARGET OBS::qt-wrappers) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") endif() -target_link_libraries(obs-studio PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network OBS::qt-wrappers) +target_link_libraries( + obs-studio + PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network OBS::qt-wrappers +) set_target_properties( obs-studio - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON) + PROPERTIES AUTOMOC ON AUTOUIC ON AUTORCC ON +) -set_property( - TARGET obs-studio - APPEND - PROPERTY AUTOUIC_SEARCH_PATHS forms forms/source-toolbar) +set_property(TARGET obs-studio APPEND PROPERTY AUTOUIC_SEARCH_PATHS forms forms/source-toolbar) -set(_qt_sources - # cmake-format: sortable - forms/AutoConfigFinishPage.ui - forms/AutoConfigStartPage.ui - forms/AutoConfigStartPage.ui - forms/AutoConfigStreamPage.ui - forms/AutoConfigTestPage.ui - forms/AutoConfigVideoPage.ui - forms/ColorSelect.ui - forms/obs.qrc - forms/OBSAbout.ui - forms/OBSAdvAudio.ui - forms/OBSBasic.ui - forms/OBSBasicControls.ui - forms/OBSBasicFilters.ui - forms/OBSBasicInteraction.ui - forms/OBSBasicProperties.ui - forms/OBSBasicSettings.ui - forms/OBSBasicSourceSelect.ui - forms/OBSBasicTransform.ui - forms/OBSBasicVCamConfig.ui - forms/OBSExtraBrowsers.ui - forms/OBSImporter.ui - forms/OBSLogReply.ui - forms/OBSLogViewer.ui - forms/OBSMissingFiles.ui - forms/OBSRemux.ui - forms/OBSUpdate.ui - forms/OBSYoutubeActions.ui - forms/source-toolbar/browser-source-toolbar.ui - forms/source-toolbar/color-source-toolbar.ui - forms/source-toolbar/device-select-toolbar.ui - forms/source-toolbar/game-capture-toolbar.ui - forms/source-toolbar/image-source-toolbar.ui - forms/source-toolbar/media-controls.ui - forms/source-toolbar/text-source-toolbar.ui) +set( + _qt_sources + forms/AutoConfigFinishPage.ui + forms/AutoConfigStartPage.ui + forms/AutoConfigStartPage.ui + forms/AutoConfigStreamPage.ui + forms/AutoConfigTestPage.ui + forms/AutoConfigVideoPage.ui + forms/ColorSelect.ui + forms/obs.qrc + forms/OBSAbout.ui + forms/OBSAdvAudio.ui + forms/OBSBasic.ui + forms/OBSBasicControls.ui + forms/OBSBasicFilters.ui + forms/OBSBasicInteraction.ui + forms/OBSBasicProperties.ui + forms/OBSBasicSettings.ui + forms/OBSBasicSourceSelect.ui + forms/OBSBasicTransform.ui + forms/OBSBasicVCamConfig.ui + forms/OBSExtraBrowsers.ui + forms/OBSImporter.ui + forms/OBSLogReply.ui + forms/OBSLogViewer.ui + forms/OBSMissingFiles.ui + forms/OBSRemux.ui + forms/OBSUpdate.ui + forms/OBSYoutubeActions.ui + forms/source-toolbar/browser-source-toolbar.ui + forms/source-toolbar/color-source-toolbar.ui + forms/source-toolbar/device-select-toolbar.ui + forms/source-toolbar/game-capture-toolbar.ui + forms/source-toolbar/image-source-toolbar.ui + forms/source-toolbar/media-controls.ui + forms/source-toolbar/text-source-toolbar.ui +) target_sources(obs-studio PRIVATE ${_qt_sources}) -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" - PREFIX "UI Files" - FILES ${_qt_sources}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" PREFIX "UI Files" FILES ${_qt_sources}) unset(_qt_sources) diff --git a/UI/cmake/ui-windows.cmake b/UI/cmake/ui-windows.cmake index 40675ecc6ca687..5cdca10fd6905d 100644 --- a/UI/cmake/ui-windows.cmake +++ b/UI/cmake/ui-windows.cmake @@ -1,60 +1,61 @@ target_sources( obs-studio - PRIVATE # cmake-format: sortable - window-basic-about.cpp - window-basic-about.hpp - window-basic-adv-audio.cpp - window-basic-adv-audio.hpp - window-basic-auto-config-test.cpp - window-basic-auto-config.cpp - window-basic-auto-config.hpp - window-basic-filters.cpp - window-basic-filters.hpp - window-basic-interaction.cpp - window-basic-interaction.hpp - window-basic-main-browser.cpp - window-basic-main-dropfiles.cpp - window-basic-main-icons.cpp - window-basic-main-outputs.cpp - window-basic-main-outputs.hpp - window-basic-main-profiles.cpp - window-basic-main-scene-collections.cpp - window-basic-main-screenshot.cpp - window-basic-main-transitions.cpp - window-basic-main.cpp - window-basic-main.hpp - window-basic-preview.cpp - window-basic-preview.hpp - window-basic-properties.cpp - window-basic-properties.hpp - window-basic-settings-a11y.cpp - window-basic-settings-appearance.cpp - window-basic-settings-stream.cpp - window-basic-settings.cpp - window-basic-settings.hpp - window-basic-source-select.cpp - window-basic-source-select.hpp - window-basic-stats.cpp - window-basic-stats.hpp - window-basic-status-bar.cpp - window-basic-status-bar.hpp - window-basic-transform.cpp - window-basic-transform.hpp - window-basic-vcam-config.cpp - window-basic-vcam-config.hpp - window-basic-vcam.hpp - window-dock.cpp - window-dock.hpp - window-importer.cpp - window-importer.hpp - window-log-reply.cpp - window-log-reply.hpp - window-main.hpp - window-missing-files.cpp - window-missing-files.hpp - window-namedialog.cpp - window-namedialog.hpp - window-projector.cpp - window-projector.hpp - window-remux.cpp - window-remux.hpp) + PRIVATE + window-basic-about.cpp + window-basic-about.hpp + window-basic-adv-audio.cpp + window-basic-adv-audio.hpp + window-basic-auto-config-test.cpp + window-basic-auto-config.cpp + window-basic-auto-config.hpp + window-basic-filters.cpp + window-basic-filters.hpp + window-basic-interaction.cpp + window-basic-interaction.hpp + window-basic-main-browser.cpp + window-basic-main-dropfiles.cpp + window-basic-main-icons.cpp + window-basic-main-outputs.cpp + window-basic-main-outputs.hpp + window-basic-main-profiles.cpp + window-basic-main-scene-collections.cpp + window-basic-main-screenshot.cpp + window-basic-main-transitions.cpp + window-basic-main.cpp + window-basic-main.hpp + window-basic-preview.cpp + window-basic-preview.hpp + window-basic-properties.cpp + window-basic-properties.hpp + window-basic-settings-a11y.cpp + window-basic-settings-appearance.cpp + window-basic-settings-stream.cpp + window-basic-settings.cpp + window-basic-settings.hpp + window-basic-source-select.cpp + window-basic-source-select.hpp + window-basic-stats.cpp + window-basic-stats.hpp + window-basic-status-bar.cpp + window-basic-status-bar.hpp + window-basic-transform.cpp + window-basic-transform.hpp + window-basic-vcam-config.cpp + window-basic-vcam-config.hpp + window-basic-vcam.hpp + window-dock.cpp + window-dock.hpp + window-importer.cpp + window-importer.hpp + window-log-reply.cpp + window-log-reply.hpp + window-main.hpp + window-missing-files.cpp + window-missing-files.hpp + window-namedialog.cpp + window-namedialog.hpp + window-projector.cpp + window-projector.hpp + window-remux.cpp + window-remux.hpp +) diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 2be9f389379bff..3d7b2e5902705a 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -11,9 +11,7 @@ find_package(LibAJANTV2 REQUIRED) find_package(Qt6 REQUIRED Widgets) -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Qt6 REQUIRED Gui) find_package(X11 REQUIRED) @@ -30,25 +28,29 @@ endif() add_library(aja-output-ui MODULE) add_library(OBS::aja-output-ui ALIAS aja-output-ui) -target_sources(aja-output-ui PRIVATE # cmake-format: sortable - aja-ui-main.cpp aja-ui-main.h AJAOutputUI.cpp AJAOutputUI.h) +target_sources(aja-output-ui PRIVATE aja-ui-main.cpp aja-ui-main.h AJAOutputUI.cpp AJAOutputUI.h) target_sources(aja-output-ui PRIVATE forms/output.ui) target_compile_options( - aja-output-ui PRIVATE $<$:-Wno-deprecated-declarations> - $<$:-Wno-quoted-include-in-framework-header>) + aja-output-ui + PRIVATE + $<$:-Wno-deprecated-declarations> + $<$:-Wno-quoted-include-in-framework-header> +) target_link_libraries( aja-output-ui - PRIVATE OBS::libobs - OBS::aja-support - OBS::frontend-api - OBS::properties-view - Qt::Widgets - AJA::LibAJANTV2 - $<$:X11::X11> - $<$:Qt::GuiPrivate>) + PRIVATE + OBS::libobs + OBS::aja-support + OBS::frontend-api + OBS::properties-view + Qt::Widgets + AJA::LibAJANTV2 + $<$:X11::X11> + $<$:Qt::GuiPrivate> +) target_link_options(aja-output-ui PRIVATE $<$:/IGNORE:4099>) @@ -56,12 +58,9 @@ if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in aja-output-ui.rc) target_sources(aja-output-ui PRIVATE aja-output-ui.rc) - # cmake-format: off set_property(TARGET aja-output-ui APPEND PROPERTY AUTORCC_OPTIONS --format-version 1) - # cmake-format: on endif() -# cmake-format: off set_target_properties_obs( aja-output-ui PROPERTIES FOLDER frontend @@ -69,5 +68,5 @@ set_target_properties_obs( AUTOMOC ON AUTOUIC ON AUTORCC ON - AUTOUIC_SEARCH_PATHS forms) -# cmake-format: on + AUTOUIC_SEARCH_PATHS forms +) diff --git a/UI/frontend-plugins/decklink-captions/CMakeLists.txt b/UI/frontend-plugins/decklink-captions/CMakeLists.txt index 7ea1c6ee0bcd3c..89a94f4a2d5eb7 100644 --- a/UI/frontend-plugins/decklink-captions/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-captions/CMakeLists.txt @@ -7,40 +7,40 @@ if(NOT ENABLE_DECKLINK) return() endif() -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(X11 REQUIRED) mark_as_advanced(X11) endif() -# cmake-format: off find_package(Qt6 REQUIRED Widgets) -# cmake-format: on add_library(decklink-captions MODULE) add_library(OBS::decklink-captions ALIAS decklink-captions) target_sources(decklink-captions PRIVATE decklink-captions.cpp decklink-captions.h forms/captions.ui) -target_compile_options(decklink-captions PRIVATE $<$:-Wno-quoted-include-in-framework-header> - $<$:-Wno-comma>) +target_compile_options( + decklink-captions + PRIVATE $<$:-Wno-quoted-include-in-framework-header> $<$:-Wno-comma> +) target_link_libraries( decklink-captions - PRIVATE OBS::frontend-api OBS::libobs Qt::Widgets - "$<$:$>" $<$:X11::X11>) + PRIVATE + OBS::frontend-api + OBS::libobs + Qt::Widgets + "$<$:$>" + $<$:X11::X11> +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in decklink-captions.rc) target_sources(decklink-captions PRIVATE decklink-captions.rc) - # cmake-format: off set_property(TARGET decklink-captions APPEND PROPERTY AUTORCC_OPTIONS --format-version 1) - # cmake-format: on endif() -# cmake-format: off set_target_properties_obs( decklink-captions PROPERTIES FOLDER plugins/decklink @@ -48,5 +48,5 @@ set_target_properties_obs( AUTOMOC ON AUTOUIC ON AUTORCC ON - AUTOUIC_SEARCH_PATHS forms) -# cmake-format: on + AUTOUIC_SEARCH_PATHS forms +) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index db08c2aa5972fc..a6d379185ea529 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -9,9 +9,7 @@ endif() find_package(Qt6 REQUIRED Widgets) -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Qt6 REQUIRED Gui) find_package(X11 REQUIRED) @@ -26,28 +24,30 @@ endif() target_sources(decklink-output-ui PRIVATE forms/output.ui) -target_sources(decklink-output-ui PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp - decklink-ui-main.h) +target_sources( + decklink-output-ui + PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp decklink-ui-main.h +) target_compile_options(decklink-output-ui PRIVATE $<$:-Wno-quoted-include-in-framework-header>) target_link_libraries( decklink-output-ui - PRIVATE OBS::libobs - OBS::frontend-api - OBS::properties-view - Qt::Widgets - "$<$:$>" - $<$:X11::X11> - $<$:Qt::GuiPrivate>) + PRIVATE + OBS::libobs + OBS::frontend-api + OBS::properties-view + Qt::Widgets + "$<$:$>" + $<$:X11::X11> + $<$:Qt::GuiPrivate> +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in decklink-output-ui.rc) target_sources(decklink-output-ui PRIVATE decklink-output-ui.rc) - # cmake-format: off set_property(TARGET decklink-output-ui APPEND PROPERTY AUTORCC_OPTIONS --format-version 1) - # cmake-format: on endif() set_target_properties_obs( @@ -57,4 +57,5 @@ set_target_properties_obs( AUTOMOC ON AUTOUIC ON AUTORCC ON - AUTOUIC_SEARCH_PATHS forms) + AUTOUIC_SEARCH_PATHS forms +) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 6b88874f1075c4..444da23f4a771d 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -4,9 +4,7 @@ legacy_check() find_package(Qt6 REQUIRED Widgets) -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Qt6 REQUIRED Gui) find_package(X11 REQUIRED) endif() @@ -28,42 +26,49 @@ endif() target_sources( frontend-tools - PRIVATE # cmake-format: sortable - $<$:auto-scene-switcher-osx.mm> - $<$:auto-scene-switcher-nix.cpp> - $<$:auto-scene-switcher-win.cpp> - $<$:captions-handler.cpp> - $<$:captions-handler.hpp> - $<$:captions-mssapi-stream.cpp> - $<$:captions-mssapi-stream.hpp> - $<$:captions-mssapi.cpp> - $<$:captions-mssapi.hpp> - $<$:captions.cpp> - $<$:captions.hpp> - auto-scene-switcher.cpp - auto-scene-switcher.hpp - frontend-tools.c - output-timer.cpp - output-timer.hpp - tool-helpers.hpp) - -target_sources(frontend-tools PRIVATE forms/auto-scene-switcher.ui forms/captions.ui forms/output-timer.ui - forms/scripts.ui) - -target_compile_options(frontend-tools PRIVATE $<$:-Wno-quoted-include-in-framework-header> - $<$:-Wno-comma>) + PRIVATE + $<$:auto-scene-switcher-osx.mm> + $<$:auto-scene-switcher-nix.cpp> + $<$:auto-scene-switcher-win.cpp> + $<$:captions-handler.cpp> + $<$:captions-handler.hpp> + $<$:captions-mssapi-stream.cpp> + $<$:captions-mssapi-stream.hpp> + $<$:captions-mssapi.cpp> + $<$:captions-mssapi.hpp> + $<$:captions.cpp> + $<$:captions.hpp> + auto-scene-switcher.cpp + auto-scene-switcher.hpp + frontend-tools.c + output-timer.cpp + output-timer.hpp + tool-helpers.hpp +) + +target_sources( + frontend-tools + PRIVATE forms/auto-scene-switcher.ui forms/captions.ui forms/output-timer.ui forms/scripts.ui +) + +target_compile_options( + frontend-tools + PRIVATE $<$:-Wno-quoted-include-in-framework-header> $<$:-Wno-comma> +) target_link_libraries( frontend-tools - PRIVATE OBS::frontend-api - OBS::libobs - OBS::properties-view - OBS::qt-plain-text-edit - OBS::qt-wrappers - Qt::Widgets - "$<$:$>" - $<$:X11::X11> - $<$:Qt::GuiPrivate>) + PRIVATE + OBS::frontend-api + OBS::libobs + OBS::properties-view + OBS::qt-plain-text-edit + OBS::qt-wrappers + Qt::Widgets + "$<$:$>" + $<$:X11::X11> + $<$:Qt::GuiPrivate> +) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-scripting" "${CMAKE_BINARY_DIR}/shared/obs-scripting") @@ -77,12 +82,9 @@ if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in frontend-tools.rc) target_sources(frontend-tools PRIVATE frontend-tools.rc) - # cmake-format: off set_property(TARGET frontend-tools APPEND PROPERTY AUTORCC_OPTIONS --format-version 1) - # cmake-format: on endif() -# cmake-format: off set_target_properties_obs( frontend-tools PROPERTIES FOLDER frontend @@ -91,5 +93,5 @@ set_target_properties_obs( AUTOUIC ON AUTORCC ON AUTOUIC_SEARCH_PATHS forms - XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES) -# cmake-format: on + XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES +) diff --git a/UI/obs-frontend-api/CMakeLists.txt b/UI/obs-frontend-api/CMakeLists.txt index ba0df55e968bc0..6a160f937bfc23 100644 --- a/UI/obs-frontend-api/CMakeLists.txt +++ b/UI/obs-frontend-api/CMakeLists.txt @@ -18,12 +18,10 @@ elseif(OS_MACOS) set_target_properties(obs-frontend-api PROPERTIES SOVERSION 1) endif() -# cmake-format: off set_target_properties_obs(obs-frontend-api PROPERTIES FOLDER frontend PUBLIC_HEADER obs-frontend-api.h) -if (OS_WINDOWS OR OS_MACOS) +if(OS_WINDOWS OR OS_MACOS) set_property(TARGET obs-frontend-api PROPERTY PREFIX "") endif() -# cmake-format: on target_export(obs-frontend-api) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 4e49fcfa5cc3ba..c8809809e2e383 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -9,18 +9,20 @@ add_executable(updater WIN32) target_sources( updater - PRIVATE hash.cpp - helpers.cpp - helpers.hpp - http.cpp - init-hook-files.c - manifest.hpp - patch.cpp - resource.h - updater.cpp - updater.hpp - updater.manifest - updater.rc) + PRIVATE + hash.cpp + helpers.cpp + helpers.hpp + http.cpp + init-hook-files.c + manifest.hpp + patch.cpp + resource.h + updater.cpp + updater.hpp + updater.manifest + updater.rc +) target_compile_definitions(updater PRIVATE NOMINMAX "PSAPI_VERSION=2") @@ -28,20 +30,13 @@ target_include_directories(updater PRIVATE "${CMAKE_SOURCE_DIR}/libobs" "${CMAKE target_link_libraries( updater - PRIVATE OBS::blake2_static - nlohmann_json::nlohmann_json - zstd::libzstd_static - comctl32 - shell32 - version - winhttp - wintrust) + PRIVATE OBS::blake2_static nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32 version winhttp wintrust +) # zstd is hardcoded with /DEFAULTLIB:LIBCMT target_link_options(updater PRIVATE $<$:/NODEFAULTLIB:LIBCMT>) set_target_properties( updater - PROPERTIES FOLDER frontend - OUTPUT_NAME updater - MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + PROPERTIES FOLDER frontend OUTPUT_NAME updater MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/UI/xdg-data/CMakeLists.txt b/UI/xdg-data/CMakeLists.txt index b10b5d56d736b6..2f0eba758c7902 100644 --- a/UI/xdg-data/CMakeLists.txt +++ b/UI/xdg-data/CMakeLists.txt @@ -4,7 +4,8 @@ if(NOT DEFINED APPDATA_RELEASE_DATE) COMMAND git log --tags -1 --pretty=%cd --date=short OUTPUT_VARIABLE APPDATA_RELEASE_DATE WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) else() file(TIMESTAMP "${CMAKE_SOURCE_DIR}/CMakeLists.txt" APPDATA_RELEASE_DATE "%Y-%m-%d") endif() @@ -16,7 +17,8 @@ if(NOT DEFINED GIT_HASH) COMMAND git rev-parse HEAD OUTPUT_VARIABLE GIT_HASH WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) else() set(GIT_HASH "master") endif() @@ -24,27 +26,33 @@ endif() configure_file(com.obsproject.Studio.metainfo.xml.in com.obsproject.Studio.metainfo.xml) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo +) install(FILES com.obsproject.Studio.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) install( FILES icons/obs-logo-128.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps - RENAME com.obsproject.Studio.png) + RENAME com.obsproject.Studio.png +) install( FILES icons/obs-logo-256.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps - RENAME com.obsproject.Studio.png) + RENAME com.obsproject.Studio.png +) install( FILES icons/obs-logo-512.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps - RENAME com.obsproject.Studio.png) + RENAME com.obsproject.Studio.png +) install( FILES icons/obs-logo-scalable.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps - RENAME com.obsproject.Studio.svg) + RENAME com.obsproject.Studio.svg +) diff --git a/cmake/32bit/projects.cmake b/cmake/32bit/projects.cmake index e32f692c625bb1..54978c87fae5f8 100644 --- a/cmake/32bit/projects.cmake +++ b/cmake/32bit/projects.cmake @@ -1,10 +1,5 @@ # OBS CMake 32-bit slice module -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=E1121 -# cmake-format: on - include_guard(GLOBAL) include(compilerconfig) @@ -17,7 +12,6 @@ macro(legacy_check) return() endmacro() -# cmake-format: off # target_disable_feature: Stub macro for 32-bit projects macro(target_disable_feature) endmacro() @@ -25,12 +19,13 @@ endmacro() # target_disable: Stub macro for 32-bit projects macro(target_disable) endmacro() -# cmake-format: on # check_uuid: Helper function to check for valid UUID function(check_uuid uuid_string return_value) set(valid_uuid TRUE) + # gersemi: off set(uuid_token_lengths 8 4 4 4 12) + # gersemi: on set(token_num 0) string(REPLACE "-" ";" uuid_tokens ${uuid_string}) @@ -57,9 +52,7 @@ function(check_uuid uuid_string return_value) set(valid_uuid FALSE) endif() message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") - # cmake-format: off set(${return_value} ${valid_uuid} PARENT_SCOPE) - # cmake-format: on endfunction() if(OS_WINDOWS) diff --git a/cmake/common/bootstrap.cmake b/cmake/common/bootstrap.cmake index 5ee72282f127de..493ef313ecda9f 100644 --- a/cmake/common/bootstrap.cmake +++ b/cmake/common/bootstrap.cmake @@ -3,14 +3,40 @@ include_guard(GLOBAL) # Map fallback configurations for optimized build configurations -set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel None "") -set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel Release RelWithDebInfo None "") -set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel None "") +# gersemi: off +set( + CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO + RelWithDebInfo + Release + MinSizeRel + None + "" +) +set( + CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL + MinSizeRel + Release + RelWithDebInfo + None + "" +) +set( + CMAKE_MAP_IMPORTED_CONFIG_RELEASE + Release + RelWithDebInfo + MinSizeRel + None + "" +) +# gersemi: on # Prohibit in-source builds if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") - message(FATAL_ERROR "In-source builds of OBS are not supported. " - "Specify a build directory via 'cmake -S -B ' instead.") + message( + FATAL_ERROR + "In-source builds of OBS are not supported. " + "Specify a build directory via 'cmake -S -B ' instead." + ) file(REMOVE_RECURSE "${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt" "${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles") endif() @@ -41,10 +67,17 @@ include(osconfig) # Allow selection of common build types via UI if(NOT CMAKE_GENERATOR MATCHES "(Xcode|Visual Studio .+)") if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE - "RelWithDebInfo" - CACHE STRING "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel) + set( + CMAKE_BUILD_TYPE + "RelWithDebInfo" + CACHE STRING + "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" + FORCE + ) + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel + ) endif() endif() diff --git a/cmake/common/buildnumber.cmake b/cmake/common/buildnumber.cmake index ef60952bb7c9a2..dcbc22d917e415 100644 --- a/cmake/common/buildnumber.cmake +++ b/cmake/common/buildnumber.cmake @@ -3,9 +3,12 @@ include_guard(GLOBAL) # Define build number cache file -set(_BUILD_NUMBER_CACHE - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/.CMakeBuildNumber" - CACHE INTERNAL "OBS build number cache file") +set( + _BUILD_NUMBER_CACHE + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/.CMakeBuildNumber" + CACHE INTERNAL + "OBS build number cache file" +) # Read build number from cache file or manual override if(NOT DEFINED OBS_BUILD_NUMBER) diff --git a/cmake/common/buildspec_common.cmake b/cmake/common/buildspec_common.cmake index 158b769db4795c..5798efa6d9c3e3 100644 --- a/cmake/common/buildspec_common.cmake +++ b/cmake/common/buildspec_common.cmake @@ -1,26 +1,15 @@ # OBS common build dependencies module -# cmake-format: off -# cmake-lint: disable=E1121 -# cmake-lint: disable=E1126 -# cmake-lint: disable=R0912 -# cmake-lint: disable=R0915 -# cmake-format: on - include_guard(GLOBAL) # _check_deps_version: Checks for obs-deps VERSION file in prefix paths function(_check_deps_version version) - set(found - FALSE - PARENT_SCOPE) + set(found FALSE PARENT_SCOPE) foreach(path IN LISTS CMAKE_PREFIX_PATH) if(EXISTS "${path}/share/obs-deps/VERSION") if(dependency STREQUAL qt6 AND NOT EXISTS "${path}/lib/cmake/Qt6/Qt6Config.cmake") - # cmake-format: off set(found FALSE PARENT_SCOPE) - # cmake-format: on continue() endif() @@ -30,25 +19,25 @@ function(_check_deps_version version) string(REPLACE "-" "." version "${version}") if(_check_version VERSION_EQUAL version) - set(found - TRUE - PARENT_SCOPE) + set(found TRUE PARENT_SCOPE) break() elseif(_check_version VERSION_LESS version) - message(AUTHOR_WARNING "Older ${label} version detected in ${path}: \n" - "Found ${_check_version}, require ${version}") + message( + AUTHOR_WARNING + "Older ${label} version detected in ${path}: \n" + "Found ${_check_version}, require ${version}" + ) list(REMOVE_ITEM CMAKE_PREFIX_PATH "${path}") list(APPEND CMAKE_PREFIX_PATH "${path}") - set(CMAKE_PREFIX_PATH - ${CMAKE_PREFIX_PATH} - PARENT_SCOPE) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) continue() else() - message(AUTHOR_WARNING "Newer ${label} version detected in ${path}: \n" - "Found ${_check_version}, require ${version}") - set(found - TRUE - PARENT_SCOPE) + message( + AUTHOR_WARNING + "Newer ${label} version detected in ${path}: \n" + "Found ${_check_version}, require ${version}" + ) + set(found TRUE PARENT_SCOPE) break() endif() endif() @@ -75,9 +64,7 @@ function(_check_dependencies) string(JSON hash GET ${data} hashes ${platform}) string(JSON url GET ${data} baseUrl) string(JSON label GET ${data} label) - # cmake-format: off string(JSON revision ERROR_VARIABLE error GET ${data} revision ${platform}) - # cmake-format: on message(STATUS "Setting up ${label} (${arch})") @@ -96,8 +83,11 @@ function(_check_dependencies) endif() if(EXISTS "${dependencies_dir}/.dependency_${dependency}_${arch}.sha256") - file(READ "${dependencies_dir}/.dependency_${dependency}_${arch}.sha256" - OBS_DEPENDENCY_${dependency}_${arch}_HASH) + file( + READ + "${dependencies_dir}/.dependency_${dependency}_${arch}.sha256" + OBS_DEPENDENCY_${dependency}_${arch}_HASH + ) endif() set(skip FALSE) @@ -130,10 +120,7 @@ function(_check_dependencies) if(NOT EXISTS "${dependencies_dir}/${file}") message(STATUS "Downloading ${url}") - file( - DOWNLOAD "${url}" "${dependencies_dir}/${file}" - STATUS download_status - EXPECTED_HASH SHA256=${hash}) + file(DOWNLOAD "${url}" "${dependencies_dir}/${file}" STATUS download_status EXPECTED_HASH SHA256=${hash}) list(GET download_status 0 error_code) list(GET download_status 1 error_message) @@ -162,16 +149,12 @@ function(_check_dependencies) file(WRITE "${dependencies_dir}/.dependency_${dependency}_${arch}.sha256" "${hash}") if(dependency STREQUAL prebuilt) - set(VLC_PATH - "${dependencies_dir}/${destination}" - CACHE PATH "VLC source code directory" FORCE) + set(VLC_PATH "${dependencies_dir}/${destination}" CACHE PATH "VLC source code directory" FORCE) list(APPEND CMAKE_PREFIX_PATH "${dependencies_dir}/${destination}") elseif(dependency STREQUAL qt6) list(APPEND CMAKE_PREFIX_PATH "${dependencies_dir}/${destination}") elseif(dependency STREQUAL cef) - set(CEF_ROOT_DIR - "${dependencies_dir}/${destination}" - CACHE PATH "CEF root directory" FORCE) + set(CEF_ROOT_DIR "${dependencies_dir}/${destination}" CACHE PATH "CEF root directory" FORCE) endif() message(STATUS "Setting up ${label} (${arch}) - done") @@ -179,7 +162,5 @@ function(_check_dependencies) list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH) - set(CMAKE_PREFIX_PATH - ${CMAKE_PREFIX_PATH} - CACHE PATH "CMake prefix search path" FORCE) + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} CACHE PATH "CMake prefix search path" FORCE) endfunction() diff --git a/cmake/common/compiler_common.cmake b/cmake/common/compiler_common.cmake index ccae3d2201bdc8..731ecf6751dee9 100644 --- a/cmake/common/compiler_common.cmake +++ b/cmake/common/compiler_common.cmake @@ -21,62 +21,66 @@ set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) # clang options for C, C++, ObjC, and ObjC++ -set(_obs_clang_common_options - -fno-strict-aliasing - -Wno-trigraphs - -Wno-missing-field-initializers - -Wno-missing-prototypes - -Werror=return-type - -Wunreachable-code - -Wquoted-include-in-framework-header - -Wno-missing-braces - -Wparentheses - -Wswitch - -Wno-unused-function - -Wno-unused-label - -Wunused-parameter - -Wunused-variable - -Wunused-value - -Wempty-body - -Wuninitialized - -Wno-unknown-pragmas - -Wfour-char-constants - -Wconstant-conversion - -Wno-conversion - -Wint-conversion - -Wbool-conversion - -Wenum-conversion - -Wnon-literal-null-conversion - -Wsign-compare - -Wshorten-64-to-32 - -Wpointer-sign - -Wnewline-eof - -Wno-implicit-fallthrough - -Wdeprecated-declarations - -Wno-sign-conversion - -Winfinite-recursion - -Wcomma - -Wno-strict-prototypes - -Wno-semicolon-before-method-body - -Wformat-security - -Wvla - -Wno-error=shorten-64-to-32 - $<$:-Wno-error=deprecated-declarations>) +set( + _obs_clang_common_options + -fno-strict-aliasing + -Wno-trigraphs + -Wno-missing-field-initializers + -Wno-missing-prototypes + -Werror=return-type + -Wunreachable-code + -Wquoted-include-in-framework-header + -Wno-missing-braces + -Wparentheses + -Wswitch + -Wno-unused-function + -Wno-unused-label + -Wunused-parameter + -Wunused-variable + -Wunused-value + -Wempty-body + -Wuninitialized + -Wno-unknown-pragmas + -Wfour-char-constants + -Wconstant-conversion + -Wno-conversion + -Wint-conversion + -Wbool-conversion + -Wenum-conversion + -Wnon-literal-null-conversion + -Wsign-compare + -Wshorten-64-to-32 + -Wpointer-sign + -Wnewline-eof + -Wno-implicit-fallthrough + -Wdeprecated-declarations + -Wno-sign-conversion + -Winfinite-recursion + -Wcomma + -Wno-strict-prototypes + -Wno-semicolon-before-method-body + -Wformat-security + -Wvla + -Wno-error=shorten-64-to-32 + $<$:-Wno-error=deprecated-declarations> +) # clang options for C set(_obs_clang_c_options ${_obs_clang_common_options} -Wno-shadow -Wno-float-conversion) # clang options for C++ -set(_obs_clang_cxx_options - ${_obs_clang_common_options} - -Wno-non-virtual-dtor - -Wno-overloaded-virtual - -Wno-exit-time-destructors - -Wno-shadow - -Winvalid-offsetof - -Wmove - -Werror=block-capture-autoreleasing - -Wrange-loop-analysis) +set( + _obs_clang_cxx_options + ${_obs_clang_common_options} + -Wno-non-virtual-dtor + -Wno-overloaded-virtual + -Wno-exit-time-destructors + -Wno-shadow + -Winvalid-offsetof + -Wmove + -Werror=block-capture-autoreleasing + -Wrange-loop-analysis +) if(CMAKE_CXX_STANDARD GREATER_EQUAL 20) list(APPEND _obs_clang_cxx_options -fno-char8_t) diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index fb82e0b4add435..0f040b2f058eca 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -1,10 +1,5 @@ # OBS CMake common helper functions module -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=E1121 -# cmake-format: on - include_guard(GLOBAL) # message_configuration: Function to print configuration outcome @@ -22,14 +17,11 @@ function(message_configuration) " | (_) | |_) \\__ \\_____\\__ \\ |_| |_| | (_| | | (_) |\n" " \\___/|_.__/|___/ |___/\\__|\\__,_|\\__,_|_|\\___/ \n" "\nOBS: Application Version: ${OBS_VERSION} - Build Number: ${OBS_BUILD_NUMBER}\n" - "==================================================================================\n\n") + "==================================================================================\n\n" + ) get_property(OBS_FEATURES_ENABLED GLOBAL PROPERTY OBS_FEATURES_ENABLED) - list( - SORT OBS_FEATURES_ENABLED - COMPARE NATURAL - CASE SENSITIVE - ORDER ASCENDING) + list(SORT OBS_FEATURES_ENABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING) if(OBS_FEATURES_ENABLED) message(NOTICE "------------------------ Enabled Features ------------------------") @@ -39,11 +31,7 @@ function(message_configuration) endif() get_property(OBS_FEATURES_DISABLED GLOBAL PROPERTY OBS_FEATURES_DISABLED) - list( - SORT OBS_FEATURES_DISABLED - COMPARE NATURAL - CASE SENSITIVE - ORDER ASCENDING) + list(SORT OBS_FEATURES_DISABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING) if(OBS_FEATURES_DISABLED) message(NOTICE "------------------------ Disabled Features ------------------------") @@ -54,11 +42,7 @@ function(message_configuration) if(ENABLE_PLUGINS) get_property(OBS_MODULES_ENABLED GLOBAL PROPERTY OBS_MODULES_ENABLED) - list( - SORT OBS_MODULES_ENABLED - COMPARE NATURAL - CASE SENSITIVE - ORDER ASCENDING) + list(SORT OBS_MODULES_ENABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING) if(OBS_MODULES_ENABLED) message(NOTICE "------------------------ Enabled Modules ------------------------") @@ -68,11 +52,7 @@ function(message_configuration) endif() get_property(OBS_MODULES_DISABLED GLOBAL PROPERTY OBS_MODULES_DISABLED) - list( - SORT OBS_MODULES_DISABLED - COMPARE NATURAL - CASE SENSITIVE - ORDER ASCENDING) + list(SORT OBS_MODULES_DISABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING) if(OBS_MODULES_DISABLED) message(NOTICE "------------------------ Disabled Modules ------------------------") @@ -167,9 +147,7 @@ function(_handle_generator_expression_dependency library) endif() if(CMAKE_VERSION VERSION_LESS 3.25) - set(${var_FOUND_VAR} - ${var_FOUND_VAR} - PARENT_SCOPE) + set(${var_FOUND_VAR} ${var_FOUND_VAR} PARENT_SCOPE) else() return(PROPAGATE ${var_FOUND_VAR}) endif() @@ -225,18 +203,14 @@ function(find_dependencies) endforeach() if(NOT is_root) - # cmake-format: off set(found_libraries ${found_libraries} PARENT_SCOPE) - # cmake-format: on # Exit recursive branch return() endif() list(REMOVE_DUPLICATES found_libraries) list(APPEND ${var_FOUND_VAR} ${found_libraries}) - # cmake-format: off set(${var_FOUND_VAR} ${${var_FOUND_VAR}} PARENT_SCOPE) - # cmake-format: on endfunction() # find_qt_plugins: Find and add Qt plugin libraries associated with Qt component to target @@ -252,9 +226,15 @@ function(find_qt_plugins) message(FATAL_ERROR "'find_qt_plugins' has to be called with a valid target from the Qt or Qt6 namespace.") endif() - # cmake-format: off - list(APPEND qt_plugins_Core platforms printsupport styles imageformats iconengines) - # cmake-format: on + list( + APPEND + qt_plugins_Core + platforms + printsupport + styles + imageformats + iconengines + ) list(APPEND qt_plugins_Gui platforminputcontexts) list(APPEND qt_plugins_Sql sqldrivers) list(APPEND qt_plugins_3dRender sceneparsers geometryloaders) @@ -285,7 +265,9 @@ function(find_qt_plugins) file( GLOB plugin_libraries RELATIVE "${plugins_location}/${plugin}" - "${plugins_location}/${plugin}/*.dylib" "${plugins_location}/${plugin}/*.dll") + "${plugins_location}/${plugin}/*.dylib" + "${plugins_location}/${plugin}/*.dll" + ) message(DEBUG "Found Qt plugin ${plugin} libraries: ${plugin_libraries}") foreach(plugin_library IN ITEMS ${plugin_libraries}) set(plugin_full_path "${plugins_location}/${plugin}/${plugin_library}") @@ -296,9 +278,7 @@ function(find_qt_plugins) endforeach() endif() - # cmake-format: off set(${var_FOUND_VAR} ${plugins_list} PARENT_SCOPE) - # cmake-format: on endfunction() # target_export: Helper function to export target as CMake package @@ -319,24 +299,13 @@ function(target_export target) install( TARGETS ${target} EXPORT ${target}Targets - RUNTIME DESTINATION "${OBS_EXECUTABLE_DESTINATION}" - COMPONENT Development - ${exclude_variant} - LIBRARY DESTINATION "${OBS_LIBRARY_DESTINATION}" - COMPONENT Development - ${exclude_variant} - ARCHIVE DESTINATION "${OBS_LIBRARY_DESTINATION}" - COMPONENT Development - ${exclude_variant} - FRAMEWORK DESTINATION Frameworks - COMPONENT Development - ${exclude_variant} - INCLUDES - DESTINATION "${include_destination}" - PUBLIC_HEADER - DESTINATION "${include_destination}" - COMPONENT Development - ${exclude_variant}) + RUNTIME DESTINATION "${OBS_EXECUTABLE_DESTINATION}" COMPONENT Development ${exclude_variant} + LIBRARY DESTINATION "${OBS_LIBRARY_DESTINATION}" COMPONENT Development ${exclude_variant} + ARCHIVE DESTINATION "${OBS_LIBRARY_DESTINATION}" COMPONENT Development ${exclude_variant} + FRAMEWORK DESTINATION Frameworks COMPONENT Development ${exclude_variant} + INCLUDES DESTINATION "${include_destination}" + PUBLIC_HEADER DESTINATION "${include_destination}" COMPONENT Development ${exclude_variant} + ) get_target_property(obs_public_headers ${target} OBS_PUBLIC_HEADERS) @@ -358,15 +327,12 @@ function(target_export target) FILES ${headers_${header_dir}} DESTINATION "${include_destination}/${header_dir}" COMPONENT Development - ${exclude_variant}) + ${exclude_variant} + ) endforeach() if(headers) - install( - FILES ${headers} - DESTINATION "${include_destination}" - COMPONENT Development - ${exclude_variant}) + install(FILES ${headers} DESTINATION "${include_destination}" COMPONENT Development ${exclude_variant}) endif() endif() @@ -375,7 +341,8 @@ function(target_export target) FILES "${CMAKE_BINARY_DIR}/config/obsconfig.h" DESTINATION "${include_destination}" COMPONENT Development - ${exclude_variant}) + ${exclude_variant} + ) endif() get_target_property(target_type ${target} TYPE) @@ -386,30 +353,29 @@ function(target_export target) generate_export_header(${target} EXPORT_FILE_NAME "${target}_EXPORT.h") target_sources(${target} PUBLIC $) - set_property( - TARGET ${target} - APPEND - PROPERTY PUBLIC_HEADER "${target}_EXPORT.h") + set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER "${target}_EXPORT.h") endif() set(TARGETS_EXPORT_NAME ${target}Targets) message( DEBUG - "Generating CMake package configuration file ${target}Config.cmake with targets file ${TARGETS_EXPORT_NAME}...") + "Generating CMake package configuration file ${target}Config.cmake with targets file ${TARGETS_EXPORT_NAME}..." + ) include(CMakePackageConfigHelpers) - configure_package_config_file(cmake/${target}Config.cmake.in ${target}Config.cmake - INSTALL_DESTINATION "${package_destination}") + configure_package_config_file( + cmake/${target}Config.cmake.in + ${target}Config.cmake + INSTALL_DESTINATION "${package_destination}" + ) message(DEBUG "Generating CMake package version configuration file ${target}ConfigVersion.cmake...") write_basic_package_version_file( "${target}ConfigVersion.cmake" VERSION ${OBS_VERSION_CANONICAL} - COMPATIBILITY SameMajorVersion) + COMPATIBILITY SameMajorVersion + ) - export( - EXPORT ${target}Targets - FILE "${TARGETS_EXPORT_NAME}.cmake" - NAMESPACE OBS::) + export(EXPORT ${target}Targets FILE "${TARGETS_EXPORT_NAME}.cmake" NAMESPACE OBS::) export(PACKAGE ${target}) @@ -419,19 +385,23 @@ function(target_export target) NAMESPACE OBS:: DESTINATION "${package_destination}" COMPONENT Development - ${exclude_variant}) + ${exclude_variant} + ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake" DESTINATION "${package_destination}" COMPONENT Development - ${exclude_variant}) + ${exclude_variant} + ) endfunction() # check_uuid: Helper function to check for valid UUID function(check_uuid uuid_string return_value) set(valid_uuid TRUE) + # gersemi: off set(uuid_token_lengths 8 4 4 4 12) + # gersemi: on set(token_num 0) string(REPLACE "-" ";" uuid_tokens ${uuid_string}) @@ -458,9 +428,7 @@ function(check_uuid uuid_string return_value) set(valid_uuid FALSE) endif() message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") - # cmake-format: off set(${return_value} ${valid_uuid} PARENT_SCOPE) - # cmake-format: on endfunction() # legacy_check: Check if new CMake framework was not enabled and load legacy rules instead diff --git a/cmake/common/versionconfig.cmake b/cmake/common/versionconfig.cmake index cdd8f1ad9289b9..7a1e753125eeec 100644 --- a/cmake/common/versionconfig.cmake +++ b/cmake/common/versionconfig.cmake @@ -13,7 +13,8 @@ if(NOT DEFINED OBS_VERSION_OVERRIDE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git ERROR_VARIABLE _git_describe_err WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE _obs_version_result - OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_STRIP_TRAILING_WHITESPACE + ) if(_git_describe_err) message(FATAL_ERROR "Could not fetch OBS version tag from git.\n" ${_git_describe_err}) @@ -24,8 +25,13 @@ if(NOT DEFINED OBS_VERSION_OVERRIDE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git endif() elseif(DEFINED OBS_VERSION_OVERRIDE) if(OBS_VERSION_OVERRIDE MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+).*") - string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+).*" "\\1;\\2;\\3" _obs_version_canonical - ${OBS_VERSION_OVERRIDE}) + string( + REGEX REPLACE + "([0-9]+)\\.([0-9]+)\\.([0-9]+).*" + "\\1;\\2;\\3" + _obs_version_canonical + ${OBS_VERSION_OVERRIDE} + ) set(_obs_version ${OBS_VERSION_OVERRIDE}) else() message(FATAL_ERROR "Invalid version supplied - must be ..[-(rc|beta)].") @@ -52,15 +58,17 @@ string(REPLACE ";" "." OBS_VERSION "${_obs_version}") if(OBS_RELEASE_CANDIDATE GREATER 0) message( AUTHOR_WARNING - "******************************************************************************\n" - " + OBS-Studio - Release candidate detected, OBS_VERSION is now: ${OBS_VERSION}\n" - "******************************************************************************") + "******************************************************************************\n" + " + OBS-Studio - Release candidate detected, OBS_VERSION is now: ${OBS_VERSION}\n" + "******************************************************************************" + ) elseif(OBS_BETA GREATER 0) message( AUTHOR_WARNING - "******************************************************************************\n" - " + OBS-Studio - Beta detected, OBS_VERSION is now: ${OBS_VERSION}\n" - "******************************************************************************") + "******************************************************************************\n" + " + OBS-Studio - Beta detected, OBS_VERSION is now: ${OBS_VERSION}\n" + "******************************************************************************" + ) endif() unset(_obs_default_version) diff --git a/cmake/finders/FindAMF.cmake b/cmake/finders/FindAMF.cmake index 18cd4c10d5ad22..0a24f40406efef 100644 --- a/cmake/finders/FindAMF.cmake +++ b/cmake/finders/FindAMF.cmake @@ -31,22 +31,17 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) -find_path( - AMF_INCLUDE_DIR - NAMES AMF/core/Factory.h - PATHS /usr/include /usr/local/include - DOC "AMF include directory") +find_path(AMF_INCLUDE_DIR NAMES AMF/core/Factory.h PATHS /usr/include /usr/local/include DOC "AMF include directory") if(EXISTS "${AMF_INCLUDE_DIR}/AMF/core/Version.h") - file(STRINGS "${AMF_INCLUDE_DIR}/AMF/core/Version.h" _version_string - REGEX "^.*VERSION_(MAJOR|MINOR|RELEASE|BUILD_NUM)[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${AMF_INCLUDE_DIR}/AMF/core/Version.h" + _version_string + REGEX "^.*VERSION_(MAJOR|MINOR|RELEASE|BUILD_NUM)[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") @@ -72,7 +67,9 @@ endif() find_package_handle_standard_args( AMF REQUIRED_VARS AMF_INCLUDE_DIR - VERSION_VAR AMF_VERSION REASON_FAILURE_MESSAGE "${AMF_ERROR_REASON}") + VERSION_VAR AMF_VERSION + REASON_FAILURE_MESSAGE "${AMF_ERROR_REASON}" +) mark_as_advanced(AMF_INCLUDE_DIR) unset(AMF_ERROR_REASON) @@ -85,8 +82,9 @@ endif() include(FeatureSummary) set_package_properties( - AMF PROPERTIES - URL "https://github.com/GPUOpen-LibrariesAndSDKs/AMF" - DESCRIPTION - "AMF is a light-weight, portable multimedia framework that abstracts away most of the platform and API-specific details and allows for easy implementation of multimedia applications using a variety of technologies, such as DirectX 11, OpenGL, and OpenCL and facilitates an efficient interop between them." + AMF + PROPERTIES + URL "https://github.com/GPUOpen-LibrariesAndSDKs/AMF" + DESCRIPTION + "AMF is a light-weight, portable multimedia framework that abstracts away most of the platform and API-specific details and allows for easy implementation of multimedia applications using a variety of technologies, such as DirectX 11, OpenGL, and OpenCL and facilitates an efficient interop between them." ) diff --git a/cmake/finders/FindAsio.cmake b/cmake/finders/FindAsio.cmake index 355ce09d4d2d33..6cdae9bb3c854e 100644 --- a/cmake/finders/FindAsio.cmake +++ b/cmake/finders/FindAsio.cmake @@ -31,11 +31,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -48,16 +43,26 @@ find_path( NAMES asio.hpp HINTS ${PC_Asio_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Asio include directory") + DOC "Asio include directory" +) if(PC_Asio_VERSION VERSION_GREATER 0) set(Asio_VERSION ${PC_Asio_VERSION}) elseif(EXISTS "${Asio_INCLUDE_DIR}/asio/version.hpp") - file(STRINGS "${Asio_INCLUDE_DIR}/asio/version.hpp" _version_string - REGEX "#define[ \t]+ASIO_VERSION[ \t]+[0-9]+[ \t]+\\/\\/[ \t][0-9]+\\.[0-9]+\\.[0-9]+") - - string(REGEX REPLACE "#define[ \t]+ASIO_VERSION[ \t]+[0-9]+[ \t]+\\/\\/[ \t]([0-9]+\\.[0-9]+\\.[0-9]+)" "\\1" - Asio_VERSION "${_version_string}") + file( + STRINGS + "${Asio_INCLUDE_DIR}/asio/version.hpp" + _version_string + REGEX "#define[ \t]+ASIO_VERSION[ \t]+[0-9]+[ \t]+\\/\\/[ \t][0-9]+\\.[0-9]+\\.[0-9]+" + ) + + string( + REGEX REPLACE + "#define[ \t]+ASIO_VERSION[ \t]+[0-9]+[ \t]+\\/\\/[ \t]([0-9]+\\.[0-9]+\\.[0-9]+)" + "\\1" + Asio_VERSION + "${_version_string}" + ) else() if(NOT Asio_FIND_QUIETLY) message(AUTHOR_WARNING "Failed to find Asio version.") @@ -74,7 +79,9 @@ endif() find_package_handle_standard_args( Asio REQUIRED_VARS Asio_INCLUDE_DIR - VERSION_VAR Asio_VERSION REASON_FAILURE_MESSAGE "${Asio_ERROR_REASON}") + VERSION_VAR Asio_VERSION + REASON_FAILURE_MESSAGE "${Asio_ERROR_REASON}" +) mark_as_advanced(Asio_INCLUDE_DIR) unset(Asio_ERROR_REASON) @@ -87,8 +94,9 @@ endif() include(FeatureSummary) set_package_properties( - Asio PROPERTIES - URL "http://think-async.com/Asio" - DESCRIPTION - "Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach." + Asio + PROPERTIES + URL "http://think-async.com/Asio" + DESCRIPTION + "Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach." ) diff --git a/cmake/finders/FindCEF.cmake b/cmake/finders/FindCEF.cmake index a644ec2e9ffdbd..2366f0b2d64666 100644 --- a/cmake/finders/FindCEF.cmake +++ b/cmake/finders/FindCEF.cmake @@ -48,34 +48,34 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0301 -# cmake-lint: disable=C0307 -# cmake-format: on - include(FindPackageHandleStandardArgs) -set(CEF_ROOT_DIR - "" - CACHE PATH "Alternative path to Chromium Embedded Framework") +set(CEF_ROOT_DIR "" CACHE PATH "Alternative path to Chromium Embedded Framework") if(NOT DEFINED CEF_ROOT_DIR OR CEF_ROOT_DIR STREQUAL "") message( FATAL_ERROR - "CEF_ROOT_DIR is not set - if ENABLE_BROWSER is enabled, " - "a CEF distribution with compiled wrapper library is required.\n" - "Please download a CEF distribution for your appropriate architecture " - "and specify CEF_ROOT_DIR to its location") + "CEF_ROOT_DIR is not set - if ENABLE_BROWSER is enabled, " + "a CEF distribution with compiled wrapper library is required.\n" + "Please download a CEF distribution for your appropriate architecture " + "and specify CEF_ROOT_DIR to its location" + ) endif() find_path( - CEF_INCLUDE_DIR "cef_version.h" + CEF_INCLUDE_DIR + "cef_version.h" HINTS "${CEF_ROOT_DIR}/include" - DOC "Chromium Embedded Framework include directory.") + DOC "Chromium Embedded Framework include directory." +) if(CEF_INCLUDE_DIR) - file(STRINGS "${CEF_INCLUDE_DIR}/cef_version.h" _VERSION_STRING - REGEX "^.*CEF_VERSION_(MAJOR|MINOR|PATCH)[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${CEF_INCLUDE_DIR}/cef_version.h" + _VERSION_STRING + REGEX "^.*CEF_VERSION_(MAJOR|MINOR|PATCH)[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*CEF_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" VERSION_MAJOR "${_VERSION_STRING}") string(REGEX REPLACE ".*CEF_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" VERSION_MINOR "${_VERSION_STRING}") string(REGEX REPLACE ".*CEF_VERSION_PATCH[ \t]+([0-9]+).*" "\\1" VERSION_PATCH "${_VERSION_STRING}") @@ -93,14 +93,16 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) NAMES cef.lib libcef.lib NO_DEFAULT_PATH PATHS "${CEF_ROOT_DIR}" "${CEF_ROOT_DIR}/Release" - DOC "Chromium Embedded Framework import library location") + DOC "Chromium Embedded Framework import library location" + ) find_program( CEF_LIBRARY_RELEASE NAMES cef.dll libcef.dll NO_DEFAULT_PATH PATHS "${CEF_ROOT_DIR}" "${CEF_ROOT_DIR}/Release" - DOC "Chromium Embedded Framework library location") + DOC "Chromium Embedded Framework library location" + ) if(NOT CEF_LIBRARY_RELEASE) set(CEF_LIBRARY_RELEASE "${CEF_IMPLIB_RELEASE}") @@ -110,53 +112,68 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) CEF_LIBRARY_WRAPPER_RELEASE NAMES cef_dll_wrapper libcef_dll_wrapper NO_DEFAULT_PATH - PATHS "${CEF_ROOT_DIR}/build/libcef_dll/Release" "${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release" - "${CEF_ROOT_DIR}/build/libcef_dll" "${CEF_ROOT_DIR}/build/libcef_dll_wrapper" - DOC "Chromium Embedded Framework static library wrapper.") + PATHS + "${CEF_ROOT_DIR}/build/libcef_dll/Release" + "${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release" + "${CEF_ROOT_DIR}/build/libcef_dll" + "${CEF_ROOT_DIR}/build/libcef_dll_wrapper" + DOC "Chromium Embedded Framework static library wrapper." + ) find_library( CEF_LIBRARY_WRAPPER_DEBUG NAMES cef_dll_wrapper libcef_dll_wrapper NO_DEFAULT_PATH PATHS "${CEF_ROOT_DIR}/build/libcef_dll/Debug" "${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Debug" - DOC "Chromium Embedded Framework static library wrapper (debug).") + DOC "Chromium Embedded Framework static library wrapper (debug)." + ) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) find_library( CEF_LIBRARY_RELEASE NAMES "Chromium Embedded Framework" NO_DEFAULT_PATH PATHS "${CEF_ROOT_DIR}" "${CEF_ROOT_DIR}/Release" - DOC "Chromium Embedded Framework") + DOC "Chromium Embedded Framework" + ) find_library( CEF_LIBRARY_WRAPPER_RELEASE NAMES cef_dll_wrapper libcef_dll_wrapper NO_DEFAULT_PATH - PATHS "${CEF_ROOT_DIR}/build/libcef_dll/Release" "${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release" - "${CEF_ROOT_DIR}/build/libcef_dll" "${CEF_ROOT_DIR}/build/libcef_dll_wrapper" - DOC "Chromium Embedded Framework static library wrapper.") + PATHS + "${CEF_ROOT_DIR}/build/libcef_dll/Release" + "${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release" + "${CEF_ROOT_DIR}/build/libcef_dll" + "${CEF_ROOT_DIR}/build/libcef_dll_wrapper" + DOC "Chromium Embedded Framework static library wrapper." + ) find_library( CEF_LIBRARY_WRAPPER_DEBUG NAMES cef_dll_wrapper libcef_dll_wrapper NO_DEFAULT_PATH PATHS "${CEF_ROOT_DIR}/build/libcef_dll/Debug" "${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Debug" - DOC "Chromium Embedded Framework static library wrapper (debug).") + DOC "Chromium Embedded Framework static library wrapper (debug)." + ) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) find_library( CEF_LIBRARY_RELEASE NAMES libcef.so NO_DEFAULT_PATH PATHS "${CEF_ROOT_DIR}" "${CEF_ROOT_DIR}/Release" - DOC "Chromium Embedded Framework") + DOC "Chromium Embedded Framework" + ) find_library( CEF_LIBRARY_WRAPPER_RELEASE NAMES cef_dll_wrapper.a libcef_dll_wrapper.a NO_DEFAULT_PATH - PATHS "${CEF_ROOT_DIR}/libcef_dll_wrapper" "${CEF_ROOT_DIR}/build/libcef_dll" - "${CEF_ROOT_DIR}/build/libcef_dll_wrapper" - DOC "Chromium Embedded Framework static library wrapper.") + PATHS + "${CEF_ROOT_DIR}/libcef_dll_wrapper" + "${CEF_ROOT_DIR}/build/libcef_dll" + "${CEF_ROOT_DIR}/build/libcef_dll_wrapper" + DOC "Chromium Embedded Framework static library wrapper." + ) endif() include(SelectLibraryConfigurations) @@ -165,8 +182,9 @@ select_library_configurations(CEF) find_package_handle_standard_args( CEF REQUIRED_VARS CEF_LIBRARY_RELEASE CEF_LIBRARY_WRAPPER_RELEASE CEF_INCLUDE_DIR - VERSION_VAR CEF_VERSION REASON_FAILURE_MESSAGE - "Ensure that location of pre-compiled Chromium Embedded Framework is set as CEF_ROOT_DIR.") + VERSION_VAR CEF_VERSION + REASON_FAILURE_MESSAGE "Ensure that location of pre-compiled Chromium Embedded Framework is set as CEF_ROOT_DIR." +) mark_as_advanced(CEF_LIBRARY CEF_LIBRARY_WRAPPER_RELEASE CEF_LIBRARY_WRAPPER_DEBUG CEF_INCLUDE_DIR) if(NOT TARGET CEF::Wrapper) @@ -177,10 +195,7 @@ if(NOT TARGET CEF::Wrapper) add_library(CEF::Wrapper INTERFACE IMPORTED) set_property(TARGET CEF::Wrapper PROPERTY IMPORTED_LIBNAME_RELEASE "${CEF_LIBRARY_WRAPPER_RELEASE}") endif() - set_property( - TARGET CEF::Wrapper - APPEND - PROPERTY IMPORTED_CONFIGURATIONS "Release") + set_property(TARGET CEF::Wrapper APPEND PROPERTY IMPORTED_CONFIGURATIONS "Release") if(CEF_LIBRARY_WRAPPER_DEBUG) if(IS_ABSOLUTE "${CEF_LIBRARY_WRAPPER_DEBUG}") @@ -188,16 +203,10 @@ if(NOT TARGET CEF::Wrapper) else() set_property(TARGET CEF::Wrapper PROPERTY IMPORTED_LIBNAME_DEBUG "${CEF_LIBRARY_WRAPPER_DEBUG}") endif() - set_property( - TARGET CEF::Wrapper - APPEND - PROPERTY IMPORTED_CONFIGURATIONS "Debug") + set_property(TARGET CEF::Wrapper APPEND PROPERTY IMPORTED_CONFIGURATIONS "Debug") endif() - set_property( - TARGET CEF::Wrapper - APPEND - PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CEF_INCLUDE_DIR}" "${CEF_ROOT_DIR}") + set_property(TARGET CEF::Wrapper APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CEF_INCLUDE_DIR}" "${CEF_ROOT_DIR}") endif() if(NOT TARGET CEF::Library) @@ -218,17 +227,15 @@ if(NOT TARGET CEF::Library) set_property(TARGET CEF::Library PROPERTY IMPORTED_LIBNAME_RELEASE "${CEF_LIBRARY_RELEASE}") endif() - set_property( - TARGET CEF::Library - APPEND - PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CEF_INCLUDE_DIR}" "${CEF_ROOT_DIR}") + set_property(TARGET CEF::Library APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CEF_INCLUDE_DIR}" "${CEF_ROOT_DIR}") set_property(TARGET CEF::Library PROPERTY IMPORTED_CONFIGURATIONS "Release") endif() include(FeatureSummary) set_package_properties( - CEF PROPERTIES - URL "https://bitbucket.org/chromiumembedded/cef/" - DESCRIPTION - "Chromium Embedded Framework (CEF). A simple framework for embedding Chromium-based browsers in other applications." + CEF + PROPERTIES + URL "https://bitbucket.org/chromiumembedded/cef/" + DESCRIPTION + "Chromium Embedded Framework (CEF). A simple framework for embedding Chromium-based browsers in other applications." ) diff --git a/cmake/finders/FindDetours.cmake b/cmake/finders/FindDetours.cmake index 81c804fe3c918a..8a7070bd0a57e2 100644 --- a/cmake/finders/FindDetours.cmake +++ b/cmake/finders/FindDetours.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# bmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -47,26 +43,14 @@ if(PKG_CONFIG_FOUND) pkg_check_modules(PC_Detours QUIET detours) endif() -find_path( - Detours_INCLUDE_DIR - NAMES detours.h - HINTS ${PC_Detours_INCLUDE_DIRS} - DOC "Detours include directory") +find_path(Detours_INCLUDE_DIR NAMES detours.h HINTS ${PC_Detours_INCLUDE_DIRS} DOC "Detours include directory") -find_library( - Detours_IMPLIB - NAMES detours - HINTS ${PC_Detours_LIBRARY_DIRS} - DOC "Detours location") +find_library(Detours_IMPLIB NAMES detours HINTS ${PC_Detours_LIBRARY_DIRS} DOC "Detours location") cmake_path(GET Detours_IMPLIB PARENT_PATH _implib_path) cmake_path(SET _bin_path NORMALIZE "${_implib_path}/../bin") -find_program( - Detours_LIBRARY - NAMES detours.dll - HINTS ${_implib_path} ${_bin_path} - DOC "Detours DLL location") +find_program(Detours_LIBRARY NAMES detours.dll HINTS ${_implib_path} ${_bin_path} DOC "Detours DLL location") if(NOT Detours_LIBRARY) set(Detours_LIBRARY "${Detours_IMPLIB}") @@ -86,7 +70,9 @@ endif() find_package_handle_standard_args( Detours REQUIRED_VARS Detours_LIBRARY Detours_INCLUDE_DIR - VERSION_VAR Detours_VERSION REASON_FAILURE_MESSAGE "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") + VERSION_VAR Detours_VERSION + REASON_FAILURE_MESSAGE "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH." +) mark_as_advanced(Detours_INCLUDE_DIR Detours_LIBRARY) if(Detours_FOUND) @@ -111,14 +97,18 @@ if(Detours_FOUND) set_target_properties( Detours::Detours - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Detours_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Detours_INCLUDE_DIR}" - VERSION ${Detours_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Detours_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Detours_INCLUDE_DIR}" + VERSION ${Detours_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Detours PROPERTIES - URL "https://github.com/microsoft/detours" - DESCRIPTION "Detours is a software package for monitoring and instrumenting API calls on Windows.") + Detours + PROPERTIES + URL "https://github.com/microsoft/detours" + DESCRIPTION "Detours is a software package for monitoring and instrumenting API calls on Windows." +) diff --git a/cmake/finders/FindFFmpeg.cmake b/cmake/finders/FindFFmpeg.cmake index e4e746915971f9..47521774860166 100644 --- a/cmake/finders/FindFFmpeg.cmake +++ b/cmake/finders/FindFFmpeg.cmake @@ -100,16 +100,18 @@ The following cache variables may also be set: include(FindPackageHandleStandardArgs) -set(_DEFAULT_COMPONENTS - avcodec - avdevice - avformat - avfilter - avresample - avutil - postproc - swscale - swresample) +set( + _DEFAULT_COMPONENTS + avcodec + avdevice + avformat + avfilter + avresample + avutil + postproc + swscale + swresample +) set(component_avcodec libavcodec avcodec avcodec.h) set(component_avdevice libavdevice avdevice avdevice.h) @@ -143,7 +145,8 @@ macro(FFmpeg_find_component component) NAMES ${component_libname}/${component_header} ${component_libname}/version.h HINTS ${PC_FFmpeg_${component}_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "FFmpeg component ${component_name} include directory") + DOC "FFmpeg component ${component_name} include directory" + ) ffmpeg_check_version() @@ -151,7 +154,8 @@ macro(FFmpeg_find_component component) find_library( FFmpeg_${component}_IMPLIB NAMES ${component_libname} ${component_name} - DOC "FFmpeg component ${component_name} import library location") + DOC "FFmpeg component ${component_name} import library location" + ) ffmpeg_find_dll() else() @@ -160,7 +164,8 @@ macro(FFmpeg_find_component component) NAMES ${component_libname} ${component_name} HINTS ${PC_FFmpeg_${component}_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "FFmpeg component ${component_name} location") + DOC "FFmpeg component ${component_name} location" + ) endif() if(FFmpeg_${component}_LIBRARY AND FFmpeg_${component}_INCLUDE_DIR) @@ -183,7 +188,8 @@ macro(FFmpeg_find_dll) FFmpeg_${component}_LIBRARY NAMES ${component_name}-${_dll_version}.dll HINTS ${_implib_path} ${_bin_path} - DOC "FFmpeg component ${component_name} DLL location") + DOC "FFmpeg component ${component_name} DLL location" + ) if(NOT FFmpeg_${component}_LIBRARY) set(FFmpeg_${component}_LIBRARY "${FFmpeg_${component}_IMPLIB}") @@ -200,17 +206,29 @@ macro(FFmpeg_check_version) set(FFmpeg_${component}_VERSION ${PC_FFmpeg_${component}_VERSION}) elseif(EXISTS "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h") if(EXISTS "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version_major.h") - file(STRINGS "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version_major.h" _version_string - REGEX "^.*VERSION_MAJOR[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version_major.h" + _version_string + REGEX "^.*VERSION_MAJOR[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") - file(STRINGS "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h" _version_string - REGEX "^.*VERSION_(MINOR|MICRO)[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h" + _version_string + REGEX "^.*VERSION_(MINOR|MICRO)[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") string(REGEX REPLACE ".*VERSION_MICRO[ \t]+([0-9]+).*" "\\1" _version_patch "${_version_string}") else() - file(STRINGS "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h" _version_string - REGEX "^.*VERSION_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h" + _version_string + REGEX "^.*VERSION_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") string(REGEX REPLACE ".*VERSION_MICRO[ \t]+([0-9]+).*" "\\1" _version_patch "${_version_string}") @@ -234,7 +252,8 @@ macro(FFmpeg_set_soname) execute_process( COMMAND sh -c "otool -D '${FFmpeg_${component}_LIBRARY}' | grep -v '${FFmpeg_${component}_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_SONAME "${_output}") @@ -243,7 +262,8 @@ macro(FFmpeg_set_soname) execute_process( COMMAND sh -c "objdump -p '${FFmpeg_${component}_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -276,8 +296,12 @@ if(NOT FFmpeg_avutil_FOUND) endif() if(EXISTS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h") - file(STRINGS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h" _version_string - REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\~.-]+\"[ \t]*$") + file( + STRINGS + "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h" + _version_string + REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\~.-]+\"[ \t]*$" + ) string(REGEX REPLACE ".*FFMPEG_VERSION[ \t]+\"n?([0-9]+\\.[0-9]).*\".*" "\\1" FFmpeg_VERSION "${_version_string}") endif() @@ -295,7 +319,9 @@ find_package_handle_standard_args( FFmpeg REQUIRED_VARS FFmpeg_LIBRARIES FFmpeg_INCLUDE_DIRS VERSION_VAR FFmpeg_VERSION - HANDLE_COMPONENTS REASON_FAILURE_MESSAGE "${FFmpeg_ERROR_REASON}") + HANDLE_COMPONENTS + REASON_FAILURE_MESSAGE "${FFmpeg_ERROR_REASON}" +) if(FFmpeg_FOUND AND NOT TARGET FFmpeg::FFmpeg) add_library(FFmpeg::FFmpeg INTERFACE IMPORTED) @@ -317,29 +343,29 @@ foreach(component IN LISTS FFmpeg_FIND_COMPONENTS) endif() set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_LOCATION "${FFmpeg_${component}_LIBRARY}") - else() add_library(FFmpeg::${component} INTERFACE IMPORTED) set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_LIBNAME "${FFmpeg_${component}_LIBRARY}") endif() set_target_properties( FFmpeg::${component} - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_FFmpeg_${component}_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_${component}_INCLUDE_DIR}" - VERSION ${FFmpeg_${component}_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_FFmpeg_${component}_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${FFmpeg_${component}_INCLUDE_DIR}" + VERSION ${FFmpeg_${component}_VERSION} + ) get_target_property(_ffmpeg_interface_libraries FFmpeg::FFmpeg INTERFACE_LINK_LIBRARIES) if(NOT FFmpeg::${component} IN_LIST _ffmpeg_interface_libraries) - set_property( - TARGET FFmpeg::FFmpeg - APPEND - PROPERTY INTERFACE_LINK_LIBRARIES FFmpeg::${component}) + set_property(TARGET FFmpeg::FFmpeg APPEND PROPERTY INTERFACE_LINK_LIBRARIES FFmpeg::${component}) endif() endif() endforeach() include(FeatureSummary) set_package_properties( - FFmpeg PROPERTIES - URL "https://www.ffmpeg.org" - DESCRIPTION "A complete, cross-platform solution to record, convert and stream audio and video.") + FFmpeg + PROPERTIES + URL "https://www.ffmpeg.org" + DESCRIPTION "A complete, cross-platform solution to record, convert and stream audio and video." +) diff --git a/cmake/finders/FindFFnvcodec.cmake b/cmake/finders/FindFFnvcodec.cmake index 5a5c200971eaa3..3d6564f5857278 100644 --- a/cmake/finders/FindFFnvcodec.cmake +++ b/cmake/finders/FindFFnvcodec.cmake @@ -31,11 +31,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -48,13 +43,18 @@ find_path( NAMES ffnvcodec/nvEncodeAPI.h HINTS ${PC_FFnvcodec_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "FFnvcodec include directory") + DOC "FFnvcodec include directory" +) if(PC_FFnvcodec_VERSION VERSION_GREATER 0) set(FFnvcodec_VERSION ${PC_FFnvcodec_VERSION}) elseif(EXISTS "${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h") - file(STRINGS "${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h" _version_string - REGEX "^.*NVENCAPI_(MAJOR|MINOR)_VERSION[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h" + _version_string + REGEX "^.*NVENCAPI_(MAJOR|MINOR)_VERSION[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*MAJOR_VERSION[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") string(REGEX REPLACE ".*MINOR_VERSION[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") @@ -78,7 +78,10 @@ endif() find_package_handle_standard_args( FFnvcodec REQUIRED_VARS FFnvcodec_INCLUDE_DIR - VERSION_VAR FFnvcodec_VERSION HANDLE_VERSION_RANGE REASON_FAILURE_MESSAGE "${FFnvcodec_ERROR_REASON}") + VERSION_VAR FFnvcodec_VERSION + HANDLE_VERSION_RANGE + REASON_FAILURE_MESSAGE "${FFnvcodec_ERROR_REASON}" +) mark_as_advanced(FFnvcodec_INCLUDE_DIR) unset(FFnvcodec_ERROR_REASON) @@ -91,6 +94,8 @@ endif() include(FeatureSummary) set_package_properties( - FFnvcodec PROPERTIES - URL "https://github.com/FFmpeg/nv-codec-headers/" - DESCRIPTION "FFmpeg version of headers required to interface with NVIDIA's codec APIs.") + FFnvcodec + PROPERTIES + URL "https://github.com/FFmpeg/nv-codec-headers/" + DESCRIPTION "FFmpeg version of headers required to interface with NVIDIA's codec APIs." +) diff --git a/cmake/finders/FindGio.cmake b/cmake/finders/FindGio.cmake index a134dc2f807074..42da5747e5e244 100644 --- a/cmake/finders/FindGio.cmake +++ b/cmake/finders/FindGio.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -55,7 +50,8 @@ find_path( HINTS ${PC_Gio_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES glib-2.0 - DOC "gio-2.0 include directory") + DOC "gio-2.0 include directory" +) find_path( GioUnix_INCLUDE_DIR @@ -63,21 +59,24 @@ find_path( HINTS ${PC_GioUnix_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES gio-unix-2.0 - DOC "gio-unix-2.0 include directory") + DOC "gio-unix-2.0 include directory" +) find_library( Gio_LIBRARY NAMES libgio-2.0 gio-2.0 gio-unix-2.0 HINTS ${PC_Gio_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "gio-2.0 location") + DOC "gio-2.0 location" +) find_path( Glib_INCLUDE_DIR NAMES glibconfig.h HINTS ${PC_Gio_INCLUDE_DIRS} PATHS /usr/lib /usr/local/lib - PATH_SUFFIXES glib-2.0/include) + PATH_SUFFIXES glib-2.0/include +) if(PC_Gio_VERSION VERSION_GREATER 0) set(Gio_VERSION ${PC_Gio_VERSION}) @@ -91,7 +90,9 @@ endif() find_package_handle_standard_args( Gio REQUIRED_VARS Gio_LIBRARY Gio_INCLUDE_DIR GioUnix_INCLUDE_DIR Glib_INCLUDE_DIR - VERSION_VAR Gio_VERSION REASON_FAILURE_MESSAGE "Ensure that glib is installed on the system.") + VERSION_VAR Gio_VERSION + REASON_FAILURE_MESSAGE "Ensure that glib is installed on the system." +) mark_as_advanced(Gio_INCLUDE_DIR Gio_LIBRARY Glib_INCLUDE_DIR) if(Gio_FOUND) @@ -106,16 +107,19 @@ if(Gio_FOUND) set_target_properties( gio::gio - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Gio_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Gio_INCLUDE_DIR};${GioUnix_INCLUDE_DIR};${Glib_INCLUDE_DIR}" - VERSION ${Gio_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Gio_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Gio_INCLUDE_DIR};${GioUnix_INCLUDE_DIR};${Glib_INCLUDE_DIR}" + VERSION ${Gio_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Gio PROPERTIES - URL "https://docs.gtk.org/gio" - DESCRIPTION - "A library providing useful classes for general purpose I/O, networking, IPC, settings, and other high level application functionality." + Gio + PROPERTIES + URL "https://docs.gtk.org/gio" + DESCRIPTION + "A library providing useful classes for general purpose I/O, networking, IPC, settings, and other high level application functionality." ) diff --git a/cmake/finders/FindJack.cmake b/cmake/finders/FindJack.cmake index a7547d0d9ffe28..523f053c7745b8 100644 --- a/cmake/finders/FindJack.cmake +++ b/cmake/finders/FindJack.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,14 +48,10 @@ find_path( NAMES jack/jack.h HINTS ${PC_Jack_INCLUDE_DIR} PATHS /usr/include /usr/local/include - DOC "Jack include directory") + DOC "Jack include directory" +) -find_library( - Jack_LIBRARY - NAMES jack - HINTS ${PC_Jack_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib - DOC "Jack location") +find_library(Jack_LIBRARY NAMES jack HINTS ${PC_Jack_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib DOC "Jack location") if(PC_Jack_VERSION VERSION_GREATER 0) set(Jack_VERSION ${PC_Jack_VERSION}) @@ -74,7 +65,9 @@ endif() find_package_handle_standard_args( Jack REQUIRED_VARS Jack_LIBRARY Jack_INCLUDE_DIR - VERSION_VAR Jack_VERSION REASON_FAILURE_MESSAGE "Ensure that Jack is installed on the system.") + VERSION_VAR Jack_VERSION + REASON_FAILURE_MESSAGE "Ensure that Jack is installed on the system." +) mark_as_advanced(Jack_INCLUDE_DIR Jack_LIBRARY) if(Jack_FOUND) @@ -89,16 +82,19 @@ if(Jack_FOUND) set_target_properties( Jack::Jack - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Jack_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Jack_INCLUDE_DIR}" - VERSION ${Jack_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Jack_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Jack_INCLUDE_DIR}" + VERSION ${Jack_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Jack PROPERTIES - URL "https://www.jackaudio.org" - DESCRIPTION - "JACK Audio Connection Kit (or JACK) is a professional sound server API and pair of daemon implementations to provide real-time, low-latency connections for both audio and MIDI data between applications." + Jack + PROPERTIES + URL "https://www.jackaudio.org" + DESCRIPTION + "JACK Audio Connection Kit (or JACK) is a professional sound server API and pair of daemon implementations to provide real-time, low-latency connections for both audio and MIDI data between applications." ) diff --git a/cmake/finders/FindLibAJANTV2.cmake b/cmake/finders/FindLibAJANTV2.cmake index 8a667a5d7e8119..9418f04cd60f41 100644 --- a/cmake/finders/FindLibAJANTV2.cmake +++ b/cmake/finders/FindLibAJANTV2.cmake @@ -42,12 +42,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-lint: disable=C0307 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -60,14 +54,16 @@ find_path( NAMES libajantv2 HINTS ${PC_LibAJANTV2_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "LibAJANTV2 (new) include directory") + DOC "LibAJANTV2 (new) include directory" +) if(${_LIBAJANTV2_NEW_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_NEW_INCLUDE_DIR-NOTFOUND") find_path( _LIBAJANTV2_OLD_INCLUDE_DIR NAMES ajalibraries HINTS ${PC_LibAJANTV2_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "LibAJANTV2 (old) include directory") + DOC "LibAJANTV2 (old) include directory" + ) if(NOT ${_LIBAJANTV2_OLD_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_OLD_INCLUDE_DIR-NOTFOUND") set(LibAJANTV2_INCLUDE_DIR ${_LIBAJANTV2_OLD_INCLUDE_DIR}/ajalibraries) if(NOT LibAJANTV2_FIND_QUIETLY) @@ -86,14 +82,16 @@ find_library( NAMES ajantv2 libajantv2 HINTS ${PC_LibAJANTV2_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "LibAJANTV2 location") + DOC "LibAJANTV2 location" +) find_library( LibAJANTV2_LIBRARY_DEBUG NAMES ajantv2d libajantv2d HINTS ${PC_LibAJANTV2_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "LibAJANTV2 debug location.") + DOC "LibAJANTV2 debug location." +) if(PC_LibAJANTV2_VERSION VERSION_GREATER 0) set(LibAJANTV2_VERSION ${PC_LibAJANTV2_VERSION}) @@ -116,7 +114,9 @@ endif() find_package_handle_standard_args( LibAJANTV2 REQUIRED_VARS LibAJANTV2_LIBRARY LibAJANTV2_INCLUDE_DIR - VERSION_VAR LibAJANTV2_VERSION REASON_FAILURE_MESSAGE ${LibAJANTV2_ERROR_REASON}) + VERSION_VAR LibAJANTV2_VERSION + REASON_FAILURE_MESSAGE ${LibAJANTV2_ERROR_REASON} +) mark_as_advanced(LibAJANTV2_LIBRARY LibAJANTV2_INCLUDE_DIR) unset(LibAJANTV2_ERROR_REASON) @@ -128,7 +128,8 @@ if(LibAJANTV2_FOUND) ${LibAJANTV2_INCLUDE_DIR}/ajaanc ${LibAJANTV2_INCLUDE_DIR}/ajabase ${LibAJANTV2_INCLUDE_DIR}/ajantv2 - ${LibAJANTV2_INCLUDE_DIR}/ajantv2/includes) + ${LibAJANTV2_INCLUDE_DIR}/ajantv2/includes + ) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/win) elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") @@ -151,10 +152,12 @@ if(LibAJANTV2_FOUND) set_target_properties( AJA::LibAJANTV2 - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_LibAJANTV2_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${LibAJANTV2_INCLUDE_DIR}" - VERSION ${LibAJANTV2_VERSION} - IMPORTED_CONFIGURATIONS Release) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_LibAJANTV2_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${LibAJANTV2_INCLUDE_DIR}" + VERSION ${LibAJANTV2_VERSION} + IMPORTED_CONFIGURATIONS Release + ) if(LibAJANTV2_LIBRARY_DEBUG) if(IS_ABSOLUTE "${LibAJANTV2_LIBRARY_DEBUG}") @@ -162,10 +165,7 @@ if(LibAJANTV2_FOUND) else() set_property(TARGET AJA::LibAJANTV2 PROPERTY IMPORTED_LIBNAME_DEBUG "${LibAJANTV2_LIBRARY_DEBUG}") endif() - set_property( - TARGET AJA::LibAJANTV2 - APPEND - PROPERTY IMPORTED_CONFIGURATIONS Debug) + set_property(TARGET AJA::LibAJANTV2 APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug) endif() set_target_properties(AJA::LibAJANTV2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LibAJANTV2_INCLUDE_DIRS}") @@ -173,29 +173,36 @@ if(LibAJANTV2_FOUND) set_property( TARGET AJA::LibAJANTV2 APPEND - PROPERTY INTERFACE_LINK_LIBRARIES - $<$:netapi32.lib> - $<$:setupapi.lib> - $<$:shlwapi.lib> - $<$:wbemuuid.lib> - $<$:winmm.lib> - $<$:ws2_32.lib> - "$<$:$>" - "$<$:$>" - "$<$:$>") + PROPERTY + INTERFACE_LINK_LIBRARIES + $<$:netapi32.lib> + $<$:setupapi.lib> + $<$:shlwapi.lib> + $<$:wbemuuid.lib> + $<$:winmm.lib> + $<$:ws2_32.lib> + "$<$:$>" + "$<$:$>" + "$<$:$>" + ) set_property( TARGET AJA::LibAJANTV2 APPEND - PROPERTY INTERFACE_COMPILE_DEFINITIONS "$<$:AJA_WINDOWS;_WINDOWS;WIN32;MSWindows>" - "$<$,$>:_DEBUG;_NDEBUG>" "$<$:AJAMac;AJA_MAC>" - "$<$:AJA_LINUX;AJALinux>") + PROPERTY + INTERFACE_COMPILE_DEFINITIONS + "$<$:AJA_WINDOWS;_WINDOWS;WIN32;MSWindows>" + "$<$,$>:_DEBUG;_NDEBUG>" + "$<$:AJAMac;AJA_MAC>" + "$<$:AJA_LINUX;AJALinux>" + ) endif() endif() include(FeatureSummary) set_package_properties( - LibAJANTV2 PROPERTIES - URL "https://www.aja.com" - DESCRIPTION - "AJA NTV2 SDK - AJA simplifies professional digital video workflows with a line of award-winning products designed and manufactured in Grass Valley, CA." + LibAJANTV2 + PROPERTIES + URL "https://www.aja.com" + DESCRIPTION + "AJA NTV2 SDK - AJA simplifies professional digital video workflows with a line of award-winning products designed and manufactured in Grass Valley, CA." ) diff --git a/cmake/finders/FindLibUUID.cmake b/cmake/finders/FindLibUUID.cmake index 5bd4c9d369324f..14c3b54d7d7627 100644 --- a/cmake/finders/FindLibUUID.cmake +++ b/cmake/finders/FindLibUUID.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -52,14 +48,16 @@ find_path( NAMES uuid/uuid.h HINTS ${PC_LibUUID_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "LibUUID include directory") + DOC "LibUUID include directory" +) find_library( LibUUID_LIBRARY NAMES uuid HINTS ${PC_LibUUID_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "LibUUID location") + DOC "LibUUID location" +) if(PC_LibUUID_VERSION VERSION_GREATER 0) set(LibUUID_VERSION ${PC_LibUUID_VERSION}) @@ -73,7 +71,9 @@ endif() find_package_handle_standard_args( LibUUID REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR - VERSION_VAR LibUUID_VERSION REASON_FAILURE_MESSAGE "Ensure that e2fsprogs is installed on the system.") + VERSION_VAR LibUUID_VERSION + REASON_FAILURE_MESSAGE "Ensure that e2fsprogs is installed on the system." +) mark_as_advanced(LibUUID_INCLUDE_DIR LibUUID_LIBRARY) if(LibUUID_FOUND) @@ -88,16 +88,19 @@ if(LibUUID_FOUND) set_target_properties( LibUUID::LibUUID - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_LibUUID_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIR}" - VERSION ${LibUUID_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_LibUUID_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIR}" + VERSION ${LibUUID_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - LibUUID PROPERTIES - URL "http://e2fsprogs.sourceforge.net/" - DESCRIPTION - "The libuuid library is used to generate unique identifiers for objects that may be accessible beyond the local system." + LibUUID + PROPERTIES + URL "http://e2fsprogs.sourceforge.net/" + DESCRIPTION + "The libuuid library is used to generate unique identifiers for objects that may be accessible beyond the local system." ) diff --git a/cmake/finders/FindLibVLC.cmake b/cmake/finders/FindLibVLC.cmake index 5a17b7300f2d35..bae5d1969b0b9c 100644 --- a/cmake/finders/FindLibVLC.cmake +++ b/cmake/finders/FindLibVLC.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,20 +49,26 @@ find_path( HINTS ${PC_LibVLC_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES vlc include/vlc include - DOC "LibVLC include directory") + DOC "LibVLC include directory" +) find_library( LibVLC_LIBRARY NAMES vlc libvlc HINTS ${PC_LibVLC_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "LibVLC location") + DOC "LibVLC location" +) if(PC_LibVLC_VERSION VERSION_GREATER 0) set(LibVLC_VERSION ${PC_LibVLC_VERSION}) elseif(EXISTS "${LibVLC_INCLUDE_DIR}/libvlc_version.h") - file(STRINGS "${LibVLC_INCLUDE_DIR}/libvlc_version.h" _VERSION_STRING - REGEX "^.*LIBVLC_VERSION_(MAJOR|MINOR|PATCH)[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${LibVLC_INCLUDE_DIR}/libvlc_version.h" + _VERSION_STRING + REGEX "^.*LIBVLC_VERSION_(MAJOR|MINOR|PATCH)[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*LIBVLC_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" VERSION_MAJOR "${_VERSION_STRING}") string(REGEX REPLACE ".*LIBVLC_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" VERSION_MINOR "${_VERSION_STRING}") string(REGEX REPLACE ".*LIBVLC_VERSION_REVISION[ \t]+([0-9]+).*" "\\1" VERSION_REVISION "${_VERSION_STRING}") @@ -82,7 +83,9 @@ endif() find_package_handle_standard_args( LibVLC REQUIRED_VARS LibVLC_LIBRARY LibVLC_INCLUDE_DIR - VERSION_VAR LibVLC_VERSION REASON_FAILURE_MESSAGE "Ensure that libvlc-dev (vlc on BSD) is installed on the system.") + VERSION_VAR LibVLC_VERSION + REASON_FAILURE_MESSAGE "Ensure that libvlc-dev (vlc on BSD) is installed on the system." +) mark_as_advanced(LibVLC_INCLUDE_DIR LibVLC_LIBRARY) if(LibVLC_FOUND) @@ -97,15 +100,19 @@ if(LibVLC_FOUND) set_target_properties( VLC::LibVLC - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_LibVLC_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${LibVLC_INCLUDE_DIR}" - VERSION ${LibVLC_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_LibVLC_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${LibVLC_INCLUDE_DIR}" + VERSION ${LibVLC_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - LibVLC PROPERTIES - URL "https://www.videolan.org/vlc/libvlc.html" - DESCRIPTION - "libVLC is the core engine and the interface to the multimedia framework on which VLC media player is based.") + LibVLC + PROPERTIES + URL "https://www.videolan.org/vlc/libvlc.html" + DESCRIPTION + "libVLC is the core engine and the interface to the multimedia framework on which VLC media player is based." +) diff --git a/cmake/finders/FindLibdrm.cmake b/cmake/finders/FindLibdrm.cmake index de5a25bc8b3af3..9b594be54ba7dd 100644 --- a/cmake/finders/FindLibdrm.cmake +++ b/cmake/finders/FindLibdrm.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,14 +49,16 @@ find_path( HINTS ${PC_Libdrm_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES libdrm - DOC "Libdrm include directory") + DOC "Libdrm include directory" +) find_library( Libdrm_LIBRARY NAMES drm libdrm HINTS ${PC_Libdrm_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libdrm location") + DOC "Libdrm location" +) if(PC_Libdrm_VERSION VERSION_GREATER 0) set(Libdrm_VERSION ${PC_Libdrm_VERSION}) @@ -75,7 +72,9 @@ endif() find_package_handle_standard_args( Libdrm REQUIRED_VARS Libdrm_LIBRARY Libdrm_INCLUDE_DIR - VERSION_VAR Libdrm_VERSION REASON_FAILURE_MESSAGE "Ensure that libdrm is installed on the system.") + VERSION_VAR Libdrm_VERSION + REASON_FAILURE_MESSAGE "Ensure that libdrm is installed on the system." +) mark_as_advanced(Libdrm_INCLUDE_DIR Libdrm_LIBRARY) if(Libdrm_FOUND) @@ -90,16 +89,19 @@ if(Libdrm_FOUND) set_target_properties( Libdrm::Libdrm - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libdrm_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}" - VERSION ${Libdrm_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libdrm_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}" + VERSION ${Libdrm_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Libdrm PROPERTIES - URL "https://gitlab.freedesktop.org/mesa/drm" - DESCRIPTION - "A low-level library, typically used by graphics drivers such as the Mesa drivers, the X drivers, libva and similar projects." + Libdrm + PROPERTIES + URL "https://gitlab.freedesktop.org/mesa/drm" + DESCRIPTION + "A low-level library, typically used by graphics drivers such as the Mesa drivers, the X drivers, libva and similar projects." ) diff --git a/cmake/finders/FindLibfdk.cmake b/cmake/finders/FindLibfdk.cmake index ee345b72c6e345..cf350fdbf718a2 100644 --- a/cmake/finders/FindLibfdk.cmake +++ b/cmake/finders/FindLibfdk.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -52,14 +48,16 @@ find_path( NAMES fdk-aac/aacenc_lib.h HINTS ${PC_Libfdk_INCLUDE_DIRS} PATHS /usr/include/ /usr/local/include - DOC "Libfdk include directory") + DOC "Libfdk include directory" +) find_library( Libfdk_LIBRARY NAMES fdk-aac Libfdk-aac HINTS ${PC_Libfdk_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libfdk location") + DOC "Libfdk location" +) if(PC_Libfdk_VERSION VERSION_GREATER 0) set(Libfdk_VERSION ${PC_Libfdk_VERSION}) @@ -73,7 +71,9 @@ endif() find_package_handle_standard_args( Libfdk REQUIRED_VARS Libfdk_LIBRARY Libfdk_INCLUDE_DIR - VERSION_VAR Libfdk_VERSION REASON_FAILURE_MESSAGE "Ensure that Libfdk is installed on the system.") + VERSION_VAR Libfdk_VERSION + REASON_FAILURE_MESSAGE "Ensure that Libfdk is installed on the system." +) mark_as_advanced(Libfdk_INCLUDE_DIR Libfdk_LIBRARY) if(Libfdk_FOUND) @@ -88,14 +88,18 @@ if(Libfdk_FOUND) set_target_properties( Libfdk::Libfdk - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libfdk_CFLAFGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libfdk_INCLUDE_DIR}" - VERSION ${Libfdk_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libfdk_CFLAFGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libfdk_INCLUDE_DIR}" + VERSION ${Libfdk_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Libfdk PROPERTIES - URL "https://github.com/mstorsjo/fdk-aac" - DESCRIPTION "A standalone library of the Fraunhofer FDK AAC code from Android.") + Libfdk + PROPERTIES + URL "https://github.com/mstorsjo/fdk-aac" + DESCRIPTION "A standalone library of the Fraunhofer FDK AAC code from Android." +) diff --git a/cmake/finders/FindLibpci.cmake b/cmake/finders/FindLibpci.cmake index 18ebe6ed90f21c..d923b46c3102d3 100644 --- a/cmake/finders/FindLibpci.cmake +++ b/cmake/finders/FindLibpci.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,14 +49,16 @@ find_path( HINTS ${PC_Libpci_INCLUDE_DIRS} PATHS /usr/include/ /usr/local/include PATH_SUFFIXES pci - DOC "Libpci include directory") + DOC "Libpci include directory" +) find_library( Libpci_LIBRARY NAMES libpci pci HINTS ${PC_Libpci_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libpci location") + DOC "Libpci location" +) if(PC_Libpci_VERSION VERSION_GREATER 0) set(Libpci_VERSION ${PC_Libpci_VERSION}) @@ -77,7 +75,9 @@ endif() find_package_handle_standard_args( Libpci REQUIRED_VARS Libpci_LIBRARY Libpci_INCLUDE_DIR - VERSION_VAR Libpci_VERSION REASON_FAILURE_MESSAGE "Ensure that libpci is installed on the system.") + VERSION_VAR Libpci_VERSION + REASON_FAILURE_MESSAGE "Ensure that libpci is installed on the system." +) mark_as_advanced(Libpci_INCLUDE_DIR Libpci_LIBRARY) if(Libpci_FOUND) @@ -92,14 +92,18 @@ if(Libpci_FOUND) set_target_properties( Libpci::pci - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libpci_CFLAFGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libpci_INCLUDE_DIR}" - VERSION ${Libpci_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libpci_CFLAFGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libpci_INCLUDE_DIR}" + VERSION ${Libpci_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Libpci PROPERTIES - URL "https://mj.ucw.cz/sw/pciutils" - DESCRIPTION "Offers access to the PCI configuration space on a variety of operating systems.") + Libpci + PROPERTIES + URL "https://mj.ucw.cz/sw/pciutils" + DESCRIPTION "Offers access to the PCI configuration space on a variety of operating systems." +) diff --git a/cmake/finders/FindLibrist.cmake b/cmake/finders/FindLibrist.cmake index bfd89df3c0e82d..aed35973ac0f28 100644 --- a/cmake/finders/FindLibrist.cmake +++ b/cmake/finders/FindLibrist.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,7 +49,8 @@ macro(Librist_set_soname) execute_process( COMMAND sh -c "otool -D '${Librist_LIBRARY}' | grep -v '${Librist_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET Librist::Librist PROPERTY IMPORTED_SONAME "${_output}") @@ -62,7 +59,8 @@ macro(Librist_set_soname) execute_process( COMMAND sh -c "objdump -p '${Librist_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -79,7 +77,8 @@ find_path( NAMES librist.h librist/librist.h HINTS ${PC_Librist_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Librist include directory") + DOC "Librist include directory" +) if(PC_Librist_VERSION VERSION_GREATER 0) set(Librist_VERSION ${PC_Librist_VERSION}) @@ -101,7 +100,8 @@ find_library( NAMES librist rist HINTS ${PC_Librist_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Librist location") + DOC "Librist location" +) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") set(Librist_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") @@ -112,7 +112,9 @@ endif() find_package_handle_standard_args( Librist REQUIRED_VARS Librist_LIBRARY Librist_INCLUDE_DIR - VERSION_VAR Librist_VERSION REASON_FAILURE_MESSAGE "${Librist_ERROR_REASON}") + VERSION_VAR Librist_VERSION + REASON_FAILURE_MESSAGE "${Librist_ERROR_REASON}" +) mark_as_advanced(Librist_INCLUDE_DIR Librist_LIBRARY) unset(Librist_ERROR_REASON) @@ -129,14 +131,18 @@ if(Librist_FOUND) librist_set_soname() set_target_properties( Librist::Librist - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Librist_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Librist_INCLUDE_DIR}" - VERSION ${Librist_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Librist_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Librist_INCLUDE_DIR}" + VERSION ${Librist_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Librist PROPERTIES - URL "https://code.videolan.org/rist/librist" - DESCRIPTION "A library that can be used to easily add the RIST protocol to your application.") + Librist + PROPERTIES + URL "https://code.videolan.org/rist/librist" + DESCRIPTION "A library that can be used to easily add the RIST protocol to your application." +) diff --git a/cmake/finders/FindLibrnnoise.cmake b/cmake/finders/FindLibrnnoise.cmake index 21424ec935b478..fb3bc6e2374ffa 100644 --- a/cmake/finders/FindLibrnnoise.cmake +++ b/cmake/finders/FindLibrnnoise.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0307 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,7 +49,8 @@ macro(librnnoise_set_soname) execute_process( COMMAND sh -c "otool -D '${Librnnoise_LIBRARY}' | grep -v '${Librnnoise_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET Librnnoise::Librnnoise PROPERTY IMPORTED_SONAME "${_output}") @@ -63,7 +59,8 @@ macro(librnnoise_set_soname) execute_process( COMMAND sh -c "objdump -p '${Librnnoise_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -80,7 +77,8 @@ find_path( NAMES rnnoise.h HINTS ${PC_Librnnoise_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Librnnoise include directory") + DOC "Librnnoise include directory" +) if(PC_Librnnoise_VERSION VERSION_GREATER 0) set(Librnnoise_VERSION ${PC_Librnnoise_VERSION}) @@ -96,7 +94,8 @@ find_library( NAMES rnnoise librnnoise HINTS ${PC_Librnnoise_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Librnnoise location") + DOC "Librnnoise location" +) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") set(Librnnoise_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") @@ -107,7 +106,9 @@ endif() find_package_handle_standard_args( Librnnoise REQUIRED_VARS Librnnoise_LIBRARY Librnnoise_INCLUDE_DIR - VERSION_VAR Librnnoise_VERSION REASON_FAILURE_MESSAGE "${Librnnoise_ERROR_REASON}") + VERSION_VAR Librnnoise_VERSION + REASON_FAILURE_MESSAGE "${Librnnoise_ERROR_REASON}" +) mark_as_advanced(Librnnoise_INCLUDE_DIR Librnnoise_LIBRARY) unset(Librnnoise_ERROR_REASON) @@ -124,14 +125,18 @@ if(Librnnoise_FOUND) set_target_properties( Librnnoise::Librnnoise - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Librnnoise_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Librnnoise_INCLUDE_DIR}" - VERSION ${Librnnoise_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Librnnoise_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Librnnoise_INCLUDE_DIR}" + VERSION ${Librnnoise_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Librnnoise PROPERTIES - URL "https://gitlab.xiph.org/xiph/rnnoise" - DESCRIPTION "Recurrent neural network for audio noise reduction.") + Librnnoise + PROPERTIES + URL "https://gitlab.xiph.org/xiph/rnnoise" + DESCRIPTION "Recurrent neural network for audio noise reduction." +) diff --git a/cmake/finders/FindLibspeexdsp.cmake b/cmake/finders/FindLibspeexdsp.cmake index 14981f7d9b8c4c..c65a7c1ab7c5fb 100644 --- a/cmake/finders/FindLibspeexdsp.cmake +++ b/cmake/finders/FindLibspeexdsp.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0307 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,7 +49,8 @@ macro(libspeexdsp_set_soname) execute_process( COMMAND sh -c "otool -D '${Libspeexdsp_LIBRARY}' | grep -v '${Libspeexdsp_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET SpeexDSP::Libspeexdsp PROPERTY IMPORTED_SONAME "${_output}") @@ -63,7 +59,8 @@ macro(libspeexdsp_set_soname) execute_process( COMMAND sh -c "objdump -p '${Libspeexdsp_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -80,7 +77,8 @@ find_path( NAMES speex/speex_preprocess.h HINTS ${PC_Libspeexdsp_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Libspeexdsp include directory") + DOC "Libspeexdsp include directory" +) if(PC_Libspeexdsp_VERSION VERSION_GREATER 0) set(Libspeexdsp_VERSION ${PC_Libspeexdsp_VERSION}) @@ -96,7 +94,8 @@ find_library( NAMES speexdsp libspeexdsp HINTS ${PC_Libspeexdsp_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libspeexdsp location") + DOC "Libspeexdsp location" +) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") set(Libspeexdsp_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") @@ -107,7 +106,9 @@ endif() find_package_handle_standard_args( Libspeexdsp REQUIRED_VARS Libspeexdsp_LIBRARY Libspeexdsp_INCLUDE_DIR - VERSION_VAR Libspeexdsp_VERSION REASON_FAILURE_MESSAGE "${Libspeexdsp_ERROR_REASON}") + VERSION_VAR Libspeexdsp_VERSION + REASON_FAILURE_MESSAGE "${Libspeexdsp_ERROR_REASON}" +) mark_as_advanced(Libspeexdsp_INCLUDE_DIR Libspeexdsp_LIBRARY) unset(Libspeexdsp_ERROR_REASON) @@ -124,14 +125,16 @@ if(Libspeexdsp_FOUND) libspeexdsp_set_soname() set_target_properties( SpeexDSP::Libspeexdsp - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libspeexdsp_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libspeexdsp_INCLUDE_DIR}" - VERSION ${Libspeexdsp_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libspeexdsp_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libspeexdsp_INCLUDE_DIR}" + VERSION ${Libspeexdsp_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Libspeexdsp PROPERTIES - URL "https://gitlab.xiph.org/xiph/speexdsp" - DESCRIPTION "DSP library derived from speex.") + Libspeexdsp + PROPERTIES URL "https://gitlab.xiph.org/xiph/speexdsp" DESCRIPTION "DSP library derived from speex." +) diff --git a/cmake/finders/FindLibsrt.cmake b/cmake/finders/FindLibsrt.cmake index 51413cf503f52f..4597a80a15731e 100644 --- a/cmake/finders/FindLibsrt.cmake +++ b/cmake/finders/FindLibsrt.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,7 +49,8 @@ macro(libsrt_set_soname) execute_process( COMMAND sh -c "otool -D '${Libsrt_LIBRARY}' | grep -v '${Libsrt_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET Libsrt::Libsrt PROPERTY IMPORTED_SONAME "${_output}") @@ -63,7 +59,8 @@ macro(libsrt_set_soname) execute_process( COMMAND sh -c "objdump -p '${Libsrt_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -80,7 +77,8 @@ find_path( NAMES srt.h srt/srt.h HINTS ${PC_Libsrt_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Libsrt include directory") + DOC "Libsrt include directory" +) if(PC_Libsrt_VERSION VERSION_GREATER 0) set(Libsrt_VERSION ${PC_Libsrt_VERSION}) @@ -99,7 +97,8 @@ find_library( NAMES srt libsrt HINTS ${PC_Libsrt_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libsrt location") + DOC "Libsrt location" +) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") set(Libsrt_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") @@ -110,7 +109,9 @@ endif() find_package_handle_standard_args( Libsrt REQUIRED_VARS Libsrt_LIBRARY Libsrt_INCLUDE_DIR - VERSION_VAR Libsrt_VERSION REASON_FAILURE_MESSAGE "${Libsrt_ERROR_REASON}") + VERSION_VAR Libsrt_VERSION + REASON_FAILURE_MESSAGE "${Libsrt_ERROR_REASON}" +) mark_as_advanced(Libsrt_INCLUDE_DIR Libsrt_LIBRARY) unset(Libsrt_ERROR_REASON) @@ -125,15 +126,20 @@ if(Libsrt_FOUND) set_property(TARGET Libsrt::Libsrt PROPERTY IMPORTED_LIBNAME "${Libsrt_LIBRARY}") endif() - set_target_properties(Libsrt::Libsrt PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libsrt_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libsrt_INCLUDE_DIR}") + set_target_properties( + Libsrt::Libsrt + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libsrt_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libsrt_INCLUDE_DIR}" + ) endif() endif() include(FeatureSummary) set_package_properties( - Libsrt PROPERTIES - URL "https://www.srtalliance.org" - DESCRIPTION - "Secure Reliable Transport (SRT) is a transport protocol for ultra low (sub-second) latency live video and audio streaming, as well as for generic bulk data transfer." + Libsrt + PROPERTIES + URL "https://www.srtalliance.org" + DESCRIPTION + "Secure Reliable Transport (SRT) is a transport protocol for ultra low (sub-second) latency live video and audio streaming, as well as for generic bulk data transfer." ) diff --git a/cmake/finders/FindLibudev.cmake b/cmake/finders/FindLibudev.cmake index 5d7cc6601fd9c8..aa58f6d9a90dd7 100644 --- a/cmake/finders/FindLibudev.cmake +++ b/cmake/finders/FindLibudev.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -52,14 +48,16 @@ find_path( NAMES libudev.h HINTS ${PC_Libudev_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Libudev include directory") + DOC "Libudev include directory" +) find_library( Libudev_LIBRARY NAMES udev libudev HINTS ${PC_Libudev_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libudev location") + DOC "Libudev location" +) if(PC_Libudev_VERSION VERSION_GREATER 0) set(Libudev_VERSION ${PC_Libudev_VERSION}) @@ -73,7 +71,9 @@ endif() find_package_handle_standard_args( Libudev REQUIRED_VARS Libudev_LIBRARY Libudev_INCLUDE_DIR - VERSION_VAR Libudev_VERSION REASON_FAILURE_MESSAGE "Ensure that Libudev is installed on the system.") + VERSION_VAR Libudev_VERSION + REASON_FAILURE_MESSAGE "Ensure that Libudev is installed on the system." +) mark_as_advanced(Libudev_INCLUDE_DIR Libudev_LIBRARY) if(Libudev_FOUND) @@ -88,14 +88,18 @@ if(Libudev_FOUND) set_target_properties( Libudev::Libudev - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libudev_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libudev_INCLUDE_DIR}" - VERSION ${Libudev_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libudev_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libudev_INCLUDE_DIR}" + VERSION ${Libudev_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Libudev PROPERTIES - URL "https://www.freedesktop.org/wiki/Software/systemd/" - DESCRIPTION "API for enumerating and introspecting local devices.") + Libudev + PROPERTIES + URL "https://www.freedesktop.org/wiki/Software/systemd/" + DESCRIPTION "API for enumerating and introspecting local devices." +) diff --git a/cmake/finders/FindLibv4l2.cmake b/cmake/finders/FindLibv4l2.cmake index 45bf1a7496593c..188c598671f782 100644 --- a/cmake/finders/FindLibv4l2.cmake +++ b/cmake/finders/FindLibv4l2.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -52,14 +48,16 @@ find_path( NAMES libv4l2.h HINTS ${PC_Libv4l2_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Libv4l2 include directory") + DOC "Libv4l2 include directory" +) find_library( Libv4l2_LIBRARY NAMES v4l2 HINTS ${PC_Libv4l2_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libv4l2 location") + DOC "Libv4l2 location" +) if(PC_Libv4l2_VERSION VERSION_GREATER 0) set(Libv4l2_VERSION ${PC_Libv4l2_VERSION}) @@ -73,7 +71,9 @@ endif() find_package_handle_standard_args( Libv4l2 REQUIRED_VARS Libv4l2_LIBRARY Libv4l2_INCLUDE_DIR - VERSION_VAR Libv4l2_VERSION REASON_FAILURE_MESSAGE "Ensure that v4l-utils is installed on the system.") + VERSION_VAR Libv4l2_VERSION + REASON_FAILURE_MESSAGE "Ensure that v4l-utils is installed on the system." +) mark_as_advanced(Libv4l2_INCLUDE_DIR Libv4l2_LIBRARY) if(Libv4l2_FOUND) @@ -88,14 +88,18 @@ if(Libv4l2_FOUND) set_target_properties( Libv4l2::Libv4l2 - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libv4l2_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libv4l2_INCLUDE_DIR}" - VERSION ${Libv4l2_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libv4l2_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libv4l2_INCLUDE_DIR}" + VERSION ${Libv4l2_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Lib4l2 PROPERTIES - URL "https://linuxtv.org/wiki/index.php/V4l-utils" - DESCRIPTION "The v4l-utils are a series of packages for handling media devices.") + Lib4l2 + PROPERTIES + URL "https://linuxtv.org/wiki/index.php/V4l-utils" + DESCRIPTION "The v4l-utils are a series of packages for handling media devices." +) diff --git a/cmake/finders/FindLibva.cmake b/cmake/finders/FindLibva.cmake index 0ead897a893199..9a0089c7caa71b 100644 --- a/cmake/finders/FindLibva.cmake +++ b/cmake/finders/FindLibva.cmake @@ -42,11 +42,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -61,27 +56,34 @@ find_path( HINTS ${PC_Libva_INCLUDE_DIRS} PATHS /usr/include/ /usr/local/include PATH_SUFFIXES va - DOC "Libva include directory") + DOC "Libva include directory" +) find_library( Libva_LIBRARY NAMES libva va HINTS ${PC_Libva_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libva location") + DOC "Libva location" +) find_library( LibvaDrm_LIBRARY NAMES libva-drm va-drm HINTS ${PC_LibvaDrm_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libva-drm location") + DOC "Libva-drm location" +) if(PC_Libva_VERSION VERSION_GREATER 0) set(Libva_VERSION ${PC_Libva_VERSION}) elseif(EXISTS "${Libva_INCLUDE_DIR}/va_version.h") - file(STRINGS "${Libva_INCLUDE_DIR}/va_version.h" _VERSION_STRING - REGEX "^.*(MAJOR|MINOR|MICRO)_VERSION[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${Libva_INCLUDE_DIR}/va_version.h" + _VERSION_STRING + REGEX "^.*(MAJOR|MINOR|MICRO)_VERSION[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*MAJOR_VERSION[ \t]+([0-9]+).*" "\\1" VERSION_MAJOR "${_VERSION_STRING}") string(REGEX REPLACE ".*MINOR_VERSION[ \t]+([0-9]+).*" "\\1" VERSION_MINOR "${_VERSION_STRING}") string(REGEX REPLACE ".*MICRO_VERSION[ \t]+([0-9]+).*" "\\1" VERSION_MICRO "${_VERSION_STRING}") @@ -97,7 +99,9 @@ endif() find_package_handle_standard_args( Libva REQUIRED_VARS Libva_LIBRARY Libva_INCLUDE_DIR - VERSION_VAR Libva_VERSION REASON_FAILURE_MESSAGE "Ensure that libva is installed on the system.") + VERSION_VAR Libva_VERSION + REASON_FAILURE_MESSAGE "Ensure that libva is installed on the system." +) mark_as_advanced(Libva_INCLUDE_DIR Libva_LIBRARY) if(Libva_FOUND) @@ -112,9 +116,11 @@ if(Libva_FOUND) set_target_properties( Libva::va - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libva_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libva_INCLUDE_DIR}" - VERSION ${Libva_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libva_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libva_INCLUDE_DIR}" + VERSION ${Libva_VERSION} + ) endif() if(LibvaDrm_LIBRARY) @@ -129,17 +135,20 @@ if(Libva_FOUND) set_target_properties( Libva::drm - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_LibvaDrm_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libva_INCLUDE_DIR}" - VERSION ${Libva_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_LibvaDrm_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libva_INCLUDE_DIR}" + VERSION ${Libva_VERSION} + ) endif() endif() endif() include(FeatureSummary) set_package_properties( - Libva PROPERTIES - URL "https://01.org/intel-media-for-linux" - DESCRIPTION - "An implementation for VA-API (Video Acceleration API) - an open-source library which provides access to graphics hardware acceleration capabilities." + Libva + PROPERTIES + URL "https://01.org/intel-media-for-linux" + DESCRIPTION + "An implementation for VA-API (Video Acceleration API) - an open-source library which provides access to graphics hardware acceleration capabilities." ) diff --git a/cmake/finders/FindLibx264.cmake b/cmake/finders/FindLibx264.cmake index a09c3f0c8933e4..feb50551cc5c95 100644 --- a/cmake/finders/FindLibx264.cmake +++ b/cmake/finders/FindLibx264.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,7 +49,8 @@ macro(Libx264_set_soname) execute_process( COMMAND sh -c "otool -D '${Libx264_LIBRARY}' | grep -v '${Libx264_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET Libx264::Libx264 PROPERTY IMPORTED_SONAME "${_output}") @@ -63,7 +59,8 @@ macro(Libx264_set_soname) execute_process( COMMAND sh -c "objdump -p '${Libx264_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -86,7 +83,8 @@ macro(Libx264_find_dll) Libx264_LIBRARY NAMES libx264-${_dll_version}.dll x264-${_dll_version}.dll libx264.dll x264.dll HINTS ${_implib_path} ${_bin_path} - DOC "Libx264 DLL location") + DOC "Libx264 DLL location" + ) if(NOT Libx264_LIBRARY) set(Libx264_LIBRARY "${Libx264_IMPLIB}") @@ -101,7 +99,8 @@ find_path( NAMES x264.h HINTS ${PC_Libx264_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Libx264 include directory") + DOC "Libx264 include directory" +) if(PC_Libx264_VERSION VERSION_GREATER 0) set(Libx264_VERSION ${PC_Libx264_VERSION}) @@ -116,10 +115,7 @@ else() endif() if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") - find_library( - Libx264_IMPLIB - NAMES x264 libx264 - DOC "Libx264 import library location") + find_library(Libx264_IMPLIB NAMES x264 libx264 DOC "Libx264 import library location") libx264_find_dll() else() @@ -128,7 +124,8 @@ else() NAMES x264 libx264 HINTS ${PC_Libx264_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Libx264 location") + DOC "Libx264 location" + ) endif() if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") @@ -140,7 +137,9 @@ endif() find_package_handle_standard_args( Libx264 REQUIRED_VARS Libx264_LIBRARY Libx264_INCLUDE_DIR - VERSION_VAR Libx264_VERSION REASON_FAILURE_MESSAGE "${Libx264_ERROR_REASON}") + VERSION_VAR Libx264_VERSION + REASON_FAILURE_MESSAGE "${Libx264_ERROR_REASON}" +) mark_as_advanced(Libx264_INCLUDE_DIR Libx264_LIBRARY Libx264_IMPLIB) unset(Libx264_ERROR_REASON) @@ -166,16 +165,19 @@ if(Libx264_FOUND) libx264_set_soname() set_target_properties( Libx264::Libx264 - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Libx264_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Libx264_INCLUDE_DIR}" - VERSION ${Libx264_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Libx264_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Libx264_INCLUDE_DIR}" + VERSION ${Libx264_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Libx264 PROPERTIES - URL "https://www.videolan.org/developers/x264.html" - DESCRIPTION - "x264 is a free software library and application for encoding video streams into the H.264/MPEG-4 AVC compression format." + Libx264 + PROPERTIES + URL "https://www.videolan.org/developers/x264.html" + DESCRIPTION + "x264 is a free software library and application for encoding video streams into the H.264/MPEG-4 AVC compression format." ) diff --git a/cmake/finders/FindLuajit.cmake b/cmake/finders/FindLuajit.cmake index 28e4ff33c089ad..8d4522a3c35a9b 100644 --- a/cmake/finders/FindLuajit.cmake +++ b/cmake/finders/FindLuajit.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,7 +49,8 @@ macro(Luajit_set_soname) execute_process( COMMAND sh -c "otool -D '${Luajit_LIBRARY}' | grep -v '${Luajit_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET Luajit::Luajit PROPERTY IMPORTED_SONAME "${_output}") @@ -62,7 +59,8 @@ macro(Luajit_set_soname) execute_process( COMMAND sh -c "objdump -p '${Luajit_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -80,7 +78,8 @@ find_path( HINTS ${PC_Luajit_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES luajit-2.1 luajit-2.0 luajit - DOC "Luajit include directory") + DOC "Luajit include directory" +) if(PC_Luajit_VERSION VERSION_GREATER 0) set(Luajit_VERSION ${PC_Luajit_VERSION}) @@ -98,7 +97,8 @@ find_library( NAMES luajit luajit-51 luajit-5.1 lua51 HINTS ${PC_Luajit_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Luajit location") + DOC "Luajit location" +) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") set(Luajit_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") @@ -109,7 +109,9 @@ endif() find_package_handle_standard_args( Luajit REQUIRED_VARS Luajit_LIBRARY Luajit_INCLUDE_DIR - VERSION_VAR Luajit_VERSION REASON_FAILURE_MESSAGE "${Luajit_ERROR_REASON}") + VERSION_VAR Luajit_VERSION + REASON_FAILURE_MESSAGE "${Luajit_ERROR_REASON}" +) mark_as_advanced(Luajit_INCLUDE_DIR Luajit_LIBRARY) unset(Luajit_ERROR_REASON) @@ -126,14 +128,18 @@ if(Luajit_FOUND) luajit_set_soname() set_target_properties( Luajit::Luajit - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Luajit_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Luajit_INCLUDE_DIR}" - VERSION ${Luajit_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Luajit_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Luajit_INCLUDE_DIR}" + VERSION ${Luajit_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Luajit PROPERTIES - URL "https://luajit.org/luajit.html" - DESCRIPTION "LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language.") + Luajit + PROPERTIES + URL "https://luajit.org/luajit.html" + DESCRIPTION "LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language." +) diff --git a/cmake/finders/FindMbedTLS.cmake b/cmake/finders/FindMbedTLS.cmake index 0bec57d53d5095..3337111b8e890c 100644 --- a/cmake/finders/FindMbedTLS.cmake +++ b/cmake/finders/FindMbedTLS.cmake @@ -61,12 +61,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-lint: disable=C0307 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -80,7 +74,8 @@ macro(MbedTLS_set_soname component) execute_process( COMMAND sh -c "otool -D '${Mbed${component}_LIBRARY}' | grep -v '${Mbed${component}_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET MbedTLS::Mbed${component} PROPERTY IMPORTED_SONAME "${_output}") @@ -89,7 +84,8 @@ macro(MbedTLS_set_soname component) execute_process( COMMAND sh -c "objdump -p '${Mbed${component}_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -106,20 +102,39 @@ find_path( NAMES mbedtls/ssl.h HINTS "${PC_MbedTLS_INCLUDE_DIRS}" PATHS /usr/include /usr/local/include - DOC "MbedTLS include directory") + DOC "MbedTLS include directory" +) if(PC_MbedTLS_VERSION VERSION_GREATER 0) set(MbedTLS_VERSION ${PC_MbedTLS_VERSION}) elseif(EXISTS "${MbedTLS_INCLUDE_DIR}/mbedtls/build_info.h") - file(STRINGS "${MbedTLS_INCLUDE_DIR}/mbedtls/build_info.h" _VERSION_STRING - REGEX "#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+.+") - string(REGEX REPLACE ".*#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"(.+)\".*" "\\1" MbedTLS_VERSION - "${_VERSION_STRING}") + file( + STRINGS + "${MbedTLS_INCLUDE_DIR}/mbedtls/build_info.h" + _VERSION_STRING + REGEX "#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+.+" + ) + string( + REGEX REPLACE + ".*#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"(.+)\".*" + "\\1" + MbedTLS_VERSION + "${_VERSION_STRING}" + ) elseif(EXISTS "${MbedTLS_INCLUDE_DIR}/mbedtls/version.h") - file(STRINGS "${MbedTLS_INCLUDE_DIR}/mbedtls/version.h" _VERSION_STRING - REGEX "#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+.+") - string(REGEX REPLACE ".*#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"(.+)\".*" "\\1" MbedTLS_VERSION - "${_VERSION_STRING}") + file( + STRINGS + "${MbedTLS_INCLUDE_DIR}/mbedtls/version.h" + _VERSION_STRING + REGEX "#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+.+" + ) + string( + REGEX REPLACE + ".*#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"(.+)\".*" + "\\1" + MbedTLS_VERSION + "${_VERSION_STRING}" + ) else() if(NOT MbedTLS_FIND_QUIETLY) message(AUTHOR_WARNING "Failed to find MbedTLS version.") @@ -132,25 +147,26 @@ find_library( NAMES libmbedtls mbedtls HINTS "${PC_MbedTLS_LIBRARY_DIRS}" PATHS /usr/lib /usr/local/lib - DOC "MbedTLS location") + DOC "MbedTLS location" +) find_library( MbedCrypto_LIBRARY NAMES libmbedcrypto mbedcrypto HINTS "${PC_MbedTLS_LIBRARY_DIRS}" PATHS /usr/lib /usr/local/lib - DOC "MbedCrypto location") + DOC "MbedCrypto location" +) find_library( MbedX509_LIBRARY NAMES libmbedx509 mbedx509 HINTS "${PC_MbedTLS_LIBRARY_DIRS}" PATHS /usr/lib /usr/local/lib - DOC "MbedX509 location") + DOC "MbedX509 location" +) -if(MbedTLS_LIBRARY - AND NOT MbedCrypto_LIBRARY - AND NOT MbedX509_LIBRARY) +if(MbedTLS_LIBRARY AND NOT MbedCrypto_LIBRARY AND NOT MbedX509_LIBRARY) set(CMAKE_REQUIRED_LIBRARIES "${MbedTLS_LIBRARY}") set(CMAKE_REQUIRED_INCLUDES "${MbedTLS_INCLUDE_DIR}") @@ -170,14 +186,18 @@ if(MbedTLS_INCLUDES_X509 AND MbedTLS_INCLUDES_CRYPTO) find_package_handle_standard_args( MbedTLS REQUIRED_VARS MbedTLS_LIBRARY MbedTLS_INCLUDE_DIR - VERSION_VAR MbedTLS_VERSION REASON_FAILURE_MESSAGE "${MbedTLS_ERROR_REASON}") + VERSION_VAR MbedTLS_VERSION + REASON_FAILURE_MESSAGE "${MbedTLS_ERROR_REASON}" + ) mark_as_advanced(MbedTLS_LIBRARY MbedTLS_INCLUDE_DIR) list(APPEND _COMPONENTS TLS) else() find_package_handle_standard_args( MbedTLS REQUIRED_VARS MbedTLS_LIBRARY MbedCrypto_LIBRARY MbedX509_LIBRARY MbedTLS_INCLUDE_DIR - VERSION_VAR MbedTLS_VERSION REASON_FAILURE_MESSAGE "${MbedTLS_ERROR_REASON}") + VERSION_VAR MbedTLS_VERSION + REASON_FAILURE_MESSAGE "${MbedTLS_ERROR_REASON}" + ) mark_as_advanced(MbedTLS_LIBRARY MbedCrypto_LIBRARY MbedX509_LIBRARY MbedTLS_INCLUDE_DIR) list(APPEND _COMPONENTS TLS Crypto X509) endif() @@ -197,10 +217,12 @@ if(MbedTLS_FOUND) mbedtls_set_soname(${component}) set_target_properties( MbedTLS::MbedTLS - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_MbedTLS_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${MbedTLS_INCLUDE_DIR}" - INTERFACE_LINK_OPTIONS "$<$,$>:/NODEFAULTLIB:MSVCRT>" - VERSION ${MbedTLS_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_MbedTLS_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${MbedTLS_INCLUDE_DIR}" + INTERFACE_LINK_OPTIONS "$<$,$>:/NODEFAULTLIB:MSVCRT>" + VERSION ${MbedTLS_VERSION} + ) endif() endforeach() @@ -214,8 +236,9 @@ endif() include(FeatureSummary) set_package_properties( - MbedTLS PROPERTIES - URL "https://www.trustedfirmware.org/projects/mbed-tls" - DESCRIPTION - "A C library implementing cryptographic primitives, X.509 certificate manipulation, and the SSL/TLS and DTLS protocols." + MbedTLS + PROPERTIES + URL "https://www.trustedfirmware.org/projects/mbed-tls" + DESCRIPTION + "A C library implementing cryptographic primitives, X.509 certificate manipulation, and the SSL/TLS and DTLS protocols." ) diff --git a/cmake/finders/FindOSS.cmake b/cmake/finders/FindOSS.cmake index c32d51946f2051..6c1f8339f2a481 100644 --- a/cmake/finders/FindOSS.cmake +++ b/cmake/finders/FindOSS.cmake @@ -34,10 +34,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_path( @@ -45,14 +41,17 @@ find_path( NAMES sys/soundcard.h HINTS ${PC_OSS_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "OSS include directory") + DOC "OSS include directory" +) set(OSS_VERSION ${CMAKE_HOST_SYSTEM_VERSION}) find_package_handle_standard_args( OSS REQUIRED_VARS OSS_INCLUDE_DIR - VERSION_VAR OSS_VERSION REASON_FAILURE_MESSAGE "Ensure that OSS is installed on the system.") + VERSION_VAR OSS_VERSION + REASON_FAILURE_MESSAGE "Ensure that OSS is installed on the system." +) mark_as_advanced(OSS_INCLUDE_DIR OSS_LIBRARY) if(OSS_FOUND) diff --git a/cmake/finders/FindPipeWire.cmake b/cmake/finders/FindPipeWire.cmake index d7c225027e7a91..377dcd776334a6 100644 --- a/cmake/finders/FindPipeWire.cmake +++ b/cmake/finders/FindPipeWire.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,7 +50,8 @@ find_path( HINTS ${PC_PipeWire_INCLUDE_DIRS} PATH_SUFFIXES pipewire-0.3 PATHS /usr/include /usr/local/include - DOC "PipeWire include directory") + DOC "PipeWire include directory" +) find_path( Libspa_INCLUDE_DIR @@ -62,20 +59,26 @@ find_path( HINTS ${PC_Libspa_INCLUDE_DIRS} PATH_SUFFIXES spa-0.2 PATHS /usr/include /usr/local/include - DOC "Libspa include directory") + DOC "Libspa include directory" +) find_library( PipeWire_LIBRARY NAMES pipewire-0.3 HINTS ${PC_PipeWire_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "PipeWire location") + DOC "PipeWire location" +) if(PC_PipeWire_VERSION VERSION_GREATER 0) set(PipeWire_VERSION ${PC_PipeWire_VERSION}) elseif(EXISTS "${PipeWire_INCLUDE_DIR}/pipewire/version.h") - file(STRINGS "${PipeWire_INCLUDE_DIR}/pipewire/version.h" _version_string - REGEX "^.*PW_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$") + file( + STRINGS + "${PipeWire_INCLUDE_DIR}/pipewire/version.h" + _version_string + REGEX "^.*PW_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$" + ) string(REGEX REPLACE ".*PW_MAJOR[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") string(REGEX REPLACE ".*PW_MINOR[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") string(REGEX REPLACE ".*PW_MICRO[ \t]+([0-9]+).*" "\\1" _version_micro "${_version_string}") @@ -94,7 +97,9 @@ endif() find_package_handle_standard_args( PipeWire REQUIRED_VARS PipeWire_LIBRARY PipeWire_INCLUDE_DIR Libspa_INCLUDE_DIR - VERSION_VAR PipeWire_VERSION REASON_FAILURE_MESSAGE "Ensure that PipeWire is installed on the system.") + VERSION_VAR PipeWire_VERSION + REASON_FAILURE_MESSAGE "Ensure that PipeWire is installed on the system." +) mark_as_advanced(PipeWire_LIBRARY PipeWire_INCLUDE_DIR) if(PipeWire_FOUND) @@ -109,14 +114,16 @@ if(PipeWire_FOUND) set_target_properties( PipeWire::PipeWire - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_PipeWire_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${PipeWire_INCLUDE_DIR};${Libspa_INCLUDE_DIR}" - VERSION ${PipeWire_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_PipeWire_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${PipeWire_INCLUDE_DIR};${Libspa_INCLUDE_DIR}" + VERSION ${PipeWire_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - PipeWire PROPERTIES - URL "https://www.pipewire.org" - DESCRIPTION "PipeWire - multimedia processing") + PipeWire + PROPERTIES URL "https://www.pipewire.org" DESCRIPTION "PipeWire - multimedia processing" +) diff --git a/cmake/finders/FindPulseAudio.cmake b/cmake/finders/FindPulseAudio.cmake index cbb035f6d858c3..d4aaa762831819 100644 --- a/cmake/finders/FindPulseAudio.cmake +++ b/cmake/finders/FindPulseAudio.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,22 +48,33 @@ find_path( NAMES pulse/pulseaudio.h HINTS ${PC_PulseAudio_INCLUDE_DIRS} PATHS /usr/include/ /usr/local/include - DOC "PulseAudio include directory") + DOC "PulseAudio include directory" +) find_library( PulseAudio_LIBRARY NAMES pulse HINTS ${PC_PulseAudio_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "PulseAudio location") + DOC "PulseAudio location" +) if(PC_PulseAudio_VERSION VERSION_GREATER 0) set(PulseAudio_VERSION ${PC_PulseAudio_VERSION}) elseif(EXISTS "${PulseAudio_INCLUDE_DIR}/version.h") - file(STRINGS "${PulseAudio_INCLUDE_DIR}/version.h" _VERSION_STRING - REGEX "^.*pa_get_headers_version\\(\\)[\t ]+\\(\".*\"\\)[ \t]*$") - string(REGEX REPLACE ".*pa_get_headers_version\\(\\)[\t ]+\\(\"([^\"]*)\"\\).*" "\\1" PulseAudio_VERSION - "${_VERSION_STRING}") + file( + STRINGS + "${PulseAudio_INCLUDE_DIR}/version.h" + _VERSION_STRING + REGEX "^.*pa_get_headers_version\\(\\)[\t ]+\\(\".*\"\\)[ \t]*$" + ) + string( + REGEX REPLACE + ".*pa_get_headers_version\\(\\)[\t ]+\\(\"([^\"]*)\"\\).*" + "\\1" + PulseAudio_VERSION + "${_VERSION_STRING}" + ) else() if(NOT PulseAudio_FIND_QUIETLY) message(AUTHOR_WARNING "Failed to find PulseAudio version.") @@ -79,7 +85,9 @@ endif() find_package_handle_standard_args( PulseAudio REQUIRED_VARS PulseAudio_INCLUDE_DIR PulseAudio_LIBRARY - VERSION_VAR PulseAudio_VERSION REASON_FAILURE_MESSAGE "Ensure that PulseAudio is installed on the system.") + VERSION_VAR PulseAudio_VERSION + REASON_FAILURE_MESSAGE "Ensure that PulseAudio is installed on the system." +) mark_as_advanced(PulseAudio_INCLUDE_DIR PulseAudio_LIBRARY) if(PulseAudio_FOUND) @@ -94,15 +102,19 @@ if(PulseAudio_FOUND) set_target_properties( PulseAudio::PulseAudio - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_PulseAudio_CFLAFGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${PulseAudio_INCLUDE_DIR}" - VERSION ${PulseAudio_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_PulseAudio_CFLAFGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${PulseAudio_INCLUDE_DIR}" + VERSION ${PulseAudio_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - PulseAudio PROPERTIES - URL "https://www.freedesktop.org/wiki/Software/PulseAudio/" - DESCRIPTION - "PulseAudio is a sound server system for POSIX OSes, meaning that it is a proxy for your sound applications.") + PulseAudio + PROPERTIES + URL "https://www.freedesktop.org/wiki/Software/PulseAudio/" + DESCRIPTION + "PulseAudio is a sound server system for POSIX OSes, meaning that it is a proxy for your sound applications." +) diff --git a/cmake/finders/FindSndio.cmake b/cmake/finders/FindSndio.cmake index 66702ba141506c..4ad15f673d1b9e 100644 --- a/cmake/finders/FindSndio.cmake +++ b/cmake/finders/FindSndio.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -52,14 +48,16 @@ find_path( NAMES sndio.h HINTS ${PC_Sndio_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Sndio include directory") + DOC "Sndio include directory" +) find_library( Sndio_LIBRARY NAMES sndio HINTS ${PC_Sndio_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Sndio location") + DOC "Sndio location" +) if(PC_Sndio_VERSION VERSION_GREATER 0) set(Sndio_VERSION ${PC_Sndio_VERSION}) @@ -73,7 +71,9 @@ endif() find_package_handle_standard_args( Sndio REQUIRED_VARS Sndio_LIBRARY Sndio_INCLUDE_DIR - VERSION_VAR Sndio_VERSION REASON_FAILURE_MESSAGE "Ensure that Sndio is installed on the system.") + VERSION_VAR Sndio_VERSION + REASON_FAILURE_MESSAGE "Ensure that Sndio is installed on the system." +) mark_as_advanced(Sndio_INCLUDE_DIR Sndio_LIBRARY) if(Sndio_FOUND) @@ -88,14 +88,18 @@ if(Sndio_FOUND) set_target_properties( Sndio::Sndio - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Sndio_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Sndio_INCLUDE_DIR}" - VERSION ${Sndio_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Sndio_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Sndio_INCLUDE_DIR}" + VERSION ${Sndio_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Sndio PROPERTIES - URL "https://www.sndio.org" - DESCRIPTION "Sndio is a small audio and MIDI framework part of the OpenBSD project and ported to FreeBSD.") + Sndio + PROPERTIES + URL "https://www.sndio.org" + DESCRIPTION "Sndio is a small audio and MIDI framework part of the OpenBSD project and ported to FreeBSD." +) diff --git a/cmake/finders/FindSysinfo.cmake b/cmake/finders/FindSysinfo.cmake index a2c101cf1da3f7..0b7ce76e438dd6 100644 --- a/cmake/finders/FindSysinfo.cmake +++ b/cmake/finders/FindSysinfo.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -52,14 +48,16 @@ find_path( NAMES sys/sysinfo.h HINTS ${PC_Sysinfo_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Sysinfo include directory") + DOC "Sysinfo include directory" +) find_library( Sysinfo_LIBRARY NAMES sysinfo libsysinfo HINTS ${PC_Sysinfo_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Sysinfo location") + DOC "Sysinfo location" +) if(PC_Sysinfo_VERSION VERSION_GREATER 0) set(Sysinfo_VERSION ${PC_Sysinfo_VERSION}) @@ -73,7 +71,9 @@ endif() find_package_handle_standard_args( Sysinfo REQUIRED_VARS Sysinfo_LIBRARY Sysinfo_INCLUDE_DIR - VERSION_VAR Sysinfo_VERSION REASON_FAILURE_MESSAGE "Ensure that Sysinfo is installed on the system.") + VERSION_VAR Sysinfo_VERSION + REASON_FAILURE_MESSAGE "Ensure that Sysinfo is installed on the system." +) mark_as_advanced(Sysinfo_INCLUDE_DIR Sysinfo_LIBRARY) if(Sysinfo_FOUND) @@ -88,8 +88,10 @@ if(Sysinfo_FOUND) set_target_properties( Sysinfo::Sysinfo - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Sysinfo_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Sysinfo_INCLUDE_DIR}" - VERSION ${Sysinfo_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Sysinfo_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Sysinfo_INCLUDE_DIR}" + VERSION ${Sysinfo_VERSION} + ) endif() endif() diff --git a/cmake/finders/FindUthash.cmake b/cmake/finders/FindUthash.cmake index fde6bd5d123212..e3e1d5f0d86191 100644 --- a/cmake/finders/FindUthash.cmake +++ b/cmake/finders/FindUthash.cmake @@ -31,25 +31,25 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) -find_path( - Uthash_INCLUDE_DIR - NAMES uthash.h - PATHS /usr/include /usr/local/include - DOC "uthash include directory") +find_path(Uthash_INCLUDE_DIR NAMES uthash.h PATHS /usr/include /usr/local/include DOC "uthash include directory") if(EXISTS "${Uthash_INCLUDE_DIR}/uthash.h") - file(STRINGS "${Uthash_INCLUDE_DIR}/uthash.h" _version_string - REGEX "#define[ \t]+UTHASH_VERSION[ \t]+[0-9]+\\.[0-9]+\\.[0-9]+") - - string(REGEX REPLACE "#define[ \t]+UTHASH_VERSION[ \t]+([0-9]+\\.[0-9]+\\.[0-9]+)" "\\1" Uthash_VERSION - "${_version_string}") + file( + STRINGS + "${Uthash_INCLUDE_DIR}/uthash.h" + _version_string + REGEX "#define[ \t]+UTHASH_VERSION[ \t]+[0-9]+\\.[0-9]+\\.[0-9]+" + ) + + string( + REGEX REPLACE + "#define[ \t]+UTHASH_VERSION[ \t]+([0-9]+\\.[0-9]+\\.[0-9]+)" + "\\1" + Uthash_VERSION + "${_version_string}" + ) else() if(NOT Uthash_FIND_QUIETLY) message(AUTHOR_WARNING "Failed to find uthash version.") @@ -66,7 +66,9 @@ endif() find_package_handle_standard_args( Uthash REQUIRED_VARS Uthash_INCLUDE_DIR - VERSION_VAR Uthash_VERSION REASON_FAILURE_MESSAGE "${Uthash_ERROR_REASON}") + VERSION_VAR Uthash_VERSION + REASON_FAILURE_MESSAGE "${Uthash_ERROR_REASON}" +) mark_as_advanced(Uthash_INCLUDE_DIR) unset(Uthash_ERROR_REASON) @@ -79,6 +81,6 @@ endif() include(FeatureSummary) set_package_properties( - Uthash PROPERTIES - URL "https://troydhanson.github.io/uthash" - DESCRIPTION "A hash table for C structures") + Uthash + PROPERTIES URL "https://troydhanson.github.io/uthash" DESCRIPTION "A hash table for C structures" +) diff --git a/cmake/finders/FindVPL.cmake b/cmake/finders/FindVPL.cmake index 21803212ac7062..e1378e604fb86f 100644 --- a/cmake/finders/FindVPL.cmake +++ b/cmake/finders/FindVPL.cmake @@ -37,11 +37,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) pkg_search_module(_VPL QUIET vpl) @@ -52,21 +47,24 @@ find_path( NAMES vpl/mfxstructures.h HINTS ${_VPL_INCLUDE_DIRS} ${_VPL_INCLUDE_DIRS} PATHS /usr/include /usr/local/include /opt/local/include /sw/include - DOC "VPL include directory") + DOC "VPL include directory" +) find_library( VPL_LIBRARY_RELEASE NAMES ${_VPL_LIBRARIES} ${_VPL_LIBRARIES} vpl HINTS ${_VPL_LIBRARY_DIRS} ${_VPL_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - DOC "VPL library location") + DOC "VPL library location" +) find_library( VPL_LIBRARY_DEBUG NAMES ${_VPL_LIBRARIES} ${_VPL_LIBRARIES} vpld HINTS ${_VPL_LIBRARY_DIRS} ${_VPL_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - DOC "VPL debug library location") + DOC "VPL debug library location" +) include(SelectLibraryConfigurations) select_library_configurations(VPL) @@ -75,7 +73,9 @@ include(FindPackageHandleStandardArgs) find_package_handle_standard_args( VPL REQUIRED_VARS VPL_LIBRARY VPL_INCLUDE_DIR - VERSION_VAR VPL_VERSION REASON_FAILURE_MESSAGE "${VPL_ERROR_REASON}") + VERSION_VAR VPL_VERSION + REASON_FAILURE_MESSAGE "${VPL_ERROR_REASON}" +) mark_as_advanced(VPL_INCLUDE_DIR VPL_LIBRARY) unset(VPL_ERROR_REASON) @@ -116,9 +116,11 @@ if(VPL_FOUND) set_target_properties( VPL::VPL - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${VPL_INCLUDE_DIRS}" - VERSION ${VPL_VERSION} - IMPORTED_CONFIGURATIONS Release) + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${VPL_INCLUDE_DIRS}" + VERSION ${VPL_VERSION} + IMPORTED_CONFIGURATIONS Release + ) if(VPL_LIBRARY_DEBUG) if(IS_ABSOLUTE "${VPL_LIBRARY_DEBUG}") @@ -126,18 +128,16 @@ if(VPL_FOUND) else() set_target_properties(VPL::VPL PROPERTIES IMPORTED_LIBNAME_DEBUG "${VPL_LIBRARY_DEBUG}") endif() - set_property( - TARGET VPL::VPL - APPEND - PROPERTY IMPORTED_CONFIGURATIONS Debug) + set_property(TARGET VPL::VPL APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug) endif() endif() endif() include(FeatureSummary) set_package_properties( - VPL PROPERTIES - URL "https://github.com/oneapi-src/oneVPL" - DESCRIPTION - "Intel® oneAPI Video Processing Library (oneVPL) supports AI visual inference, media delivery, cloud gaming, and virtual desktop infrastructure use cases by providing access to hardware accelerated video decode, encode, and frame processing capabilities on Intel® GPUs." + VPL + PROPERTIES + URL "https://github.com/oneapi-src/oneVPL" + DESCRIPTION + "Intel® oneAPI Video Processing Library (oneVPL) supports AI visual inference, media delivery, cloud gaming, and virtual desktop infrastructure use cases by providing access to hardware accelerated video decode, encode, and frame processing capabilities on Intel® GPUs." ) diff --git a/cmake/finders/FindWayland.cmake b/cmake/finders/FindWayland.cmake index ed43398c3cfb27..6cf01698389f60 100644 --- a/cmake/finders/FindWayland.cmake +++ b/cmake/finders/FindWayland.cmake @@ -66,11 +66,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -97,14 +92,16 @@ macro(Wayland_find_component component) NAMES ${COMPONENT_LIBRARY}.h HINTS ${PC_Wayland_${COMPONENT_NAME}_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "Wayland ${COMPONENT_NAME} include directory") + DOC "Wayland ${COMPONENT_NAME} include directory" + ) find_library( Wayland_${COMPONENT_NAME}_LIBRARY NAMES ${COMPONENT_LIBRARY} HINTS ${PC_Wayland_${COMPONENT_NAME}_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "Wayland ${COMPONENT_NAME} location") + DOC "Wayland ${COMPONENT_NAME} location" + ) if(PC_Wayland_${COMPONENT_NAME}_VERSION VERSION_GREATER 0) set(Wayland_${COMPONENT_NAME}_VERSION ${PC_Wayland_${COMPONENT_NAME}_VERSION}) @@ -124,8 +121,10 @@ macro(Wayland_find_component component) if(IS_ABSOLUTE "${Wayland_${COMPONENT_NAME}_LIBRARY}") add_library(Wayland::${COMPONENT_NAME} UNKNOWN IMPORTED) - set_property(TARGET Wayland::${COMPONENT_NAME} PROPERTY IMPORTED_LOCATION - "${Wayland_${COMPONENT_NAME}_LIBRARY}") + set_property( + TARGET Wayland::${COMPONENT_NAME} + PROPERTY IMPORTED_LOCATION "${Wayland_${COMPONENT_NAME}_LIBRARY}" + ) else() add_library(Wayland::${COMPONENT_NAME} INTERFACE IMPORTED) set_property(TARGET Wayland::${COMPONENT_NAME} PROPERTY IMPORTED_LIBNAME "${Wayland_${COMPONENT_NAME}_LIBRARY}") @@ -133,9 +132,11 @@ macro(Wayland_find_component component) set_target_properties( Wayland::${COMPONENT_NAME} - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Wayland_${COMPONENT_NAME}_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Wayland_${COMPONENT_NAME}_INCLUDE_DIR}" - VERSION ${Wayland_${COMPONENT_NAME}_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Wayland_${COMPONENT_NAME}_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Wayland_${COMPONENT_NAME}_INCLUDE_DIR}" + VERSION ${Wayland_${COMPONENT_NAME}_VERSION} + ) list(APPEND Wayland_COMPONENTS Wayland::${COMPONENT_NAME}) list(APPEND Wayland_INCLUDE_DIRS ${Wayland_${COMPONENT_NAME}_INCLUDE_DIR}) list(APPEND Wayland_LIBRARIES ${Wayland_${COMPONENT_NAME}_LIBRARY}) @@ -163,15 +164,18 @@ find_package_handle_standard_args( Wayland REQUIRED_VARS Wayland_LIBRARIES Wayland_INCLUDE_DIRS VERSION_VAR Wayland_VERSION - HANDLE_COMPONENTS REASON_FAILURE_MESSAGE "Ensure that Wayland is installed on the system.") + HANDLE_COMPONENTS + REASON_FAILURE_MESSAGE "Ensure that Wayland is installed on the system." +) unset(_Wayland_DEFAULT_COMPONENTS) unset(_Wayland_LIBRARIES) include(FeatureSummary) set_package_properties( - Wayland PROPERTIES - URL "https://wayland.freedesktop.org" - DESCRIPTION - "A replacement for the X11 window system protocol and architecture with the aim to be easier to develop, extend, and maintain." + Wayland + PROPERTIES + URL "https://wayland.freedesktop.org" + DESCRIPTION + "A replacement for the X11 window system protocol and architecture with the aim to be easier to develop, extend, and maintain." ) diff --git a/cmake/finders/FindWebsocketpp.cmake b/cmake/finders/FindWebsocketpp.cmake index 0806438a653ac8..5d928596c7e954 100644 --- a/cmake/finders/FindWebsocketpp.cmake +++ b/cmake/finders/FindWebsocketpp.cmake @@ -31,22 +31,22 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_path( Websocketpp_INCLUDE_DIR NAMES websocketpp/client.hpp PATHS /usr/include /usr/local/include - DOC "WebSocket++ include directory") + DOC "WebSocket++ include directory" +) if(EXISTS "${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp") - file(STRINGS "${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp" _version_string - REGEX "^.*(major|minor|patch)_version[ \t]+=[ \t]+[0-9]+") + file( + STRINGS + "${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp" + _version_string + REGEX "^.*(major|minor|patch)_version[ \t]+=[ \t]+[0-9]+" + ) string(REGEX REPLACE ".*major_version[ \t]+=[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") string(REGEX REPLACE ".*minor_version[ \t]+=[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") @@ -72,20 +72,26 @@ endif() find_package_handle_standard_args( Websocketpp REQUIRED_VARS Websocketpp_INCLUDE_DIR - VERSION_VAR Websocketpp_VERSION REASON_FAILURE_MESSAGE "${Websocketpp_ERROR_REASON}") + VERSION_VAR Websocketpp_VERSION + REASON_FAILURE_MESSAGE "${Websocketpp_ERROR_REASON}" +) mark_as_advanced(Websocketpp_INCLUDE_DIR) unset(Websocketpp_ERROR_REASON) if(Websocketpp_FOUND) if(NOT TARGET Websocketpp::Websocketpp) add_library(Websocketpp::Websocketpp INTERFACE IMPORTED) - set_target_properties(Websocketpp::Websocketpp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${Websocketpp_INCLUDE_DIR}") + set_target_properties( + Websocketpp::Websocketpp + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Websocketpp_INCLUDE_DIR}" + ) endif() endif() include(FeatureSummary) set_package_properties( - Websocketpp PROPERTIES - URL "https://www.zaphoyd.com/websocketpp/" - DESCRIPTION "WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket Protocol.") + Websocketpp + PROPERTIES + URL "https://www.zaphoyd.com/websocketpp/" + DESCRIPTION "WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket Protocol." +) diff --git a/cmake/finders/FindX11-xcb.cmake b/cmake/finders/FindX11-xcb.cmake index 3d7281f9fa86c4..d6dc52e457dd7f 100644 --- a/cmake/finders/FindX11-xcb.cmake +++ b/cmake/finders/FindX11-xcb.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,14 +48,16 @@ find_path( NAMES X11/Xlib-xcb.h HINTS ${PC_X11-xcb_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "X11-xcb include directory") + DOC "X11-xcb include directory" +) find_library( X11-xcb_LIBRARY NAMES X11-xcb HINTS ${PC_x11-xcb-LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "X11-xcb location") + DOC "X11-xcb location" +) if(PC_X11-xcb_VERSION VERSION_GREATER 0) set(X11-xcb_VERSION ${PC_X11-xcb_VERSION}) @@ -74,7 +71,9 @@ endif() find_package_handle_standard_args( X11-xcb REQUIRED_VARS X11-xcb_LIBRARY X11-xcb_INCLUDE_DIR - VERSION_VAR X11-xcb_VERSION REASON_FAILURE_MESSAGE "Ensure that X11-xcb is installed on the system.") + VERSION_VAR X11-xcb_VERSION + REASON_FAILURE_MESSAGE "Ensure that X11-xcb is installed on the system." +) mark_as_advanced(X11-xcb_INCLUDE_DIR X11-xcb_LIBRARY) if(X11-xcb_FOUND) @@ -89,16 +88,19 @@ if(X11-xcb_FOUND) set_target_properties( X11::x11-xcb - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_X11-xcb_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${X11-xcb_INCLUDE_DIR}" - VERSION ${X11-xcb_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_X11-xcb_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${X11-xcb_INCLUDE_DIR}" + VERSION ${X11-xcb_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - X11-xcb PROPERTIES - URL "https://www.X.org" - DESCRIPTION - "Provides functions needed by clients which take advantage of Xlib/XCB to mix calls to both Xlib and XCB over the same X connection." + X11-xcb + PROPERTIES + URL "https://www.X.org" + DESCRIPTION + "Provides functions needed by clients which take advantage of Xlib/XCB to mix calls to both Xlib and XCB over the same X connection." ) diff --git a/cmake/finders/FindXcb.cmake b/cmake/finders/FindXcb.cmake index 979471c026b1af..68db4bc2abc13e 100644 --- a/cmake/finders/FindXcb.cmake +++ b/cmake/finders/FindXcb.cmake @@ -134,12 +134,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-lint: disable=R0915 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -167,7 +161,8 @@ list( xcb-xtest xcb-xv xcb-xinput - xcb-xinerama) + xcb-xinerama +) list( APPEND _Xcb_HEADERS @@ -191,7 +186,8 @@ list( xtest.h xv.h xinput.h - xinerama.h) + xinerama.h +) if(NOT Xcb_FIND_COMPONENTS) set(Xcb_FIND_COMPONENTS ${_Xcb_DEFAULT_COMPONENTS}) @@ -212,14 +208,16 @@ macro(Xcb_find_component component) NAMES "xcb/${COMPONENT_HEADER}" HINTS ${PC_Xcb_${COMPONENT_NAME}_INCLUDE_DIRS} PATHS /usr/include /usr/local/include - DOC "XCB component ${COMPONENT_NAME} include directory") + DOC "XCB component ${COMPONENT_NAME} include directory" + ) find_library( Xcb_${COMPONENT_NAME}_LIBRARY NAMES "${COMPONENT_NAME}" HINTS ${PC_Xcb_${COMPONENT_NAME}_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "XCB component ${COMPONENT_NAME} location") + DOC "XCB component ${COMPONENT_NAME} location" + ) if(PC_Xcb_${COMPONENT_NAME}_VERSION VERSION_GREATER 0) set(Xcb_${COMPONENT_NAME}_VERSION ${PC_Xcb_${COMPONENT_NAME}_VERSION}) @@ -247,9 +245,11 @@ macro(Xcb_find_component component) set_target_properties( xcb::${COMPONENT_NAME} - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Xcb_${COMPONENT_NAME}_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Xcb_${COMPONENT_NAME}_INCLUDE_DIR}" - VERSION ${Xcb_${COMPONENT_NAME}_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Xcb_${COMPONENT_NAME}_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Xcb_${COMPONENT_NAME}_INCLUDE_DIR}" + VERSION ${Xcb_${COMPONENT_NAME}_VERSION} + ) list(APPEND Xcb_COMPONENTS xcb::${COMPONENT_NAME}) list(APPEND Xcb_LIBRARIES ${Xcb_${COMPONENT_NAME}_LIBRARY}) list(APPEND Xcb_INCLUDE_DIRS ${Xcb_${COMPONENT_NAME}_INCLUDE_DIR}) @@ -288,15 +288,18 @@ find_package_handle_standard_args( Xcb REQUIRED_VARS Xcb_LIBRARIES Xcb_INCLUDE_DIRS VERSION_VAR Xcb_VERSION - HANDLE_COMPONENTS REASON_FAILURE_MESSAGE "Ensure xcb is installed on the system.") + HANDLE_COMPONENTS + REASON_FAILURE_MESSAGE "Ensure xcb is installed on the system." +) unset(_Xcb_DEFAULT_COMPONENTS) unset(_Xcb_HEADERS) include(FeatureSummary) set_package_properties( - Xcb PROPERTIES - URL "https://xcb.freedesktop.org" - DESCRIPTION - "A replacement for Xlib featuring a small footprint, latency hiding, direct access to the protocol, improved threading support, and extensibility." + Xcb + PROPERTIES + URL "https://xcb.freedesktop.org" + DESCRIPTION + "A replacement for Xlib featuring a small footprint, latency hiding, direct access to the protocol, improved threading support, and extensibility." ) diff --git a/cmake/finders/FindXkbcommon.cmake b/cmake/finders/FindXkbcommon.cmake index 1071c4ec8a89fe..ba0fa99f80fb3b 100644 --- a/cmake/finders/FindXkbcommon.cmake +++ b/cmake/finders/FindXkbcommon.cmake @@ -36,11 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -54,14 +49,16 @@ find_path( HINTS ${PC_Xkbcommon_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES xkbcommon - DOC "xkbcommon include directory") + DOC "xkbcommon include directory" +) find_library( Xkbcommon_LIBRARY NAMES xkbcommon libxkbcommon HINTS ${PC_Xkbcommon_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "xkbcommon location") + DOC "xkbcommon location" +) if(PC_Xkbcommon_VERSION VERSION_GREATER 0) set(Xkbcommon_VERSION ${PC_Xkbcommon_VERSION}) @@ -75,7 +72,9 @@ endif() find_package_handle_standard_args( Xkbcommon REQUIRED_VARS Xkbcommon_LIBRARY Xkbcommon_INCLUDE_DIR - VERSION_VAR Xkbcommon_VERSION REASON_FAILURE_MESSAGE "Ensure that xkbcommon is installed on the system.") + VERSION_VAR Xkbcommon_VERSION + REASON_FAILURE_MESSAGE "Ensure that xkbcommon is installed on the system." +) mark_as_advanced(Xkbcommon_INCLUDE_DIR Xkbcommon_LIBRARY) if(Xkbcommon_FOUND) @@ -90,16 +89,19 @@ if(Xkbcommon_FOUND) set_target_properties( xkbcommon::xkbcommon - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_Xkbcommon_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${Xkbcommon_INCLUDE_DIR}" - VERSION ${Xkbcommon_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_Xkbcommon_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${Xkbcommon_INCLUDE_DIR}" + VERSION ${Xkbcommon_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Xkbcommon PROPERTIES - URL "https://www.xkbcommon.org" - DESCRIPTION - "A library for handling of keyboard descriptions, including loading them from disk, parsing them and handling their state." + Xkbcommon + PROPERTIES + URL "https://www.xkbcommon.org" + DESCRIPTION + "A library for handling of keyboard descriptions, including loading them from disk, parsing them and handling their state." ) diff --git a/cmake/finders/Findjansson.cmake b/cmake/finders/Findjansson.cmake index 07d6c8918b63c9..7624be318a3c9c 100644 --- a/cmake/finders/Findjansson.cmake +++ b/cmake/finders/Findjansson.cmake @@ -36,10 +36,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -53,7 +49,8 @@ macro(jansson_set_soname) execute_process( COMMAND sh -c "otool -D '${jansson_LIBRARY}' | grep -v '${jansson_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET jansson::jansson PROPERTY IMPORTED_SONAME "${_output}") @@ -62,7 +59,8 @@ macro(jansson_set_soname) execute_process( COMMAND sh -c "objdump -p '${jansson_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -80,7 +78,8 @@ find_path( HINTS ${PC_jansson_INCLUDE_DIR} PATHS /usr/include /usr/local/include PATH_SUFFIXES jansson-2.1 - DOC "jansson include directory") + DOC "jansson include directory" +) if(PC_jansson_VERSION VERSION_GREATER 0) set(jansson_VERSION ${PC_jansson_VERSION}) @@ -99,7 +98,8 @@ find_library( NAMES jansson HINTS ${PC_jansson_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "jansson location") + DOC "jansson location" +) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") set(jansson_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") @@ -110,7 +110,9 @@ endif() find_package_handle_standard_args( jansson REQUIRED_VARS jansson_LIBRARY jansson_INCLUDE_DIR - VERSION_VAR jansson_VERSION REASON_FAILURE_MESSAGE "${jansson_ERROR_REASON}") + VERSION_VAR jansson_VERSION + REASON_FAILURE_MESSAGE "${jansson_ERROR_REASON}" +) mark_as_advanced(jansson_INCLUDE_DIR jansson_LIBRARY) unset(jansson_ERROR_REASON) @@ -127,14 +129,18 @@ if(jansson_FOUND) jansson_set_soname() set_target_properties( jansson::jansson - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_jansson_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${jansson_INCLUDE_DIR}" - VERSION ${jansson_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_jansson_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${jansson_INCLUDE_DIR}" + VERSION ${jansson_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - jansson PROPERTIES - URL "https://www.digip.org/jansson/" - DESCRIPTION "A C library for encoding, decoding, and manipulating JSON data.") + jansson + PROPERTIES + URL "https://www.digip.org/jansson/" + DESCRIPTION "A C library for encoding, decoding, and manipulating JSON data." +) diff --git a/cmake/finders/Findqrcodegencpp.cmake b/cmake/finders/Findqrcodegencpp.cmake index 6ee0638595f27e..a7259511557834 100644 --- a/cmake/finders/Findqrcodegencpp.cmake +++ b/cmake/finders/Findqrcodegencpp.cmake @@ -33,11 +33,6 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) find_package(PkgConfig QUIET) @@ -51,7 +46,8 @@ macro(qrcodegencpp_set_soname) execute_process( COMMAND sh -c "otool -D '${qrcodegencpp_LIBRARY}' | grep -v '${qrcodegencpp_LIBRARY}'" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_SONAME "${_output}") @@ -60,7 +56,8 @@ macro(qrcodegencpp_set_soname) execute_process( COMMAND sh -c "${CMAKE_OBJDUMP} -p '${qrcodegencpp_LIBRARY}' | grep SONAME" OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) + RESULT_VARIABLE _result + ) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") @@ -83,7 +80,8 @@ macro(qrcodegencpp_find_dll) qrcodegencpp_LIBRARY NAMES qrcodegencpp.dll HINTS ${_implib_path} ${_bin_path} - DOC "qrcodegencpp DLL location") + DOC "qrcodegencpp DLL location" + ) if(NOT qrcodegencpp_LIBRARY) set(qrcodegencpp_LIBRARY "${qrcodegencpp_IMPLIB}") @@ -99,7 +97,8 @@ find_path( HINTS ${PC_qrcodegencpp_INCLUDE_DIRS} PATHS /usr/include /usr/local/include PATH_SUFFIXES qrcodegencpp qrcodegen - DOC "qrcodegencpp include directory") + DOC "qrcodegencpp include directory" +) if(PC_qrcodegencpp_VERSION VERSION_GREATER 0) set(qrcodegencpp_VERSION ${PC_qrcodegencpp_VERSION}) @@ -111,10 +110,7 @@ else() endif() if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") - find_library( - qrcodegencpp_IMPLIB - NAMES qrcodegencpp qrcodegencpp - DOC "qrcodegencpp import library location") + find_library(qrcodegencpp_IMPLIB NAMES qrcodegencpp qrcodegencpp DOC "qrcodegencpp import library location") qrcodegencpp_find_dll() else() @@ -123,7 +119,8 @@ else() NAMES qrcodegencpp qrcodegencpp HINTS ${PC_qrcodegencpp_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib - DOC "qrcodegencpp location") + DOC "qrcodegencpp location" + ) endif() if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") @@ -135,7 +132,9 @@ endif() find_package_handle_standard_args( qrcodegencpp REQUIRED_VARS qrcodegencpp_LIBRARY qrcodegencpp_INCLUDE_DIR - VERSION_VAR qrcodegencpp_VERSION REASON_FAILURE_MESSAGE "${qrcodegencpp_ERROR_REASON}") + VERSION_VAR qrcodegencpp_VERSION + REASON_FAILURE_MESSAGE "${qrcodegencpp_ERROR_REASON}" +) mark_as_advanced(qrcodegencpp_INCLUDE_DIR qrcodegencpp_LIBRARY qrcodegencpp_IMPLIB) unset(qrcodegencpp_ERROR_REASON) @@ -161,14 +160,18 @@ if(qrcodegencpp_FOUND) qrcodegencpp_set_soname() set_target_properties( qrcodegencpp::qrcodegencpp - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_qrcodegencpp_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${qrcodegencpp_INCLUDE_DIR}" - VERSION ${qrcodegencpp_VERSION}) + PROPERTIES + INTERFACE_COMPILE_OPTIONS "${PC_qrcodegencpp_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "${qrcodegencpp_INCLUDE_DIR}" + VERSION ${qrcodegencpp_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - qrcodegencpp PROPERTIES - URL "https://www.nayuki.io/page/qr-code-generator-library" - DESCRIPTION "This project aims to be the best, clearest library for generating QR Codes in C++.") + qrcodegencpp + PROPERTIES + URL "https://www.nayuki.io/page/qr-code-generator-library" + DESCRIPTION "This project aims to be the best, clearest library for generating QR Codes in C++." +) diff --git a/cmake/linux/compilerconfig.cmake b/cmake/linux/compilerconfig.cmake index bc218df379499e..c88857fdea24bc 100644 --- a/cmake/linux/compilerconfig.cmake +++ b/cmake/linux/compilerconfig.cmake @@ -9,36 +9,37 @@ option(ENABLE_COMPILER_TRACE "Enable Clang time-trace (requires Clang and Ninja) mark_as_advanced(ENABLE_COMPILER_TRACE) # gcc options for C -set(_obs_gcc_c_options - # cmake-format: sortable - $<$:-Wno-error=deprecated-declarations> - -fno-strict-aliasing - -fopenmp-simd - -Wdeprecated-declarations - -Wempty-body - -Wenum-conversion - -Werror=return-type - -Wextra - -Wformat - -Wformat-security - -Wno-conversion - -Wno-float-conversion - -Wno-implicit-fallthrough - -Wno-missing-braces - -Wno-missing-field-initializers - -Wno-shadow - -Wno-sign-conversion - -Wno-trigraphs - -Wno-unknown-pragmas - -Wno-unused-function - -Wno-unused-label - -Wparentheses - -Wuninitialized - -Wunreachable-code - -Wunused-parameter - -Wunused-value - -Wunused-variable - -Wvla) +set( + _obs_gcc_c_options + $<$:-Wno-error=deprecated-declarations> + -fno-strict-aliasing + -fopenmp-simd + -Wdeprecated-declarations + -Wempty-body + -Wenum-conversion + -Werror=return-type + -Wextra + -Wformat + -Wformat-security + -Wno-conversion + -Wno-float-conversion + -Wno-implicit-fallthrough + -Wno-missing-braces + -Wno-missing-field-initializers + -Wno-shadow + -Wno-sign-conversion + -Wno-trigraphs + -Wno-unknown-pragmas + -Wno-unused-function + -Wno-unused-label + -Wparentheses + -Wuninitialized + -Wunreachable-code + -Wunused-parameter + -Wunused-value + -Wunused-variable + -Wvla +) add_compile_options( -fopenmp-simd @@ -47,12 +48,15 @@ add_compile_options( "$<$:${_obs_gcc_c_options}>" "$<$:-Winvalid-offsetof;-Wno-overloaded-virtual>" "$<$:${_obs_clang_c_options}>" - "$<$:${_obs_clang_cxx_options}>") + "$<$:${_obs_clang_cxx_options}>" +) # Add support for color diagnostics and CMake switch for warnings as errors to CMake < 3.24 if(CMAKE_VERSION VERSION_LESS 3.24.0) - add_compile_options($<$:-fcolor-diagnostics> - $<$:-fcolor-diagnostics>) + add_compile_options( + $<$:-fcolor-diagnostics> + $<$:-fcolor-diagnostics> + ) if(CMAKE_COMPILE_WARNING_AS_ERROR) add_compile_options(-Werror) endif() @@ -82,9 +86,7 @@ endif() if(ENABLE_COMPILER_TRACE AND CMAKE_GENERATOR STREQUAL "Ninja") add_compile_options($<$:-ftime-trace> $<$:-ftime-trace>) else() - set(ENABLE_COMPILER_TRACE - OFF - CACHE STRING "Enable Clang time-trace (required Clang and Ninja)" FORCE) + set(ENABLE_COMPILER_TRACE OFF CACHE STRING "Enable Clang time-trace (required Clang and Ninja)" FORCE) endif() add_compile_definitions($<$:DEBUG> $<$:_DEBUG> SIMDE_ENABLE_OPENMP) diff --git a/cmake/linux/cpackconfig.cmake b/cmake/linux/cpackconfig.cmake index 6a45f8346c2702..bc1883b8599e72 100644 --- a/cmake/linux/cpackconfig.cmake +++ b/cmake/linux/cpackconfig.cmake @@ -1,9 +1,5 @@ # OBS CMake Linux CPack configuration module -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-format: on - include_guard(GLOBAL) include(cpackconfig_common) @@ -35,35 +31,36 @@ elseif(OS_FREEBSD) set(CPACK_FREEBSD_PACKAGE_MAINTAINER "${OBS_COMPANY_NAME}") set(CPACK_FREEBSD_PACKAGE_LICENSE "GPLv2") - set(CPACK_FREEBSD_PACKAGE_DEPS - # cmake-format: sortable - "audio/alsa-lib" - "audio/fdk-aac" - "audio/jack" - "audio/pulseaudio" - "audio/sndio" - "devel/jansson" - "devel/libpci" - "devel/libsysinfo" - "devel/nlohmann-json" - "devel/qt6-base" - "devel/qt6-svg" - "devel/swig" - "devel/websocketpp" - "ftp/curl" - "graphics/mesa-libs" - "graphics/qr-code-generator" - "lang/luajit" - "lang/python39" - "misc/e2fsprogs-libuuid" - "multimedia/ffmpeg" - "multimedia/librist" - "multimedia/pipewire" - "multimedia/v4l_compat" - "multimedia/vlc" - "net/asio" - "www/libdatachannel" - "www/srt") + set( + CPACK_FREEBSD_PACKAGE_DEPS + "audio/alsa-lib" + "audio/fdk-aac" + "audio/jack" + "audio/pulseaudio" + "audio/sndio" + "devel/jansson" + "devel/libpci" + "devel/libsysinfo" + "devel/nlohmann-json" + "devel/qt6-base" + "devel/qt6-svg" + "devel/swig" + "devel/websocketpp" + "ftp/curl" + "graphics/mesa-libs" + "graphics/qr-code-generator" + "lang/luajit" + "lang/python39" + "misc/e2fsprogs-libuuid" + "multimedia/ffmpeg" + "multimedia/librist" + "multimedia/pipewire" + "multimedia/v4l_compat" + "multimedia/vlc" + "net/asio" + "www/libdatachannel" + "www/srt" + ) endif() include(CPack) diff --git a/cmake/linux/helpers.cmake b/cmake/linux/helpers.cmake index 05808c2b26e566..e390405a640a33 100644 --- a/cmake/linux/helpers.cmake +++ b/cmake/linux/helpers.cmake @@ -1,9 +1,5 @@ # OBS CMake Linux helper functions module -# cmake-format: off -# cmake-lint: disable=C0301 -# cmake-format: on - include_guard(GLOBAL) include(helpers_common) @@ -31,10 +27,12 @@ function(set_target_properties_obs target) TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" - "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$" + "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" COMMENT "Copy ${target} to binary directory" - VERBATIM) + VERBATIM + ) if(target STREQUAL obs-studio) get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES) @@ -42,7 +40,8 @@ function(set_target_properties_obs target) add_dependencies(${target} ${obs_executables} ${obs_modules}) target_add_resource(${target} "${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS" - "${OBS_DATA_DESTINATION}/obs-studio/authors") + "${OBS_DATA_DESTINATION}/obs-studio/authors" + ) elseif(target STREQUAL browser-helper) set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target}) return() @@ -50,39 +49,47 @@ function(set_target_properties_obs target) set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target}) endif() - set_target_properties(${target} PROPERTIES BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" - INSTALL_RPATH "${OBS_EXECUTABLE_RPATH}") + set_target_properties( + ${target} + PROPERTIES + BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" + INSTALL_RPATH "${OBS_EXECUTABLE_RPATH}" + ) elseif(target_type STREQUAL SHARED_LIBRARY) set_target_properties( ${target} - PROPERTIES VERSION ${OBS_VERSION_CANONICAL} - SOVERSION ${OBS_VERSION_MAJOR} - BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" - INSTALL_RPATH "${OBS_LIBRARY_RPATH}") + PROPERTIES + VERSION ${OBS_VERSION_CANONICAL} + SOVERSION ${OBS_VERSION_MAJOR} + BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" + INSTALL_RPATH "${OBS_LIBRARY_RPATH}" + ) install( TARGETS ${target} LIBRARY DESTINATION "${OBS_LIBRARY_DESTINATION}" COMPONENT Runtime - PUBLIC_HEADER - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT Development - EXCLUDE_FROM_ALL) + PUBLIC_HEADER DESTINATION "${OBS_INCLUDE_DESTINATION}" COMPONENT Development EXCLUDE_FROM_ALL + ) add_custom_command( TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" - "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}/" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" - "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}/" + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$" + "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}/" + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$" + "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}/" COMMENT "Copy ${target} to library directory (${OBS_LIBRARY_DESTINATION})" - VERBATIM) + VERBATIM + ) if(target STREQUAL libobs OR target STREQUAL obs-frontend-api) install( FILES "$/$$.so.0" - DESTINATION "${OBS_LIBRARY_DESTINATION}") + DESTINATION "${OBS_LIBRARY_DESTINATION}" + ) add_custom_command( TARGET ${target} @@ -95,19 +102,21 @@ function(set_target_properties_obs target) "${CMAKE_COMMAND}" -E copy_if_different "$/$$.so.0" "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" - COMMENT "Create symlink for legacy ${target}") + COMMENT "Create symlink for legacy ${target}" + ) endif() - elseif(target_type STREQUAL MODULE_LIBRARY) if(target STREQUAL obs-browser) set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_VERSION_MAJOR}) else() set_target_properties( ${target} - PROPERTIES VERSION 0 - SOVERSION ${OBS_VERSION_MAJOR} - BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" - INSTALL_RPATH "${OBS_MODULE_RPATH}") + PROPERTIES + VERSION 0 + SOVERSION ${OBS_VERSION_MAJOR} + BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" + INSTALL_RPATH "${OBS_MODULE_RPATH}" + ) endif() if(${target} STREQUAL obspython OR ${target} STREQUAL obslua) @@ -119,32 +128,36 @@ function(set_target_properties_obs target) install( TARGETS ${target} - LIBRARY DESTINATION "${plugin_destination}" - COMPONENT Runtime - NAMELINK_COMPONENT Development) + LIBRARY DESTINATION "${plugin_destination}" COMPONENT Runtime NAMELINK_COMPONENT Development + ) add_custom_command( TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${plugin_destination}" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" - "${OBS_OUTPUT_DIR}/$/${plugin_destination}" + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$" + "${OBS_OUTPUT_DIR}/$/${plugin_destination}" COMMENT "Copy ${target} to plugin directory (${plugin_destination})" - VERBATIM) + VERBATIM + ) if(${target} STREQUAL obspython) add_custom_command( TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_SCRIPT_PLUGIN_DESTINATION}/" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$/obspython.py" - "${OBS_OUTPUT_DIR}/$/${OBS_SCRIPT_PLUGIN_DESTINATION}/" - COMMENT "Add obspython import module") + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$/obspython.py" + "${OBS_OUTPUT_DIR}/$/${OBS_SCRIPT_PLUGIN_DESTINATION}/" + COMMENT "Add obspython import module" + ) install( FILES "$/obspython.py" DESTINATION "${OBS_SCRIPT_PLUGIN_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) elseif(${target} STREQUAL obs-browser) message(DEBUG "Add Chromium Embedded Framework to project for obs-browser plugin...") if(TARGET CEF::Library) @@ -167,32 +180,37 @@ function(set_target_properties_obs target) "${CMAKE_COMMAND}" -E copy_if_different "${cef_root_location}/Resources/chrome_100_percent.pak" "${cef_root_location}/Resources/chrome_200_percent.pak" "${cef_root_location}/Resources/icudtl.dat" "${cef_root_location}/Resources/resources.pak" "${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION}/" - COMMAND "${CMAKE_COMMAND}" -E copy_directory "${cef_root_location}/Resources/locales" - "${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION}/locales" - COMMENT "Add Chromium Embedded Framwork to library directory") + COMMAND + "${CMAKE_COMMAND}" -E copy_directory "${cef_root_location}/Resources/locales" + "${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION}/locales" + COMMENT "Add Chromium Embedded Framwork to library directory" + ) install( - FILES "${imported_location}" - "${cef_location}/chrome-sandbox" - "${cef_location}/libEGL.so" - "${cef_location}/libGLESv2.so" - "${cef_location}/libvk_swiftshader.so" - "${cef_location}/libvulkan.so.1" - "${cef_location}/snapshot_blob.bin" - "${cef_location}/v8_context_snapshot.bin" - "${cef_location}/vk_swiftshader_icd.json" - "${cef_root_location}/Resources/chrome_100_percent.pak" - "${cef_root_location}/Resources/chrome_200_percent.pak" - "${cef_root_location}/Resources/icudtl.dat" - "${cef_root_location}/Resources/resources.pak" + FILES + "${imported_location}" + "${cef_location}/chrome-sandbox" + "${cef_location}/libEGL.so" + "${cef_location}/libGLESv2.so" + "${cef_location}/libvk_swiftshader.so" + "${cef_location}/libvulkan.so.1" + "${cef_location}/snapshot_blob.bin" + "${cef_location}/v8_context_snapshot.bin" + "${cef_location}/vk_swiftshader_icd.json" + "${cef_root_location}/Resources/chrome_100_percent.pak" + "${cef_root_location}/Resources/chrome_200_percent.pak" + "${cef_root_location}/Resources/icudtl.dat" + "${cef_root_location}/Resources/resources.pak" DESTINATION "${OBS_PLUGIN_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) install( DIRECTORY "${cef_root_location}/Resources/locales" DESTINATION "${OBS_PLUGIN_DESTINATION}" USE_SOURCE_PERMISSIONS - COMPONENT Runtime) + COMPONENT Runtime + ) endif() endif() endif() @@ -209,8 +227,12 @@ function(target_install_resources target) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") foreach(data_file IN LISTS data_files) - cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE - relative_path) + cmake_path( + RELATIVE_PATH + data_file + BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" + OUTPUT_VARIABLE relative_path + ) cmake_path(GET relative_path PARENT_PATH relative_path) target_sources(${target} PRIVATE "${data_file}") source_group("Resources/${relative_path}" FILES "${data_file}") @@ -229,16 +251,19 @@ function(target_install_resources target) DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" DESTINATION "${target_destination}" USE_SOURCE_PERMISSIONS - COMPONENT Runtime) + COMPONENT Runtime + ) add_custom_command( TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${target_destination}" - COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/data" - "${OBS_OUTPUT_DIR}/$/${target_destination}" + COMMAND + "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/data" + "${OBS_OUTPUT_DIR}/$/${target_destination}" COMMENT "Copy ${target} resources to data directory (${target_destination})" - VERBATIM) + VERBATIM + ) endif() endfunction() @@ -257,10 +282,7 @@ function(target_add_resource target resource) message(DEBUG "Add resource ${resource} to target ${target} at destination ${target_destination}...") - install( - FILES "${resource}" - DESTINATION "${target_destination}" - COMPONENT Runtime) + install(FILES "${resource}" DESTINATION "${target_destination}" COMPONENT Runtime) add_custom_command( TARGET ${target} @@ -268,7 +290,8 @@ function(target_add_resource target resource) COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${target_destination}/" COMMAND "${CMAKE_COMMAND}" -E copy "${resource}" "${OBS_OUTPUT_DIR}/$/${target_destination}/" COMMENT "Copy ${target} resource ${resource} to library directory (${target_destination})" - VERBATIM) + VERBATIM + ) endfunction() # target_export: Helper function to export target as CMake package @@ -282,12 +305,15 @@ function(target_export target) install(CODE "set(OBS_VERSION_CANONICAL ${OBS_VERSION_CANONICAL})" COMPONENT Development) install(CODE "set(CMAKE_C_STANDARD ${CMAKE_C_STANDARD})" COMPONENT Development) install( - CODE "configure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux/${target}.pc.in\" \"${CMAKE_CURRENT_BINARY_DIR}/${target}.pc\" @ONLY)" - COMPONENT Development) + CODE + "configure_file(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux/${target}.pc.in\" \"${CMAKE_CURRENT_BINARY_DIR}/${target}.pc\" @ONLY)" + COMPONENT Development + ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}.pc" DESTINATION "${OBS_LIBRARY_DESTINATION}/pkgconfig" - COMPONENT Development) + COMPONENT Development + ) endif() endfunction() diff --git a/cmake/macos/buildspec.cmake b/cmake/macos/buildspec.cmake index 3927faaf050ff2..f4670c21ca23e8 100644 --- a/cmake/macos/buildspec.cmake +++ b/cmake/macos/buildspec.cmake @@ -22,8 +22,11 @@ function(_check_dependencies_macos) _check_dependencies() - execute_process(COMMAND "xattr" -r -d com.apple.quarantine "${dependencies_dir}/${destination}" - RESULT_VARIABLE result COMMAND_ERROR_IS_FATAL ANY) + execute_process( + COMMAND "xattr" -r -d com.apple.quarantine "${dependencies_dir}/${destination}" + RESULT_VARIABLE result + COMMAND_ERROR_IS_FATAL ANY + ) endfunction() _check_dependencies_macos() diff --git a/cmake/macos/compilerconfig.cmake b/cmake/macos/compilerconfig.cmake index d193e1feadadb3..584b5a05a0e890 100644 --- a/cmake/macos/compilerconfig.cmake +++ b/cmake/macos/compilerconfig.cmake @@ -16,9 +16,7 @@ add_compile_options("$<$>:-fopenmp-simd>") # Enable selection between arm64 and x86_64 targets if(NOT CMAKE_OSX_ARCHITECTURES) - set(CMAKE_OSX_ARCHITECTURES - arm64 - CACHE STRING "Build architectures for macOS" FORCE) + set(CMAKE_OSX_ARCHITECTURES arm64 CACHE STRING "Build architectures for macOS" FORCE) endif() set_property(CACHE CMAKE_OSX_ARCHITECTURES PROPERTY STRINGS arm64 x86_64) @@ -31,8 +29,10 @@ set(_obs_macos_current_sdk ${CMAKE_MATCH_1}) message(DEBUG "macOS SDK version: ${_obs_macos_current_sdk}") if(_obs_macos_current_sdk VERSION_LESS _obs_macos_minimum_sdk) message( - FATAL_ERROR "Your macOS SDK version (${_obs_macos_current_sdk}) is too low. " - "The macOS ${_obs_macos_minimum_sdk} SDK (Xcode ${_obs_macos_minimum_xcode}) is required to build OBS.") + FATAL_ERROR + "Your macOS SDK version (${_obs_macos_current_sdk}) is too low. " + "The macOS ${_obs_macos_minimum_sdk} SDK (Xcode ${_obs_macos_minimum_xcode}) is required to build OBS." + ) endif() unset(_obs_macos_current_sdk) unset(_obs_macos_minimum_sdk) @@ -69,12 +69,15 @@ string(APPEND CMAKE_OBJCXX_FLAGS_RELEASE " -g") add_compile_definitions( $<$>:$<$:DEBUG>> - $<$>:$<$:_DEBUG>> $<$>:SIMDE_ENABLE_OPENMP>) + $<$>:$<$:_DEBUG>> + $<$>:SIMDE_ENABLE_OPENMP> +) if(ENABLE_COMPILER_TRACE) add_compile_options( $<$>:-ftime-trace> "$<$:SHELL:-Xfrontend -debug-time-expression-type-checking>" - "$<$:SHELL:-Xfrontend -debug-time-function-bodies>") + "$<$:SHELL:-Xfrontend -debug-time-function-bodies>" + ) add_link_options(LINKER:-print_statistics) endif() diff --git a/cmake/macos/defaults.cmake b/cmake/macos/defaults.cmake index 8bc947e16229b9..c8b668392dcfdd 100644 --- a/cmake/macos/defaults.cmake +++ b/cmake/macos/defaults.cmake @@ -4,15 +4,11 @@ include_guard(GLOBAL) # Set empty codesigning team if not specified as cache variable if(NOT OBS_CODESIGN_TEAM) - set(OBS_CODESIGN_TEAM - "" - CACHE STRING "OBS code signing team for macOS" FORCE) + set(OBS_CODESIGN_TEAM "" CACHE STRING "OBS code signing team for macOS" FORCE) # Set ad-hoc codesigning identity if not specified as cache variable if(NOT OBS_CODESIGN_IDENTITY) - set(OBS_CODESIGN_IDENTITY - "-" - CACHE STRING "OBS code signing identity for macOS" FORCE) + set(OBS_CODESIGN_IDENTITY "-" CACHE STRING "OBS code signing identity for macOS" FORCE) endif() endif() @@ -46,5 +42,8 @@ set(OBS_LIBRARY_DESTINATION "lib") set(OBS_INCLUDE_DESTINATION "include/obs") set(OBS_CMAKE_DESTINATION "lib/cmake") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/package.applescript" - "${CMAKE_CURRENT_BINARY_DIR}/package.applescript" @ONLY) +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/package.applescript" + "${CMAKE_CURRENT_BINARY_DIR}/package.applescript" + @ONLY +) diff --git a/cmake/macos/helpers.cmake b/cmake/macos/helpers.cmake index da731ccdace2fa..cdfd88ae3ac870 100644 --- a/cmake/macos/helpers.cmake +++ b/cmake/macos/helpers.cmake @@ -1,13 +1,5 @@ # OBS CMake macOS helper functions module -# cmake-format: off -# cmake-lint: disable=C0301 -# cmake-lint: disable=C0307 -# cmake-lint: disable=E1126 -# cmake-lint: disable=R0912 -# cmake-lint: disable=R0915 -# cmake-format: on - include_guard(GLOBAL) include(helpers_common) @@ -50,15 +42,16 @@ function(set_target_properties_obs target) if(target STREQUAL obs-studio) set_target_properties( ${target} - PROPERTIES OUTPUT_NAME OBS - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in" - XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY YES - XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES - XCODE_EMBED_PLUGINS_REMOVE_HEADERS_ON_COPY YES - XCODE_EMBED_PLUGINS_CODE_SIGN_ON_COPY YES) - - # cmake-format: off + PROPERTIES + OUTPUT_NAME OBS + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in" + XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY YES + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES + XCODE_EMBED_PLUGINS_REMOVE_HEADERS_ON_COPY YES + XCODE_EMBED_PLUGINS_CODE_SIGN_ON_COPY YES + ) + set_target_xcode_properties( ${target} PROPERTIES PRODUCT_BUNDLE_IDENTIFIER com.obsproject.obs-studio @@ -74,49 +67,41 @@ function(set_target_properties_obs target) INFOPLIST_KEY_CFBundleDisplayName "OBS Studio" INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2012-${CURRENT_YEAR} Lain Bailey" INFOPLIST_KEY_NSCameraUsageDescription "OBS needs to access the camera to enable camera sources to work." - INFOPLIST_KEY_NSMicrophoneUsageDescription "OBS needs to access the microphone to enable audio input.") - # cmake-format: on + INFOPLIST_KEY_NSMicrophoneUsageDescription "OBS needs to access the microphone to enable audio input." + ) get_property(obs_dependencies GLOBAL PROPERTY _OBS_DEPENDENCIES) add_dependencies(${target} ${obs_dependencies}) get_property(obs_frameworks GLOBAL PROPERTY _OBS_FRAMEWORKS) - set_property( - TARGET ${target} - APPEND - PROPERTY XCODE_EMBED_FRAMEWORKS ${obs_frameworks}) + set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${obs_frameworks}) if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY) - set_property( - TARGET ${target} - APPEND - PROPERTY XCODE_EMBED_FRAMEWORKS ${SPARKLE}) + set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${SPARKLE}) endif() if(TARGET mac-syphon) - set_property( - TARGET ${target} - APPEND - PROPERTY XCODE_EMBED_FRAMEWORKS ${SYPHON}) + set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${SYPHON}) endif() get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES) add_dependencies(${target} ${obs_executables}) foreach(executable IN LISTS obs_executables) set_target_xcode_properties(${executable} PROPERTIES INSTALL_PATH - "$(LOCAL_APPS_DIR)/$/Contents/MacOS") + "$(LOCAL_APPS_DIR)/$/Contents/MacOS" + ) add_custom_command( TARGET ${target} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$" - "$/MacOS/" - COMMENT "Copy ${executable} to application bundle") + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$" + "$/MacOS/" + COMMENT "Copy ${executable} to application bundle" + ) endforeach() - if(VIRTUALCAM_DEVICE_UUID - AND VIRTUALCAM_SOURCE_UUID - AND VIRTUALCAM_SINK_UUID) + if(VIRTUALCAM_DEVICE_UUID AND VIRTUALCAM_SOURCE_UUID AND VIRTUALCAM_SINK_UUID) set(has_virtualcam_uuids TRUE) else() set(has_virtualcam_uuids FALSE) @@ -131,8 +116,10 @@ function(set_target_properties_obs target) else() if(has_virtualcam_uuids AND OBS_PROVISIONING_PROFILE) set(entitlements_file "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements-extension.plist") - set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER - "${OBS_PROVISIONING_PROFILE}") + set_target_properties( + ${target} + PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "${OBS_PROVISIONING_PROFILE}" + ) configure_file(cmake/macos/exportOptions-extension.plist.in ${CMAKE_BINARY_DIR}/exportOptions.plist) else() set(entitlements_file "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist") @@ -151,7 +138,8 @@ function(set_target_properties_obs target) POST_BUILD COMMAND /bin/ln -fs obs-frontend-api.dylib libobs-frontend-api.1.dylib WORKING_DIRECTORY "$/Frameworks" - COMMENT "Create symlink for legacy obs-frontend-api") + COMMENT "Create symlink for legacy obs-frontend-api" + ) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/qt.conf") target_add_resource(${target} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/qt.conf") @@ -164,22 +152,28 @@ function(set_target_properties_obs target) add_custom_command( TARGET ${target} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory "$" - "$/Resources/$" - COMMENT "Add OBS DAL plugin to application bundle") + COMMAND + "${CMAKE_COMMAND}" -E copy_directory "$" + "$/Resources/$" + COMMENT "Add OBS DAL plugin to application bundle" + ) endif() if(TARGET obspython) add_custom_command( TARGET ${target} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$/obspython.py" - "$/Resources" - COMMENT "Add OBS::python import module") + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$/obspython.py" + "$/Resources" + COMMENT "Add OBS::python import module" + ) endif() - if(TARGET mac-camera-extension AND (CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE STREQUAL Automatic - OR OBS_PROVISIONING_PROFILE)) + if( + TARGET mac-camera-extension + AND (CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE STREQUAL Automatic OR OBS_PROVISIONING_PROFILE) + ) target_enable_feature(mac-camera-extension "macOS CMIO Camera Extension") add_custom_command( TARGET ${target} @@ -187,7 +181,8 @@ function(set_target_properties_obs target) COMMAND "${CMAKE_COMMAND}" -E copy_directory "$" "$/Library/SystemExtensions/$" - COMMENT "Add Camera Extension to application bundle") + COMMENT "Add Camera Extension to application bundle" + ) else() target_disable_feature(mac-camera-extension "macOS CMIO Camera Extension") endif() @@ -215,28 +210,30 @@ function(set_target_properties_obs target) elseif(target_type STREQUAL SHARED_LIBRARY) set_target_properties( ${target} - PROPERTIES NO_SONAME TRUE - MACHO_COMPATIBILITY_VERSION 1.0 - MACHO_CURRENT_VERSION ${OBS_VERSION_MAJOR} - SOVERSION 0 - VERSION 0) + PROPERTIES + NO_SONAME TRUE + MACHO_COMPATIBILITY_VERSION 1.0 + MACHO_CURRENT_VERSION ${OBS_VERSION_MAJOR} + SOVERSION 0 + VERSION 0 + ) - # cmake-format: off set_target_xcode_properties( ${target} PROPERTIES DYLIB_COMPATIBILITY_VERSION 1.0 DYLIB_CURRENT_VERSION ${OBS_VERSION_MAJOR} PRODUCT_NAME ${target} PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target} - SKIP_INSTALL YES) - # cmake-format: on + SKIP_INSTALL YES + ) get_target_property(is_framework ${target} FRAMEWORK) if(is_framework) - set_target_properties(${target} PROPERTIES FRAMEWORK_VERSION A MACOSX_FRAMEWORK_IDENTIFIER - com.obsproject.${target}) + set_target_properties( + ${target} + PROPERTIES FRAMEWORK_VERSION A MACOSX_FRAMEWORK_IDENTIFIER com.obsproject.${target} + ) - # cmake-format: off set_target_xcode_properties( ${target} PROPERTIES CODE_SIGN_IDENTITY "" @@ -249,27 +246,25 @@ function(set_target_properties_obs target) GENERATE_INFOPLIST_FILE YES INFOPLIST_FILE "" INFOPLIST_KEY_CFBundleDisplayName ${target} - INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2012-${CURRENT_YEAR} Lain Bailey") - # cmake-format: on + INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2012-${CURRENT_YEAR} Lain Bailey" + ) endif() set_property(GLOBAL APPEND PROPERTY _OBS_FRAMEWORKS ${target}) set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target}) elseif(target_type STREQUAL MODULE_LIBRARY) if(target STREQUAL obspython) - # cmake-format: off set_target_xcode_properties( ${target} PROPERTIES PRODUCT_NAME ${target} - PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}) - # cmake-format: on + PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target} + ) elseif(target STREQUAL obslua) - # cmake-format: off set_target_xcode_properties( ${target} PROPERTIES PRODUCT_NAME ${target} - PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}) - # cmake-format: on + PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target} + ) elseif(target STREQUAL obs-dal-plugin) set_target_properties(${target} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target}) @@ -277,7 +272,6 @@ function(set_target_properties_obs target) else() set_target_properties(${target} PROPERTIES BUNDLE TRUE BUNDLE_EXTENSION plugin) - # cmake-format: off set_target_xcode_properties( ${target} PROPERTIES PRODUCT_NAME ${target} @@ -286,8 +280,8 @@ function(set_target_properties_obs target) MARKETING_VERSION ${OBS_VERSION_CANONICAL} GENERATE_INFOPLIST_FILE YES INFOPLIST_KEY_CFBundleDisplayName ${target} - INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2012-${CURRENT_YEAR} Lain Bailey") - # cmake-format: on + INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2012-${CURRENT_YEAR} Lain Bailey" + ) if(target STREQUAL obs-browser) # Good-enough for now as there are no other variants - in _theory_ we should only add the appropriate variant, @@ -317,10 +311,7 @@ function(set_target_properties_obs target) get_target_property(target_sources ${target} SOURCES) set(target_ui_files ${target_sources}) list(FILTER target_ui_files INCLUDE REGEX ".+\\.(ui|qrc)") - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "UI Files" - FILES ${target_ui_files}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "UI Files" FILES ${target_ui_files}) if(${target} STREQUAL libobs) set(target_source_files ${target_sources}) @@ -328,14 +319,8 @@ function(set_target_properties_obs target) list(FILTER target_source_files INCLUDE REGEX ".+\\.(m|c[cp]?p?|swift)") list(FILTER target_header_files INCLUDE REGEX ".+\\.h(pp)?") - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Source Files" - FILES ${target_source_files}) - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Header Files" - FILES ${target_header_files}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${target_source_files}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Header Files" FILES ${target_header_files}) endif() endfunction() @@ -349,11 +334,10 @@ endmacro() # _add_entitlements: Macro to add entitlements shipped with project macro(_add_entitlements) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist") - # cmake-format: off set_target_xcode_properties( ${target} - PROPERTIES CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist") - # cmake-format: on + PROPERTIES CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist" + ) endif() endmacro() @@ -370,8 +354,12 @@ function(target_install_resources target) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") foreach(data_file IN LISTS data_files) - cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE - relative_path) + cmake_path( + RELATIVE_PATH + data_file + BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" + OUTPUT_VARIABLE relative_path + ) cmake_path(GET relative_path PARENT_PATH relative_path) target_sources(${target} PRIVATE "${data_file}") set_property(SOURCE "${data_file}" PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${relative_path}") @@ -398,10 +386,7 @@ function(_bundle_dependencies target) list(LENGTH obs_module_list num_modules) if(num_modules GREATER 0) add_dependencies(${target} ${obs_module_list}) - set_property( - TARGET ${target} - APPEND - PROPERTY XCODE_EMBED_PLUGINS ${obs_module_list}) + set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_PLUGINS ${obs_module_list}) foreach(module IN LISTS obs_module_list) find_dependencies(TARGET ${module} FOUND_VAR found_dependencies) endforeach() @@ -466,14 +451,13 @@ function(_bundle_dependencies target) cmake_path(SET plugin_stem_dir NORMALIZE "${plugin_base_dir}") cmake_path(RELATIVE_PATH plugin_path BASE_DIRECTORY "${plugin_stem_dir}" OUTPUT_VARIABLE plugin_file_name) target_sources(${target} PRIVATE "${plugin}") - set_source_files_properties("${plugin}" PROPERTIES MACOSX_PACKAGE_LOCATION "plugins/${plugin_file_name}" - XCODE_FILE_ATTRIBUTES "CodeSignOnCopy") + set_source_files_properties( + "${plugin}" + PROPERTIES MACOSX_PACKAGE_LOCATION "plugins/${plugin_file_name}" XCODE_FILE_ATTRIBUTES "CodeSignOnCopy" + ) source_group("Qt plugins" FILES "${plugin}") endforeach() list(REMOVE_DUPLICATES library_paths) - set_property( - TARGET ${target} - APPEND - PROPERTY XCODE_EMBED_FRAMEWORKS ${library_paths}) + set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${library_paths}) endfunction() diff --git a/cmake/macos/xcode.cmake b/cmake/macos/xcode.cmake index 708935c5d10364..9f058df574665b 100644 --- a/cmake/macos/xcode.cmake +++ b/cmake/macos/xcode.cmake @@ -9,8 +9,9 @@ if(ENABLE_CCACHE AND CCACHE_PROGRAM) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/ccache-launcher-c.in" ccache-launcher-c) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/ccache-launcher-cxx.in" ccache-launcher-cxx) - execute_process(COMMAND chmod a+rx "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-c" - "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-cxx") + execute_process( + COMMAND chmod a+rx "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-c" "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-cxx" + ) set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-c") set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CURRENT_BINARY_DIR}/ccache-launcher-cxx") set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_C_COMPILER}") @@ -26,9 +27,7 @@ set(CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION ${OBS_VERSION_CANONICAL}) set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) if(NOT OBS_PROVISIONING_PROFILE) - set(OBS_PROVISIONING_PROFILE - "" - CACHE STRING "OBS provisioning profile name for macOS" FORCE) + set(OBS_PROVISIONING_PROFILE "" CACHE STRING "OBS provisioning profile name for macOS" FORCE) else() set(CMAKE_XCODE_ATTRIBUTE_PROVISIONING_PROFILE_NAME "${OBS_PROVISIONING_PROFILE}") endif() diff --git a/cmake/windows/FindPython.cmake b/cmake/windows/FindPython.cmake index cc2adeae062381..0baeacd7b56ba3 100644 --- a/cmake/windows/FindPython.cmake +++ b/cmake/windows/FindPython.cmake @@ -40,24 +40,11 @@ The following cache variables may also be set: #]=======================================================================] -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - include(FindPackageHandleStandardArgs) -find_path( - Python_INCLUDE_DIR - NAMES Python.h - PATH_SUFFIXES include include/python - DOC "Python include directory") +find_path(Python_INCLUDE_DIR NAMES Python.h PATH_SUFFIXES include include/python DOC "Python include directory") -find_library( - Python_LIBRARY - NAMES python3 - PATHS lib - DOC "Python location") +find_library(Python_LIBRARY NAMES python3 PATHS lib DOC "Python location") if(EXISTS "${Python_INCLUDE_DIR}/patchlevel.h") file(STRINGS "${Python_INCLUDE_DIR}/patchlevel.h" _VERSION_STRING REGEX "^.*PY_VERSION[ \t]+\"[0-9\\.]+\"[ \t]*$") @@ -72,8 +59,10 @@ endif() find_package_handle_standard_args( Python REQUIRED_VARS Python_LIBRARY Python_INCLUDE_DIR - VERSION_VAR Python_VERSION HANDLE_VERSION_RANGE REASON_FAILURE_MESSAGE - "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") + VERSION_VAR Python_VERSION + HANDLE_VERSION_RANGE + REASON_FAILURE_MESSAGE "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH." +) mark_as_advanced(Python_INCLUDE_DIR Python_LIBRARY) if(Python_FOUND) @@ -90,14 +79,18 @@ if(Python_FOUND) set_property(TARGET Python::Python PROPERTY IMPORTED_LIBNAME "${Python_LIBRARY}") endif() - set_target_properties(Python::Python PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Python_INCLUDE_DIR}" - VERSION ${Python_VERSION}) + set_target_properties( + Python::Python + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Python_INCLUDE_DIR}" VERSION ${Python_VERSION} + ) endif() endif() include(FeatureSummary) set_package_properties( - Python PROPERTIES - URL "https://www.python.org" - DESCRIPTION - "Python is a programming language that lets you work more quickly and integrate your systems more effectively.") + Python + PROPERTIES + URL "https://www.python.org" + DESCRIPTION + "Python is a programming language that lets you work more quickly and integrate your systems more effectively." +) diff --git a/cmake/windows/compilerconfig.cmake b/cmake/windows/compilerconfig.cmake index f836824ca40906..5939d1517012d2 100644 --- a/cmake/windows/compilerconfig.cmake +++ b/cmake/windows/compilerconfig.cmake @@ -1,9 +1,5 @@ # OBS CMake Windows compiler configuration module -# cmake-format: off -# cmake-lint: disable=E1126 -# cmake-format: on - include_guard(GLOBAL) include(ccache) @@ -12,14 +8,24 @@ include(compiler_common) if(ENABLE_CCACHE AND CCACHE_PROGRAM) if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") file(COPY_FILE ${CCACHE_PROGRAM} "${CMAKE_CURRENT_BINARY_DIR}/cl.exe") - set(CMAKE_VS_GLOBALS "CLToolExe=cl.exe" "CLToolPath=${CMAKE_BINARY_DIR}" "TrackFileAccess=false" - "UseMultiToolTask=true") + set( + CMAKE_VS_GLOBALS + "CLToolExe=cl.exe" + "CLToolPath=${CMAKE_BINARY_DIR}" + "TrackFileAccess=false" + "UseMultiToolTask=true" + ) # Ccache does not support debug information stored in program database (PDB) files set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded) elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") file(COPY_FILE ${CCACHE_PROGRAM} "${CMAKE_CURRENT_BINARY_DIR}/clang-cl.exe") - set(CMAKE_VS_GLOBALS "CLToolExe=clang-cl.exe" "CLToolPath=${CMAKE_BINARY_DIR}" "TrackFileAccess=false" - "UseMultiToolTask=true") + set( + CMAKE_VS_GLOBALS + "CLToolExe=clang-cl.exe" + "CLToolPath=${CMAKE_BINARY_DIR}" + "TrackFileAccess=false" + "UseMultiToolTask=true" + ) endif() endif() @@ -43,8 +49,11 @@ if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM) endif() if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS 10.0.20348) - message(FATAL_ERROR "OBS requires Windows SDK version 10.0.20348.0 or more recent.\n" - "Please download and install the most recent Windows SDK.") + message( + FATAL_ERROR + "OBS requires Windows SDK version 10.0.20348.0 or more recent.\n" + "Please download and install the most recent Windows SDK." + ) endif() set(_obs_msvc_c_options /MP /Zc:__cplusplus /Zc:preprocessor) @@ -65,19 +74,26 @@ add_compile_options( "$<$:${_obs_clang_cxx_options}>" $<$>:/Gy> $<$>:/GL> - $<$>:/Oi>) + $<$>:/Oi> +) -add_compile_definitions(UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS $<$:DEBUG> - $<$:_DEBUG>) +add_compile_definitions( + UNICODE + _UNICODE + _CRT_SECURE_NO_WARNINGS + _CRT_NONSTDC_NO_WARNINGS + $<$:DEBUG> + $<$:_DEBUG> +) -# cmake-format: off -add_link_options($<$>:/OPT:REF> - $<$>:/OPT:ICF> - $<$>:/LTCG> - $<$>:/INCREMENTAL:NO> - /DEBUG - /Brepro) -# cmake-format: on +add_link_options( + $<$>:/OPT:REF> + $<$>:/OPT:ICF> + $<$>:/LTCG> + $<$>:/INCREMENTAL:NO> + /DEBUG + /Brepro +) if(CMAKE_COMPILE_WARNING_AS_ERROR) add_link_options(/WX) diff --git a/cmake/windows/defaults.cmake b/cmake/windows/defaults.cmake index ff0dae7b310867..137f0415eae200 100644 --- a/cmake/windows/defaults.cmake +++ b/cmake/windows/defaults.cmake @@ -31,5 +31,7 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8) "${CMAKE_GENERATOR}" -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DOBS_CMAKE_VERSION:STRING=3.0.0 -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID} -DCMAKE_MESSAGE_LOG_LEVEL=${CMAKE_MESSAGE_LOG_LEVEL} -DENABLE_CCACHE=${ENABLE_CCACHE} - RESULT_VARIABLE _process_result COMMAND_ERROR_IS_FATAL ANY) + RESULT_VARIABLE _process_result + COMMAND_ERROR_IS_FATAL ANY + ) endif() diff --git a/cmake/windows/helpers.cmake b/cmake/windows/helpers.cmake index a8cb6b41c3383f..f9b2e940b7e8d7 100644 --- a/cmake/windows/helpers.cmake +++ b/cmake/windows/helpers.cmake @@ -1,11 +1,5 @@ # OBS CMake Windows helper functions module -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=R0912 -# cmake-lint: disable=R0915 -# cmake-format: on - include_guard(GLOBAL) include(helpers_common) @@ -32,14 +26,10 @@ function(set_target_properties_obs target) elseif(target STREQUAL inject-helper OR target STREQUAL get-graphics-offsets) set(OBS_EXECUTABLE_DESTINATION "${OBS_DATA_DESTINATION}/obs-plugins/win-capture") - # cmake-format: off _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION} 32BIT) - # cmake-format: on endif() - # cmake-format: off _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION}) - # cmake-format: on if(target STREQUAL obs-studio) get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES) @@ -47,7 +37,8 @@ function(set_target_properties_obs target) add_dependencies(${target} ${obs_executables} ${obs_modules}) _bundle_dependencies(${target}) target_add_resource(${target} "${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS" - "${OBS_DATA_DESTINATION}/obs-studio/authors") + "${OBS_DATA_DESTINATION}/obs-studio/authors" + ) elseif(target STREQUAL obs-browser-helper) set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target}) return() @@ -57,19 +48,16 @@ function(set_target_properties_obs target) elseif(target_type STREQUAL SHARED_LIBRARY) set_target_properties(${target} PROPERTIES VERSION ${OBS_VERSION_MAJOR} SOVERSION ${OBS_VERSION_CANONICAL}) - # cmake-format: off _target_install_obs( ${target} DESTINATION "${OBS_EXECUTABLE_DESTINATION}" LIBRARY_DESTINATION "${OBS_LIBRARY_DESTINATION}" - HEADER_DESTINATION "${OBS_INCLUDE_DESTINATION}") - # cmake-format: on + HEADER_DESTINATION "${OBS_INCLUDE_DESTINATION}" + ) elseif(target_type STREQUAL MODULE_LIBRARY) set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_VERSION_CANONICAL}) - if(target STREQUAL libobs-d3d11 - OR target STREQUAL libobs-opengl - OR target STREQUAL libobs-winrt) + if(target STREQUAL libobs-d3d11 OR target STREQUAL libobs-opengl OR target STREQUAL libobs-winrt) set(target_destination "${OBS_EXECUTABLE_DESTINATION}") elseif(target STREQUAL "obspython" OR target STREQUAL "obslua") set(target_destination "${OBS_SCRIPT_PLUGIN_DESTINATION}") @@ -78,22 +66,16 @@ function(set_target_properties_obs target) target_add_resource(graphics-hook "${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan64.json" "${target_destination}") target_add_resource(graphics-hook "${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan32.json" "${target_destination}") - # cmake-format: off _target_install_obs(${target} DESTINATION ${target_destination} 32BIT) - # cmake-format: on elseif(target STREQUAL obs-virtualcam-module) set(target_destination "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow") - # cmake-format: off _target_install_obs(${target} DESTINATION ${target_destination} 32BIT) - # cmake-format: on else() set(target_destination "${OBS_PLUGIN_DESTINATION}") endif() - # cmake-format: off _target_install_obs(${target} DESTINATION ${target_destination}) - # cmake-format: on if(${target} STREQUAL obspython) add_custom_command( @@ -101,14 +83,17 @@ function(set_target_properties_obs target) POST_BUILD COMMAND "${CMAKE_COMMAND}" -E echo "Add obspython import module" COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_SCRIPT_PLUGIN_DESTINATION}/" - COMMAND "${CMAKE_COMMAND}" -E copy_if_different "$/obspython.py" - "${OBS_OUTPUT_DIR}/$/${OBS_SCRIPT_PLUGIN_DESTINATION}/" - COMMENT "") + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "$/obspython.py" + "${OBS_OUTPUT_DIR}/$/${OBS_SCRIPT_PLUGIN_DESTINATION}/" + COMMENT "" + ) install( FILES "$/obspython.py" DESTINATION "${OBS_SCRIPT_PLUGIN_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) elseif(${target} STREQUAL obs-browser) message(DEBUG "Add Chromium Embedded Framework to project for obs-browser plugin...") if(TARGET CEF::Library) @@ -130,29 +115,34 @@ function(set_target_properties_obs target) "${CMAKE_COMMAND}" -E copy_if_different "${cef_root_location}/Resources/chrome_100_percent.pak" "${cef_root_location}/Resources/chrome_200_percent.pak" "${cef_root_location}/Resources/icudtl.dat" "${cef_root_location}/Resources/resources.pak" "${OBS_OUTPUT_DIR}/$/${target_destination}/" - COMMAND "${CMAKE_COMMAND}" -E copy_directory "${cef_root_location}/Resources/locales" - "${OBS_OUTPUT_DIR}/$/${target_destination}/locales" - COMMENT "") + COMMAND + "${CMAKE_COMMAND}" -E copy_directory "${cef_root_location}/Resources/locales" + "${OBS_OUTPUT_DIR}/$/${target_destination}/locales" + COMMENT "" + ) install( - FILES "${imported_location}" - "${cef_location}/chrome_elf.dll" - "${cef_location}/libEGL.dll" - "${cef_location}/libGLESv2.dll" - "${cef_location}/snapshot_blob.bin" - "${cef_location}/v8_context_snapshot.bin" - "${cef_root_location}/Resources/chrome_100_percent.pak" - "${cef_root_location}/Resources/chrome_200_percent.pak" - "${cef_root_location}/Resources/icudtl.dat" - "${cef_root_location}/Resources/resources.pak" + FILES + "${imported_location}" + "${cef_location}/chrome_elf.dll" + "${cef_location}/libEGL.dll" + "${cef_location}/libGLESv2.dll" + "${cef_location}/snapshot_blob.bin" + "${cef_location}/v8_context_snapshot.bin" + "${cef_root_location}/Resources/chrome_100_percent.pak" + "${cef_root_location}/Resources/chrome_200_percent.pak" + "${cef_root_location}/Resources/icudtl.dat" + "${cef_root_location}/Resources/resources.pak" DESTINATION "${target_destination}" - COMPONENT Runtime) + COMPONENT Runtime + ) install( DIRECTORY "${cef_root_location}/Resources/locales" DESTINATION "${target_destination}" USE_SOURCE_PERMISSIONS - COMPONENT Runtime) + COMPONENT Runtime + ) endif() endif() endif() @@ -166,10 +156,7 @@ function(set_target_properties_obs target) get_target_property(target_sources ${target} SOURCES) set(target_ui_files ${target_sources}) list(FILTER target_ui_files INCLUDE REGEX ".+\\.(ui|qrc)") - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "UI Files" - FILES ${target_ui_files}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "UI Files" FILES ${target_ui_files}) if(${target} STREQUAL libobs) set(target_source_files ${target_sources}) @@ -177,14 +164,8 @@ function(set_target_properties_obs target) list(FILTER target_source_files INCLUDE REGEX ".+\\.(m|c[cp]?p?|swift)") list(FILTER target_header_files INCLUDE REGEX ".+\\.h(pp)?") - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Source Files" - FILES ${target_source_files}) - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Header Files" - FILES ${target_header_files}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${target_source_files}) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Header Files" FILES ${target_header_files}) endif() endfunction() @@ -214,7 +195,8 @@ function(_target_install_obs target) FILES "${32bit_project_path}/$/${target}32.${suffix}" DESTINATION "${_TIO_DESTINATION}" COMPONENT Runtime - OPTIONAL) + OPTIONAL + ) else() set(target_file "$") set(target_pdb_file "$") @@ -233,19 +215,14 @@ function(_target_install_obs target) install( TARGETS ${target} RUNTIME DESTINATION "${_TIO_DESTINATION}" - LIBRARY DESTINATION "${_TIO_LIBRARY_DESTINATION}" - COMPONENT Runtime - EXCLUDE_FROM_ALL - PUBLIC_HEADER - DESTINATION "${_TIO_HEADER_DESTINATION}" - COMPONENT Development - EXCLUDE_FROM_ALL) + LIBRARY DESTINATION "${_TIO_LIBRARY_DESTINATION}" COMPONENT Runtime EXCLUDE_FROM_ALL + PUBLIC_HEADER DESTINATION "${_TIO_HEADER_DESTINATION}" COMPONENT Development EXCLUDE_FROM_ALL + ) elseif(target_type STREQUAL MODULE_LIBRARY) install( TARGETS ${target} - LIBRARY DESTINATION "${_TIO_DESTINATION}" - COMPONENT Runtime - NAMELINK_COMPONENT Development) + LIBRARY DESTINATION "${_TIO_DESTINATION}" COMPONENT Runtime NAMELINK_COMPONENT Development + ) endif() endif() @@ -255,17 +232,20 @@ function(_target_install_obs target) COMMAND "${CMAKE_COMMAND}" -E echo "${comment}" COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${_TIO_DESTINATION}" COMMAND "${CMAKE_COMMAND}" -E copy ${target_file} "${OBS_OUTPUT_DIR}/$/${_TIO_DESTINATION}" - COMMAND "${CMAKE_COMMAND}" -E $,copy,true> ${target_pdb_file} - "${OBS_OUTPUT_DIR}/$/${_TIO_DESTINATION}" + COMMAND + "${CMAKE_COMMAND}" -E $,copy,true> ${target_pdb_file} + "${OBS_OUTPUT_DIR}/$/${_TIO_DESTINATION}" COMMENT "" - VERBATIM) + VERBATIM + ) install( FILES ${target_pdb_file} CONFIGURATIONS RelWithDebInfo Debug Release DESTINATION "${_TIO_DESTINATION}" COMPONENT Runtime - OPTIONAL) + OPTIONAL + ) endfunction() # target_export: Helper function to export target as CMake package @@ -281,7 +261,8 @@ function(target_export target) CONFIGURATIONS RelWithDebInfo Debug Release DESTINATION "${OBS_EXECUTABLE_DESTINATION}" COMPONENT Development - OPTIONAL) + OPTIONAL + ) endif() endfunction() @@ -291,8 +272,12 @@ function(target_install_resources target) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") foreach(data_file IN LISTS data_files) - cmake_path(RELATIVE_PATH data_file BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" OUTPUT_VARIABLE - relative_path) + cmake_path( + RELATIVE_PATH + data_file + BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" + OUTPUT_VARIABLE relative_path + ) cmake_path(GET relative_path PARENT_PATH relative_path) target_sources(${target} PRIVATE "${data_file}") source_group("Resources/${relative_path}" FILES "${data_file}") @@ -311,17 +296,20 @@ function(target_install_resources target) DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/" DESTINATION "${target_destination}" USE_SOURCE_PERMISSIONS - COMPONENT Runtime) + COMPONENT Runtime + ) add_custom_command( TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E echo "Copy ${target} resources to data directory" COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${target_destination}" - COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/data" - "${OBS_OUTPUT_DIR}/$/${target_destination}" + COMMAND + "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/data" + "${OBS_OUTPUT_DIR}/$/${target_destination}" COMMENT "" - VERBATIM) + VERBATIM + ) endif() endfunction() @@ -340,10 +328,7 @@ function(target_add_resource target resource) message(DEBUG "Add resource '${resource}' to target ${target} at destination '${target_destination}'...") - install( - FILES "${resource}" - DESTINATION "${target_destination}" - COMPONENT Runtime) + install(FILES "${resource}" DESTINATION "${target_destination}" COMPONENT Runtime) add_custom_command( TARGET ${target} @@ -352,7 +337,8 @@ function(target_add_resource target resource) COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${target_destination}/" COMMAND "${CMAKE_COMMAND}" -E copy "${resource}" "${OBS_OUTPUT_DIR}/$/${target_destination}/" COMMENT "" - VERBATIM) + VERBATIM + ) source_group("Resources" FILES "${resource}") endfunction() @@ -417,43 +403,51 @@ function(_bundle_dependencies target) POST_BUILD COMMAND "${CMAKE_COMMAND}" -E echo "Copy dependencies to binary directory (${OBS_EXECUTABLE_DESTINATION})..." COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" - COMMAND "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" - "$<$:${library_paths_DEBUG}>" "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" + COMMAND + "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" "$<$:${library_paths_DEBUG}>" + "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" COMMAND "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" "$<$:${library_paths_RELWITHDEBINFO}>" "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" - COMMAND "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" - "$<$:${library_paths_RELEASE}>" "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" + COMMAND + "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" + "$<$:${library_paths_RELEASE}>" "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" COMMAND "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" "$<$:${library_paths_MINSIZEREL}>" "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}" COMMENT "." - VERBATIM COMMAND_EXPAND_LISTS) + VERBATIM + COMMAND_EXPAND_LISTS + ) install( FILES ${library_paths_DEBUG} CONFIGURATIONS Debug DESTINATION "${OBS_EXECUTABLE_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) install( FILES ${library_paths_RELWITHDEBINFO} CONFIGURATIONS RelWithDebInfo DESTINATION "${OBS_EXECUTABLE_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) install( FILES ${library_paths_RELEASE} CONFIGURATIONS Release DESTINATION "${OBS_EXECUTABLE_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) install( FILES ${library_paths_MINSIZEREL} CONFIGURATIONS MinSizeRel DESTINATION "${OBS_EXECUTABLE_DESTINATION}" - COMPONENT Runtime) + COMPONENT Runtime + ) list(REMOVE_DUPLICATES plugins_list) foreach(plugin IN LISTS plugins_list) @@ -478,27 +472,33 @@ function(_bundle_dependencies target) add_custom_command( TARGET ${target} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E echo - "Copy Qt plugins ${stem} to binary directory (${OBS_EXECUTABLE_DESTINATION}/${stem})" + COMMAND + "${CMAKE_COMMAND}" -E echo "Copy Qt plugins ${stem} to binary directory (${OBS_EXECUTABLE_DESTINATION}/${stem})" COMMAND "${CMAKE_COMMAND}" -E make_directory "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}/${stem}" - COMMAND "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" "${plugin_list_debug}" - "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}/${stem}" - COMMAND "${CMAKE_COMMAND}" -E "$,true,copy_if_different>" "${plugin_list}" - "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}/${stem}" + COMMAND + "${CMAKE_COMMAND}" -E "$,copy_if_different,true>" "${plugin_list_debug}" + "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}/${stem}" + COMMAND + "${CMAKE_COMMAND}" -E "$,true,copy_if_different>" "${plugin_list}" + "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}/${stem}" COMMENT "" - VERBATIM COMMAND_EXPAND_LISTS) + VERBATIM + COMMAND_EXPAND_LISTS + ) install( FILES ${plugin_list_debug} CONFIGURATIONS Debug DESTINATION "${OBS_EXECUTABLE_DESTINATION}/${stem}" - COMPONENT Runtime) + COMPONENT Runtime + ) install( FILES ${plugin_list} CONFIGURATIONS RelWithDebInfo Release MinSizeRel DESTINATION "${OBS_EXECUTABLE_DESTINATION}/${stem}" - COMPONENT Runtime) + COMPONENT Runtime + ) endforeach() endfunction() @@ -515,17 +515,9 @@ macro(_check_library_location location) string(REPLACE ".lib" ".dll" _dll_name "${_dll_name}") string(REPLACE ".dll" ".pdb" _pdb_name "${_dll_name}") - find_program( - _dll_path - NAMES "${_dll_name}" - HINTS ${_implib_path} ${_bin_path} NO_CACHE - NO_DEFAULT_PATH) - - find_program( - _pdb_path - NAMES "${_pdb_name}" - HINTS ${_implib_path} ${_bin_path} NO_CACHE - NO_DEFAULT_PATH) + find_program(_dll_path NAMES "${_dll_name}" HINTS ${_implib_path} ${_bin_path} NO_CACHE NO_DEFAULT_PATH) + + find_program(_pdb_path NAMES "${_pdb_name}" HINTS ${_implib_path} ${_bin_path} NO_CACHE NO_DEFAULT_PATH) if(_dll_path) set(library_location "${_dll_path}") diff --git a/cmake/windows/idlfilehelper.cmake b/cmake/windows/idlfilehelper.cmake index b24e4a5bde529f..77aba37eb1b5e8 100644 --- a/cmake/windows/idlfilehelper.cmake +++ b/cmake/windows/idlfilehelper.cmake @@ -16,10 +16,12 @@ function(target_add_idl_files target) add_custom_command( OUTPUT "${idl_file_header}" "${idl_file_source}" DEPENDS "${idl_file}" - COMMAND midl /h "${idl_file_name}.h" /iid "${idl_file_name}_i.c" "$<$>:/notlb>" - /win64 "${CMAKE_CURRENT_SOURCE_DIR}/${idl_file}" + COMMAND + midl /h "${idl_file_name}.h" /iid "${idl_file_name}_i.c" "$<$>:/notlb>" /win64 + "${CMAKE_CURRENT_SOURCE_DIR}/${idl_file}" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - COMMENT "Generate idl files") + COMMENT "Generate idl files" + ) set_source_files_properties(${idl_file} PROPERTIES HEADER_FILE_ONLY TRUE) target_sources(${target} PRIVATE "${idl_file_source}" "${idl_file_header}") diff --git a/deps/blake2/CMakeLists.txt b/deps/blake2/CMakeLists.txt index 3df7d3bd8a0d90..f6911749f6ad2b 100644 --- a/deps/blake2/CMakeLists.txt +++ b/deps/blake2/CMakeLists.txt @@ -3,10 +3,7 @@ cmake_minimum_required(VERSION 3.22...3.25) add_library(blake2 OBJECT) add_library(OBS::blake2 ALIAS blake2) -target_sources( - blake2 - PRIVATE src/blake2-impl.h src/blake2b-ref.c - PUBLIC src/blake2.h) +target_sources(blake2 PRIVATE src/blake2-impl.h src/blake2b-ref.c PUBLIC src/blake2.h) target_include_directories(blake2 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") @@ -16,10 +13,7 @@ if(OS_WINDOWS) add_library(blake2_static OBJECT) add_library(OBS::blake2_static ALIAS blake2_static) - target_sources( - blake2_static - PRIVATE src/blake2-impl.h src/blake2b-ref.c - PUBLIC src/blake2.h) + target_sources(blake2_static PRIVATE src/blake2-impl.h src/blake2b-ref.c PUBLIC src/blake2.h) target_include_directories(blake2_static PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") set_target_properties(blake2_static PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") diff --git a/deps/glad/CMakeLists.txt b/deps/glad/CMakeLists.txt index fba3f3ff980cdb..457de0a9d0bdeb 100644 --- a/deps/glad/CMakeLists.txt +++ b/deps/glad/CMakeLists.txt @@ -9,17 +9,18 @@ if(OBS_CMAKE_VERSION VERSION_LESS 3.0.0) add_library(OBS::obsglad ALIAS obsglad) endif() -# cmake-format: off target_sources( obsglad - PRIVATE src/glad.c - $<$:src/glad_wgl.c> - $<$:src/glad_egl.c> - $<$:include/EGL/eglplatform.h> - PUBLIC include/glad/glad.h - "$<$:${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad_wgl.h>" - "$<$:${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad_egl.h>") -# cmake-format: on + PRIVATE + src/glad.c + $<$:src/glad_wgl.c> + $<$:src/glad_egl.c> + $<$:include/EGL/eglplatform.h> + PUBLIC + include/glad/glad.h + "$<$:${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad_wgl.h>" + "$<$:${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad_egl.h>" +) target_compile_options(obsglad PRIVATE $<$:-Wno-strict-prototypes>) @@ -28,6 +29,7 @@ target_include_directories(obsglad PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries( obsglad PRIVATE $<$>:${CMAKE_DL_LIBS}> - PUBLIC OpenGL::GL $<$:OpenGL::EGL>) + PUBLIC OpenGL::GL $<$:OpenGL::EGL> +) set_target_properties(obsglad PROPERTIES FOLDER deps POSITION_INDEPENDENT_CODE TRUE) diff --git a/deps/json11/CMakeLists.txt b/deps/json11/CMakeLists.txt index 4835313b0be712..9e1c6804b19fcf 100644 --- a/deps/json11/CMakeLists.txt +++ b/deps/json11/CMakeLists.txt @@ -3,15 +3,14 @@ cmake_minimum_required(VERSION 3.22...3.25) add_library(json11 OBJECT) add_library(OBS::json11 ALIAS json11) -target_sources( - json11 - PRIVATE json11.cpp - PUBLIC json11.hpp) +target_sources(json11 PRIVATE json11.cpp PUBLIC json11.hpp) target_include_directories(json11 PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -if((CMAKE_CXX_COMPILER_ID STREQUAL AppleClang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14) - OR (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15)) +if( + (CMAKE_CXX_COMPILER_ID STREQUAL AppleClang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14) + OR (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15) +) target_compile_options(json11 PUBLIC -Wno-unqualified-std-cast-call) endif() diff --git a/deps/libcaption/CMakeLists.txt b/deps/libcaption/CMakeLists.txt index 007cb3e876719a..8ee7431541e641 100644 --- a/deps/libcaption/CMakeLists.txt +++ b/deps/libcaption/CMakeLists.txt @@ -1,42 +1,40 @@ cmake_minimum_required(VERSION 3.22...3.25) -add_library(caption STATIC EXCLUDE_FROM_ALL ) +add_library(caption STATIC EXCLUDE_FROM_ALL) add_library(OBS::caption ALIAS caption) target_sources( caption - PRIVATE # cmake-format: sortable - caption/cea708.h - caption/eia608.h - caption/eia608_charmap.h - caption/mpeg.h - caption/scc.h - caption/utf8.h - src/caption.c - src/cea708.c - src/eia608.c - src/eia608_charmap.c - src/eia608_from_utf8.c - src/mpeg.c - src/scc.c - src/srt.c - src/utf8.c - src/xds.c - PUBLIC caption/caption.h) + PRIVATE + caption/cea708.h + caption/eia608.h + caption/eia608_charmap.h + caption/mpeg.h + caption/scc.h + caption/utf8.h + src/caption.c + src/cea708.c + src/eia608.c + src/eia608_charmap.c + src/eia608_from_utf8.c + src/mpeg.c + src/scc.c + src/srt.c + src/utf8.c + src/xds.c + PUBLIC caption/caption.h +) -target_include_directories( - caption - PRIVATE caption - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_include_directories(caption PRIVATE caption PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_compile_definitions(caption PRIVATE __STDC_CONSTANT_MACROS) -# cmake-format: off target_compile_options( caption - PRIVATE $<$:-Wno-unused-but-set-parameter> - $<$:-Wno-strict-prototypes> - $<$:-Wno-comma>) -# cmake-format: on + PRIVATE + $<$:-Wno-unused-but-set-parameter> + $<$:-Wno-strict-prototypes> + $<$:-Wno-comma> +) set_target_properties(caption PROPERTIES FOLDER deps POSITION_INDEPENDENT_CODE TRUE) diff --git a/deps/w32-pthreads/CMakeLists.txt b/deps/w32-pthreads/CMakeLists.txt index d6ee0aa656f46a..67dba8040220e1 100644 --- a/deps/w32-pthreads/CMakeLists.txt +++ b/deps/w32-pthreads/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.24...3.25) legacy_check() -add_library(w32-pthreads SHARED EXCLUDE_FROM_ALL ) +add_library(w32-pthreads SHARED EXCLUDE_FROM_ALL) add_library(OBS::w32-pthreads ALIAS w32-pthreads) -target_sources(w32-pthreads PRIVATE # cmake-format: sortable - implement.h pthread.c pthread.h sched.h semaphore.h w32-pthreads.rc) +target_sources( + w32-pthreads + PRIVATE implement.h pthread.c pthread.h sched.h semaphore.h w32-pthreads.rc +) target_compile_definitions(w32-pthreads PRIVATE __CLEANUP_C PTW32_BUILD) @@ -16,9 +18,6 @@ configure_file(cmake/windows/obs-module.rc.in w32-pthreads.rc) set_target_properties_obs(w32-pthreads PROPERTIES FOLDER deps) -set_property( - TARGET w32-pthreads - APPEND - PROPERTY PUBLIC_HEADER pthread.h sched.h) +set_property(TARGET w32-pthreads APPEND PROPERTY PUBLIC_HEADER pthread.h sched.h) target_export(w32-pthreads) diff --git a/libobs-d3d11/CMakeLists.txt b/libobs-d3d11/CMakeLists.txt index a05dde6a769f0f..6882b4be1da0c5 100644 --- a/libobs-d3d11/CMakeLists.txt +++ b/libobs-d3d11/CMakeLists.txt @@ -7,38 +7,44 @@ add_library(OBS::libobs-d3d11 ALIAS libobs-d3d11) target_sources( libobs-d3d11 - PRIVATE # cmake-format: sortable - d3d11-duplicator.cpp - d3d11-indexbuffer.cpp - d3d11-rebuild.cpp - d3d11-samplerstate.cpp - d3d11-shader.cpp - d3d11-shaderprocessor.cpp - d3d11-shaderprocessor.hpp - d3d11-stagesurf.cpp - d3d11-subsystem.cpp - d3d11-subsystem.hpp - d3d11-texture2d.cpp - d3d11-texture3d.cpp - d3d11-vertexbuffer.cpp - d3d11-zstencilbuffer.cpp) + PRIVATE + d3d11-duplicator.cpp + d3d11-indexbuffer.cpp + d3d11-rebuild.cpp + d3d11-samplerstate.cpp + d3d11-shader.cpp + d3d11-shaderprocessor.cpp + d3d11-shaderprocessor.hpp + d3d11-stagesurf.cpp + d3d11-subsystem.cpp + d3d11-subsystem.hpp + d3d11-texture2d.cpp + d3d11-texture3d.cpp + d3d11-vertexbuffer.cpp + d3d11-zstencilbuffer.cpp +) configure_file(cmake/windows/obs-module.rc.in libobs-d3d11.rc) target_sources(libobs-d3d11 PRIVATE libobs-d3d11.rc) target_compile_definitions( - libobs-d3d11 PRIVATE $<$:USE_GPU_PRIORITY> - "$,GPU_PRIORITY_VAL=${GPU_PRIORITY_VAL},GPU_PRIORITY_VAL=0>") + libobs-d3d11 + PRIVATE + $<$:USE_GPU_PRIORITY> + "$,GPU_PRIORITY_VAL=${GPU_PRIORITY_VAL},GPU_PRIORITY_VAL=0>" +) -target_link_libraries(libobs-d3d11 PRIVATE OBS::libobs d3d9 d3d11 d3dcompiler dxgi shcore) +target_link_libraries( + libobs-d3d11 + PRIVATE OBS::libobs d3d9 d3d11 d3dcompiler dxgi shcore +) target_enable_feature(libobs "Direct3D 11 renderer") -# cmake-format: off set_target_properties_obs( libobs-d3d11 PROPERTIES FOLDER core VERSION 0 SOVERSION ${OBS_VERSION_MAJOR} - COMPILE_WARNING_AS_ERROR FALSE) -# cmake-format: on + COMPILE_WARNING_AS_ERROR FALSE +) diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt index bb85d959dcb957..d91495ef8b1eaf 100644 --- a/libobs-opengl/CMakeLists.txt +++ b/libobs-opengl/CMakeLists.txt @@ -9,13 +9,9 @@ if(NOT TARGET OBS::glad) add_subdirectory("${CMAKE_SOURCE_DIR}/deps/glad" "${CMAKE_BINARY_DIR}/deps/glad") endif() -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(X11 REQUIRED) - # cmake-format: off find_package(Xcb REQUIRED xcb) - # cmake-format: on find_package(X11-xcb REQUIRED) if(ENABLE_WAYLAND) @@ -26,27 +22,28 @@ endif() target_sources( libobs-opengl - PRIVATE # cmake-format: sortable - $<$,$>:gl-wayland-egl.c> - $<$:gl-cocoa.m> - $<$:gl-egl-common.c> - $<$:gl-nix.c> - $<$:gl-x11-egl.c> - $<$:gl-windows.c> - gl-helpers.c - gl-helpers.h - gl-indexbuffer.c - gl-shader.c - gl-shaderparser.c - gl-shaderparser.h - gl-stagesurf.c - gl-subsystem.c - gl-subsystem.h - gl-texture2d.c - gl-texture3d.c - gl-texturecube.c - gl-vertexbuffer.c - gl-zstencil.c) + PRIVATE + $<$,$>:gl-wayland-egl.c> + $<$:gl-cocoa.m> + $<$:gl-egl-common.c> + $<$:gl-nix.c> + $<$:gl-x11-egl.c> + $<$:gl-windows.c> + gl-helpers.c + gl-helpers.h + gl-indexbuffer.c + gl-shader.c + gl-shaderparser.c + gl-shaderparser.h + gl-stagesurf.c + gl-subsystem.c + gl-subsystem.h + gl-texture2d.c + gl-texture3d.c + gl-texturecube.c + gl-vertexbuffer.c + gl-zstencil.c +) target_compile_options(libobs-opengl PRIVATE $<$:-Wno-strict-prototypes>) @@ -54,14 +51,16 @@ target_compile_definitions(libobs-opengl PRIVATE $<$:GL_SILE target_link_libraries( libobs-opengl - PRIVATE OBS::libobs - OBS::glad - "$<$:$>" - "$<$:$>" - $<$:xcb::xcb> - $<$:X11::x11-xcb> - $<$,$>:OpenGL::EGL> - $<$,$>:Wayland::EGL>) + PRIVATE + OBS::libobs + OBS::glad + "$<$:$>" + "$<$:$>" + $<$:xcb::xcb> + $<$:X11::x11-xcb> + $<$,$>:OpenGL::EGL> + $<$,$>:Wayland::EGL> +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in libobs-opengl.rc) @@ -70,11 +69,10 @@ endif() target_enable_feature(libobs "OpenGL renderer") -# cmake-format: off set_target_properties_obs( libobs-opengl PROPERTIES FOLDER core VERSION 0 PREFIX "" - SOVERSION "${OBS_VERSION_MAJOR}") -# cmake-format: on + SOVERSION "${OBS_VERSION_MAJOR}" +) diff --git a/libobs-winrt/CMakeLists.txt b/libobs-winrt/CMakeLists.txt index fb6b957ca9983a..09daed3a6d994f 100644 --- a/libobs-winrt/CMakeLists.txt +++ b/libobs-winrt/CMakeLists.txt @@ -16,19 +16,18 @@ target_sources(libobs-winrt PRIVATE winrt-capture.cpp winrt-capture.h winrt-disp target_precompile_headers( libobs-winrt PRIVATE - - - - - - - - - - ) + + + + + + + + + + +) target_link_libraries(libobs-winrt PRIVATE OBS::libobs OBS::COMutils Dwmapi windowsapp) -# cmake-format: off set_target_properties_obs(libobs-winrt PROPERTIES FOLDER core) -# cmake-format: on diff --git a/libobs-winrt/cmake/legacy.cmake b/libobs-winrt/cmake/legacy.cmake index 63aa345f672f4c..578be6fb4d30ab 100644 --- a/libobs-winrt/cmake/legacy.cmake +++ b/libobs-winrt/cmake/legacy.cmake @@ -25,8 +25,7 @@ target_compile_features(libobs-winrt PRIVATE cxx_std_17) set_target_properties( libobs-winrt - PROPERTIES OUTPUT_NAME libobs-winrt - FOLDER "core" - PREFIX "") + PROPERTIES OUTPUT_NAME libobs-winrt FOLDER "core" PREFIX "" +) setup_binary_target(libobs-winrt) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 98092bbbb2c207..4226e7fe88e295 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -6,21 +6,13 @@ include(cmake/obs-version.cmake) find_package(Threads REQUIRED) -# cmake-format: off if(OS_WINDOWS OR OS_MACOS) set(ffmpeg_version 6) else() set(ffmpeg_version 4.4) endif() -find_package( - FFmpeg ${ffmpeg_version} - REQUIRED avformat - avutil - swscale - swresample - OPTIONAL_COMPONENTS avcodec) -# cmake-format: on +find_package(FFmpeg ${ffmpeg_version} REQUIRED avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec) find_package(ZLIB REQUIRED) find_package(Uthash REQUIRED) @@ -39,323 +31,116 @@ add_library(OBS::libobs ALIAS libobs) target_sources( libobs - PRIVATE # cmake-format: sortable - $<$:obs-hevc.c> - $<$:obs-hevc.h> - obs-audio-controls.c - obs-audio-controls.h - obs-audio.c - obs-av1.c - obs-av1.h - obs-avc.c - obs-avc.h - obs-config.h - obs-data.c - obs-data.h - obs-defs.h - obs-display.c - obs-encoder.c - obs-encoder.h - obs-ffmpeg-compat.h - obs-hotkey-name-map.c - obs-hotkey.c - obs-hotkey.h - obs-hotkeys.h - obs-interaction.h - obs-internal.h - obs-missing-files.c - obs-missing-files.h - obs-module.c - obs-module.h - obs-nal.c - obs-nal.h - obs-output-delay.c - obs-output.c - obs-output.h - obs-properties.c - obs-properties.h - obs-scene.c - obs-scene.h - obs-service.c - obs-service.h - obs-source-deinterlace.c - obs-source-transition.c - obs-source.c - obs-source.h - obs-video-gpu-encode.c - obs-video.c - obs-view.c - obs.c - obs.h - obs.hpp) - -target_sources( - libobs - PRIVATE # cmake-format: sortable - util/array-serializer.c - util/array-serializer.h - util/base.c - util/base.h - util/bitstream.c - util/bitstream.h - util/bmem.c - util/bmem.h - util/buffered-file-serializer.c - util/buffered-file-serializer.h - util/c99defs.h - util/cf-lexer.c - util/cf-lexer.h - util/cf-parser.c - util/cf-parser.h - util/circlebuf.h - util/config-file.c - util/config-file.h - util/crc32.c - util/crc32.h - util/curl/curl-helper.h - util/darray.h - util/deque.h - util/dstr.c - util/dstr.h - util/file-serializer.c - util/file-serializer.h - util/lexer.c - util/lexer.h - util/pipe.c - util/pipe.h - util/platform.c - util/platform.h - util/profiler.c - util/profiler.h - util/profiler.hpp - util/serializer.h - util/source-profiler.c - util/source-profiler.h - util/sse-intrin.h - util/task.c - util/task.h - util/text-lookup.c - util/text-lookup.h - util/threading.h - util/utf8.c - util/utf8.h - util/uthash.h - util/util.hpp - util/util_uint128.h - util/util_uint64.h) - -target_sources( - libobs - PRIVATE # cmake-format: sortable - util/simde/check.h - util/simde/debug-trap.h - util/simde/hedley.h - util/simde/simde-align.h - util/simde/simde-arch.h - util/simde/simde-common.h - util/simde/simde-constify.h - util/simde/simde-detect-clang.h - util/simde/simde-diagnostic.h - util/simde/simde-features.h - util/simde/simde-math.h - util/simde/x86/mmx.h - util/simde/x86/sse.h - util/simde/x86/sse2.h) - -target_sources( - libobs - PRIVATE # cmake-format: sortable - callback/calldata.c - callback/calldata.h - callback/decl.c - callback/decl.h - callback/proc.c - callback/proc.h - callback/signal.c - callback/signal.h) - -target_sources( - libobs - PRIVATE # cmake-format: sortable - media-io/audio-io.c - media-io/audio-io.h - media-io/audio-math.h - media-io/audio-resampler-ffmpeg.c - media-io/audio-resampler.h - media-io/format-conversion.c - media-io/format-conversion.h - media-io/frame-rate.h - media-io/media-io-defs.h - media-io/media-remux.c - media-io/media-remux.h - media-io/video-fourcc.c - media-io/video-frame.c - media-io/video-frame.h - media-io/video-io.c - media-io/video-io.h - media-io/video-matrices.c - media-io/video-scaler-ffmpeg.c - media-io/video-scaler.h) - -target_sources( - libobs - PRIVATE # cmake-format: sortable - graphics/axisang.c - graphics/axisang.h - graphics/bounds.c - graphics/bounds.h - graphics/device-exports.h - graphics/effect-parser.c - graphics/effect-parser.h - graphics/effect.c - graphics/effect.h - graphics/graphics-ffmpeg.c - graphics/graphics-imports.c - graphics/graphics-internal.h - graphics/graphics.c - graphics/graphics.h - graphics/half.h - graphics/image-file.c - graphics/image-file.h - graphics/input.h - graphics/libnsgif/libnsgif.c - graphics/libnsgif/libnsgif.h - graphics/math-defs.h - graphics/math-extra.c - graphics/math-extra.h - graphics/matrix3.c - graphics/matrix3.h - graphics/matrix4.c - graphics/matrix4.h - graphics/plane.c - graphics/plane.h - graphics/quat.c - graphics/quat.h - graphics/shader-parser.c - graphics/shader-parser.h - graphics/srgb.h - graphics/texture-render.c - graphics/vec2.c - graphics/vec2.h - graphics/vec3.c - graphics/vec3.h - graphics/vec4.c - graphics/vec4.h) - -target_compile_features(libobs PUBLIC cxx_std_17) - -target_compile_definitions( - libobs - PRIVATE IS_LIBOBS - PUBLIC $:ENABLE_HEVC>> - $:SHOW_SUBPROCESSES>>) - -target_link_libraries( - libobs - PRIVATE OBS::caption - OBS::libobs-version - FFmpeg::avcodec - FFmpeg::avformat - FFmpeg::avutil - FFmpeg::swscale - FFmpeg::swresample - jansson::jansson - Uthash::Uthash - ZLIB::ZLIB - PUBLIC Threads::Threads) - -if(OS_WINDOWS) - include(cmake/os-windows.cmake) -elseif(OS_MACOS) - include(cmake/os-macos.cmake) -elseif(OS_LINUX) - include(cmake/os-linux.cmake) -elseif(OS_FREEBSD OR OS_OPENBSD) - include(cmake/os-freebsd.cmake) -endif() - -configure_file(obsconfig.h.in "${CMAKE_BINARY_DIR}/config/obsconfig.h" @ONLY) - -target_include_directories(libobs PUBLIC "$" - "$") - -target_compile_definitions(libobs PUBLIC HAVE_OBSCONFIG_H) - -set(public_headers - # cmake-format: sortable - callback/calldata.h - callback/decl.h - callback/proc.h - callback/signal.h - graphics/axisang.h - graphics/bounds.h - graphics/effect-parser.h - graphics/effect.h - graphics/graphics.h - graphics/image-file.h - graphics/input.h - graphics/libnsgif/libnsgif.h - graphics/math-defs.h - graphics/math-extra.h - graphics/matrix3.h - graphics/matrix4.h - graphics/plane.h - graphics/quat.h - graphics/shader-parser.h - graphics/srgb.h - graphics/vec2.h - graphics/vec3.h - graphics/vec4.h - media-io/audio-io.h - media-io/audio-math.h - media-io/audio-resampler.h - media-io/format-conversion.h - media-io/frame-rate.h - media-io/media-io-defs.h - media-io/media-remux.h - media-io/video-frame.h - media-io/video-io.h - media-io/video-scaler.h + PRIVATE + $<$:obs-hevc.c> + $<$:obs-hevc.h> + obs-audio-controls.c obs-audio-controls.h + obs-audio.c + obs-av1.c + obs-av1.h + obs-avc.c obs-avc.h obs-config.h + obs-data.c obs-data.h obs-defs.h + obs-display.c + obs-encoder.c obs-encoder.h + obs-ffmpeg-compat.h + obs-hotkey-name-map.c + obs-hotkey.c obs-hotkey.h obs-hotkeys.h obs-interaction.h + obs-internal.h + obs-missing-files.c obs-missing-files.h + obs-module.c obs-module.h + obs-nal.c obs-nal.h - obs-nix-platform.h + obs-output-delay.c + obs-output.c obs-output.h + obs-properties.c obs-properties.h + obs-scene.c + obs-scene.h + obs-service.c obs-service.h + obs-source-deinterlace.c + obs-source-transition.c + obs-source.c obs-source.h + obs-video-gpu-encode.c + obs-video.c + obs-view.c + obs.c obs.h obs.hpp +) + +target_sources( + libobs + PRIVATE + util/array-serializer.c util/array-serializer.h + util/base.c util/base.h + util/bitstream.c util/bitstream.h + util/bmem.c util/bmem.h + util/buffered-file-serializer.c + util/buffered-file-serializer.h util/c99defs.h + util/cf-lexer.c util/cf-lexer.h + util/cf-parser.c util/cf-parser.h util/circlebuf.h + util/config-file.c util/config-file.h + util/crc32.c util/crc32.h + util/curl/curl-helper.h util/darray.h util/deque.h + util/dstr.c util/dstr.h - util/dstr.hpp + util/file-serializer.c util/file-serializer.h + util/lexer.c util/lexer.h + util/pipe.c util/pipe.h + util/platform.c util/platform.h + util/profiler.c util/profiler.h util/profiler.hpp util/serializer.h + util/source-profiler.c + util/source-profiler.h + util/sse-intrin.h + util/task.c + util/task.h + util/text-lookup.c + util/text-lookup.h + util/threading.h + util/utf8.c + util/utf8.h + util/uthash.h + util/util.hpp + util/util_uint128.h + util/util_uint64.h +) + +target_sources( + libobs + PRIVATE util/simde/check.h util/simde/debug-trap.h util/simde/hedley.h @@ -370,15 +155,235 @@ set(public_headers util/simde/x86/mmx.h util/simde/x86/sse.h util/simde/x86/sse2.h - util/sse-intrin.h - util/task.h - util/text-lookup.h - util/threading-posix.h - util/threading.h - util/uthash.h - util/util.hpp - util/util_uint128.h - util/util_uint64.h) +) + +target_sources( + libobs + PRIVATE + callback/calldata.c + callback/calldata.h + callback/decl.c + callback/decl.h + callback/proc.c + callback/proc.h + callback/signal.c + callback/signal.h +) + +target_sources( + libobs + PRIVATE + media-io/audio-io.c + media-io/audio-io.h + media-io/audio-math.h + media-io/audio-resampler-ffmpeg.c + media-io/audio-resampler.h + media-io/format-conversion.c + media-io/format-conversion.h + media-io/frame-rate.h + media-io/media-io-defs.h + media-io/media-remux.c + media-io/media-remux.h + media-io/video-fourcc.c + media-io/video-frame.c + media-io/video-frame.h + media-io/video-io.c + media-io/video-io.h + media-io/video-matrices.c + media-io/video-scaler-ffmpeg.c + media-io/video-scaler.h +) + +target_sources( + libobs + PRIVATE + graphics/axisang.c + graphics/axisang.h + graphics/bounds.c + graphics/bounds.h + graphics/device-exports.h + graphics/effect-parser.c + graphics/effect-parser.h + graphics/effect.c + graphics/effect.h + graphics/graphics-ffmpeg.c + graphics/graphics-imports.c + graphics/graphics-internal.h + graphics/graphics.c + graphics/graphics.h + graphics/half.h + graphics/image-file.c + graphics/image-file.h + graphics/input.h + graphics/libnsgif/libnsgif.c + graphics/libnsgif/libnsgif.h + graphics/math-defs.h + graphics/math-extra.c + graphics/math-extra.h + graphics/matrix3.c + graphics/matrix3.h + graphics/matrix4.c + graphics/matrix4.h + graphics/plane.c + graphics/plane.h + graphics/quat.c + graphics/quat.h + graphics/shader-parser.c + graphics/shader-parser.h + graphics/srgb.h + graphics/texture-render.c + graphics/vec2.c + graphics/vec2.h + graphics/vec3.c + graphics/vec3.h + graphics/vec4.c + graphics/vec4.h +) + +target_compile_features(libobs PUBLIC cxx_std_17) + +target_compile_definitions( + libobs + PRIVATE IS_LIBOBS + PUBLIC + $:ENABLE_HEVC>> + $:SHOW_SUBPROCESSES>> +) + +target_link_libraries( + libobs + PRIVATE + OBS::caption + OBS::libobs-version + FFmpeg::avcodec + FFmpeg::avformat + FFmpeg::avutil + FFmpeg::swscale + FFmpeg::swresample + jansson::jansson + Uthash::Uthash + ZLIB::ZLIB + PUBLIC Threads::Threads +) + +if(OS_WINDOWS) + include(cmake/os-windows.cmake) +elseif(OS_MACOS) + include(cmake/os-macos.cmake) +elseif(OS_LINUX) + include(cmake/os-linux.cmake) +elseif(OS_FREEBSD OR OS_OPENBSD) + include(cmake/os-freebsd.cmake) +endif() + +configure_file(obsconfig.h.in "${CMAKE_BINARY_DIR}/config/obsconfig.h" @ONLY) + +target_include_directories( + libobs + PUBLIC "$" "$" +) + +target_compile_definitions(libobs PUBLIC HAVE_OBSCONFIG_H) + +set( + public_headers + callback/calldata.h + callback/decl.h + callback/proc.h + callback/signal.h + graphics/axisang.h + graphics/bounds.h + graphics/effect-parser.h + graphics/effect.h + graphics/graphics.h + graphics/image-file.h + graphics/input.h + graphics/libnsgif/libnsgif.h + graphics/math-defs.h + graphics/math-extra.h + graphics/matrix3.h + graphics/matrix4.h + graphics/plane.h + graphics/quat.h + graphics/shader-parser.h + graphics/srgb.h + graphics/vec2.h + graphics/vec3.h + graphics/vec4.h + media-io/audio-io.h + media-io/audio-math.h + media-io/audio-resampler.h + media-io/format-conversion.h + media-io/frame-rate.h + media-io/media-io-defs.h + media-io/media-remux.h + media-io/video-frame.h + media-io/video-io.h + media-io/video-scaler.h + obs-audio-controls.h + obs-avc.h + obs-config.h + obs-data.h + obs-defs.h + obs-encoder.h + obs-hotkey.h + obs-hotkeys.h + obs-interaction.h + obs-missing-files.h + obs-module.h + obs-nal.h + obs-nix-platform.h + obs-output.h + obs-properties.h + obs-service.h + obs-source.h + obs.h + obs.hpp + util/array-serializer.h + util/base.h + util/bitstream.h + util/bmem.h + util/c99defs.h + util/cf-lexer.h + util/cf-parser.h + util/circlebuf.h + util/config-file.h + util/crc32.h + util/darray.h + util/deque.h + util/dstr.h + util/dstr.hpp + util/file-serializer.h + util/lexer.h + util/pipe.h + util/platform.h + util/profiler.h + util/profiler.hpp + util/serializer.h + util/simde/check.h + util/simde/debug-trap.h + util/simde/hedley.h + util/simde/simde-align.h + util/simde/simde-arch.h + util/simde/simde-common.h + util/simde/simde-constify.h + util/simde/simde-detect-clang.h + util/simde/simde-diagnostic.h + util/simde/simde-features.h + util/simde/simde-math.h + util/simde/x86/mmx.h + util/simde/x86/sse.h + util/simde/x86/sse2.h + util/sse-intrin.h + util/task.h + util/text-lookup.h + util/threading-posix.h + util/threading.h + util/uthash.h + util/util.hpp + util/util_uint128.h + util/util_uint64.h +) if(OS_WINDOWS) list( @@ -392,7 +397,8 @@ if(OS_WINDOWS) util/windows/win-registry.h util/windows/win-version.h util/windows/window-helpers.h - util/windows/WinHandle.hpp) + util/windows/WinHandle.hpp + ) elseif(OS_MACOS) list(APPEND public_headers util/apple/cfstring-utils.h) endif() @@ -401,14 +407,13 @@ if(ENABLE_HEVC) list(APPEND public_headers obs-hevc.h) endif() -# cmake-format: off set_property(TARGET libobs APPEND PROPERTY OBS_PUBLIC_HEADERS ${public_headers}) set_target_properties_obs( libobs PROPERTIES FOLDER core VERSION 0 - SOVERSION "${OBS_VERSION_MAJOR}") -# cmake-format: on + SOVERSION "${OBS_VERSION_MAJOR}" +) target_export(libobs) diff --git a/libobs/cmake/legacy.cmake b/libobs/cmake/legacy.cmake index e6f1a0d65ac39e..177554060ff288 100644 --- a/libobs/cmake/legacy.cmake +++ b/libobs/cmake/legacy.cmake @@ -4,10 +4,8 @@ endif() project(libobs) -# cmake-format: off add_library(libobs-version STATIC EXCLUDE_FROM_ALL) add_library(OBS::libobs-version ALIAS libobs-version) -# cmake-format: on configure_file(obsversion.c.in obsversion.c @ONLY) target_sources(libobs-version PRIVATE obsversion.c obsversion.h) target_include_directories(libobs-version PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) @@ -15,10 +13,7 @@ set_property(TARGET libobs-version PROPERTY FOLDER core) find_package(Jansson 2.5 REQUIRED) find_package(Threads REQUIRED) -find_package( - FFmpeg REQUIRED - COMPONENTS avformat avutil swscale swresample - OPTIONAL_COMPONENTS avcodec) +find_package(FFmpeg REQUIRED COMPONENTS avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec) find_package(ZLIB REQUIRED) find_package(Uthash REQUIRED) @@ -27,144 +22,154 @@ add_library(OBS::libobs ALIAS libobs) target_sources( libobs - PRIVATE obs.c - obs.h - obs.hpp - obs-audio.c - obs-audio-controls.c - obs-audio-controls.h - obs-av1.c - obs-av1.h - obs-avc.c - obs-avc.h - obs-data.c - obs-data.h - obs-defs.h - obs-display.c - obs-encoder.c - obs-encoder.h - obs-ffmpeg-compat.h - obs-hotkey.c - obs-hotkey.h - obs-hotkeys.h - obs-missing-files.c - obs-missing-files.h - obs-nal.c - obs-nal.h - obs-hotkey-name-map.c - obs-interaction.h - obs-internal.h - obs-module.c - obs-module.h - obs-output.c - obs-output.h - obs-output-delay.c - obs-properties.c - obs-properties.h - obs-service.c - obs-service.h - obs-scene.c - obs-scene.h - obs-source.c - obs-source.h - obs-source-deinterlace.c - obs-source-transition.c - obs-video.c - obs-video-gpu-encode.c - obs-view.c - obs-config.h) + PRIVATE + obs.c + obs.h + obs.hpp + obs-audio.c + obs-audio-controls.c + obs-audio-controls.h + obs-av1.c + obs-av1.h + obs-avc.c + obs-avc.h + obs-data.c + obs-data.h + obs-defs.h + obs-display.c + obs-encoder.c + obs-encoder.h + obs-ffmpeg-compat.h + obs-hotkey.c + obs-hotkey.h + obs-hotkeys.h + obs-missing-files.c + obs-missing-files.h + obs-nal.c + obs-nal.h + obs-hotkey-name-map.c + obs-interaction.h + obs-internal.h + obs-module.c + obs-module.h + obs-output.c + obs-output.h + obs-output-delay.c + obs-properties.c + obs-properties.h + obs-service.c + obs-service.h + obs-scene.c + obs-scene.h + obs-source.c + obs-source.h + obs-source-deinterlace.c + obs-source-transition.c + obs-video.c + obs-video-gpu-encode.c + obs-view.c + obs-config.h +) target_sources( libobs - PRIVATE util/simde/check.h - util/simde/debug-trap.h - util/simde/hedley.h - util/simde/simde-align.h - util/simde/simde-arch.h - util/simde/simde-common.h - util/simde/simde-constify.h - util/simde/simde-detect-clang.h - util/simde/simde-diagnostic.h - util/simde/simde-features.h - util/simde/simde-math.h - util/simde/x86/mmx.h - util/simde/x86/sse2.h - util/simde/x86/sse.h) + PRIVATE + util/simde/check.h + util/simde/debug-trap.h + util/simde/hedley.h + util/simde/simde-align.h + util/simde/simde-arch.h + util/simde/simde-common.h + util/simde/simde-constify.h + util/simde/simde-detect-clang.h + util/simde/simde-diagnostic.h + util/simde/simde-features.h + util/simde/simde-math.h + util/simde/x86/mmx.h + util/simde/x86/sse2.h + util/simde/x86/sse.h +) target_sources( libobs - PRIVATE callback/calldata.c - callback/calldata.h - callback/decl.c - callback/decl.h - callback/signal.c - callback/signal.h - callback/proc.c - callback/proc.h) + PRIVATE + callback/calldata.c + callback/calldata.h + callback/decl.c + callback/decl.h + callback/signal.c + callback/signal.h + callback/proc.c + callback/proc.h +) target_sources( libobs - PRIVATE graphics/graphics.c - graphics/graphics.h - graphics/graphics-imports.c - graphics/graphics-internal.h - graphics/axisang.c - graphics/axisang.h - graphics/bounds.c - graphics/bounds.h - graphics/device-exports.h - graphics/effect.c - graphics/effect.h - graphics/effect-parser.c - graphics/effect-parser.h - graphics/half.h - graphics/image-file.c - graphics/image-file.h - graphics/math-extra.c - graphics/math-extra.h - graphics/matrix3.c - graphics/matrix3.h - graphics/matrix4.c - graphics/matrix4.h - graphics/plane.c - graphics/plane.h - graphics/quat.c - graphics/quat.h - graphics/shader-parser.c - graphics/shader-parser.h - graphics/srgb.h - graphics/texture-render.c - graphics/vec2.c - graphics/vec2.h - graphics/vec3.c - graphics/vec3.h - graphics/vec4.c - graphics/vec4.h - graphics/libnsgif/libnsgif.c - graphics/libnsgif/libnsgif.h - graphics/graphics-ffmpeg.c) + PRIVATE + graphics/graphics.c + graphics/graphics.h + graphics/graphics-imports.c + graphics/graphics-internal.h + graphics/axisang.c + graphics/axisang.h + graphics/bounds.c + graphics/bounds.h + graphics/device-exports.h + graphics/effect.c + graphics/effect.h + graphics/effect-parser.c + graphics/effect-parser.h + graphics/half.h + graphics/image-file.c + graphics/image-file.h + graphics/math-extra.c + graphics/math-extra.h + graphics/matrix3.c + graphics/matrix3.h + graphics/matrix4.c + graphics/matrix4.h + graphics/plane.c + graphics/plane.h + graphics/quat.c + graphics/quat.h + graphics/shader-parser.c + graphics/shader-parser.h + graphics/srgb.h + graphics/texture-render.c + graphics/vec2.c + graphics/vec2.h + graphics/vec3.c + graphics/vec3.h + graphics/vec4.c + graphics/vec4.h + graphics/libnsgif/libnsgif.c + graphics/libnsgif/libnsgif.h + graphics/graphics-ffmpeg.c +) target_sources( libobs - PRIVATE media-io/audio-io.c - media-io/audio-io.h - media-io/audio-math.h - media-io/audio-resampler.h - media-io/audio-resampler-ffmpeg.c - media-io/format-conversion.c - media-io/format-conversion.h - media-io/frame-rate.h - media-io/media-remux.c - media-io/media-remux.h - media-io/video-fourcc.c - media-io/video-frame.c - media-io/video-frame.h - media-io/video-io.c - media-io/video-io.h - media-io/media-io-defs.h - media-io/video-matrices.c - media-io/video-scaler-ffmpeg.c - media-io/video-scaler.h) + PRIVATE + media-io/audio-io.c + media-io/audio-io.h + media-io/audio-math.h + media-io/audio-resampler.h + media-io/audio-resampler-ffmpeg.c + media-io/format-conversion.c + media-io/format-conversion.h + media-io/frame-rate.h + media-io/media-remux.c + media-io/media-remux.h + media-io/video-fourcc.c + media-io/video-frame.c + media-io/video-frame.h + media-io/video-io.c + media-io/video-io.h + media-io/media-io-defs.h + media-io/video-matrices.c + media-io/video-scaler-ffmpeg.c + media-io/video-scaler.h +) target_sources( libobs @@ -228,61 +233,62 @@ endif() if(NOT OS_MACOS) target_sources( libobs - PRIVATE data/area.effect - data/bicubic_scale.effect - data/bilinear_lowres_scale.effect - data/color.effect - data/default.effect - data/default_rect.effect - data/deinterlace_base.effect - data/deinterlace_blend.effect - data/deinterlace_blend_2x.effect - data/deinterlace_discard.effect - data/deinterlace_discard_2x.effect - data/deinterlace_linear.effect - data/deinterlace_linear_2x.effect - data/deinterlace_yadif.effect - data/deinterlace_yadif_2x.effect - data/format_conversion.effect - data/lanczos_scale.effect - data/opaque.effect - data/premultiplied_alpha.effect - data/repeat.effect - data/solid.effect) + PRIVATE + data/area.effect + data/bicubic_scale.effect + data/bilinear_lowres_scale.effect + data/color.effect + data/default.effect + data/default_rect.effect + data/deinterlace_base.effect + data/deinterlace_blend.effect + data/deinterlace_blend_2x.effect + data/deinterlace_discard.effect + data/deinterlace_discard_2x.effect + data/deinterlace_linear.effect + data/deinterlace_linear_2x.effect + data/deinterlace_yadif.effect + data/deinterlace_yadif_2x.effect + data/format_conversion.effect + data/lanczos_scale.effect + data/opaque.effect + data/premultiplied_alpha.effect + data/repeat.effect + data/solid.effect + ) endif() target_link_libraries( libobs - PRIVATE FFmpeg::avcodec - FFmpeg::avformat - FFmpeg::avutil - FFmpeg::swscale - FFmpeg::swresample - Jansson::Jansson - OBS::caption - OBS::libobs-version - Uthash::Uthash - ZLIB::ZLIB - PUBLIC Threads::Threads) + PRIVATE + FFmpeg::avcodec + FFmpeg::avformat + FFmpeg::avutil + FFmpeg::swscale + FFmpeg::swresample + Jansson::Jansson + OBS::caption + OBS::libobs-version + Uthash::Uthash + ZLIB::ZLIB + PUBLIC Threads::Threads +) set_target_properties( libobs - PROPERTIES OUTPUT_NAME obs - FOLDER "core" - VERSION "${OBS_VERSION_MAJOR}" - SOVERSION "0") + PROPERTIES OUTPUT_NAME obs FOLDER "core" VERSION "${OBS_VERSION_MAJOR}" SOVERSION "0" +) -target_compile_definitions( - libobs - PUBLIC ${ARCH_SIMD_DEFINES} - PRIVATE IS_LIBOBS) +target_compile_definitions(libobs PUBLIC ${ARCH_SIMD_DEFINES} PRIVATE IS_LIBOBS) target_compile_features(libobs PRIVATE cxx_alias_templates) target_compile_options(libobs PUBLIC ${ARCH_SIMD_FLAGS}) -target_include_directories(libobs PUBLIC $ - $) +target_include_directories( + libobs + PUBLIC $ $ +) if(OS_WINDOWS) set(MODULE_DESCRIPTION "OBS Library") @@ -292,34 +298,41 @@ if(OS_WINDOWS) target_sources( libobs - PRIVATE obs-win-crash-handler.c - obs-windows.c - util/threading-windows.c - util/threading-windows.h - util/pipe-windows.c - util/platform-windows.c - util/windows/device-enum.c - util/windows/device-enum.h - util/windows/obfuscate.c - util/windows/obfuscate.h - util/windows/win-registry.h - util/windows/win-version.h - util/windows/window-helpers.c - util/windows/window-helpers.h - util/windows/ComPtr.hpp - util/windows/CoTaskMemPtr.hpp - util/windows/HRError.hpp - util/windows/WinHandle.hpp - libobs.rc - audio-monitoring/win32/wasapi-output.c - audio-monitoring/win32/wasapi-enum-devices.c - audio-monitoring/win32/wasapi-output.h - audio-monitoring/win32/wasapi-monitoring-available.c) + PRIVATE + obs-win-crash-handler.c + obs-windows.c + util/threading-windows.c + util/threading-windows.h + util/pipe-windows.c + util/platform-windows.c + util/windows/device-enum.c + util/windows/device-enum.h + util/windows/obfuscate.c + util/windows/obfuscate.h + util/windows/win-registry.h + util/windows/win-version.h + util/windows/window-helpers.c + util/windows/window-helpers.h + util/windows/ComPtr.hpp + util/windows/CoTaskMemPtr.hpp + util/windows/HRError.hpp + util/windows/WinHandle.hpp + libobs.rc + audio-monitoring/win32/wasapi-output.c + audio-monitoring/win32/wasapi-enum-devices.c + audio-monitoring/win32/wasapi-output.h + audio-monitoring/win32/wasapi-monitoring-available.c + ) target_compile_definitions(libobs PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS) - set_source_files_properties(obs-win-crash-handler.c PROPERTIES COMPILE_DEFINITIONS - OBS_VERSION="${OBS_VERSION_CANONICAL}") - target_link_libraries(libobs PRIVATE dxgi Avrt Dwmapi winmm Rpcrt4) + set_source_files_properties( + obs-win-crash-handler.c + PROPERTIES COMPILE_DEFINITIONS OBS_VERSION="${OBS_VERSION_CANONICAL}" + ) + target_link_libraries( + libobs + PRIVATE dxgi Avrt Dwmapi winmm Rpcrt4 + ) if(MSVC) target_link_libraries(libobs PUBLIC OBS::w32-pthreads) @@ -343,9 +356,7 @@ if(OS_WINDOWS) target_sources(obs-winhandle INTERFACE util/windows/WinHandle.hpp) target_include_directories(obs-winhandle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") endif() - elseif(OS_MACOS) - find_library(COCOA Cocoa) find_library(COREAUDIO CoreAudio) find_library(AUDIOTOOLBOX AudioToolbox) @@ -361,36 +372,33 @@ elseif(OS_MACOS) AUDIOUNIT APPKIT IOKIT - CARBON) + CARBON + ) target_link_libraries( libobs - PRIVATE ${COCOA} - ${COREAUDIO} - ${AUDIOTOOLBOX} - ${AUDIOUNIT} - ${APPKIT} - ${IOKIT} - ${CARBON}) + PRIVATE ${COCOA} ${COREAUDIO} ${AUDIOTOOLBOX} ${AUDIOUNIT} ${APPKIT} ${IOKIT} ${CARBON} + ) target_sources( libobs - PRIVATE obs-cocoa.m - util/pipe-posix.c - util/platform-cocoa.m - util/platform-nix.c - util/threading-posix.c - util/threading-posix.h - util/apple/cfstring-utils.h - audio-monitoring/osx/coreaudio-enum-devices.c - audio-monitoring/osx/coreaudio-output.c - audio-monitoring/osx/coreaudio-monitoring-available.c - audio-monitoring/osx/mac-helpers.h) + PRIVATE + obs-cocoa.m + util/pipe-posix.c + util/platform-cocoa.m + util/platform-nix.c + util/threading-posix.c + util/threading-posix.h + util/apple/cfstring-utils.h + audio-monitoring/osx/coreaudio-enum-devices.c + audio-monitoring/osx/coreaudio-output.c + audio-monitoring/osx/coreaudio-monitoring-available.c + audio-monitoring/osx/mac-helpers.h + ) set_source_files_properties(util/platform-cocoa.m obs-cocoa.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set_target_properties(libobs PROPERTIES SOVERSION "1" BUILD_RPATH "$") - elseif(OS_POSIX) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) target_compile_definitions(libobs PRIVATE ENABLE_DARRAY_TYPE_TEST) @@ -398,23 +406,21 @@ elseif(OS_POSIX) find_package(LibUUID REQUIRED) find_package(X11 REQUIRED) - find_package( - XCB - COMPONENTS XCB - OPTIONAL_COMPONENTS XINPUT - QUIET) + find_package(XCB COMPONENTS XCB OPTIONAL_COMPONENTS XINPUT QUIET) find_package(X11_XCB REQUIRED) target_sources( libobs - PRIVATE obs-nix.c - obs-nix-platform.c - obs-nix-platform.h - obs-nix-x11.c - util/threading-posix.c - util/threading-posix.h - util/pipe-posix.c - util/platform-nix.c) + PRIVATE + obs-nix.c + obs-nix-platform.c + obs-nix-platform.h + obs-nix-x11.c + util/threading-posix.c + util/threading-posix.h + util/pipe-posix.c + util/platform-nix.c + ) target_link_libraries(libobs PRIVATE X11::X11_xcb XCB::XCB LibUUID::LibUUID) @@ -427,9 +433,13 @@ elseif(OS_POSIX) obs_status(STATUS "-> PulseAudio found - audio monitoring enabled") target_sources( libobs - PRIVATE audio-monitoring/pulse/pulseaudio-output.c audio-monitoring/pulse/pulseaudio-enum-devices.c - audio-monitoring/pulse/pulseaudio-wrapper.c audio-monitoring/pulse/pulseaudio-wrapper.h - audio-monitoring/pulse/pulseaudio-monitoring-available.c) + PRIVATE + audio-monitoring/pulse/pulseaudio-output.c + audio-monitoring/pulse/pulseaudio-enum-devices.c + audio-monitoring/pulse/pulseaudio-wrapper.c + audio-monitoring/pulse/pulseaudio-wrapper.h + audio-monitoring/pulse/pulseaudio-monitoring-available.c + ) target_link_libraries(libobs PRIVATE ${PULSEAUDIO_LIBRARY}) else() @@ -449,10 +459,7 @@ elseif(OS_POSIX) endif() if(ENABLE_WAYLAND) - find_package( - Wayland - COMPONENTS Client - REQUIRED) + find_package(Wayland COMPONENTS Client REQUIRED) find_package(Xkbcommon REQUIRED) target_link_libraries(libobs PRIVATE Wayland::Client Xkbcommon::Xkbcommon) @@ -477,7 +484,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/obsconfig.h.in ${CMAKE_BINARY_DIR}/co target_compile_definitions( libobs PUBLIC HAVE_OBSCONFIG_H - PRIVATE "OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\"" "$<$:LINUX_PORTABLE>") + PRIVATE "OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\"" "$<$:LINUX_PORTABLE>" +) if(ENABLE_FFMPEG_MUX_DEBUG) target_compile_definitions(libobs PRIVATE SHOW_SUBPROCESSES) @@ -490,18 +498,9 @@ list(FILTER _OBS_HEADERS INCLUDE REGEX ".*\\.h(pp)?") list(FILTER _OBS_SOURCES INCLUDE REGEX ".*\\.(m|c[cp]?p?)") list(FILTER _OBS_FILTERS INCLUDE REGEX ".*\\.effect") -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Source Files" - FILES ${_OBS_SOURCES}) -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Header Files" - FILES ${_OBS_HEADERS}) -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Effect Files" - FILES ${_OBS_FILTERS}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${_OBS_SOURCES}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Header Files" FILES ${_OBS_HEADERS}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Effect Files" FILES ${_OBS_FILTERS}) setup_binary_target(libobs) setup_target_resources(libobs libobs) diff --git a/libobs/cmake/obs-version.cmake b/libobs/cmake/obs-version.cmake index 01a4d73cf986da..d1be5eb2225e37 100644 --- a/libobs/cmake/obs-version.cmake +++ b/libobs/cmake/obs-version.cmake @@ -3,10 +3,7 @@ add_library(OBS::libobs-version ALIAS libobs-version) configure_file(obsversion.c.in obsversion.c @ONLY) -target_sources( - libobs-version - PRIVATE obsversion.c - PUBLIC obsversion.h) +target_sources(libobs-version PRIVATE obsversion.c PUBLIC obsversion.h) target_include_directories(libobs-version PUBLIC "$") diff --git a/libobs/cmake/os-freebsd.cmake b/libobs/cmake/os-freebsd.cmake index ad30f387d5f42d..ed5f238bb426fc 100644 --- a/libobs/cmake/os-freebsd.cmake +++ b/libobs/cmake/os-freebsd.cmake @@ -1,9 +1,7 @@ find_package(LibUUID REQUIRED) find_package(X11 REQUIRED) find_package(X11-xcb REQUIRED) -# cmake-format: off find_package(Xcb REQUIRED xcb OPTIONAL_COMPONENTS xcb-xinput) -# cmake-format: on find_package(Gio) find_package(Sysinfo REQUIRED) @@ -22,41 +20,49 @@ endif() target_sources( libobs - PRIVATE # cmake-format: sortable - obs-nix-platform.c - obs-nix-platform.h - obs-nix-x11.c - obs-nix.c - util/pipe-posix.c - util/platform-nix.c - util/threading-posix.c - util/threading-posix.h) + PRIVATE + obs-nix-platform.c + obs-nix-platform.h + obs-nix-x11.c + obs-nix.c + util/pipe-posix.c + util/platform-nix.c + util/threading-posix.c + util/threading-posix.h +) target_compile_definitions( - libobs PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" $<$:ENABLE_DARRAY_TYPE_TEST> - $<$:ENABLE_DARRAY_TYPE_TEST>) + libobs + PRIVATE + OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" + $<$:ENABLE_DARRAY_TYPE_TEST> + $<$:ENABLE_DARRAY_TYPE_TEST> +) target_link_libraries( libobs - PRIVATE X11::x11-xcb - xcb::xcb - LibUUID::LibUUID - Sysinfo::Sysinfo - ${CMAKE_DL_LIBS} - $<$>:m> - $<$:xcb::xcb-input>) + PRIVATE + X11::x11-xcb + xcb::xcb + LibUUID::LibUUID + Sysinfo::Sysinfo + ${CMAKE_DL_LIBS} + $<$>:m> + $<$:xcb::xcb-input> +) if(ENABLE_PULSEAUDIO) find_package(PulseAudio REQUIRED) target_sources( libobs - PRIVATE # cmake-format: sortable - audio-monitoring/pulse/pulseaudio-enum-devices.c - audio-monitoring/pulse/pulseaudio-monitoring-available.c - audio-monitoring/pulse/pulseaudio-output.c - audio-monitoring/pulse/pulseaudio-wrapper.c - audio-monitoring/pulse/pulseaudio-wrapper.h) + PRIVATE + audio-monitoring/pulse/pulseaudio-enum-devices.c + audio-monitoring/pulse/pulseaudio-monitoring-available.c + audio-monitoring/pulse/pulseaudio-output.c + audio-monitoring/pulse/pulseaudio-wrapper.c + audio-monitoring/pulse/pulseaudio-wrapper.h + ) target_link_libraries(libobs PRIVATE PulseAudio::PulseAudio) target_enable_feature(libobs "PulseAudio audio monitoring (FreeBSD)") diff --git a/libobs/cmake/os-linux.cmake b/libobs/cmake/os-linux.cmake index bc6b2395a9644e..d2a2fb582aec22 100644 --- a/libobs/cmake/os-linux.cmake +++ b/libobs/cmake/os-linux.cmake @@ -1,27 +1,30 @@ find_package(LibUUID REQUIRED) find_package(X11 REQUIRED) find_package(X11-xcb REQUIRED) -# cmake-format: off find_package(Xcb REQUIRED xcb OPTIONAL_COMPONENTS xcb-xinput) -# cmake-format: on find_package(Gio) target_sources( libobs - PRIVATE # cmake-format: sortable - obs-nix-platform.c - obs-nix-platform.h - obs-nix-x11.c - obs-nix.c - util/pipe-posix.c - util/platform-nix.c - util/threading-posix.c - util/threading-posix.h) + PRIVATE + obs-nix-platform.c + obs-nix-platform.h + obs-nix-x11.c + obs-nix.c + util/pipe-posix.c + util/platform-nix.c + util/threading-posix.c + util/threading-posix.h +) target_compile_definitions( libobs - PRIVATE USE_XDG OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" $<$:ENABLE_DARRAY_TYPE_TEST> - $<$:ENABLE_DARRAY_TYPE_TEST>) + PRIVATE + USE_XDG + OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" + $<$:ENABLE_DARRAY_TYPE_TEST> + $<$:ENABLE_DARRAY_TYPE_TEST> +) if(CMAKE_C_COMPILER_ID STREQUAL GNU) # * Silence type-limits warning in line 292 of libobs/utils/utf8.c @@ -43,20 +46,28 @@ if(NOT HAVE_UUID_HEADER) endif() target_link_libraries( - libobs PRIVATE X11::x11-xcb xcb::xcb LibUUID::LibUUID ${CMAKE_DL_LIBS} $<$>:m> - $<$:xcb::xcb-input>) + libobs + PRIVATE + X11::x11-xcb + xcb::xcb + LibUUID::LibUUID + ${CMAKE_DL_LIBS} + $<$>:m> + $<$:xcb::xcb-input> +) if(ENABLE_PULSEAUDIO) find_package(PulseAudio REQUIRED) target_sources( libobs - PRIVATE # cmake-format: sortable - audio-monitoring/pulse/pulseaudio-enum-devices.c - audio-monitoring/pulse/pulseaudio-monitoring-available.c - audio-monitoring/pulse/pulseaudio-output.c - audio-monitoring/pulse/pulseaudio-wrapper.c - audio-monitoring/pulse/pulseaudio-wrapper.h) + PRIVATE + audio-monitoring/pulse/pulseaudio-enum-devices.c + audio-monitoring/pulse/pulseaudio-monitoring-available.c + audio-monitoring/pulse/pulseaudio-output.c + audio-monitoring/pulse/pulseaudio-wrapper.c + audio-monitoring/pulse/pulseaudio-wrapper.h + ) target_link_libraries(libobs PRIVATE PulseAudio::PulseAudio) target_enable_feature(libobs "PulseAudio audio monitoring (Linux)") diff --git a/libobs/cmake/os-macos.cmake b/libobs/cmake/os-macos.cmake index 7877236c08debd..4d63b5fa035178 100644 --- a/libobs/cmake/os-macos.cmake +++ b/libobs/cmake/os-macos.cmake @@ -1,28 +1,30 @@ target_link_libraries( libobs - PRIVATE # cmake-format: sortable - "$" - "$" - "$" - "$" - "$" - "$" - "$") + PRIVATE + "$" + "$" + "$" + "$" + "$" + "$" + "$" +) target_sources( libobs - PRIVATE # cmake-format: sortable - audio-monitoring/osx/coreaudio-enum-devices.c - audio-monitoring/osx/coreaudio-monitoring-available.c - audio-monitoring/osx/coreaudio-output.c - audio-monitoring/osx/mac-helpers.h - obs-cocoa.m - util/apple/cfstring-utils.h - util/pipe-posix.c - util/platform-cocoa.m - util/platform-nix.c - util/threading-posix.c - util/threading-posix.h) + PRIVATE + audio-monitoring/osx/coreaudio-enum-devices.c + audio-monitoring/osx/coreaudio-monitoring-available.c + audio-monitoring/osx/coreaudio-output.c + audio-monitoring/osx/mac-helpers.h + obs-cocoa.m + util/apple/cfstring-utils.h + util/pipe-posix.c + util/platform-cocoa.m + util/platform-nix.c + util/threading-posix.c + util/threading-posix.h +) target_compile_options(libobs PUBLIC -Wno-strict-prototypes -Wno-shorten-64-to-32) diff --git a/libobs/cmake/os-windows.cmake b/libobs/cmake/os-windows.cmake index 461ef5f5daac9b..caa8d25d06309c 100644 --- a/libobs/cmake/os-windows.cmake +++ b/libobs/cmake/os-windows.cmake @@ -21,45 +21,42 @@ target_include_directories(obs-winhandle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}" target_sources( libobs - PRIVATE # cmake-format: sortable - audio-monitoring/win32/wasapi-enum-devices.c - audio-monitoring/win32/wasapi-monitoring-available.c - audio-monitoring/win32/wasapi-output.c - audio-monitoring/win32/wasapi-output.h - libobs.rc - obs-win-crash-handler.c - obs-windows.c - util/pipe-windows.c - util/platform-windows.c - util/threading-windows.c - util/threading-windows.h - util/windows/CoTaskMemPtr.hpp - util/windows/device-enum.c - util/windows/device-enum.h - util/windows/HRError.hpp - util/windows/obfuscate.c - util/windows/obfuscate.h - util/windows/win-registry.h - util/windows/win-version.h - util/windows/window-helpers.c - util/windows/window-helpers.h) + PRIVATE + audio-monitoring/win32/wasapi-enum-devices.c + audio-monitoring/win32/wasapi-monitoring-available.c + audio-monitoring/win32/wasapi-output.c + audio-monitoring/win32/wasapi-output.h + libobs.rc + obs-win-crash-handler.c + obs-windows.c + util/pipe-windows.c + util/platform-windows.c + util/threading-windows.c + util/threading-windows.h + util/windows/CoTaskMemPtr.hpp + util/windows/device-enum.c + util/windows/device-enum.h + util/windows/HRError.hpp + util/windows/obfuscate.c + util/windows/obfuscate.h + util/windows/win-registry.h + util/windows/win-version.h + util/windows/window-helpers.c + util/windows/window-helpers.h +) target_compile_options(libobs PRIVATE $<$:/EHc->) -set_source_files_properties(obs-win-crash-handler.c PROPERTIES COMPILE_DEFINITIONS - OBS_VERSION="${OBS_VERSION_CANONICAL}") +set_source_files_properties( + obs-win-crash-handler.c + PROPERTIES COMPILE_DEFINITIONS OBS_VERSION="${OBS_VERSION_CANONICAL}" +) target_link_libraries( libobs - PRIVATE Avrt - Dwmapi - Dxgi - winmm - Rpcrt4 - OBS::obfuscate - OBS::winhandle - OBS::COMutils - PUBLIC OBS::w32-pthreads) + PRIVATE Avrt Dwmapi Dxgi winmm Rpcrt4 OBS::obfuscate OBS::winhandle OBS::COMutils + PUBLIC OBS::w32-pthreads +) target_link_options(libobs PRIVATE /IGNORE:4098 /SAFESEH:NO) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 632a40be4604f2..67d842328ce107 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -11,9 +11,7 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_ENABLED "Plugin Support") macro(check_obs_browser) - if((OS_WINDOWS AND CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") - OR OS_MACOS - OR OS_LINUX) + if((OS_WINDOWS AND CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") OR OS_MACOS OR OS_LINUX) if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") message(FATAL_ERROR "Required submodule 'obs-browser' not available.") else() @@ -37,12 +35,14 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) add_obs_plugin( aja PLATFORMS WINDOWS MACOS LINUX - WITH_MESSAGE) + WITH_MESSAGE + ) add_obs_plugin(coreaudio-encoder PLATFORMS WINDOWS MACOS) add_obs_plugin( decklink PLATFORMS WINDOWS MACOS LINUX - WITH_MESSAGE) + WITH_MESSAGE + ) add_obs_plugin(image-source) add_obs_plugin(linux-alsa PLATFORMS LINUX FREEBSD OPENBSD) add_obs_plugin(linux-capture PLATFORMS LINUX FREEBSD OPENBSD) @@ -68,13 +68,15 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) obs-qsv11 PLATFORMS WINDOWS LINUX ARCHITECTURES x64 x86_64 - WITH_MESSAGE) + WITH_MESSAGE + ) add_obs_plugin(obs-text PLATFORMS WINDOWS) add_obs_plugin(obs-transitions) add_obs_plugin( obs-vst PLATFORMS WINDOWS MACOS LINUX - WITH_MESSAGE) + WITH_MESSAGE + ) add_obs_plugin(obs-webrtc) check_obs_websocket() diff --git a/plugins/aja/CMakeLists.txt b/plugins/aja/CMakeLists.txt index ae170e6f5fc92e..ea9e8c6320b80a 100644 --- a/plugins/aja/CMakeLists.txt +++ b/plugins/aja/CMakeLists.txt @@ -19,24 +19,25 @@ add_library(OBS::aja-support ALIAS aja-support) target_sources( aja-support - PRIVATE # cmake-format: sortable - aja-card-manager.cpp - aja-common.cpp - aja-presets.cpp - aja-props.cpp - aja-routing.cpp - aja-vpid-data.cpp - aja-widget-io.cpp - PUBLIC # cmake-format: sortable - aja-card-manager.hpp - aja-common.hpp - aja-enums.hpp - aja-presets.hpp - aja-props.hpp - aja-routing.hpp - aja-ui-props.hpp - aja-vpid-data.hpp - aja-widget-io.hpp) + PRIVATE + aja-card-manager.cpp + aja-common.cpp + aja-presets.cpp + aja-props.cpp + aja-routing.cpp + aja-vpid-data.cpp + aja-widget-io.cpp + PUBLIC + aja-card-manager.hpp + aja-common.hpp + aja-enums.hpp + aja-presets.hpp + aja-props.hpp + aja-routing.hpp + aja-ui-props.hpp + aja-vpid-data.hpp + aja-widget-io.hpp +) target_link_libraries(aja-support PUBLIC OBS::libobs AJA::LibAJANTV2) @@ -44,19 +45,23 @@ set_target_properties(aja-support PROPERTIES FOLDER plugins/aja POSITION_INDEPEN target_sources( aja - PRIVATE # cmake-format: sortable - aja-output.cpp - aja-output.hpp - aja-source.cpp - aja-source.hpp - audio-repack.c - audio-repack.h - audio-repack.hpp - main.cpp) + PRIVATE + aja-output.cpp + aja-output.hpp + aja-source.cpp + aja-source.hpp + audio-repack.c + audio-repack.h + audio-repack.hpp + main.cpp +) target_compile_options( - aja-support PUBLIC $<$:-Wno-deprecated-declarations> - $<$:-Wno-unused-variable>) + aja-support + PUBLIC + $<$:-Wno-deprecated-declarations> + $<$:-Wno-unused-variable> +) target_link_libraries(aja PRIVATE OBS::aja-support) @@ -66,6 +71,4 @@ if(OS_WINDOWS) set_property(TARGET aja PROPERTY COMPILE_WARNING_AS_ERROR FALSE) endif() -# cmake-format: off set_target_properties_obs(aja PROPERTIES FOLDER plugins/aja PREFIX "") -# cmake-format: on diff --git a/plugins/coreaudio-encoder/CMakeLists.txt b/plugins/coreaudio-encoder/CMakeLists.txt index fc2bc7c38b1364..230035eb0f505f 100644 --- a/plugins/coreaudio-encoder/CMakeLists.txt +++ b/plugins/coreaudio-encoder/CMakeLists.txt @@ -15,20 +15,18 @@ add_library(OBS::coreaudio-encoder ALIAS coreaudio-encoder) target_sources(coreaudio-encoder PRIVATE encoder.cpp $<$:windows-imports.h>) -# cmake-format: off target_link_libraries( coreaudio-encoder - PRIVATE OBS::libobs - "$<$:$>" - "$<$:$>" - "$<$:$>") -# cmake-format: on + PRIVATE + OBS::libobs + "$<$:$>" + "$<$:$>" + "$<$:$>" +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in coreaudio-encoder.rc) target_sources(coreaudio-encoder PRIVATE coreaudio-encoder.rc) endif() -# cmake-format: off set_target_properties_obs(coreaudio-encoder PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/decklink/CMakeLists.txt b/plugins/decklink/CMakeLists.txt index 9553319f7020a7..130f3e90b43010 100644 --- a/plugins/decklink/CMakeLists.txt +++ b/plugins/decklink/CMakeLists.txt @@ -17,7 +17,6 @@ add_library(Decklink::SDK ALIAS decklink-sdk) target_sources( decklink-sdk INTERFACE - # cmake-format: sortable "$<$:${CMAKE_CURRENT_SOURCE_DIR}/mac/decklink-sdk/DeckLinkAPI.h>" "$<$:${CMAKE_CURRENT_SOURCE_DIR}/mac/decklink-sdk/DeckLinkAPIConfiguration.h>" "$<$:${CMAKE_CURRENT_SOURCE_DIR}/mac/decklink-sdk/DeckLinkAPIDeckControl.h>" @@ -36,47 +35,55 @@ target_sources( "$<$:${CMAKE_CURRENT_SOURCE_DIR}/linux/decklink-sdk/DeckLinkAPITypes.h>" "$<$:${CMAKE_CURRENT_SOURCE_DIR}/linux/decklink-sdk/DeckLinkAPIVersion.h>" "$<$:${CMAKE_CURRENT_SOURCE_DIR}/linux/decklink-sdk/LinuxCOM.h>" - "$<$:${CMAKE_CURRENT_SOURCE_DIR}/win/decklink-sdk/DeckLinkAPIVersion.h>") + "$<$:${CMAKE_CURRENT_SOURCE_DIR}/win/decklink-sdk/DeckLinkAPIVersion.h>" +) target_sources( decklink - PRIVATE # cmake-format: sortable - $<$:mac/platform.cpp> - $<$:linux/platform.cpp> - $<$:win/platform.cpp> - audio-repack.c - audio-repack.h - audio-repack.hpp - const.h - decklink-device-discovery.cpp - decklink-device-discovery.hpp - decklink-device-instance.cpp - decklink-device-instance.hpp - decklink-device-mode.cpp - decklink-device-mode.hpp - decklink-device.cpp - decklink-device.hpp - decklink-devices.cpp - decklink-devices.hpp - decklink-output.cpp - decklink-source.cpp - DecklinkBase.cpp - DecklinkBase.h - DecklinkInput.cpp - DecklinkInput.hpp - DecklinkOutput.cpp - DecklinkOutput.hpp - OBSVideoFrame.cpp - OBSVideoFrame.h - platform.hpp - plugin-main.cpp - util.cpp - util.hpp) + PRIVATE + $<$:mac/platform.cpp> + $<$:linux/platform.cpp> + $<$:win/platform.cpp> + audio-repack.c + audio-repack.h + audio-repack.hpp + const.h + decklink-device-discovery.cpp + decklink-device-discovery.hpp + decklink-device-instance.cpp + decklink-device-instance.hpp + decklink-device-mode.cpp + decklink-device-mode.hpp + decklink-device.cpp + decklink-device.hpp + decklink-devices.cpp + decklink-devices.hpp + decklink-output.cpp + decklink-source.cpp + DecklinkBase.cpp + DecklinkBase.h + DecklinkInput.cpp + DecklinkInput.hpp + DecklinkOutput.cpp + DecklinkOutput.hpp + OBSVideoFrame.cpp + OBSVideoFrame.h + platform.hpp + plugin-main.cpp + util.cpp + util.hpp +) target_compile_definitions(decklink PRIVATE $<$:NOMINMAX>) -target_link_libraries(decklink PRIVATE OBS::libobs OBS::caption Decklink::SDK - "$<$:$>") +target_link_libraries( + decklink + PRIVATE + OBS::libobs + OBS::caption + Decklink::SDK + "$<$:$>" +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in win-decklink.rc) @@ -88,6 +95,4 @@ if(OS_WINDOWS) target_sources(decklink-sdk INTERFACE win/decklink-sdk/DeckLinkAPIVersion.h) endif() -# cmake-format: off set_target_properties_obs(decklink PROPERTIES FOLDER plugins/decklink PREFIX "") -# cmake-format: on diff --git a/plugins/image-source/CMakeLists.txt b/plugins/image-source/CMakeLists.txt index 03489ddc1043ae..07fa2ad68dc2a2 100644 --- a/plugins/image-source/CMakeLists.txt +++ b/plugins/image-source/CMakeLists.txt @@ -14,6 +14,4 @@ if(OS_WINDOWS) target_sources(image-source PRIVATE image-source.rc) endif() -# cmake-format: off set_target_properties_obs(image-source PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/linux-alsa/CMakeLists.txt b/plugins/linux-alsa/CMakeLists.txt index b50038286e07f5..caafd18b48b1ae 100644 --- a/plugins/linux-alsa/CMakeLists.txt +++ b/plugins/linux-alsa/CMakeLists.txt @@ -14,8 +14,7 @@ find_package(ALSA REQUIRED) add_library(linux-alsa MODULE) add_library(OBS::alsa ALIAS linux-alsa) -target_sources(linux-alsa PRIVATE # cmake-format: sortable - alsa-input.c linux-alsa.c) +target_sources(linux-alsa PRIVATE alsa-input.c linux-alsa.c) target_link_libraries(linux-alsa PRIVATE OBS::libobs ALSA::ALSA) set_target_properties_obs(linux-alsa PROPERTIES FOLDER plugins PREFIX "") diff --git a/plugins/linux-capture/CMakeLists.txt b/plugins/linux-capture/CMakeLists.txt index 04c74a938ad99d..b5c7f0b9d49738 100644 --- a/plugins/linux-capture/CMakeLists.txt +++ b/plugins/linux-capture/CMakeLists.txt @@ -4,42 +4,39 @@ legacy_check() find_package(X11 REQUIRED) -# cmake-format: off -find_package(Xcb REQUIRED xcb - xcb-xfixes - xcb-randr - xcb-shm - xcb-xinerama - xcb-composite) -# cmake-format: on +find_package( + Xcb + REQUIRED xcb xcb-xfixes xcb-randr xcb-shm xcb-xinerama xcb-composite +) add_library(linux-capture MODULE) add_library(OBS::capture ALIAS linux-capture) target_sources( linux-capture - PRIVATE # cmake-format: sortable - linux-capture.c - xcomposite-input.c - xcomposite-input.h - xcursor-xcb.c - xcursor-xcb.h - xhelpers.c - xhelpers.h - xshm-input.c) + PRIVATE + linux-capture.c + xcomposite-input.c + xcomposite-input.h + xcursor-xcb.c + xcursor-xcb.h + xhelpers.c + xhelpers.h + xshm-input.c +) target_link_libraries( linux-capture - PRIVATE OBS::libobs - OBS::glad - X11::X11 - xcb::xcb - xcb::xcb-xfixes - xcb::xcb-randr - xcb::xcb-shm - xcb::xcb-xinerama - xcb::xcb-composite) + PRIVATE + OBS::libobs + OBS::glad + X11::X11 + xcb::xcb + xcb::xcb-xfixes + xcb::xcb-randr + xcb::xcb-shm + xcb::xcb-xinerama + xcb::xcb-composite +) -# cmake-format: off set_target_properties_obs(linux-capture PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/linux-jack/CMakeLists.txt b/plugins/linux-jack/CMakeLists.txt index 59f0eb2670a33a..a3a101b6f29f07 100644 --- a/plugins/linux-jack/CMakeLists.txt +++ b/plugins/linux-jack/CMakeLists.txt @@ -13,8 +13,7 @@ find_package(Jack REQUIRED) add_library(linux-jack MODULE) add_library(OBS::jack ALIAS linux-jack) -target_sources(linux-jack PRIVATE # cmake-format: sortable - jack-input.c jack-wrapper.c linux-jack.c) +target_sources(linux-jack PRIVATE jack-input.c jack-wrapper.c linux-jack.c) target_compile_options(linux-jack PRIVATE $<$:-Wno-error=sign-compare>) diff --git a/plugins/linux-pipewire/CMakeLists.txt b/plugins/linux-pipewire/CMakeLists.txt index 1186a3df01da3d..75e772f3fb5a18 100644 --- a/plugins/linux-pipewire/CMakeLists.txt +++ b/plugins/linux-pipewire/CMakeLists.txt @@ -25,18 +25,19 @@ endif() target_sources( linux-pipewire - PRIVATE # cmake-format: sortable - $<$:camera-portal.c> - $<$:camera-portal.h> - formats.c - formats.h - linux-pipewire.c - pipewire.c - pipewire.h - portal.c - portal.h - screencast-portal.c - screencast-portal.h) + PRIVATE + $<$:camera-portal.c> + $<$:camera-portal.h> + formats.c + formats.h + linux-pipewire.c + pipewire.c + pipewire.h + portal.c + portal.h + screencast-portal.c + screencast-portal.h +) target_include_directories(linux-pipewire PRIVATE ${libdrm_include_directories}) diff --git a/plugins/linux-pulseaudio/CMakeLists.txt b/plugins/linux-pulseaudio/CMakeLists.txt index 7fff7d7b9f020e..43676c0254aadb 100644 --- a/plugins/linux-pulseaudio/CMakeLists.txt +++ b/plugins/linux-pulseaudio/CMakeLists.txt @@ -12,8 +12,7 @@ find_package(PulseAudio REQUIRED) add_library(linux-pulseaudio MODULE) add_library(OBS::pulseaudio ALIAS linux-pulseaudio) -target_sources(linux-pulseaudio PRIVATE # cmake-format: sortable - linux-pulseaudio.c pulse-input.c pulse-wrapper.c) +target_sources(linux-pulseaudio PRIVATE linux-pulseaudio.c pulse-input.c pulse-wrapper.c) target_link_libraries(linux-pulseaudio PRIVATE OBS::libobs PulseAudio::PulseAudio) set_target_properties_obs(linux-pulseaudio PROPERTIES FOLDER plugins PREFIX "") diff --git a/plugins/linux-v4l2/CMakeLists.txt b/plugins/linux-v4l2/CMakeLists.txt index d992a5557e5b82..9ae7b8a4bfe938 100644 --- a/plugins/linux-v4l2/CMakeLists.txt +++ b/plugins/linux-v4l2/CMakeLists.txt @@ -27,10 +27,15 @@ endif() add_library(linux-v4l2 MODULE) add_library(OBS::v4l2 ALIAS linux-v4l2) -target_sources(linux-v4l2 PRIVATE # cmake-format: sortable - linux-v4l2.c v4l2-controls.c v4l2-decoder.c v4l2-helpers.c v4l2-input.c v4l2-output.c) - -target_link_libraries(linux-v4l2 PRIVATE OBS::libobs Libv4l2::Libv4l2 FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil) +target_sources( + linux-v4l2 + PRIVATE linux-v4l2.c v4l2-controls.c v4l2-decoder.c v4l2-helpers.c v4l2-input.c v4l2-output.c +) + +target_link_libraries( + linux-v4l2 + PRIVATE OBS::libobs Libv4l2::Libv4l2 FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil +) if(ENABLE_UDEV) find_package(Libudev REQUIRED) diff --git a/plugins/mac-avcapture/CMakeLists.txt b/plugins/mac-avcapture/CMakeLists.txt index 197cae0b9a825e..7a4f11c019d30b 100644 --- a/plugins/mac-avcapture/CMakeLists.txt +++ b/plugins/mac-avcapture/CMakeLists.txt @@ -8,41 +8,43 @@ target_include_directories(mac-avcapture-legacy PRIVATE legacy) target_link_libraries( mac-avcapture-legacy - PRIVATE OBS::libobs - "$" - "$" - "$" - "$" - "$" - "$") - -# cmake-format: off + PRIVATE + OBS::libobs + "$" + "$" + "$" + "$" + "$" + "$" +) + set_target_properties_obs( mac-avcapture-legacy PROPERTIES FOLDER plugins PREFIX "" XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES - XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES) -# cmake-format: on + XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES +) add_library(mac-avcapture MODULE) add_library(OBS::avcapture ALIAS mac-avcapture) target_sources( mac-avcapture - PRIVATE plugin-main.m - plugin-main.h - plugin-properties.m - plugin-properties.h - OBSAVCapture.m - OBSAVCapture.h - OBSAVCapturePresetInfo.m - OBSAVCapturePresetInfo.h) + PRIVATE + plugin-main.m + plugin-main.h + plugin-properties.m + plugin-properties.h + OBSAVCapture.m + OBSAVCapture.h + OBSAVCapturePresetInfo.m + OBSAVCapturePresetInfo.h +) target_link_libraries(mac-avcapture PRIVATE OBS::libobs) -# cmake-format: off set_target_properties_obs( mac-avcapture PROPERTIES FOLDER plugins @@ -52,11 +54,11 @@ set_target_properties_obs( XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES YES XCODE_ATTRIBUTE_CLANG_MODULES_AUTOLINK YES - XCODE_ATTRIBUTE_GCC_STRICT_ALIASING YES) - + XCODE_ATTRIBUTE_GCC_STRICT_ALIASING YES +) string(TIMESTAMP CURRENT_YEAR "%Y") set_target_xcode_properties( mac-avcapture - PROPERTIES INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2023-${CURRENT_YEAR} Patrick Heyer") -# cmake-format: on + PROPERTIES INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2023-${CURRENT_YEAR} Patrick Heyer" +) diff --git a/plugins/mac-avcapture/legacy/CMakeLists.txt b/plugins/mac-avcapture/legacy/CMakeLists.txt index edea3dc5f8acf1..c9695a0e826f86 100644 --- a/plugins/mac-avcapture/legacy/CMakeLists.txt +++ b/plugins/mac-avcapture/legacy/CMakeLists.txt @@ -11,13 +11,15 @@ endif() target_link_libraries( mac-avcapture - PRIVATE OBS::libobs - "$" - "$" - "$" - "$" - "$" - "$") + PRIVATE + OBS::libobs + "$" + "$" + "$" + "$" + "$" + "$" +) set_target_properties_obs( mac-avcapture @@ -25,4 +27,5 @@ set_target_properties_obs( PREFIX "" XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES - XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES) + XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES +) diff --git a/plugins/mac-capture/CMakeLists.txt b/plugins/mac-capture/CMakeLists.txt index 4123b1a7bb7964..8998141e4062e8 100644 --- a/plugins/mac-capture/CMakeLists.txt +++ b/plugins/mac-capture/CMakeLists.txt @@ -5,40 +5,42 @@ add_library(OBS::capture ALIAS mac-capture) target_sources( mac-capture - PRIVATE # cmake-format: sortable - audio-device-enum.c - audio-device-enum.h - mac-audio.c - mac-display-capture.m - mac-sck-audio-capture.m - mac-sck-common.h - mac-sck-common.m - mac-sck-video-capture.m - mac-window-capture.m - plugin-main.c - window-utils.h - window-utils.m) + PRIVATE + audio-device-enum.c + audio-device-enum.h + mac-audio.c + mac-display-capture.m + mac-sck-audio-capture.m + mac-sck-common.h + mac-sck-common.m + mac-sck-video-capture.m + mac-window-capture.m + plugin-main.c + window-utils.h + window-utils.m +) -# cmake-format: off target_link_libraries( mac-capture - PRIVATE OBS::libobs - "$" - "$" - "$" - "$" - "$" - "$" - "$" - "$") + PRIVATE + OBS::libobs + "$" + "$" + "$" + "$" + "$" + "$" + "$" + "$" +) set_target_properties_obs( mac-capture PROPERTIES FOLDER plugins PREFIX "" XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES - XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES) -# cmake-format: on + XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES +) if(CMAKE_VERSION VERSION_LESS_EQUAL 3.25.0) set_property(TARGET mac-capture PROPERTY XCODE_LINK_BUILD_PHASE_MODE BUILT_ONLY) diff --git a/plugins/mac-syphon/CMakeLists.txt b/plugins/mac-syphon/CMakeLists.txt index 9957153ba8de6c..eb7033e6cf4e30 100644 --- a/plugins/mac-syphon/CMakeLists.txt +++ b/plugins/mac-syphon/CMakeLists.txt @@ -19,19 +19,20 @@ target_sources(mac-syphon PRIVATE syphon.m plugin-main.c SyphonOBSClient.h Sypho target_compile_options(mac-syphon PRIVATE -fobjc-arc) -# cmake-format: off target_link_libraries( mac-syphon - PRIVATE OBS::libobs - "$" - "$" - "$" - "$") + PRIVATE + OBS::libobs + "$" + "$" + "$" + "$" +) set_target_properties_obs( mac-syphon PROPERTIES FOLDER "plugins" PREFIX "" XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES - XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES) -# cmake-format: on + XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES +) diff --git a/plugins/mac-videotoolbox/CMakeLists.txt b/plugins/mac-videotoolbox/CMakeLists.txt index e4349041994418..4e8c5aa336106a 100644 --- a/plugins/mac-videotoolbox/CMakeLists.txt +++ b/plugins/mac-videotoolbox/CMakeLists.txt @@ -5,21 +5,22 @@ add_library(OBS::mac-videotoolbox ALIAS mac-videotoolbox) target_sources(mac-videotoolbox PRIVATE encoder.c) -# cmake-format: off target_link_libraries( mac-videotoolbox - PRIVATE OBS::libobs - "$" - "$" - "$" - "$" - "$" - "$") + PRIVATE + OBS::libobs + "$" + "$" + "$" + "$" + "$" + "$" +) set_target_properties_obs( mac-videotoolbox PROPERTIES FOLDER plugins PREFIX "" XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES - XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES) -# cmake-format: on + XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES +) diff --git a/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt b/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt index 8636bac09e9d63..9a52c5a94ee363 100644 --- a/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt @@ -22,13 +22,19 @@ add_executable(OBS:mac-camera-extension ALIAS mac-camera-extension) set(_placeholder_location "${CMAKE_CURRENT_SOURCE_DIR}/../common/data/placeholder.png") target_sources( - mac-camera-extension PRIVATE "${_placeholder_location}" main.swift OBSCameraDeviceSource.swift - OBSCameraProviderSource.swift OBSCameraStreamSink.swift OBSCameraStreamSource.swift) + mac-camera-extension + PRIVATE + "${_placeholder_location}" + main.swift + OBSCameraDeviceSource.swift + OBSCameraProviderSource.swift + OBSCameraStreamSink.swift + OBSCameraStreamSource.swift +) set_property(SOURCE "${_placeholder_location}" PROPERTY MACOSX_PACKAGE_LOCATION "Resources") source_group("Resources" FILES "${_placeholder_location}") -# cmake-format: off set_target_properties_obs( mac-camera-extension PROPERTIES FOLDER plugins @@ -37,7 +43,8 @@ set_target_properties_obs( MACOSX_BUNDLE ON MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in" BUNDLE_EXTENSION systemextension - XCODE_PRODUCT_TYPE com.apple.product-type.system-extension) + XCODE_PRODUCT_TYPE com.apple.product-type.system-extension +) string(TIMESTAMP CURRENT_YEAR "%Y") set_target_xcode_properties( @@ -53,5 +60,5 @@ set_target_xcode_properties( GENERATE_INFOPLIST_FILE YES INFOPLIST_KEY_CFBundleDisplayName "OBS Virtual Camera" INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2022-${CURRENT_YEAR} Sebastian Beckmann, Patrick Heyer" - INFOPLIST_KEY_NSSystemExtensionUsageDescription "This Camera Extension enables virtual camera functionality in OBS Studio.") -# cmake-format: on + INFOPLIST_KEY_NSSystemExtensionUsageDescription "This Camera Extension enables virtual camera functionality in OBS Studio." +) diff --git a/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt b/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt index 5c9258d4308d43..40b9fbbf1bd9c3 100644 --- a/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt @@ -5,39 +5,41 @@ add_library(OBS::dal-plugin ALIAS obs-dal-plugin) target_sources( obs-dal-plugin - PRIVATE # cmake-format: sortable - CMSampleBufferUtils.h - CMSampleBufferUtils.mm - Defines.h - Logging.h - OBSDALDevice.h - OBSDALDevice.mm - OBSDALMachClient.h - OBSDALMachClient.mm - OBSDALObjectStore.h - OBSDALObjectStore.mm - OBSDALPlugIn.h - OBSDALPlugIn.mm - OBSDALPlugInInterface.h - OBSDALPlugInInterface.mm - OBSDALPlugInMain.mm - OBSDALStream.h - OBSDALStream.mm) + PRIVATE + CMSampleBufferUtils.h + CMSampleBufferUtils.mm + Defines.h + Logging.h + OBSDALDevice.h + OBSDALDevice.mm + OBSDALMachClient.h + OBSDALMachClient.mm + OBSDALObjectStore.h + OBSDALObjectStore.mm + OBSDALPlugIn.h + OBSDALPlugIn.mm + OBSDALPlugInInterface.h + OBSDALPlugInInterface.mm + OBSDALPlugInMain.mm + OBSDALStream.h + OBSDALStream.mm +) set(_placeholder_location "${CMAKE_CURRENT_SOURCE_DIR}/../common/data/placeholder.png") set_property(SOURCE "${_placeholder_location}" PROPERTY MACOSX_PACKAGE_LOCATION "Resources") source_group("Resources" FILES "${_placeholder_location}") -# cmake-format: off target_link_libraries( obs-dal-plugin - PRIVATE OBS::mach-protocol - "$" - "$" - "$" - "$" - "$") + PRIVATE + OBS::mach-protocol + "$" + "$" + "$" + "$" + "$" +) set_target_properties_obs( obs-dal-plugin @@ -46,7 +48,8 @@ set_target_properties_obs( PREFIX "" BUNDLE TRUE BUNDLE_EXTENSION plugin MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../../") + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../../" +) set_property(TARGET obs-dal-plugin PROPERTY OSX_ARCHITECTURES x86_64 arm64) @@ -62,5 +65,5 @@ set_target_xcode_properties( MARKETING_VERSION ${OBS_VERSION_CANONICAL} GENERATE_INFOPLIST_FILE YES INFOPLIST_KEY_CFBundleDisplayName obs-mac-virtualcam - INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2020-${CURRENT_YEAR} John Boiles, Patrick Heyer, Sebastian Beckmann") -# cmake-format: on + INFOPLIST_KEY_NSHumanReadableCopyright "(c) 2020-${CURRENT_YEAR} John Boiles, Patrick Heyer, Sebastian Beckmann" +) diff --git a/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt b/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt index d6236fe3bb658f..ef083725c9e614 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt @@ -8,7 +8,6 @@ target_compile_options(mac-virtualcam PRIVATE -fmodules -fcxx-modules) target_link_libraries(mac-virtualcam PRIVATE OBS::mach-protocol OBS::libobs OBS::frontend-api) -# cmake-format: off set_target_properties_obs( mac-virtualcam PROPERTIES FOLDER plugins @@ -19,5 +18,5 @@ set_target_properties_obs( XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION YES XCODE_ATTRIBUTE_GCC_WARN_SHADOW YES XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES YES - XCODE_ATTRIBUTE_CLANG_MODULES_AUTOLINK YES) -# cmake-format: on + XCODE_ATTRIBUTE_CLANG_MODULES_AUTOLINK YES +) diff --git a/plugins/nv-filters/CMakeLists.txt b/plugins/nv-filters/CMakeLists.txt index e74fb755809401..21b9968ab2ae16 100644 --- a/plugins/nv-filters/CMakeLists.txt +++ b/plugins/nv-filters/CMakeLists.txt @@ -26,7 +26,5 @@ if(OS_WINDOWS) target_link_libraries(nv-filters PRIVATE OBS::libobs $<$:OBS::w32-pthreads>) - # cmake-format: off set_target_properties_obs(nv-filters PROPERTIES FOLDER plugins PREFIX "") - # cmake-format: on endif() diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 906f0bca5b70fc..766f04052682ec 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -18,65 +18,68 @@ add_subdirectory(ffmpeg-mux) target_sources( obs-ffmpeg - PRIVATE # cmake-format: sortable - $<$:obs-ffmpeg-logging.c> - $<$:obs-ffmpeg-nvenc.c> - $<$:obs-ffmpeg-mpegts.c> - $<$:obs-ffmpeg-rist.h> - $<$:obs-ffmpeg-srt.h> - $<$:obs-ffmpeg-url.h> - $<$:obs-ffmpeg-vaapi.c> - $<$:vaapi-utils.c> - $<$:vaapi-utils.h> - $<$:texture-amf-opts.hpp> - $<$:texture-amf.cpp> - obs-ffmpeg-audio-encoders.c - obs-ffmpeg-av1.c - obs-ffmpeg-compat.h - obs-ffmpeg-formats.h - obs-ffmpeg-hls-mux.c - obs-ffmpeg-mux.c - obs-ffmpeg-mux.h - obs-ffmpeg-output.c - obs-ffmpeg-output.h - obs-ffmpeg-source.c - obs-ffmpeg-video-encoders.c - obs-ffmpeg.c) + PRIVATE + $<$:obs-ffmpeg-logging.c> + $<$:obs-ffmpeg-nvenc.c> + $<$:obs-ffmpeg-mpegts.c> + $<$:obs-ffmpeg-rist.h> + $<$:obs-ffmpeg-srt.h> + $<$:obs-ffmpeg-url.h> + $<$:obs-ffmpeg-vaapi.c> + $<$:vaapi-utils.c> + $<$:vaapi-utils.h> + $<$:texture-amf-opts.hpp> + $<$:texture-amf.cpp> + obs-ffmpeg-audio-encoders.c + obs-ffmpeg-av1.c + obs-ffmpeg-compat.h + obs-ffmpeg-formats.h + obs-ffmpeg-hls-mux.c + obs-ffmpeg-mux.c + obs-ffmpeg-mux.h + obs-ffmpeg-output.c + obs-ffmpeg-output.h + obs-ffmpeg-source.c + obs-ffmpeg-video-encoders.c + obs-ffmpeg.c +) target_compile_options(obs-ffmpeg PRIVATE $<$:-Wno-shorten-64-to-32>) target_compile_definitions( obs-ffmpeg - PRIVATE $<$:ENABLE_FFMPEG_LOGGING> - $<$:ENABLE_FFMPEG_NVENC> - $<$:NEW_MPEGTS_OUTPUT>) + PRIVATE + $<$:ENABLE_FFMPEG_LOGGING> + $<$:ENABLE_FFMPEG_NVENC> + $<$:NEW_MPEGTS_OUTPUT> +) target_link_libraries( obs-ffmpeg - PRIVATE OBS::libobs - OBS::media-playback - OBS::opts-parser - FFmpeg::avcodec - FFmpeg::avfilter - FFmpeg::avformat - FFmpeg::avdevice - FFmpeg::avutil - FFmpeg::swscale - FFmpeg::swresample - $<$:OBS::w32-pthreads> - $<$:AMF::AMF> - $<$:ws2_32> - $<$:Libva::va> - $<$:Libva::drm> - $<$:Libpci::pci> - $<$:Libdrm::Libdrm> - $<$:Librist::Librist> - $<$:Libsrt::Libsrt>) + PRIVATE + OBS::libobs + OBS::media-playback + OBS::opts-parser + FFmpeg::avcodec + FFmpeg::avfilter + FFmpeg::avformat + FFmpeg::avdevice + FFmpeg::avutil + FFmpeg::swscale + FFmpeg::swresample + $<$:OBS::w32-pthreads> + $<$:AMF::AMF> + $<$:ws2_32> + $<$:Libva::va> + $<$:Libva::drm> + $<$:Libpci::pci> + $<$:Libdrm::Libdrm> + $<$:Librist::Librist> + $<$:Libsrt::Libsrt> +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in obs-ffmpeg.rc) target_sources(obs-ffmpeg PRIVATE obs-ffmpeg.rc) endif() -# cmake-format: off set_target_properties_obs(obs-ffmpeg PROPERTIES FOLDER plugins/obs-ffmpeg PREFIX "") -# cmake-format: on diff --git a/plugins/obs-ffmpeg/cmake/dependencies.cmake b/plugins/obs-ffmpeg/cmake/dependencies.cmake index da110e110b1a4f..f85ac9d3012d31 100644 --- a/plugins/obs-ffmpeg/cmake/dependencies.cmake +++ b/plugins/obs-ffmpeg/cmake/dependencies.cmake @@ -1,4 +1,3 @@ -# cmake-format: off if(OS_WINDOWS OR OS_MACOS) set(ffmpeg_version 6) else() @@ -6,15 +5,10 @@ else() endif() find_package( - FFmpeg ${ffmpeg_version} - REQUIRED avcodec - avfilter - avdevice - avutil - swscale - avformat - swresample) -# cmake-format: on + FFmpeg + ${ffmpeg_version} + REQUIRED avcodec avfilter avdevice avutil swscale avformat swresample +) if(NOT TARGET OBS::media-playback) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/media-playback" "${CMAKE_BINARY_DIR}/shared/media-playback") @@ -27,10 +21,7 @@ endif() if(OS_WINDOWS) find_package(AMF 1.4.29 REQUIRED) add_subdirectory(obs-amf-test) -elseif( - OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +elseif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Libva REQUIRED) find_package(Libpci REQUIRED) find_package(Libdrm REQUIRED) @@ -50,7 +41,7 @@ if(ENABLE_NEW_MPEGTS_OUTPUT) list(JOIN _error_messages "\n" _error_string) message( FATAL_ERROR - "${_error_string}\n Disable this error by setting ENABLE_NEW_MPEGTS_OUTPUT to OFF or providing the build system with required SRT and Rist libraries." + "${_error_string}\n Disable this error by setting ENABLE_NEW_MPEGTS_OUTPUT to OFF or providing the build system with required SRT and Rist libraries." ) endif() endif() diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt index afa22b7a569594..43e10c513b1889 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt +++ b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt @@ -11,8 +11,10 @@ add_executable(OBS::ffmpeg-mux ALIAS obs-ffmpeg-mux) target_sources(obs-ffmpeg-mux PRIVATE ffmpeg-mux.c ffmpeg-mux.h) -target_link_libraries(obs-ffmpeg-mux PRIVATE OBS::libobs FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat - $<$:OBS::w32-pthreads>) +target_link_libraries( + obs-ffmpeg-mux + PRIVATE OBS::libobs FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat $<$:OBS::w32-pthreads> +) target_compile_definitions(obs-ffmpeg-mux PRIVATE $<$:ENABLE_FFMPEG_MUX_DEBUG>) diff --git a/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt b/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt index 6221c69df0d211..991f421b68ac0d 100644 --- a/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt +++ b/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt @@ -8,8 +8,9 @@ add_executable(obs-amf-test) add_executable(OBS::amf-test ALIAS obs-amf-test) target_sources(obs-amf-test PRIVATE obs-amf-test.cpp) -target_link_libraries(obs-amf-test PRIVATE OBS::COMutils AMF::AMF d3d11 dxgi dxguid) +target_link_libraries( + obs-amf-test + PRIVATE OBS::COMutils AMF::AMF d3d11 dxgi dxguid +) -# cmake-format: off set_target_properties_obs(obs-amf-test PROPERTIES FOLDER plugins/obs-ffmpeg) -# cmake-format: on diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 0a9aeaa7d09938..3deb2b57b50233 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -11,28 +11,29 @@ endif() target_sources( obs-filters - PRIVATE # cmake-format: sortable - async-delay-filter.c - chroma-key-filter.c - color-correction-filter.c - color-grade-filter.c - color-key-filter.c - compressor-filter.c - crop-filter.c - eq-filter.c - expander-filter.c - gain-filter.c - gpu-delay.c - hdr-tonemap-filter.c - invert-audio-polarity.c - limiter-filter.c - luma-key-filter.c - mask-filter.c - noise-gate-filter.c - obs-filters.c - scale-filter.c - scroll-filter.c - sharpness-filter.c) + PRIVATE + async-delay-filter.c + chroma-key-filter.c + color-correction-filter.c + color-grade-filter.c + color-key-filter.c + compressor-filter.c + crop-filter.c + eq-filter.c + expander-filter.c + gain-filter.c + gpu-delay.c + hdr-tonemap-filter.c + invert-audio-polarity.c + limiter-filter.c + luma-key-filter.c + mask-filter.c + noise-gate-filter.c + obs-filters.c + scale-filter.c + scroll-filter.c + sharpness-filter.c +) target_link_libraries(obs-filters PRIVATE OBS::libobs $<$:OBS::w32-pthreads>) @@ -44,6 +45,4 @@ if(OS_WINDOWS) target_sources(obs-filters PRIVATE obs-filters.rc) endif() -# cmake-format: off set_target_properties_obs(obs-filters PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/obs-filters/cmake/rnnoise.cmake b/plugins/obs-filters/cmake/rnnoise.cmake index a7ff86e4742821..47f51a7f1ad561 100644 --- a/plugins/obs-filters/cmake/rnnoise.cmake +++ b/plugins/obs-filters/cmake/rnnoise.cmake @@ -12,25 +12,26 @@ if(ENABLE_RNNOISE) target_sources( obs-rnnoise - PRIVATE # cmake-format: sortable - rnnoise/src/_kiss_fft_guts.h - rnnoise/src/arch.h - rnnoise/src/celt_lpc.c - rnnoise/src/celt_lpc.h - rnnoise/src/common.h - rnnoise/src/denoise.c - rnnoise/src/kiss_fft.c - rnnoise/src/kiss_fft.h - rnnoise/src/opus_types.h - rnnoise/src/pitch.c - rnnoise/src/pitch.h - rnnoise/src/rnn.c - rnnoise/src/rnn.h - rnnoise/src/rnn_data.c - rnnoise/src/rnn_data.h - rnnoise/src/rnn_reader.c - rnnoise/src/tansig_table.h - PUBLIC rnnoise/include/rnnoise.h) + PRIVATE + rnnoise/src/_kiss_fft_guts.h + rnnoise/src/arch.h + rnnoise/src/celt_lpc.c + rnnoise/src/celt_lpc.h + rnnoise/src/common.h + rnnoise/src/denoise.c + rnnoise/src/kiss_fft.c + rnnoise/src/kiss_fft.h + rnnoise/src/opus_types.h + rnnoise/src/pitch.c + rnnoise/src/pitch.h + rnnoise/src/rnn.c + rnnoise/src/rnn.h + rnnoise/src/rnn_data.c + rnnoise/src/rnn_data.h + rnnoise/src/rnn_reader.c + rnnoise/src/tansig_table.h + PUBLIC rnnoise/include/rnnoise.h + ) add_library(Librnnoise::Librnnoise ALIAS obs-rnnoise) diff --git a/plugins/obs-nvenc/CMakeLists.txt b/plugins/obs-nvenc/CMakeLists.txt index 301dd7edfc8476..be73ee0ac19cd6 100644 --- a/plugins/obs-nvenc/CMakeLists.txt +++ b/plugins/obs-nvenc/CMakeLists.txt @@ -28,23 +28,26 @@ add_subdirectory(obs-nvenc-test) target_sources( obs-nvenc PRIVATE # cmake-format: sortable - $<$:nvenc-opengl.c> - $<$:nvenc-d3d11.c> - cuda-helpers.c - cuda-helpers.h - nvenc-compat.c - nvenc-cuda.c - nvenc-helpers.c - nvenc-helpers.h - nvenc-internal.h - nvenc-opts-parser.c - nvenc-properties.c - nvenc.c - obs-nvenc.c - obs-nvenc.h) - -target_link_libraries(obs-nvenc PRIVATE OBS::libobs OBS::opts-parser FFnvcodec::FFnvcodec - $<$:OBS::glad>) + $<$:nvenc-opengl.c> + $<$:nvenc-d3d11.c> + cuda-helpers.c + cuda-helpers.h + nvenc-compat.c + nvenc-cuda.c + nvenc-helpers.c + nvenc-helpers.h + nvenc-internal.h + nvenc-opts-parser.c + nvenc-properties.c + nvenc.c + obs-nvenc.c + obs-nvenc.h +) + +target_link_libraries( + obs-nvenc + PRIVATE OBS::libobs OBS::opts-parser FFnvcodec::FFnvcodec $<$:OBS::glad> +) target_compile_definitions(obs-nvenc PRIVATE $<$:REGISTER_FFMPEG_IDS>) diff --git a/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt b/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt index db51d25e1ed068..97ece87724da57 100644 --- a/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt +++ b/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt @@ -7,6 +7,4 @@ add_executable(obs-nvenc-test) target_sources(obs-nvenc-test PRIVATE obs-nvenc-test.cpp) target_link_libraries(obs-nvenc-test FFnvcodec::FFnvcodec) -# cmake-format: off set_target_properties_obs(obs-nvenc-test PROPERTIES FOLDER plugins/obs-nvenc) -# cmake-format: on diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index da530079973a64..7046214362c6ae 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -18,66 +18,71 @@ add_library(OBS::outputs ALIAS obs-outputs) target_sources( obs-outputs - PRIVATE # cmake-format: sortable - $<$:rtmp-hevc.c> - $<$:rtmp-hevc.h> - flv-mux.c - flv-mux.h - flv-output.c - librtmp/amf.c - librtmp/amf.h - librtmp/bytes.h - librtmp/cencode.c - librtmp/cencode.h - librtmp/handshake.h - librtmp/hashswf.c - librtmp/http.h - librtmp/log.c - librtmp/log.h - librtmp/md5.c - librtmp/md5.h - librtmp/parseurl.c - librtmp/rtmp.c - librtmp/rtmp.h - librtmp/rtmp_sys.h - mp4-mux-internal.h - mp4-mux.c - mp4-mux.h - mp4-output.c - net-if.c - net-if.h - null-output.c - obs-output-ver.h - obs-outputs.c - rtmp-av1.c - rtmp-av1.h - rtmp-helpers.h - rtmp-stream.c - rtmp-stream.h - rtmp-windows.c - utils.h) + PRIVATE + $<$:rtmp-hevc.c> + $<$:rtmp-hevc.h> + flv-mux.c + flv-mux.h + flv-output.c + librtmp/amf.c + librtmp/amf.h + librtmp/bytes.h + librtmp/cencode.c + librtmp/cencode.h + librtmp/handshake.h + librtmp/hashswf.c + librtmp/http.h + librtmp/log.c + librtmp/log.h + librtmp/md5.c + librtmp/md5.h + librtmp/parseurl.c + librtmp/rtmp.c + librtmp/rtmp.h + librtmp/rtmp_sys.h + mp4-mux-internal.h + mp4-mux.c + mp4-mux.h + mp4-output.c + net-if.c + net-if.h + null-output.c + obs-output-ver.h + obs-outputs.c + rtmp-av1.c + rtmp-av1.h + rtmp-helpers.h + rtmp-stream.c + rtmp-stream.h + rtmp-windows.c + utils.h +) target_compile_definitions(obs-outputs PRIVATE USE_MBEDTLS CRYPTO) target_compile_options( obs-outputs - PRIVATE $<$:-Wno-comma> - $<$,$>:-Wno-error=unreachable-code>) + PRIVATE + $<$:-Wno-comma> + $<$,$>:-Wno-error=unreachable-code> +) target_link_libraries( obs-outputs - PRIVATE OBS::libobs - OBS::happy-eyeballs - OBS::opts-parser - MbedTLS::MbedTLS - ZLIB::ZLIB - $<$:OBS::w32-pthreads> - $<$:crypt32> - $<$:iphlpapi> - $<$:winmm> - $<$:ws2_32> - "$<$:$>" - "$<$:$>") + PRIVATE + OBS::libobs + OBS::happy-eyeballs + OBS::opts-parser + MbedTLS::MbedTLS + ZLIB::ZLIB + $<$:OBS::w32-pthreads> + $<$:crypt32> + $<$:iphlpapi> + $<$:winmm> + $<$:ws2_32> + "$<$:$>" + "$<$:$>" +) # Remove once jansson has been fixed on obs-deps target_link_options(obs-outputs PRIVATE $<$:/IGNORE:4098>) @@ -87,6 +92,4 @@ if(OS_WINDOWS) target_sources(obs-outputs PRIVATE obs-outputs.rc) endif() -# cmake-format: off set_target_properties_obs(obs-outputs PROPERTIES FOLDER plugins/obs-outputs PREFIX "") -# cmake-format: on diff --git a/plugins/obs-qsv11/CMakeLists.txt b/plugins/obs-qsv11/CMakeLists.txt index 7a6f5d831aaed7..d5ffd41ae725c8 100644 --- a/plugins/obs-qsv11/CMakeLists.txt +++ b/plugins/obs-qsv11/CMakeLists.txt @@ -20,31 +20,34 @@ endif() target_sources( obs-qsv11 - PRIVATE # cmake-format: sortable - $<$:common_utils_linux.cpp> - $<$:common_directx11.cpp> - $<$:common_directx11.h> - $<$:common_utils_windows.cpp> - common_utils.cpp - common_utils.h - obs-qsv11-plugin-main.c - obs-qsv11.c - QSV_Encoder.cpp - QSV_Encoder.h - QSV_Encoder_Internal.cpp - QSV_Encoder_Internal.h) + PRIVATE + $<$:common_utils_linux.cpp> + $<$:common_directx11.cpp> + $<$:common_directx11.h> + $<$:common_utils_windows.cpp> + common_utils.cpp + common_utils.h + obs-qsv11-plugin-main.c + obs-qsv11.c + QSV_Encoder.cpp + QSV_Encoder.h + QSV_Encoder_Internal.cpp + QSV_Encoder_Internal.h +) target_compile_definitions(obs-qsv11 PRIVATE $<$:DX11_D3D>) target_link_libraries( obs-qsv11 - PRIVATE OBS::libobs - VPL::VPL - $<$:Libva::va> - $<$:Libva::drm> - $<$:d3d11> - $<$:dxgi> - $<$:dxguid>) + PRIVATE + OBS::libobs + VPL::VPL + $<$:Libva::va> + $<$:Libva::drm> + $<$:d3d11> + $<$:dxgi> + $<$:dxguid> +) target_link_options(obs-qsv11 PRIVATE $<$:/IGNORE:4099>) @@ -55,6 +58,4 @@ if(OS_WINDOWS) target_sources(obs-qsv11 PRIVATE obs-qsv11.rc) endif() -# cmake-format: off set_target_properties_obs(obs-qsv11 PROPERTIES FOLDER plugins/obs-qsv11 PREFIX "") -# cmake-format: on diff --git a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt index 0d582e6ccbacfa..ca77731bfa334b 100644 --- a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt +++ b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt @@ -10,10 +10,15 @@ target_sources(obs-qsv-test PRIVATE obs-qsv-test.cpp) target_compile_definitions(obs-qsv-test PRIVATE "$<$:ENABLE_HEVC>") -target_link_libraries(obs-qsv-test d3d11 dxgi dxguid VPL::VPL OBS::COMutils) +target_link_libraries( + obs-qsv-test + d3d11 + dxgi + dxguid + VPL::VPL + OBS::COMutils +) target_link_options(obs-qsv-test PRIVATE /IGNORE:4099) -# cmake-format: off set_target_properties_obs(obs-qsv-test PROPERTIES FOLDER plugins/obs-qsv11 PREFIX "") -# cmake-format: on diff --git a/plugins/obs-text/CMakeLists.txt b/plugins/obs-text/CMakeLists.txt index cd0f1fdaace9c8..c6b654f83b6430 100644 --- a/plugins/obs-text/CMakeLists.txt +++ b/plugins/obs-text/CMakeLists.txt @@ -11,6 +11,4 @@ target_sources(obs-text PRIVATE gdiplus/obs-text.cpp obs-text.rc) target_link_libraries(obs-text PRIVATE OBS::libobs gdiplus) -# cmake-format: off set_target_properties_obs(obs-text PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/obs-transitions/CMakeLists.txt b/plugins/obs-transitions/CMakeLists.txt index f6c29caefe2b1b..c63b46d4164d88 100644 --- a/plugins/obs-transitions/CMakeLists.txt +++ b/plugins/obs-transitions/CMakeLists.txt @@ -7,15 +7,16 @@ add_library(OBS::transition ALIAS obs-transitions) target_sources( obs-transitions - PRIVATE # cmake-format: sortable - obs-transitions.c - transition-cut.c - transition-fade-to-color.c - transition-fade.c - transition-luma-wipe.c - transition-slide.c - transition-stinger.c - transition-swipe.c) + PRIVATE + obs-transitions.c + transition-cut.c + transition-fade-to-color.c + transition-fade.c + transition-luma-wipe.c + transition-slide.c + transition-stinger.c + transition-swipe.c +) target_link_libraries(obs-transitions PRIVATE OBS::libobs) @@ -24,6 +25,4 @@ if(OS_WINDOWS) target_sources(obs-transitions PRIVATE obs-transitions.rc) endif() -# cmake-format: off set_target_properties_obs(obs-transitions PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/obs-vst/CMakeLists.txt b/plugins/obs-vst/CMakeLists.txt index 7149451d30af3d..62c9ed9e790c11 100644 --- a/plugins/obs-vst/CMakeLists.txt +++ b/plugins/obs-vst/CMakeLists.txt @@ -16,44 +16,42 @@ find_package(Qt6 REQUIRED Widgets) target_sources( obs-vst - PRIVATE # cmake-format: sortable - $<$:mac/EditorWidget-osx.mm> - $<$:mac/VSTPlugin-osx.mm> - $<$:linux/EditorWidget-linux.cpp> - $<$:linux/VSTPlugin-linux.cpp> - $<$:win/EditorWidget-win.cpp> - $<$:win/VSTPlugin-win.cpp> - EditorWidget.cpp - headers/EditorWidget.h - headers/vst-plugin-callbacks.hpp - headers/VSTPlugin.h - obs-vst.cpp - vst_header/aeffectx.h - VSTPlugin.cpp) + PRIVATE + $<$:mac/EditorWidget-osx.mm> + $<$:mac/VSTPlugin-osx.mm> + $<$:linux/EditorWidget-linux.cpp> + $<$:linux/VSTPlugin-linux.cpp> + $<$:win/EditorWidget-win.cpp> + $<$:win/VSTPlugin-win.cpp> + EditorWidget.cpp + headers/EditorWidget.h + headers/vst-plugin-callbacks.hpp + headers/VSTPlugin.h + obs-vst.cpp + vst_header/aeffectx.h + VSTPlugin.cpp +) target_include_directories(obs-vst PRIVATE vst_header) -# cmake-format: off target_link_libraries( - obs-vst PRIVATE OBS::libobs - Qt::Widgets - "$<$:$>" - "$<$:$>") -# cmake-format: on + obs-vst + PRIVATE + OBS::libobs + Qt::Widgets + "$<$:$>" + "$<$:$>" +) if(OS_WINDOWS) - set_property( - TARGET obs-vst - APPEND - PROPERTY AUTORCC_OPTIONS --format-version 1) + set_property(TARGET obs-vst APPEND PROPERTY AUTORCC_OPTIONS --format-version 1) endif() -# cmake-format: off set_target_properties_obs( obs-vst PROPERTIES FOLDER plugins PREFIX "" AUTOMOC ON AUTOUIC ON - AUTORCC ON) -# cmake-format: on + AUTORCC ON +) diff --git a/plugins/obs-webrtc/CMakeLists.txt b/plugins/obs-webrtc/CMakeLists.txt index ced0c98efbc2f1..f6150003822b73 100644 --- a/plugins/obs-webrtc/CMakeLists.txt +++ b/plugins/obs-webrtc/CMakeLists.txt @@ -15,11 +15,10 @@ add_library(obs-webrtc MODULE) add_library(OBS::webrtc ALIAS obs-webrtc) target_sources( - obs-webrtc PRIVATE # cmake-format: sortable - obs-webrtc.cpp whip-output.cpp whip-output.h whip-service.cpp whip-service.h whip-utils.h) + obs-webrtc + PRIVATE obs-webrtc.cpp whip-output.cpp whip-output.h whip-service.cpp whip-service.h whip-utils.h +) target_link_libraries(obs-webrtc PRIVATE OBS::libobs LibDataChannel::LibDataChannel CURL::libcurl) -# cmake-format: off set_target_properties_obs(obs-webrtc PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/obs-x264/CMakeLists.txt b/plugins/obs-x264/CMakeLists.txt index 557d9655da1747..e6479b4d6e19a7 100644 --- a/plugins/obs-x264/CMakeLists.txt +++ b/plugins/obs-x264/CMakeLists.txt @@ -19,8 +19,6 @@ if(OS_WINDOWS) target_sources(obs-x264 PRIVATE obs-x264.rc) endif() -# cmake-format: off set_target_properties_obs(obs-x264 PROPERTIES FOLDER plugins/obs-x264 PREFIX "") -# cmake-format: on include(cmake/x264-test.cmake) diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index f3003784797f43..3b4ff65a7818b2 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -4,9 +4,13 @@ legacy_check() option(ENABLE_SERVICE_UPDATES "Checks for service updates" ON) -set(RTMP_SERVICES_URL - "https://obsproject.com/obs2_update/rtmp-services" - CACHE STRING "Default services package URL" FORCE) +set( + RTMP_SERVICES_URL + "https://obsproject.com/obs2_update/rtmp-services" + CACHE STRING + "Default services package URL" + FORCE +) mark_as_advanced(RTMP_SERVICES_URL) if(NOT TARGET OBS::file-updater) @@ -20,22 +24,25 @@ add_library(OBS::rtmp-services ALIAS rtmp-services) target_sources( rtmp-services - PRIVATE # cmake-format: sortable - rtmp-common.c - rtmp-custom.c - rtmp-format-ver.h - rtmp-services-main.c - service-specific/dacast.c - service-specific/dacast.h - service-specific/nimotv.c - service-specific/nimotv.h - service-specific/showroom.c - service-specific/showroom.h - service-specific/twitch.c - service-specific/twitch.h) - -target_compile_definitions(rtmp-services PRIVATE SERVICES_URL="${RTMP_SERVICES_URL}" - $<$:ENABLE_SERVICE_UPDATES>) + PRIVATE + rtmp-common.c + rtmp-custom.c + rtmp-format-ver.h + rtmp-services-main.c + service-specific/dacast.c + service-specific/dacast.h + service-specific/nimotv.c + service-specific/nimotv.h + service-specific/showroom.c + service-specific/showroom.h + service-specific/twitch.c + service-specific/twitch.h +) + +target_compile_definitions( + rtmp-services + PRIVATE SERVICES_URL="${RTMP_SERVICES_URL}" $<$:ENABLE_SERVICE_UPDATES> +) target_link_libraries(rtmp-services PRIVATE OBS::libobs OBS::file-updater jansson::jansson) @@ -47,6 +54,4 @@ if(OS_WINDOWS) target_sources(rtmp-services PRIVATE rtmp-services.rc) endif() -# cmake-format: off set_target_properties_obs(rtmp-services PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/text-freetype2/CMakeLists.txt b/plugins/text-freetype2/CMakeLists.txt index 6cc7ed55d94519..a158dd4021247e 100644 --- a/plugins/text-freetype2/CMakeLists.txt +++ b/plugins/text-freetype2/CMakeLists.txt @@ -13,10 +13,7 @@ find_package(Freetype REQUIRED) if(OS_MACOS) find_package(Iconv REQUIRED) -elseif( - OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +elseif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Fontconfig REQUIRED) endif() @@ -25,28 +22,29 @@ add_library(OBS::text-freetype2 ALIAS text-freetype2) target_sources( text-freetype2 - PRIVATE # cmake-format: sortable - $<$:find-font-cocoa.m> - $<$:find-font-iconv.c> - $<$:find-font-unix.c> - $<$:find-font.c> - $<$:find-font-windows.c> - find-font.h - obs-convenience.c - obs-convenience.h - text-freetype2.c - text-freetype2.h - text-functionality.c) - -# cmake-format: off + PRIVATE + $<$:find-font-cocoa.m> + $<$:find-font-iconv.c> + $<$:find-font-unix.c> + $<$:find-font.c> + $<$:find-font-windows.c> + find-font.h + obs-convenience.c + obs-convenience.h + text-freetype2.c + text-freetype2.h + text-functionality.c +) + target_link_libraries( text-freetype2 - PRIVATE OBS::libobs - Freetype::Freetype - $<$:Iconv::Iconv> - "$<$:$>" - $<$:Fontconfig::Fontconfig>) -# cmake-format: on + PRIVATE + OBS::libobs + Freetype::Freetype + $<$:Iconv::Iconv> + "$<$:$>" + $<$:Fontconfig::Fontconfig> +) # FreeType is hard-coded with /DEFAULTLIB:MSVCRT target_link_options(text-freetype2 PRIVATE $<$:/IGNORE:4098>) @@ -56,6 +54,4 @@ if(OS_WINDOWS) target_sources(text-freetype2 PRIVATE text-freetype2.rc) endif() -# cmake-format: off set_target_properties_obs(text-freetype2 PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/vlc-video/CMakeLists.txt b/plugins/vlc-video/CMakeLists.txt index b9ee1a564824ee..03a40e0b5d5834 100644 --- a/plugins/vlc-video/CMakeLists.txt +++ b/plugins/vlc-video/CMakeLists.txt @@ -13,7 +13,8 @@ macro(check_vlc_path) NAMES vlc/libvlc.h HINTS ${PC_Libvlc_INCLUDEDIR} ${VLC_PATH}/include PATHS /usr/include /usr/local/include - DOC "LibVLC include directory") + DOC "LibVLC include directory" + ) if(LibVLC_INCLUDE_DIR) target_include_directories(vlc-video PRIVATE "${LibVLC_INCLUDE_DIR}") @@ -44,6 +45,4 @@ if(OS_WINDOWS) target_sources(vlc-video PRIVATE vlc-video.rc) endif() -# cmake-format: off set_target_properties_obs(vlc-video PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index c7026bf2c7aa7d..e354ede6fbe6cc 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -21,10 +21,8 @@ legacy_check() option(ENABLE_COMPAT_UPDATES "Checks for capture compatibility data updates" ON) -# cmake-format: off set(COMPAT_URL "https://obsproject.com/obs2_update/win-capture" CACHE STRING "Default services package URL") mark_as_advanced(COMPAT_URL) -# cmake-format: on if(NOT TARGET OBS::ipc-util) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") @@ -41,50 +39,55 @@ add_library(OBS::capture ALIAS win-capture) target_sources( win-capture - PRIVATE # cmake-format: sortable - app-helpers.c - app-helpers.h - audio-helpers.c - audio-helpers.h - compat-format-ver.h - compat-helpers.c - compat-helpers.h - cursor-capture.c - cursor-capture.h - dc-capture.c - dc-capture.h - duplicator-monitor-capture.c - game-capture-file-init.c - game-capture.c - load-graphics-offsets.c - monitor-capture.c - nt-stuff.c - nt-stuff.h - plugin-main.c - window-capture.c) + PRIVATE + app-helpers.c + app-helpers.h + audio-helpers.c + audio-helpers.h + compat-format-ver.h + compat-helpers.c + compat-helpers.h + cursor-capture.c + cursor-capture.h + dc-capture.c + dc-capture.h + duplicator-monitor-capture.c + game-capture-file-init.c + game-capture.c + load-graphics-offsets.c + monitor-capture.c + nt-stuff.c + nt-stuff.h + plugin-main.c + window-capture.c +) target_compile_definitions( - win-capture PRIVATE COMPAT_URL="${COMPAT_URL}" "$<$:ENABLE_COMPAT_UPDATES>" - OBS_VERSION="${OBS_VERSION_CANONICAL}") + win-capture + PRIVATE + COMPAT_URL="${COMPAT_URL}" + "$<$:ENABLE_COMPAT_UPDATES>" + OBS_VERSION="${OBS_VERSION_CANONICAL}" +) target_link_libraries( win-capture - PRIVATE OBS::file-updater - OBS::hook-config - OBS::inject-library - OBS::ipc-util - OBS::libobs - OBS::obfuscate - OBS::w32-pthreads - OBS::winrt-headers - jansson::jansson) + PRIVATE + OBS::file-updater + OBS::hook-config + OBS::inject-library + OBS::ipc-util + OBS::libobs + OBS::obfuscate + OBS::w32-pthreads + OBS::winrt-headers + jansson::jansson +) # Remove once jansson has been fixed on obs-deps target_link_options(win-capture PRIVATE /IGNORE:4098) -# cmake-format: off set_target_properties_obs(win-capture PROPERTIES FOLDER plugins/win-capture PREFIX "") -# cmake-format: on add_subdirectory(graphics-hook) add_subdirectory(get-graphics-offsets) diff --git a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt index 1e4ee466817929..a99df6c6ffe3b9 100644 --- a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt +++ b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt @@ -1,10 +1,15 @@ cmake_minimum_required(VERSION 3.24...3.25) add_library(_get-graphics-offsets INTERFACE) -target_sources(_get-graphics-offsets INTERFACE d3d8-offsets.cpp d3d9-offsets.cpp dxgi-offsets.cpp - get-graphics-offsets.c get-graphics-offsets.h) +target_sources( + _get-graphics-offsets + INTERFACE d3d8-offsets.cpp d3d9-offsets.cpp dxgi-offsets.cpp get-graphics-offsets.c get-graphics-offsets.h +) -target_link_libraries(_get-graphics-offsets INTERFACE OBS::hook-config OBS::d3d8-api d3d9.lib dxgi.lib d3d11.lib) +target_link_libraries( + _get-graphics-offsets + INTERFACE OBS::hook-config OBS::d3d8-api d3d9.lib dxgi.lib d3d11.lib +) legacy_check() @@ -13,8 +18,6 @@ target_link_libraries(get-graphics-offsets PRIVATE _get-graphics-offsets) include(cmake/32bit.cmake) -# cmake-format: off set_target_properties_obs(get-graphics-offsets PROPERTIES FOLDER plugins/win-capture OUTPUT_NAME get-graphics-offsets64 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# cmake-format: on add_dependencies(win-capture get-graphics-offsets) diff --git a/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake b/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake index f6358a48626a02..76865eaea7e596 100644 --- a/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake +++ b/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake @@ -3,6 +3,7 @@ project(get-graphics-offsets) add_executable(get-graphics-offsets) target_link_libraries(get-graphics-offsets PRIVATE _get-graphics-offsets) -# cmake-format: off -set_target_properties(get-graphics-offsets PROPERTIES OUTPUT_NAME get-graphics-offsets32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# cmake-format: on +set_target_properties( + get-graphics-offsets + PROPERTIES OUTPUT_NAME get-graphics-offsets32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake b/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake index a411604b9261d6..44f757657b57d1 100644 --- a/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake +++ b/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake @@ -2,4 +2,5 @@ add_custom_command( TARGET get-graphics-offsets POST_BUILD COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t get-graphics-offsets - COMMENT "Build 32-bit get-graphics-offsets") + COMMENT "Build 32-bit get-graphics-offsets" +) diff --git a/plugins/win-capture/graphics-hook/CMakeLists.txt b/plugins/win-capture/graphics-hook/CMakeLists.txt index e5ad38324bb9ad..9434e3e5387918 100644 --- a/plugins/win-capture/graphics-hook/CMakeLists.txt +++ b/plugins/win-capture/graphics-hook/CMakeLists.txt @@ -15,24 +15,27 @@ add_library(_graphics-hook INTERFACE) target_sources( _graphics-hook - INTERFACE # cmake-format: sortable - d3d10-capture.cpp - d3d11-capture.cpp - d3d12-capture.cpp - d3d8-capture.cpp - d3d9-capture.cpp - d3d9-patches.hpp - dxgi-capture.cpp - gl-capture.c - gl-decs.h - graphics-hook.c - graphics-hook.h - graphics-hook.rc) + INTERFACE + d3d10-capture.cpp + d3d11-capture.cpp + d3d12-capture.cpp + d3d8-capture.cpp + d3d9-capture.cpp + d3d9-patches.hpp + dxgi-capture.cpp + gl-capture.c + gl-decs.h + graphics-hook.c + graphics-hook.h + graphics-hook.rc +) target_compile_definitions(_graphics-hook INTERFACE COMPILE_D3D12_HOOK) -target_link_libraries(_graphics-hook INTERFACE OBS::d3d8-api OBS::hook-config OBS::ipc-util OBS::obfuscate - Detours::Detours dxguid) +target_link_libraries( + _graphics-hook + INTERFACE OBS::d3d8-api OBS::hook-config OBS::ipc-util OBS::obfuscate Detours::Detours dxguid +) target_link_options(_graphics-hook INTERFACE /IGNORE:4099) @@ -51,8 +54,6 @@ target_link_libraries(graphics-hook PRIVATE _graphics-hook) include(cmake/32bit.cmake) -# cmake-format: off set_target_properties_obs(graphics-hook PROPERTIES FOLDER "plugins/win-capture" OUTPUT_NAME graphics-hook64 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# cmake-format: on add_dependencies(win-capture graphics-hook) diff --git a/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake b/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake index 3d146039ba2f5e..7ff387fb9e87e9 100644 --- a/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake +++ b/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake @@ -7,6 +7,7 @@ endif() add_library(graphics-hook MODULE) target_link_libraries(graphics-hook PRIVATE _graphics-hook) -# cmake-format: off -set_target_properties(graphics-hook PROPERTIES OUTPUT_NAME graphics-hook32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# cmake-format: on +set_target_properties( + graphics-hook + PROPERTIES OUTPUT_NAME graphics-hook32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-capture/graphics-hook/cmake/32bit.cmake b/plugins/win-capture/graphics-hook/cmake/32bit.cmake index af87a228f910fa..bf332e6652ed67 100644 --- a/plugins/win-capture/graphics-hook/cmake/32bit.cmake +++ b/plugins/win-capture/graphics-hook/cmake/32bit.cmake @@ -2,4 +2,5 @@ add_custom_command( TARGET graphics-hook POST_BUILD COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t graphics-hook - COMMENT "Build 32-bit graphics-hook") + COMMENT "Build 32-bit graphics-hook" +) diff --git a/plugins/win-capture/inject-helper/CMakeLists.txt b/plugins/win-capture/inject-helper/CMakeLists.txt index 6f1c9417b66316..4d6b4b787ce6c5 100644 --- a/plugins/win-capture/inject-helper/CMakeLists.txt +++ b/plugins/win-capture/inject-helper/CMakeLists.txt @@ -17,8 +17,6 @@ target_link_libraries(inject-helper PRIVATE _inject-helper) include(cmake/32bit.cmake) -# cmake-format: off set_target_properties_obs(inject-helper PROPERTIES FOLDER plugins/win-capture OUTPUT_NAME inject-helper64 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# cmake-format: on add_dependencies(win-capture inject-helper) diff --git a/plugins/win-capture/inject-helper/cmake/32bit-build.cmake b/plugins/win-capture/inject-helper/cmake/32bit-build.cmake index c7aa677e64e76a..d6fc2a3aade9f5 100644 --- a/plugins/win-capture/inject-helper/cmake/32bit-build.cmake +++ b/plugins/win-capture/inject-helper/cmake/32bit-build.cmake @@ -3,6 +3,7 @@ project(inject-helper) add_executable(inject-helper) target_link_libraries(inject-helper PRIVATE _inject-helper) -# cmake-format: off -set_target_properties(inject-helper PROPERTIES OUTPUT_NAME inject-helper32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# cmake-format: on +set_target_properties( + inject-helper + PROPERTIES OUTPUT_NAME inject-helper32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-capture/inject-helper/cmake/32bit.cmake b/plugins/win-capture/inject-helper/cmake/32bit.cmake index 7a02e99c67e229..a7b3be3217d2c3 100644 --- a/plugins/win-capture/inject-helper/cmake/32bit.cmake +++ b/plugins/win-capture/inject-helper/cmake/32bit.cmake @@ -2,4 +2,5 @@ add_custom_command( TARGET inject-helper POST_BUILD COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t inject-helper - COMMENT "Build 32-bit inject-helper") + COMMENT "Build 32-bit inject-helper" +) diff --git a/plugins/win-dshow/CMakeLists.txt b/plugins/win-dshow/CMakeLists.txt index 047774366f7057..a01c6a10fab4cc 100644 --- a/plugins/win-dshow/CMakeLists.txt +++ b/plugins/win-dshow/CMakeLists.txt @@ -4,8 +4,10 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) add_library(obs-virtualcam-interface INTERFACE) add_library(OBS::virtualcam-interface ALIAS obs-virtualcam-interface) - target_sources(obs-virtualcam-interface INTERFACE shared-memory-queue.c shared-memory-queue.h tiny-nv12-scale.c - tiny-nv12-scale.h) + target_sources( + obs-virtualcam-interface + INTERFACE shared-memory-queue.c shared-memory-queue.h tiny-nv12-scale.c tiny-nv12-scale.h + ) target_include_directories(obs-virtualcam-interface INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") include(cmake/libdshowcapture.cmake) @@ -21,21 +23,16 @@ add_library(OBS::dshow ALIAS win-dshow) target_sources( win-dshow - PRIVATE # cmake-format: sortable - dshow-plugin.cpp encode-dstr.hpp ffmpeg-decode.c ffmpeg-decode.h win-dshow-encoder.cpp win-dshow.cpp) + PRIVATE dshow-plugin.cpp encode-dstr.hpp ffmpeg-decode.c ffmpeg-decode.h win-dshow-encoder.cpp win-dshow.cpp +) configure_file(cmake/windows/obs-module.rc.in win-dshow.rc) target_sources(win-dshow PRIVATE win-dshow.rc) target_link_libraries( win-dshow - PRIVATE OBS::libobs - OBS::w32-pthreads - OBS::libdshowcapture - FFmpeg::avcodec - FFmpeg::avutil - strmiids - winmm) + PRIVATE OBS::libobs OBS::w32-pthreads OBS::libdshowcapture FFmpeg::avcodec FFmpeg::avutil strmiids winmm +) if(TARGET OBS::virtualcam AND TARGET OBS::virtualcam-guid) target_sources(win-dshow PRIVATE virtualcam.c) @@ -45,6 +42,4 @@ if(TARGET OBS::virtualcam AND TARGET OBS::virtualcam-guid) add_dependencies(win-dshow obs-virtualcam-module) endif() -# cmake-format: off set_target_properties_obs(win-dshow PROPERTIES FOLDER plugins/win-dshow PREFIX "") -# cmake-format: on diff --git a/plugins/win-dshow/cmake/libdshowcapture.cmake b/plugins/win-dshow/cmake/libdshowcapture.cmake index 2cc78e64089f90..e05c027c61be31 100644 --- a/plugins/win-dshow/cmake/libdshowcapture.cmake +++ b/plugins/win-dshow/cmake/libdshowcapture.cmake @@ -3,41 +3,46 @@ add_library(OBS::libdshowcapture ALIAS libdshowcapture) target_sources( libdshowcapture - INTERFACE libdshowcapture/dshowcapture.hpp - libdshowcapture/source/capture-filter.cpp - libdshowcapture/source/capture-filter.hpp - libdshowcapture/source/device-vendor.cpp - libdshowcapture/source/device.cpp - libdshowcapture/source/device.hpp - libdshowcapture/source/dshow-base.cpp - libdshowcapture/source/dshow-base.hpp - libdshowcapture/source/dshow-demux.cpp - libdshowcapture/source/dshow-demux.hpp - libdshowcapture/source/dshow-device-defs.hpp - libdshowcapture/source/dshow-encoded-device.cpp - libdshowcapture/source/dshow-enum.cpp - libdshowcapture/source/dshow-enum.hpp - libdshowcapture/source/dshow-formats.cpp - libdshowcapture/source/dshow-formats.hpp - libdshowcapture/source/dshow-media-type.cpp - libdshowcapture/source/dshow-media-type.hpp - libdshowcapture/source/dshowcapture.cpp - libdshowcapture/source/dshowencode.cpp - libdshowcapture/source/encoder.cpp - libdshowcapture/source/encoder.hpp - libdshowcapture/source/external/IVideoCaptureFilter.h - libdshowcapture/source/log.cpp - libdshowcapture/source/log.hpp - libdshowcapture/source/output-filter.cpp - libdshowcapture/source/output-filter.hpp - libdshowcapture/external/capture-device-support/Library/EGAVResult.cpp - libdshowcapture/external/capture-device-support/Library/ElgatoUVCDevice.cpp - libdshowcapture/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp - libdshowcapture/external/capture-device-support/SampleCode/DriverInterface.cpp) + INTERFACE + libdshowcapture/dshowcapture.hpp + libdshowcapture/source/capture-filter.cpp + libdshowcapture/source/capture-filter.hpp + libdshowcapture/source/device-vendor.cpp + libdshowcapture/source/device.cpp + libdshowcapture/source/device.hpp + libdshowcapture/source/dshow-base.cpp + libdshowcapture/source/dshow-base.hpp + libdshowcapture/source/dshow-demux.cpp + libdshowcapture/source/dshow-demux.hpp + libdshowcapture/source/dshow-device-defs.hpp + libdshowcapture/source/dshow-encoded-device.cpp + libdshowcapture/source/dshow-enum.cpp + libdshowcapture/source/dshow-enum.hpp + libdshowcapture/source/dshow-formats.cpp + libdshowcapture/source/dshow-formats.hpp + libdshowcapture/source/dshow-media-type.cpp + libdshowcapture/source/dshow-media-type.hpp + libdshowcapture/source/dshowcapture.cpp + libdshowcapture/source/dshowencode.cpp + libdshowcapture/source/encoder.cpp + libdshowcapture/source/encoder.hpp + libdshowcapture/source/external/IVideoCaptureFilter.h + libdshowcapture/source/log.cpp + libdshowcapture/source/log.hpp + libdshowcapture/source/output-filter.cpp + libdshowcapture/source/output-filter.hpp + libdshowcapture/external/capture-device-support/Library/EGAVResult.cpp + libdshowcapture/external/capture-device-support/Library/ElgatoUVCDevice.cpp + libdshowcapture/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp + libdshowcapture/external/capture-device-support/SampleCode/DriverInterface.cpp +) target_include_directories( - libdshowcapture INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture" - "${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture/external/capture-device-support/Library") + libdshowcapture + INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture" + "${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture/external/capture-device-support/Library" +) target_compile_definitions(libdshowcapture INTERFACE _UP_WINDOWS=1) target_compile_options(libdshowcapture INTERFACE /wd4018) diff --git a/plugins/win-dshow/virtualcam-module/CMakeLists.txt b/plugins/win-dshow/virtualcam-module/CMakeLists.txt index 2fe8cb269d9438..413d8fa5c6c6f2 100644 --- a/plugins/win-dshow/virtualcam-module/CMakeLists.txt +++ b/plugins/win-dshow/virtualcam-module/CMakeLists.txt @@ -9,10 +9,8 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) endif() if(NOT VIRTUALCAM_GUID) - # cmake-format: off set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) mark_as_advanced(VIRTUALCAM_GUID) - # cmake-format: on message(WARNING "Empty Virtual Camera GUID set.") target_disable_feature(obs-virtualcam-module "Virtual Camera Support (empty GUID)") @@ -59,26 +57,22 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) configure_file(cmake/windows/obs-module.rc.in virtualcam-module.rc) target_sources( _virtualcam - INTERFACE # cmake-format: sortable - ${CMAKE_CURRENT_BINARY_DIR}/virtualcam-module.rc - placeholder.cpp - sleepto.c - sleepto.h - virtualcam-filter.cpp - virtualcam-filter.hpp - virtualcam-module.cpp) + INTERFACE + ${CMAKE_CURRENT_BINARY_DIR}/virtualcam-module.rc + placeholder.cpp + sleepto.c + sleepto.h + virtualcam-filter.cpp + virtualcam-filter.hpp + virtualcam-module.cpp + ) target_include_directories(_virtualcam INTERFACE "${CMAKE_CURRENT_BINARY_DIR}") target_compile_definitions(_virtualcam INTERFACE VIRTUALCAM_AVAILABLE) target_link_libraries( _virtualcam - INTERFACE OBS::virtualcam-interface - OBS::virtualcam-guid - OBS::libdshowcapture - OBS::winhandle - gdiplus - strmiids - winmm) + INTERFACE OBS::virtualcam-interface OBS::virtualcam-guid OBS::libdshowcapture OBS::winhandle gdiplus strmiids winmm + ) endif() legacy_check() @@ -93,14 +87,14 @@ set_property(TARGET obs-virtualcam-module PROPERTY MSVC_RUNTIME_LIBRARY "MultiTh configure_file(virtualcam-install.bat.in virtualcam-install.bat) target_add_resource(obs-virtualcam-module "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-install.bat" - "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow") + "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow" +) configure_file(virtualcam-uninstall.bat.in virtualcam-uninstall.bat) target_add_resource(obs-virtualcam-module "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-uninstall.bat" - "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow") + "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow" +) include(cmake/32bit.cmake) -# cmake-format: off set_target_properties_obs(obs-virtualcam-module PROPERTIES FOLDER plugins/win-dshow OUTPUT_NAME obs-virtualcam-module64) -# cmake-format: on diff --git a/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake b/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake index 229c206f1de2ad..fe748a6fecef55 100644 --- a/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake +++ b/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake @@ -4,9 +4,7 @@ if(NOT ENABLE_VIRTUALCAM) endif() if(NOT VIRTUALCAM_GUID) - set(VIRTUALCAM_GUID - "" - CACHE STRING "Virtual Camera GUID" FORCE) + set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) mark_as_advanced(VIRTUALCAM_GUID) message(WARNING "Empty Virtual Camera GUID set.") diff --git a/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake b/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake index daf4fca56ea1bf..5ea1cf3e8ec7cd 100644 --- a/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake +++ b/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake @@ -2,4 +2,5 @@ add_custom_command( TARGET obs-virtualcam-module POST_BUILD COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t obs-virtualcam-module - COMMENT "Build 32-bit obs-virtualcam") + COMMENT "Build 32-bit obs-virtualcam" +) diff --git a/plugins/win-wasapi/CMakeLists.txt b/plugins/win-wasapi/CMakeLists.txt index 6838a62a07625c..1c8c4feb0ab2c3 100644 --- a/plugins/win-wasapi/CMakeLists.txt +++ b/plugins/win-wasapi/CMakeLists.txt @@ -5,14 +5,14 @@ legacy_check() add_library(win-wasapi MODULE) add_library(OBS::wasapi ALIAS win-wasapi) -target_sources(win-wasapi PRIVATE win-wasapi.cpp wasapi-notify.cpp wasapi-notify.hpp enum-wasapi.cpp enum-wasapi.hpp - plugin-main.cpp) +target_sources( + win-wasapi + PRIVATE win-wasapi.cpp wasapi-notify.cpp wasapi-notify.hpp enum-wasapi.cpp enum-wasapi.hpp plugin-main.cpp +) configure_file(cmake/windows/obs-module.rc.in win-wasapi.rc) target_sources(win-wasapi PRIVATE win-wasapi.rc) target_link_libraries(win-wasapi PRIVATE OBS::libobs Avrt) -# cmake-format: off set_target_properties_obs(win-wasapi PROPERTIES FOLDER plugins PREFIX "") -# cmake-format: on diff --git a/shared/happy-eyeballs/CMakeLists.txt b/shared/happy-eyeballs/CMakeLists.txt index c6e143c8fc47d8..efbb2629497290 100644 --- a/shared/happy-eyeballs/CMakeLists.txt +++ b/shared/happy-eyeballs/CMakeLists.txt @@ -3,10 +3,7 @@ cmake_minimum_required(VERSION 3.22...3.25) add_library(happy-eyeballs OBJECT) add_library(OBS::happy-eyeballs ALIAS happy-eyeballs) -target_sources( - happy-eyeballs - PRIVATE happy-eyeballs.c - PUBLIC happy-eyeballs.h) +target_sources(happy-eyeballs PRIVATE happy-eyeballs.c PUBLIC happy-eyeballs.h) target_include_directories(happy-eyeballs PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/ipc-util/CMakeLists.txt b/shared/ipc-util/CMakeLists.txt index bda6f80a0d715f..cfcdc3c71585b3 100644 --- a/shared/ipc-util/CMakeLists.txt +++ b/shared/ipc-util/CMakeLists.txt @@ -4,8 +4,12 @@ add_library(ipc-util INTERFACE) add_library(OBS::ipc-util ALIAS ipc-util) target_sources( - ipc-util INTERFACE "$<$:${CMAKE_CURRENT_SOURCE_DIR}/ipc-util/pipe-windows.c>" ipc-util/pipe.h - "$<$:${CMAKE_CURRENT_SOURCE_DIR}/ipc-util/pipe-windows.h>") + ipc-util + INTERFACE + "$<$:${CMAKE_CURRENT_SOURCE_DIR}/ipc-util/pipe-windows.c>" + ipc-util/pipe.h + "$<$:${CMAKE_CURRENT_SOURCE_DIR}/ipc-util/pipe-windows.h>" +) target_include_directories(ipc-util INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/media-playback/CMakeLists.txt b/shared/media-playback/CMakeLists.txt index ab3a0fe7c809e9..59321510ebf498 100644 --- a/shared/media-playback/CMakeLists.txt +++ b/shared/media-playback/CMakeLists.txt @@ -7,16 +7,17 @@ add_library(OBS::media-playback ALIAS media-playback) target_sources( media-playback - INTERFACE # cmake-format: sortable - media-playback/cache.c - media-playback/cache.h - media-playback/closest-format.h - media-playback/decode.c - media-playback/decode.h - media-playback/media-playback.c - media-playback/media-playback.h - media-playback/media.c - media-playback/media.h) + INTERFACE + media-playback/cache.c + media-playback/cache.h + media-playback/closest-format.h + media-playback/decode.c + media-playback/decode.h + media-playback/media-playback.c + media-playback/media-playback.h + media-playback/media.c + media-playback/media.h +) target_include_directories(media-playback INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/obs-scripting/CMakeLists.txt b/shared/obs-scripting/CMakeLists.txt index 07826e3ea60727..305780ddeb8200 100644 --- a/shared/obs-scripting/CMakeLists.txt +++ b/shared/obs-scripting/CMakeLists.txt @@ -27,32 +27,33 @@ endif() target_sources( obs-scripting PUBLIC obs-scripting.h - PRIVATE obs-scripting-callback.h obs-scripting-logging.c obs-scripting.c) + PRIVATE obs-scripting-callback.h obs-scripting-logging.c obs-scripting.c +) -target_compile_definitions(obs-scripting PRIVATE SCRIPT_DIR="${OBS_SCRIPT_PLUGIN_PATH}" - $<$:ENABLE_UI>) +target_compile_definitions( + obs-scripting + PRIVATE SCRIPT_DIR="${OBS_SCRIPT_PLUGIN_PATH}" $<$:ENABLE_UI> +) target_include_directories(obs-scripting PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -# cmake-format: off target_link_libraries( obs-scripting - PRIVATE OBS::libobs - OBS::cstrcache - $<$:OBS::frontend-api> - $<$:OBS::w32-pthreads> - $<$:objc>) -# cmake-format: on + PRIVATE + OBS::libobs + OBS::cstrcache + $<$:OBS::frontend-api> + $<$:OBS::w32-pthreads> + $<$:objc> +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in obs-scripting.rc) target_sources(obs-scripting PRIVATE obs-scripting.rc) endif() -# cmake-format: off set_target_properties_obs(obs-scripting PROPERTIES FOLDER scripting) -if (OS_WINDOWS OR OS_MACOS) +if(OS_WINDOWS OR OS_MACOS) set_property(TARGET obs-scripting PROPERTY PREFIX "") endif() -# cmake-format: on diff --git a/shared/obs-scripting/cmake/lua.cmake b/shared/obs-scripting/cmake/lua.cmake index 656cb1a697a403..f46d90b15ceada 100644 --- a/shared/obs-scripting/cmake/lua.cmake +++ b/shared/obs-scripting/cmake/lua.cmake @@ -13,21 +13,28 @@ if(ENABLE_SCRIPTING_LUA) PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory swig COMMAND ${CMAKE_COMMAND} -E env "SWIG_LIB=${SWIG_DIR}" ${SWIG_EXECUTABLE} -lua -external-runtime swig/swigluarun.h - COMMENT "obs-scripting - generating Luajit SWIG interface headers") + COMMENT "obs-scripting - generating Luajit SWIG interface headers" + ) set_source_files_properties(swig/swigluarun.h PROPERTIES GENERATED TRUE) target_sources( obs-scripting - PRIVATE # cmake-format: sortable - $<$:obs-scripting-lua-frontend.c> obs-scripting-lua-source.c obs-scripting-lua.c - obs-scripting-lua.h swig/swigluarun.h) + PRIVATE + $<$:obs-scripting-lua-frontend.c> + obs-scripting-lua-source.c + obs-scripting-lua.c + obs-scripting-lua.h + swig/swigluarun.h + ) target_compile_definitions(obs-scripting PUBLIC LUAJIT_FOUND) set_source_files_properties( - obs-scripting-lua.c obs-scripting-lua-source.c - PROPERTIES COMPILE_OPTIONS $<$:-Wno-error=shorten-64-to-32>) + obs-scripting-lua.c + obs-scripting-lua-source.c + PROPERTIES COMPILE_OPTIONS $<$:-Wno-error=shorten-64-to-32> + ) target_link_libraries(obs-scripting PRIVATE Luajit::Luajit) else() diff --git a/shared/obs-scripting/cmake/python.cmake b/shared/obs-scripting/cmake/python.cmake index 7eb98260e69b12..9696d2b0e9ae1b 100644 --- a/shared/obs-scripting/cmake/python.cmake +++ b/shared/obs-scripting/cmake/python.cmake @@ -7,10 +7,7 @@ if(ENABLE_SCRIPTING_PYTHON) if(OS_WINDOWS) find_package(Python 3.8...<3.11 REQUIRED Interpreter Development) - elseif( - OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) + elseif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Python 3.8 REQUIRED Interpreter Development) else() find_package(Python 3.8...<3.12 REQUIRED Interpreter Development) @@ -21,30 +18,35 @@ if(ENABLE_SCRIPTING_PYTHON) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory swig - COMMAND ${CMAKE_COMMAND} -E env "SWIG_LIB=${SWIG_DIR}" ${SWIG_EXECUTABLE} -python - $<$:-py3-stable-abi> -external-runtime swig/swigpyrun.h - COMMENT "obs-scripting - generating Python 3 SWIG interface headers") + COMMAND + ${CMAKE_COMMAND} -E env "SWIG_LIB=${SWIG_DIR}" ${SWIG_EXECUTABLE} -python + $<$:-py3-stable-abi> -external-runtime swig/swigpyrun.h + COMMENT "obs-scripting - generating Python 3 SWIG interface headers" + ) set_source_files_properties(swig/swigpyrun.h PROPERTIES GENERATED TRUE) target_sources( obs-scripting - PRIVATE # cmake-format: sortable - $<$:obs-scripting-python-frontend.c> - $<$:obs-scripting-python-import.c> - obs-scripting-python-import.h - obs-scripting-python.c - obs-scripting-python.h - swig/swigpyrun.h) + PRIVATE + $<$:obs-scripting-python-frontend.c> + $<$:obs-scripting-python-import.c> + obs-scripting-python-import.h + obs-scripting-python.c + obs-scripting-python.h + swig/swigpyrun.h + ) target_compile_definitions( obs-scripting PRIVATE ENABLE_SCRIPTING PYTHON_LIB="$" - PUBLIC Python_FOUND) + PUBLIC Python_FOUND + ) target_include_directories( obs-scripting - PRIVATE "$<$:$>") + PRIVATE "$<$:$>" + ) target_link_libraries(obs-scripting PRIVATE $<$:Python::Python>) diff --git a/shared/obs-scripting/obslua/CMakeLists.txt b/shared/obs-scripting/obslua/CMakeLists.txt index 204459aa817600..be4888db9ad5fb 100644 --- a/shared/obs-scripting/obslua/CMakeLists.txt +++ b/shared/obs-scripting/obslua/CMakeLists.txt @@ -17,36 +17,40 @@ include(UseSWIG) set_source_files_properties(obslua.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES TRUE) -swig_add_library( - obslua - LANGUAGE lua - TYPE MODULE - SOURCES obslua.i) +swig_add_library(obslua LANGUAGE lua TYPE MODULE SOURCES obslua.i) add_library(OBS::lua ALIAS obslua) target_compile_options( obslua - PRIVATE $<$:/wd4054> - $<$:/wd4197> - $<$:/wd4244> - $<$:/wd4267> - $<$:-Wno-shorten-64-to-32> - $<$:-Wno-unreachable-code> - $<$:-Wno-maybe-uninitialized>) - -target_compile_definitions(obslua PRIVATE SWIG_TYPE_TABLE=obslua SWIG_LUA_INTERPRETER_NO_DEBUG - $<$:ENABLE_UI>) + PRIVATE + $<$:/wd4054> + $<$:/wd4197> + $<$:/wd4244> + $<$:/wd4267> + $<$:-Wno-shorten-64-to-32> + $<$:-Wno-unreachable-code> + $<$:-Wno-maybe-uninitialized> +) + +target_compile_definitions( + obslua + PRIVATE SWIG_TYPE_TABLE=obslua SWIG_LUA_INTERPRETER_NO_DEBUG $<$:ENABLE_UI> +) -target_link_libraries(obslua PRIVATE OBS::cstrcache OBS::libobs OBS::scripting Luajit::Luajit - $<$:OBS::frontend-api>) +target_link_libraries( + obslua + PRIVATE OBS::cstrcache OBS::libobs OBS::scripting Luajit::Luajit $<$:OBS::frontend-api> +) set_property( TARGET obslua APPEND - PROPERTY SWIG_COMPILE_DEFINITIONS "SWIG_TYPE_TABLE=obslua" "SWIG_LUA_INTERPRETER_NO_DEBUG" - "$<$:ENABLE_UI>") + PROPERTY + SWIG_COMPILE_DEFINITIONS + "SWIG_TYPE_TABLE=obslua" + "SWIG_LUA_INTERPRETER_NO_DEBUG" + "$<$:ENABLE_UI>" +) -# cmake-format: off set_target_properties_obs(obslua PROPERTIES FOLDER scripting XCODE_ATTRIBUTE_STRIP_STYLE non-global) -# cmake-format: on diff --git a/shared/obs-scripting/obspython/CMakeLists.txt b/shared/obs-scripting/obspython/CMakeLists.txt index e90b1499546f8b..2841ac9ccfe98b 100644 --- a/shared/obs-scripting/obspython/CMakeLists.txt +++ b/shared/obs-scripting/obspython/CMakeLists.txt @@ -14,9 +14,7 @@ if(POLICY CMP0094) cmake_policy(SET CMP0094 NEW) endif() -if(OS_LINUX - OR OS_FREEBSD - OR OS_OPENBSD) +if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) find_package(Python 3.8 REQUIRED Interpreter Development) else() find_package(Python 3.8...<3.12 REQUIRED Interpreter Development) @@ -25,40 +23,56 @@ find_package(SWIG 4 REQUIRED) include(UseSWIG) -set_source_files_properties(obspython.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES TRUE - SWIG_FLAGS $<$:-py3-stable-abi>) +set_source_files_properties( + obspython.i + PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES TRUE SWIG_FLAGS $<$:-py3-stable-abi> +) -swig_add_library( - obspython - LANGUAGE python - TYPE MODULE - SOURCES obspython.i) +swig_add_library(obspython LANGUAGE python TYPE MODULE SOURCES obspython.i) add_library(OBS::python ALIAS obspython) file( - GENERATE - OUTPUT $<$:$/>obspython.h - CONTENT "#pragma once\n\n#define PYTHON_LIB \"$\"\n") + GENERATE OUTPUT + $<$:$/>obspython.h + CONTENT "#pragma once\n\n#define PYTHON_LIB \"$\"\n" +) target_include_directories( - obspython PRIVATE "$<$:${CMAKE_CURRENT_BINARY_DIR}/$>" - "$<$:$>") + obspython + PRIVATE + "$<$:${CMAKE_CURRENT_BINARY_DIR}/$>" + "$<$:$>" +) target_compile_options( obspython - PRIVATE $<$:/wd4100> - $<$:/wd4197> - $<$:-Wno-unused-parameter> - $<$:-Wno-macro-redefined> - $<$:-Wno-unreachable-code>) - -target_compile_definitions(obspython PRIVATE SWIG_TYPE_TABLE=obspython Py_ENABLE_SHARED=1 - SWIG_PYTHON_INTERPRETER_NO_DEBUG $<$:ENABLE_UI>) + PRIVATE + $<$:/wd4100> + $<$:/wd4197> + $<$:-Wno-unused-parameter> + $<$:-Wno-macro-redefined> + $<$:-Wno-unreachable-code> +) + +target_compile_definitions( + obspython + PRIVATE + SWIG_TYPE_TABLE=obspython + Py_ENABLE_SHARED=1 + SWIG_PYTHON_INTERPRETER_NO_DEBUG + $<$:ENABLE_UI> +) target_link_libraries( - obspython PRIVATE OBS::cstrcache OBS::libobs OBS::scripting $<$:OBS::frontend-api> - $<$>:Python::Python>) + obspython + PRIVATE + OBS::cstrcache + OBS::libobs + OBS::scripting + $<$:OBS::frontend-api> + $<$>:Python::Python> +) target_link_options(obspython PRIVATE $<$:LINKER:-undefined,dynamic_lookup>) @@ -66,17 +80,23 @@ if(MSVC OR XCODE) add_custom_command( TARGET obspython POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" - "${CMAKE_CURRENT_BINARY_DIR}/$/obspython.py" - VERBATIM) + COMMAND + "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" + "${CMAKE_CURRENT_BINARY_DIR}/$/obspython.py" + VERBATIM + ) endif() set_property( TARGET obspython APPEND - PROPERTY SWIG_COMPILE_DEFINITIONS "SWIG_TYPE_TABLE=obspython" "Py_ENABLE_SHARED=1" "SWIG_PYTHON_INTERPRETER_NO_DEBUG" - "$<$:ENABLE_UI>" "$<$:MS_NO_COREDLL>") + PROPERTY + SWIG_COMPILE_DEFINITIONS + "SWIG_TYPE_TABLE=obspython" + "Py_ENABLE_SHARED=1" + "SWIG_PYTHON_INTERPRETER_NO_DEBUG" + "$<$:ENABLE_UI>" + "$<$:MS_NO_COREDLL>" +) -# cmake-format: off set_target_properties_obs(obspython PROPERTIES FOLDER scripting PREFIX "_" XCODE_ATTRIBUTE_STRIP_STYLE non-global) -# cmake-format: on diff --git a/shared/opts-parser/CMakeLists.txt b/shared/opts-parser/CMakeLists.txt index 4248de2cc53414..4f74cb66df76c9 100644 --- a/shared/opts-parser/CMakeLists.txt +++ b/shared/opts-parser/CMakeLists.txt @@ -3,10 +3,7 @@ cmake_minimum_required(VERSION 3.22...3.25) add_library(opts-parser OBJECT) add_library(OBS::opts-parser ALIAS opts-parser) -target_sources( - opts-parser - PRIVATE opts-parser.c - PUBLIC opts-parser.h) +target_sources(opts-parser PRIVATE opts-parser.c PUBLIC opts-parser.h) target_include_directories(opts-parser PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/properties-view/CMakeLists.txt b/shared/properties-view/CMakeLists.txt index b66393fcf53f7a..af3fdba3a78b10 100644 --- a/shared/properties-view/CMakeLists.txt +++ b/shared/properties-view/CMakeLists.txt @@ -11,13 +11,17 @@ if(NOT TARGET OBS::qt-plain-text-edit) endif() if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") + add_subdirectory( + "${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" + "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area" + ) endif() if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") + add_subdirectory( + "${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" + "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel" + ) endif() add_library(properties-view INTERFACE) @@ -25,14 +29,15 @@ add_library(OBS::properties-view ALIAS properties-view) target_sources( properties-view - INTERFACE # cmake-format: sortable - double-slider.cpp - double-slider.hpp - properties-view.cpp - properties-view.hpp - properties-view.moc.hpp - spinbox-ignorewheel.cpp - spinbox-ignorewheel.hpp) + INTERFACE + double-slider.cpp + double-slider.hpp + properties-view.cpp + properties-view.hpp + properties-view.moc.hpp + spinbox-ignorewheel.cpp + spinbox-ignorewheel.hpp +) target_include_directories(properties-view INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") if(OS_LINUX AND Qt6_VERSION VERSION_LESS 6.3) @@ -41,11 +46,13 @@ endif() target_link_libraries( properties-view - INTERFACE OBS::frontend-api - OBS::libobs - OBS::qt-wrappers - OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area - OBS::qt-slider-ignorewheel - Qt::Core - Qt::Widgets) + INTERFACE + OBS::frontend-api + OBS::libobs + OBS::qt-wrappers + OBS::qt-plain-text-edit + OBS::qt-vertical-scroll-area + OBS::qt-slider-ignorewheel + Qt::Core + Qt::Widgets +) diff --git a/test/test-input/CMakeLists.txt b/test/test-input/CMakeLists.txt index 5bcce1b009f39b..547fba00f20d9d 100644 --- a/test/test-input/CMakeLists.txt +++ b/test/test-input/CMakeLists.txt @@ -14,15 +14,16 @@ add_library(OBS::test-input ALIAS test-input) target_sources( test-input - PRIVATE # cmake-format: sortable - sync-async-source.c - sync-audio-buffering.c - sync-pair-aud.c - sync-pair-vid.c - test-filter.c - test-input.c - test-random.c - test-sinewave.c) + PRIVATE + sync-async-source.c + sync-audio-buffering.c + sync-pair-aud.c + sync-pair-vid.c + test-filter.c + test-input.c + test-random.c + test-sinewave.c +) target_link_libraries(test-input PRIVATE OBS::libobs) From 76230b106caa0f68e268bb1d71efe96020639201 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 14 Aug 2024 12:10:35 -0400 Subject: [PATCH 0394/1073] .git-blame-ignore-revs: Add change from cmake-format to gersemi --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 420715c58daf25..1b11b911375bf4 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -10,3 +10,4 @@ dcc07cfe4ed3f7fb60c7a0d1563236eac0a0b053 64139a6bbd6f85155c709035d82e91f52c2e36fe 7628265099724671a1682f6b298b509d2fa23855 f4733ec6a26bac21699daf3dfd6857ff5a1d3c07 +b8cfacaec38d31413b0cd82718c9dc1e36beb9af From ee1b7822561e12587189b1ee8884590c957cfee2 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 14 Aug 2024 15:22:09 -0400 Subject: [PATCH 0395/1073] obs-browser: Update version to 2.24.1 d18fc7a - Enable building with CEF 6533 & 6613 22db20b - Fix CSS injection for sites with strict CSP --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index 75d3eb11371498..be9f1b646406d2 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit 75d3eb113714985743cd14176b0c82ce25798ede +Subproject commit be9f1b646406d2250b402581b043f1558042d7f0 From dc7a58484d3ef2c610a5184dd05d1d02dbd3e549 Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 9 Aug 2024 03:02:36 +0200 Subject: [PATCH 0396/1073] CI: Remove game capture dual-signing --- .github/actions/windows-signing/action.yaml | 7 ------- .github/actions/windows-signing/config.toml | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/actions/windows-signing/action.yaml b/.github/actions/windows-signing/action.yaml index 57371b0ddb2668..4d230f4bba7159 100644 --- a/.github/actions/windows-signing/action.yaml +++ b/.github/actions/windows-signing/action.yaml @@ -90,13 +90,6 @@ runs: Ensure-Location "${{ github.workspace }}/old_builds" rclone copy --transfers 100 ":gcs:obs-latest/${{ inputs.channel }}" . - - name: Download Presigned Game Capture Files (REMOVE AFTER 30.2!!) - shell: pwsh - env: - RCLONE_GCS_ENV_AUTH: 'true' - run: | - rclone copy :gcs:obs-game-capture "${{ github.workspace }}/build/data/obs-plugins/win-capture" - - name: Run bouf shell: pwsh run: | diff --git a/.github/actions/windows-signing/config.toml b/.github/actions/windows-signing/config.toml index 06b89ae6548dbc..aae3852936700a 100644 --- a/.github/actions/windows-signing/config.toml +++ b/.github/actions/windows-signing/config.toml @@ -24,8 +24,7 @@ sign_kms_key_id = "projects/ci-signing/locations/global/keyRings/production/cryp sign_digest = "sha384" sign_ts_serv = "http://timestamp.digicert.com" sign_exts = ['exe', 'dll', 'pyd'] -sign_append = true -sign_ts_algo = "sha256" +sign_append = false [prepare.strip_pdbs] # PDBs to not strip From 5cbeb1536a4816b68f00afe2c4dd6a7e1e5df67c Mon Sep 17 00:00:00 2001 From: derrod Date: Fri, 9 Aug 2024 03:02:57 +0200 Subject: [PATCH 0397/1073] CI: Bump sign-windows commit hash --- .github/workflows/push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 4f2fb4d53fa759..9652cec71fa683 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -207,7 +207,7 @@ jobs: sign-windows-build: name: Windows Signing ✍️ - uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@d2b05a6e0c6a0f51c42f247efd581bed8f4a1877 + uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@dc7a58484d3ef2c610a5184dd05d1d02dbd3e549 if: github.repository_owner == 'obsproject' && github.ref_type == 'tag' needs: build-project permissions: From df137d9efed6600adcdeb7e774baf60e9cda92d1 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 8 Aug 2024 15:06:47 -0400 Subject: [PATCH 0398/1073] CI: Update deps to 2024-08-08 release Notable changes: * deps.ffmpeg: Enable Media Foundation * deps.ffmpeg: Update nv-codec to 12.2.72.0 * deps.macos: Use correct CMake vars for libajantv2 * deps.macos: Add SIMD Everywhere to macOS deps * deps.windows: Add SIMD Everywhere to Windows deps * deps.windows: Update VPL to v2.12.0 --- buildspec.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/buildspec.json b/buildspec.json index 6cde01eaa4247f..2da30bfd56fe40 100644 --- a/buildspec.json +++ b/buildspec.json @@ -1,25 +1,25 @@ { "dependencies": { "prebuilt": { - "version": "2024-05-08", + "version": "2024-08-08", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "label": "Pre-Built obs-deps", "hashes": { - "macos-universal": "da3167a3efecfa67dd72e4f2b7964c3456d74c639d4c62aa21ec300ebd5007a8", - "windows-x64": "773c87a0d173125ef2768aaca6f6de64a7d6053213cc9f7d3970a301152042d8", - "windows-x86": "8f4bc6b37acbabe5434e7ebce3bbc8a213d6c265cd7b4e708cd17ca4aa334130" + "macos-universal": "6faaa5737fc80087e13bd5cdff4df820359fff7bef7141a59e7170fdab8ce36f", + "windows-x64": "2b14cbf46064939d56a57f952d22b80d18b2a982c8bd6a2d30044452b8e99b1a", + "windows-x86": "334d08d4d59704372773c981d4f426661798bb2f4d3c5432a6f61d6368235b45" } }, "qt6": { - "version": "2024-05-08", + "version": "2024-08-08", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "label": "Pre-Built Qt6", "hashes": { - "macos-universal": "248fb342e7ddf574af0960aaedeffb832deda1485dc81583302646e979593a6e", - "windows-x64": "8f459af5115ce081ae24b108712327e113893f250e14a902b1bd188b43873ed1" + "macos-universal": "aca8e9888be610926abcf601718d9ec9a70ca7e5c7df102174de6572c56cee83", + "windows-x64": "169cf5f8218fc0be51dbe7d900e8c4f95b96e7c72b915e3de0b223a0560b94f7" }, "debugSymbols": { - "windows-x64": "e4bc882f23195becbe53f1594fe75bdb9ff4f1d01e319f6caf8e0a38000cb42b" + "windows-x64": "9dbc41ddb9fe50954b76a606d27003c51cec3a19584d43883fc01770e7c7be90" } }, "cef": { From 7c85253667020dc7aea22825e5cd1e24343349a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Batlle=20i=20Rossell?= Date: Thu, 15 Aug 2024 11:59:57 +0200 Subject: [PATCH 0399/1073] obs-ffmpeg: Fix VAAPI/NVENC ifdefs VAAPI init was under NVENC clauses, so it was disabled if no NVENC. That was introduced in af555b9372a70d6c83370bf6c2f08c8b6100b915 from #10536, which refactored NVENC. --- plugins/obs-ffmpeg/obs-ffmpeg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c index 04c715e420fc42..83651e95df2141 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg.c @@ -377,6 +377,7 @@ bool obs_module_load(void) obs_register_encoder(&hevc_nvenc_encoder_info); #endif } +#endif #ifdef _WIN32 amf_load(); @@ -415,7 +416,6 @@ bool obs_module_load(void) } #endif #endif -#endif #if ENABLE_FFMPEG_LOGGING obs_ffmpeg_load_logging(); From 418c9b87cf1bf4d089bb731d32973e62e71dc3b5 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 11 Aug 2024 02:03:52 +0200 Subject: [PATCH 0400/1073] UI: Fixup ResolveVariable to be less jank --- UI/obs-app-theming.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index 7884836e453844..b219aadd3df4ed 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -511,25 +511,22 @@ void OBSApp::FindThemes() static bool ResolveVariable(const QHash &vars, OBSThemeVariable &var) { - const OBSThemeVariable *varPtr = &var; - const OBSThemeVariable *realVar = varPtr; + if (var.type != OBSThemeVariable::Alias) + return true; - while (realVar->type == OBSThemeVariable::Alias) { - QString newKey = realVar->value.toString(); + QString key = var.value.toString(); + while (vars[key].type == OBSThemeVariable::Alias) { + key = vars[key].value.toString(); - if (!vars.contains(newKey)) { + if (!vars.contains(key)) { blog(LOG_ERROR, R"(Variable "%s" (aliased by "%s") does not exist!)", - QT_TO_UTF8(newKey), QT_TO_UTF8(var.name)); + QT_TO_UTF8(key), QT_TO_UTF8(var.name)); return false; } - - const OBSThemeVariable &newVar = vars[newKey]; - realVar = &newVar; } - if (realVar != varPtr) - var = *realVar; + var = vars[key]; return true; } From f35646ae3cd1eb94fa64fa7719335b7865c7d2c0 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 07:03:52 +0200 Subject: [PATCH 0401/1073] obs-nvenc: Remove non-functional legacy lossless mode --- plugins/obs-nvenc/nvenc-compat.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/plugins/obs-nvenc/nvenc-compat.c b/plugins/obs-nvenc/nvenc-compat.c index e8958d3b065900..946537ba95866d 100644 --- a/plugins/obs-nvenc/nvenc-compat.c +++ b/plugins/obs-nvenc/nvenc-compat.c @@ -15,21 +15,11 @@ /* ------------------------------------------------------------------------- */ /* Actual redirector implementation. */ -static void migrate_settings(obs_data_t *settings, enum codec_type codec) +static void migrate_settings(obs_data_t *settings) { - struct encoder_caps *caps = get_encoder_caps(codec); - const char *preset = obs_data_get_string(settings, "preset2"); obs_data_set_string(settings, "preset", preset); - const char *rc = obs_data_get_string(settings, "rate_control"); - /* Old NVENC allowed lossless even if unsupported, - * and just emulated it via CQP 0, do the same here. */ - if (!caps->lossless && strcmp(rc, "lossless") == 0) { - obs_data_set_string(settings, "rate_control", "CQP"); - obs_data_set_int(settings, "cqp", 0); - } - obs_data_set_bool(settings, "adaptive_quantization", obs_data_get_bool(settings, "psycho_aq")); @@ -44,7 +34,7 @@ static void *nvenc_reroute(enum codec_type codec, obs_data_t *settings, obs_encoder_t *encoder, bool texture) { /* Update settings object to v2 encoder configuration */ - migrate_settings(settings, codec); + migrate_settings(settings); switch (codec) { case CODEC_H264: From ebc50f0d2a2ed855ffa71ce037bfd7bccf5a7c46 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 07:31:28 +0200 Subject: [PATCH 0402/1073] obs-nvenc: Improve logging of user settings - Omit irrelevant settings (e.g. CQP in CBR mode) - Set rate control string to "lossless" in lossless mode --- plugins/obs-nvenc/nvenc.c | 73 +++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/plugins/obs-nvenc/nvenc.c b/plugins/obs-nvenc/nvenc.c index 9bd27adced56bc..d183198f6d0987 100644 --- a/plugins/obs-nvenc/nvenc.c +++ b/plugins/obs-nvenc/nvenc.c @@ -234,6 +234,7 @@ static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings) bool cqvbr = astrcmpi(enc->props.rate_control, "CQVBR") == 0; bool vbr = cqvbr || astrcmpi(enc->props.rate_control, "VBR") == 0; bool lossless = strcmp(enc->props.rate_control, "lossless") == 0; + bool cqp = strcmp(enc->props.rate_control, "CQP") == 0; NVENCSTATUS err; @@ -255,6 +256,7 @@ static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings) nv_multipass = NV_ENC_MULTI_PASS_DISABLED; enc->props.adaptive_quantization = false; enc->props.cqp = 0; + enc->props.rate_control = "Lossless"; } /* -------------------------- */ @@ -354,7 +356,7 @@ static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings) config->rcParams.maxBitRate = vbr ? max_bitrate * 1000 : bitrate * 1000; config->rcParams.vbvBufferSize = bitrate * 1000; - if (strcmp(enc->props.rate_control, "CQP") == 0 || lossless) { + if (cqp || lossless) { int cqp_val = enc->codec == CODEC_AV1 ? (int)enc->props.cqp * 4 : (int)enc->props.cqp; @@ -380,34 +382,47 @@ static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings) config->rcParams.qpMapMode = NV_ENC_QP_MAP_DELTA; /* -------------------------- */ - /* initialize */ - - info("settings:\n" - "\tcodec: %s\n" - "\trate_control: %s\n" - "\tbitrate: %d\n" - "\tmax_bitrate: %d\n" - "\tcq/cqp: %ld\n" - "\tkeyint: %d\n" - "\tpreset: %s\n" - "\ttuning: %s\n" - "\tmultipass: %s\n" - "\tprofile: %s\n" - "\twidth: %d\n" - "\theight: %d\n" - "\tb-frames: %ld\n" - "\tb-ref-mode: %ld\n" - "\tlookahead: %s (%d)\n" - "\taq: %s\n" - "\tsplit encode: %ld\n" - "\tuser opts: %s\n", - get_codec_name(enc->codec), enc->props.rate_control, bitrate, - max_bitrate, vbr ? enc->props.target_quality : enc->props.cqp, - gop_size, enc->props.preset, enc->props.tune, enc->props.multipass, - enc->props.profile, enc->cx, enc->cy, enc->props.bf, - enc->props.bframe_ref_mode, lookahead ? "true" : "false", - rc_lookahead, enc->props.adaptive_quantization ? "true" : "false", - enc->props.split_encode, enc->props.opts_str); + /* log settings */ + + struct dstr log = {0}; + + dstr_catf(&log, "\tcodec: %s\n", get_codec_name(enc->codec)); + dstr_catf(&log, "\trate_control: %s\n", enc->props.rate_control); + + if (bitrate && !cqvbr) + dstr_catf(&log, "\tbitrate: %d\n", bitrate); + if (vbr) + dstr_catf(&log, "\tmax_bitrate: %d\n", max_bitrate); + if (cqp) + dstr_catf(&log, "\tcqp: %ld\n", enc->props.cqp); + if (cqvbr) { + dstr_catf(&log, "\tcq: %ld\n", + enc->props.target_quality); + } + + dstr_catf(&log, "\tkeyint: %d\n", gop_size); + dstr_catf(&log, "\tpreset: %s\n", enc->props.preset); + dstr_catf(&log, "\ttuning: %s\n", enc->props.tune); + dstr_catf(&log, "\tmultipass: %s\n", enc->props.multipass); + dstr_catf(&log, "\tprofile: %s\n", enc->props.profile); + dstr_catf(&log, "\twidth: %d\n", enc->cx); + dstr_catf(&log, "\theight: %d\n", enc->cy); + dstr_catf(&log, "\tb-frames: %ld\n", enc->props.bf); + dstr_catf(&log, "\tb-ref-mode: %ld\n", enc->props.bframe_ref_mode); + dstr_catf(&log, "\tlookahead: %s (%d frames)\n", + lookahead ? "true" : "false", rc_lookahead); + dstr_catf(&log, "\taq: %s\n", + enc->props.adaptive_quantization ? "true" : "false"); + + if (enc->props.split_encode) { + dstr_catf(&log, "\tsplit encode: %ld\n", + enc->props.split_encode); + } + if (enc->props.opts.count) + dstr_catf(&log, "\tuser opts: %s\n", enc->props.opts_str); + + info("settings:\n%s", log.array); + dstr_free(&log); return true; } From dac13eb144f16731fcb109bcb4cead1684f1ec91 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 04:39:00 +0200 Subject: [PATCH 0403/1073] libobs: Add warning if created encoder is deprecated --- libobs/obs-encoder.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 908ef2993eff3c..841ff050b1c4c3 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -122,6 +122,13 @@ create_encoder(const char *id, enum obs_encoder_type type, const char *name, } blog(LOG_DEBUG, "encoder '%s' (%s) created", name, id); + + if (ei->caps & OBS_ENCODER_CAP_DEPRECATED) { + blog(LOG_WARNING, + "Encoder ID '%s' is deprecated and may be removed in a future version.", + id); + } + return encoder; } From 539e47e6fffc746f4245bc933e7db031bc863730 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 23:10:15 +0200 Subject: [PATCH 0404/1073] obs-nvenc: Remove untranslated deprecated suffix from encoder names --- plugins/obs-nvenc/nvenc-compat.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/obs-nvenc/nvenc-compat.c b/plugins/obs-nvenc/nvenc-compat.c index 946537ba95866d..b4a0d0d694d991 100644 --- a/plugins/obs-nvenc/nvenc-compat.c +++ b/plugins/obs-nvenc/nvenc-compat.c @@ -59,21 +59,21 @@ static void *nvenc_reroute(enum codec_type codec, obs_data_t *settings, static const char *h264_nvenc_get_name(void *type_data) { UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC H.264 (deprecated)"; + return "NVIDIA NVENC H.264"; } #ifdef ENABLE_HEVC static const char *hevc_nvenc_get_name(void *type_data) { UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC HEVC (deprecated)"; + return "NVIDIA NVENC HEVC"; } #endif static const char *av1_nvenc_get_name(void *type_data) { UNUSED_PARAMETER(type_data); - return "NVIDIA NVENC AV1 (deprecated)"; + return "NVIDIA NVENC AV1"; } static void *h264_nvenc_create(obs_data_t *settings, obs_encoder_t *encoder) From 7a90c80b36b5805cf671538e2818ee6248683340 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 23:18:32 +0200 Subject: [PATCH 0405/1073] UI: Add suffix to deprecated encoders --- UI/window-basic-settings.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 787a5c047a4eba..16d92395efde09 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -2174,9 +2174,12 @@ void OBSBasicSettings::LoadAdvOutputStreamingEncoderProperties() if (!SetComboByValue(ui->advOutEncoder, type)) { uint32_t caps = obs_get_encoder_caps(type); if ((caps & ENCODER_HIDE_FLAGS) != 0) { - const char *name = obs_encoder_get_display_name(type); + QString encName = + QT_UTF8(obs_encoder_get_display_name(type)); + if (caps & OBS_ENCODER_CAP_DEPRECATED) + encName += " (" + QTStr("Deprecated") + ")"; - ui->advOutEncoder->insertItem(0, QT_UTF8(name), + ui->advOutEncoder->insertItem(0, encName, QT_UTF8(type)); SetComboByValue(ui->advOutEncoder, type); } @@ -2292,9 +2295,12 @@ void OBSBasicSettings::LoadAdvOutputRecordingEncoderProperties() if (!SetComboByValue(ui->advOutRecEncoder, type)) { uint32_t caps = obs_get_encoder_caps(type); if ((caps & ENCODER_HIDE_FLAGS) != 0) { - const char *name = obs_encoder_get_display_name(type); + QString encName = + QT_UTF8(obs_encoder_get_display_name(type)); + if (caps & OBS_ENCODER_CAP_DEPRECATED) + encName += " (" + QTStr("Deprecated") + ")"; - ui->advOutRecEncoder->insertItem(1, QT_UTF8(name), + ui->advOutRecEncoder->insertItem(1, encName, QT_UTF8(type)); SetComboByValue(ui->advOutRecEncoder, type); } else { @@ -5083,6 +5089,11 @@ static void DisableIncompatibleCodecs(QComboBox *cbox, const QString &format, if (encoderId.empty()) continue; + if (obs_get_encoder_caps(encoderId.c_str()) & + OBS_ENCODER_CAP_DEPRECATED) { + encDisplayName += " (" + QTStr("Deprecated") + ")"; + } + const char *codec = obs_get_encoder_codec(encoderId.c_str()); bool is_compatible = From f07d2cd6216b025d1b03d9d0f37164e597f64df3 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Fri, 7 Jun 2024 22:47:28 -0500 Subject: [PATCH 0406/1073] UI: Use signal vector for advanced audio dialog Simplifies code by using a vector instead of individual signals. --- UI/adv-audio-control.cpp | 36 +++++++++++-------------- UI/adv-audio-control.hpp | 12 +-------- UI/window-basic-adv-audio.cpp | 50 +++++++++++++---------------------- UI/window-basic-adv-audio.hpp | 5 +--- 4 files changed, 36 insertions(+), 67 deletions(-) diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 161534766c9dc7..1acdecce172bc0 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -51,28 +51,22 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) mixer5 = new QCheckBox(); mixer6 = new QCheckBox(); - activateSignal.Connect(handler, "activate", OBSSourceActivated, this); - deactivateSignal.Connect(handler, "deactivate", OBSSourceDeactivated, - this); - audioActivateSignal.Connect(handler, "audio_activate", - OBSSourceActivated, this); - audioDeactivateSignal.Connect(handler, "audio_deactivate", - OBSSourceDeactivated, this); - volChangedSignal.Connect(handler, "volume", OBSSourceVolumeChanged, - this); - syncOffsetSignal.Connect(handler, "audio_sync", OBSSourceSyncChanged, - this); - flagsSignal.Connect(handler, "update_flags", OBSSourceFlagsChanged, - this); + sigs.emplace_back(handler, "activate", OBSSourceActivated, this); + sigs.emplace_back(handler, "deactivate", OBSSourceDeactivated, this); + sigs.emplace_back(handler, "audio_activate", OBSSourceActivated, this); + sigs.emplace_back(handler, "audio_deactivate", OBSSourceDeactivated, + this); + sigs.emplace_back(handler, "volume", OBSSourceVolumeChanged, this); + sigs.emplace_back(handler, "audio_sync", OBSSourceSyncChanged, this); + sigs.emplace_back(handler, "update_flags", OBSSourceFlagsChanged, this); if (obs_audio_monitoring_available()) - monitoringTypeSignal.Connect(handler, "audio_monitoring", - OBSSourceMonitoringTypeChanged, - this); - mixersSignal.Connect(handler, "audio_mixers", OBSSourceMixersChanged, - this); - balChangedSignal.Connect(handler, "audio_balance", - OBSSourceBalanceChanged, this); - renameSignal.Connect(handler, "rename", OBSSourceRenamed, this); + sigs.emplace_back(handler, "audio_monitoring", + OBSSourceMonitoringTypeChanged, this); + sigs.emplace_back(handler, "audio_mixers", OBSSourceMixersChanged, + this); + sigs.emplace_back(handler, "audio_balance", OBSSourceBalanceChanged, + this); + sigs.emplace_back(handler, "rename", OBSSourceRenamed, this); hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); diff --git a/UI/adv-audio-control.hpp b/UI/adv-audio-control.hpp index ec88f1362bc63a..1d13e7c665ece0 100644 --- a/UI/adv-audio-control.hpp +++ b/UI/adv-audio-control.hpp @@ -46,17 +46,7 @@ class OBSAdvAudioCtrl : public QObject { QPointer mixer5; QPointer mixer6; - OBSSignal volChangedSignal; - OBSSignal syncOffsetSignal; - OBSSignal flagsSignal; - OBSSignal monitoringTypeSignal; - OBSSignal mixersSignal; - OBSSignal activateSignal; - OBSSignal deactivateSignal; - OBSSignal audioActivateSignal; - OBSSignal audioDeactivateSignal; - OBSSignal balChangedSignal; - OBSSignal renameSignal; + std::vector sigs; static void OBSSourceActivated(void *param, calldata_t *calldata); static void OBSSourceDeactivated(void *param, calldata_t *calldata); diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index 9d80e29ff54188..cc3cb3aaf848d4 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -12,19 +12,17 @@ Q_DECLARE_METATYPE(OBSSource); OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) : QDialog(parent), ui(new Ui::OBSAdvAudio), - sourceAddedSignal(obs_get_signal_handler(), "source_audio_activate", - OBSSourceAdded, this), - sourceRemovedSignal(obs_get_signal_handler(), - "source_audio_deactivate", OBSSourceRemoved, - this), - sourceActivatedSignal(obs_get_signal_handler(), "source_activate", - OBSSourceActivated, this), - sourceDeactivatedSignal(obs_get_signal_handler(), "source_deactivate", - OBSSourceRemoved, this), showInactive(false) { ui->setupUi(this); + signal_handler_t *sh = obs_get_signal_handler(); + sigs.emplace_back(sh, "source_audio_activate", OBSSourceAdded, this); + sigs.emplace_back(sh, "source_audio_deactivate", OBSSourceRemoved, + this); + sigs.emplace_back(sh, "source_activate", OBSSourceActivated, this); + sigs.emplace_back(sh, "source_deactivate", OBSSourceRemoved, this); + VolumeType volType = (VolumeType)config_get_int( GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType"); @@ -158,35 +156,25 @@ void OBSBasicAdvAudio::SetShowInactive(bool show) showInactive = show; - sourceAddedSignal.Disconnect(); - sourceRemovedSignal.Disconnect(); - sourceActivatedSignal.Disconnect(); - sourceDeactivatedSignal.Disconnect(); + sigs.clear(); + signal_handler_t *sh = obs_get_signal_handler(); if (showInactive) { - sourceAddedSignal.Connect(obs_get_signal_handler(), - "source_create", OBSSourceAdded, - this); - sourceRemovedSignal.Connect(obs_get_signal_handler(), - "source_remove", OBSSourceRemoved, - this); + sigs.emplace_back(sh, "source_create", OBSSourceAdded, this); + sigs.emplace_back(sh, "source_remove", OBSSourceRemoved, this); obs_enum_sources(EnumSources, this); SetIconsVisible(showVisible); } else { - sourceAddedSignal.Connect(obs_get_signal_handler(), - "source_audio_activate", - OBSSourceAdded, this); - sourceRemovedSignal.Connect(obs_get_signal_handler(), - "source_audio_deactivate", - OBSSourceRemoved, this); - sourceActivatedSignal.Connect(obs_get_signal_handler(), - "source_activate", - OBSSourceActivated, this); - sourceDeactivatedSignal.Connect(obs_get_signal_handler(), - "source_deactivate", - OBSSourceRemoved, this); + sigs.emplace_back(sh, "source_audio_activate", OBSSourceAdded, + this); + sigs.emplace_back(sh, "source_audio_deactivate", + OBSSourceRemoved, this); + sigs.emplace_back(sh, "source_activate", OBSSourceActivated, + this); + sigs.emplace_back(sh, "source_deactivate", OBSSourceRemoved, + this); for (size_t i = 0; i < controls.size(); i++) { const auto source = controls[i]->GetSource(); diff --git a/UI/window-basic-adv-audio.hpp b/UI/window-basic-adv-audio.hpp index ab561a2da558d9..28b6fa05b36ee1 100644 --- a/UI/window-basic-adv-audio.hpp +++ b/UI/window-basic-adv-audio.hpp @@ -14,10 +14,7 @@ class OBSBasicAdvAudio : public QDialog { Q_OBJECT private: - OBSSignal sourceAddedSignal; - OBSSignal sourceRemovedSignal; - OBSSignal sourceActivatedSignal; - OBSSignal sourceDeactivatedSignal; + std::vector sigs; bool showInactive; bool showVisible; From 32b3517ef19d8cdbb0d7cc39b353f4ba24f2b539 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Wed, 26 Jun 2024 10:58:24 +0800 Subject: [PATCH 0407/1073] libobs: Also determine WinUI 3 Window --- libobs/util/windows/window-helpers.c | 6 ++++-- plugins/win-capture/data/compatibility.json | 14 ++++++++++++++ plugins/win-capture/window-capture.c | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libobs/util/windows/window-helpers.c b/libobs/util/windows/window-helpers.c index 1828c98b131c91..919d6e275c8499 100644 --- a/libobs/util/windows/window-helpers.c +++ b/libobs/util/windows/window-helpers.c @@ -326,7 +326,8 @@ bool ms_is_uwp_window(HWND hwnd) if (!GetClassNameW(hwnd, name, sizeof(name) / sizeof(wchar_t))) return false; - return wcscmp(name, L"ApplicationFrameWindow") == 0; + return wcscmp(name, L"ApplicationFrameWindow") == 0 || + wcscmp(name, L"WinUIDesktopWin32WindowClass") == 0; } HWND ms_get_uwp_actual_window(HWND parent) @@ -499,7 +500,8 @@ static bool is_generic_class(const char *current_class) static bool is_uwp_class(const char *window_class) { - return strcmp(window_class, "Windows.UI.Core.CoreWindow") == 0; + return strcmp(window_class, "Windows.UI.Core.CoreWindow") == 0 || + strcmp(window_class, "WinUIDesktopWin32WindowClass") == 0; } HWND ms_find_window(enum window_search_mode mode, enum window_priority priority, diff --git a/plugins/win-capture/data/compatibility.json b/plugins/win-capture/data/compatibility.json index 74fc308dcfa448..6d64f03a8cddc6 100644 --- a/plugins/win-capture/data/compatibility.json +++ b/plugins/win-capture/data/compatibility.json @@ -114,6 +114,20 @@ "message": "UWP applications may not be capturable using the selected Capture Method (BitBlt).", "url": "" }, + { + "name": "WinUI 3", + "translation_key": "Compatibility.WindowCapture.BitBlt.Applications", + "severity": 0, + "executable": "", + "window_class": "WinUIDesktopWin32WindowClass", + "window_title": "", + "match_flags": 4, + "game_capture": false, + "window_capture": true, + "window_capture_wgc": false, + "message": "WinUI 3 applications may not be capturable using the selected Capture Method (BitBlt).", + "url": "" + }, { "name": "Gaming Services", "translation_key": "Compatibility.WindowCapture.BitBlt.Applications", diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index 7166e0b59b14ef..eb540016a4191d 100644 --- a/plugins/win-capture/window-capture.c +++ b/plugins/win-capture/window-capture.c @@ -126,6 +126,7 @@ static const char *wgc_partial_match_classes[] = { static const char *wgc_whole_match_classes[] = { "ApplicationFrameWindow", "Windows.UI.Core.CoreWindow", + "WinUIDesktopWin32WindowClass", "GAMINGSERVICESUI_HOSTING_WINDOW_CLASS", "XLMAIN", /* Microsoft Excel */ "PPTFrameClass", /* Microsoft PowerPoint */ From 71509ad00c79c629e88d1994497cb1268168d88d Mon Sep 17 00:00:00 2001 From: derrod Date: Sat, 9 Mar 2024 00:37:30 +0100 Subject: [PATCH 0408/1073] image-source: Move loop/randomize checkboxes to playback mode combobox --- plugins/image-source/data/locale/en-US.ini | 4 +++ plugins/image-source/obs-slideshow-mk2.c | 42 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/plugins/image-source/data/locale/en-US.ini b/plugins/image-source/data/locale/en-US.ini index 4c2d66380b0912..28b11af5084a5a 100644 --- a/plugins/image-source/data/locale/en-US.ini +++ b/plugins/image-source/data/locale/en-US.ini @@ -29,6 +29,10 @@ SlideShow.Stop="Stop" SlideShow.NextSlide="Next Slide" SlideShow.PreviousSlide="Previous Slide" SlideShow.HideWhenDone="Hide when slideshow is done" +SlideShow.PlaybackMode="Playback Mode" +SlideShow.PlaybackMode.Once="Once" +SlideShow.PlaybackMode.Loop="Loop" +SlideShow.PlaybackMode.Random="Random" ColorSource="Color Source" ColorSource.Color="Color" diff --git a/plugins/image-source/obs-slideshow-mk2.c b/plugins/image-source/obs-slideshow-mk2.c index b30b12d2025582..64bfdaee37cf32 100644 --- a/plugins/image-source/obs-slideshow-mk2.c +++ b/plugins/image-source/obs-slideshow-mk2.c @@ -31,6 +31,10 @@ static const char *S_BEHAVIOR_ALWAYS_PLAY = "always_play"; static const char *S_MODE = "slide_mode"; static const char *S_MODE_AUTO = "mode_auto"; static const char *S_MODE_MANUAL = "mode_manual"; +static const char *S_PLAYBACK_MODE = "playback_mode"; +static const char *S_PLAYBACK_ONCE = "once"; +static const char *S_PLAYBACK_LOOP = "loop"; +static const char *S_PLAYBACK_RANDOM = "random"; static const char *TR_CUT = "cut"; static const char *TR_FADE = "fade"; @@ -42,8 +46,6 @@ static const char *TR_SLIDE = "slide"; #define T_CUSTOM_SIZE T_("CustomSize") #define T_SLIDE_TIME T_("SlideTime") #define T_TRANSITION T_("Transition") -#define T_RANDOMIZE T_("Randomize") -#define T_LOOP T_("Loop") #define T_HIDE T_("HideWhenDone") #define T_FILES T_("Files") #define T_BEHAVIOR T_("PlaybackBehavior") @@ -53,6 +55,10 @@ static const char *TR_SLIDE = "slide"; #define T_MODE T_("SlideMode") #define T_MODE_AUTO T_("SlideMode.Auto") #define T_MODE_MANUAL T_("SlideMode.Manual") +#define T_PLAYBACK_MODE T_("PlaybackMode") +#define T_PLAYBACK_ONCE T_("PlaybackMode.Once") +#define T_PLAYBACK_LOOP T_("PlaybackMode.Loop") +#define T_PLAYBACK_RANDOM T_("PlaybackMode.Random") #define T_TR_(text) obs_module_text("SlideShow.Transition." text) #define T_TR_CUT T_TR_("Cut") @@ -452,8 +458,25 @@ static void ss_update(void *data, obs_data_t *settings) else tr_name = "fade_transition"; - new_data.randomize = obs_data_get_bool(settings, S_RANDOMIZE); - new_data.loop = obs_data_get_bool(settings, S_LOOP); + /* Migrate and old loop/random settings to playback mode. */ + if (!obs_data_has_user_value(settings, S_PLAYBACK_MODE)) { + if (obs_data_has_user_value(settings, S_RANDOMIZE) && + obs_data_get_bool(settings, S_RANDOMIZE)) { + obs_data_set_string(settings, S_PLAYBACK_MODE, + S_PLAYBACK_RANDOM); + } else if (obs_data_has_user_value(settings, S_LOOP)) { + bool loop = obs_data_get_bool(settings, S_LOOP); + obs_data_set_string(settings, S_PLAYBACK_MODE, + loop ? S_PLAYBACK_LOOP + : S_PLAYBACK_ONCE); + } + } + + const char *playback_mode = + obs_data_get_string(settings, S_PLAYBACK_MODE); + new_data.randomize = strcmp(playback_mode, S_PLAYBACK_RANDOM) == 0; + new_data.loop = strcmp(playback_mode, S_PLAYBACK_LOOP) == 0; + new_data.hide = obs_data_get_bool(settings, S_HIDE); if (!old_data.tr_name || strcmp(tr_name, old_data.tr_name) != 0) @@ -972,7 +995,7 @@ static void ss_defaults(obs_data_t *settings) obs_data_set_default_string(settings, S_BEHAVIOR, S_BEHAVIOR_ALWAYS_PLAY); obs_data_set_default_string(settings, S_MODE, S_MODE_AUTO); - obs_data_set_default_bool(settings, S_LOOP, true); + obs_data_set_default_string(settings, S_PLAYBACK_MODE, S_PLAYBACK_LOOP); } static const char *file_filter = "Image files (*.bmp *.tga *.png *.jpeg *.jpg" @@ -1030,9 +1053,14 @@ static obs_properties_t *ss_properties(void *data) 50); obs_property_int_set_suffix(p, " ms"); - obs_properties_add_bool(ppts, S_LOOP, T_LOOP); + p = obs_properties_add_list(ppts, S_PLAYBACK_MODE, T_PLAYBACK_MODE, + OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, T_PLAYBACK_ONCE, S_PLAYBACK_ONCE); + obs_property_list_add_string(p, T_PLAYBACK_LOOP, S_PLAYBACK_LOOP); + obs_property_list_add_string(p, T_PLAYBACK_RANDOM, S_PLAYBACK_RANDOM); + obs_properties_add_bool(ppts, S_HIDE, T_HIDE); - obs_properties_add_bool(ppts, S_RANDOMIZE, T_RANDOMIZE); p = obs_properties_add_list(ppts, S_CUSTOM_SIZE, T_CUSTOM_SIZE, OBS_COMBO_TYPE_EDITABLE, From 81fa608cdebd431d8eed157815bd9ebea17b9156 Mon Sep 17 00:00:00 2001 From: Warchamp7 Date: Wed, 31 Jul 2024 10:53:59 -0400 Subject: [PATCH 0409/1073] UI: Add preview scrollbars This adds scrollbars to the preview, so users can move around the preview without using the spacebar + clicking. Co-Authored-By: Clayton Groeneveld <19962531+cg2121@users.noreply.github.com> --- UI/cmake/legacy.cmake | 2 + UI/cmake/ui-elements.cmake | 2 + UI/data/locale/en-US.ini | 1 + UI/data/themes/Yami.obt | 53 +++++++++ UI/forms/OBSBasic.ui | 217 ++++++++++++++++++++++++++++-------- UI/preview-controls.cpp | 137 +++++++++++++++++++++++ UI/preview-controls.hpp | 81 ++++++++++++++ UI/window-basic-main.cpp | 77 ++++++++++++- UI/window-basic-main.hpp | 12 ++ UI/window-basic-preview.cpp | 84 ++++++++++++++ UI/window-basic-preview.hpp | 25 ++++- 11 files changed, 638 insertions(+), 53 deletions(-) create mode 100644 UI/preview-controls.cpp create mode 100644 UI/preview-controls.hpp diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index c38c11d3e784af..d25e900dba7e68 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -211,6 +211,8 @@ target_sources( menu-button.hpp mute-checkbox.hpp noncheckable-button.hpp + preview-controls.cpp + preview-controls.hpp remote-text.cpp remote-text.hpp scene-tree.cpp diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index 2a93088aeb0fcc..99d4060ecc8f08 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -58,6 +58,8 @@ target_sources( menu-button.hpp mute-checkbox.hpp noncheckable-button.hpp + preview-controls.cpp + preview-controls.hpp remote-text.cpp remote-text.hpp scene-tree.cpp diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index b3303340cbe028..04f0e0a6131311 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -785,6 +785,7 @@ Basic.MainMenu.Edit.Scale="Preview &Scaling" Basic.MainMenu.Edit.Scale.Window="Scale to Window" Basic.MainMenu.Edit.Scale.Canvas="Canvas (%1x%2)" Basic.MainMenu.Edit.Scale.Output="Output (%1x%2)" +Basic.MainMenu.Edit.Scale.Manual="Scaled (%1x%2)" Basic.MainMenu.Edit.Transform="&Transform" Basic.MainMenu.Edit.Transform.EditTransform="&Edit Transform..." Basic.MainMenu.Edit.Transform.CopyTransform="Copy Transform" diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index b97f65abe49508..7c515f5bda3c61 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -105,6 +105,7 @@ --font_base: calc(1pt * var(--font_base_value)); --font_small: calc(0.9pt * var(--font_base_value)); + --font_xsmall: calc(0.85pt * var(--font_base_value)); --font_large: calc(1.1pt * var(--font_base_value)); --font_xlarge: calc(1.5pt * var(--font_base_value)); @@ -634,6 +635,11 @@ QScrollBar::handle:horizontal { min-width: 32px; } +QScrollBar::handle:disabled { + background: transparent; + border-color: transparent; +} + /* Source Context Bar */ #contextContainer { @@ -1963,3 +1969,50 @@ OBSBasicStats { OBSBasicAdvAudio #scrollAreaWidgetContents { background: var(--bg_base); } + +#previewScalePercent, +#previewScalingMode { + background: transparent; + color: var(--text_muted); + font-size: var(--font_xsmall); + height: 14px; + max-height: 14px; + padding: 0px var(--padding_xlarge); + margin: 0; + border: none; + border-radius: 0; +} + +#previewXContainer { + border: 1px solid var(--grey6); +} + +#previewScalingMode { + border: 1px solid var(--grey6); +} + +#previewScalingMode:hover, +#previewScalingMode:focus { + border-color: var(--input_border_hover); +} + +#previewXScrollBar, +#previewYScrollBar { + background: transparent; + border: 1px solid var(--grey6); + border-radius: 0; +} + +#previewXScrollBar { + border-left: none; + height: 16px; +} + +#previewXScrollBar::handle, +#previewYScrollBar::handle { + margin: 3px; +} + +#previewYScrollBar { + width: 16px; +} \ No newline at end of file diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 052d867350099b..b97fe095021ba4 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -69,6 +69,21 @@ + + 0 + + + 1 + + + 1 + + + 1 + + + 1 + @@ -152,53 +167,151 @@
- - - 0 - - - - - - 0 - 0 - - - - StudioMode.PreviewSceneName - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - previewProgramLabels - - - - - - - - 0 - 0 - - - - - 32 - 32 - - - - Qt::ClickFocus - - - Qt::CustomContextMenu - - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + StudioMode.PreviewSceneName + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + previewProgramLabels + + + + + + + 0 + + + + + + 0 + 0 + + + + + 32 + 32 + + + + Qt::ClickFocus + + + Qt::CustomContextMenu + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 100% + + + Qt::AlignCenter + + + + + + + + Basic.MainMenu.Edit.Scale.Window + + + + + Basic.MainMenu.Edit.Scale.Canvas + + + + + + + + + 0 + 0 + + + + -200 + + + 200 + + + 10 + + + 100 + + + Qt::Horizontal + + + + + + + + + + Qt::Vertical + + + + + + +
@@ -2150,6 +2263,16 @@
window-dock.hpp
1 + + OBSPreviewScalingLabel + QLabel +
preview-controls.hpp
+
+ + OBSPreviewScalingComboBox + QComboBox +
preview-controls.hpp
+
diff --git a/UI/preview-controls.cpp b/UI/preview-controls.cpp new file mode 100644 index 00000000000000..ba4d6d400e4d78 --- /dev/null +++ b/UI/preview-controls.cpp @@ -0,0 +1,137 @@ +/****************************************************************************** + Copyright (C) 2024 by Taylor Giampaolo + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#include "preview-controls.hpp" +#include + +/* Preview Scale Label */ +void OBSPreviewScalingLabel::PreviewScaleChanged(float scale) +{ + previewScale = scale; + UpdateScaleLabel(); +} + +void OBSPreviewScalingLabel::UpdateScaleLabel() +{ + float previewScalePercent = floor(100.0f * previewScale); + setText(QString::number(previewScalePercent) + "%"); +} + +/* Preview Scaling ComboBox */ +void OBSPreviewScalingComboBox::PreviewFixedScalingChanged(bool fixed) +{ + if (fixedScaling == fixed) + return; + + fixedScaling = fixed; + UpdateSelection(); +} + +void OBSPreviewScalingComboBox::CanvasResized(uint32_t width, uint32_t height) +{ + SetCanvasSize(width, height); + UpdateCanvasText(); +} + +void OBSPreviewScalingComboBox::OutputResized(uint32_t width, uint32_t height) +{ + SetOutputSize(width, height); + + bool canvasMatchesOutput = output_width == canvas_width && + output_height == canvas_height; + + SetScaleOutputEnabled(!canvasMatchesOutput); + UpdateOutputText(); +} + +void OBSPreviewScalingComboBox::PreviewScaleChanged(float scale) +{ + previewScale = scale; + + if (fixedScaling) { + UpdateSelection(); + UpdateAllText(); + } else { + UpdateScaledText(); + } +} + +void OBSPreviewScalingComboBox::SetScaleOutputEnabled(bool show) +{ + if (scaleOutputEnabled == show) + return; + + scaleOutputEnabled = show; + + if (scaleOutputEnabled) { + addItem(QTStr("Basic.MainMenu.Edit.Scale.Output")); + } else { + removeItem(2); + } +} + +void OBSPreviewScalingComboBox::UpdateAllText() +{ + UpdateCanvasText(); + UpdateOutputText(); + UpdateScaledText(); +} + +void OBSPreviewScalingComboBox::UpdateCanvasText() +{ + QString text = QTStr("Basic.MainMenu.Edit.Scale.Canvas"); + text = text.arg(QString::number(canvas_width), + QString::number(canvas_height)); + setItemText(1, text); +} + +void OBSPreviewScalingComboBox::UpdateOutputText() +{ + if (scaleOutputEnabled) { + QString text = QTStr("Basic.MainMenu.Edit.Scale.Output"); + text = text.arg(QString::number(output_width), + QString::number(output_height)); + setItemText(2, text); + } +} + +void OBSPreviewScalingComboBox::UpdateScaledText() +{ + QString text = QTStr("Basic.MainMenu.Edit.Scale.Manual"); + text = text.arg(QString::number(floor(canvas_width * previewScale)), + QString::number(floor(canvas_height * previewScale))); + setPlaceholderText(text); +} + +void OBSPreviewScalingComboBox::UpdateSelection() +{ + QSignalBlocker sb(this); + float outputScale = float(output_width) / float(canvas_width); + + if (!fixedScaling) { + setCurrentIndex(0); + } else { + if (previewScale == 1.0f) { + setCurrentIndex(1); + } else if (scaleOutputEnabled && + (previewScale == outputScale)) { + setCurrentIndex(2); + } else { + setCurrentIndex(-1); + } + } +} diff --git a/UI/preview-controls.hpp b/UI/preview-controls.hpp new file mode 100644 index 00000000000000..360d992cd3d7b7 --- /dev/null +++ b/UI/preview-controls.hpp @@ -0,0 +1,81 @@ +/****************************************************************************** + Copyright (C) 2024 by Taylor Giampaolo + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include +#include + +class OBSPreviewScalingLabel : public QLabel { + Q_OBJECT + +public: + OBSPreviewScalingLabel(QWidget *parent = nullptr) : QLabel(parent) {} + +public slots: + void PreviewScaleChanged(float scale); + +private: + float previewScale = 0.0f; + void UpdateScaleLabel(); +}; + +class OBSPreviewScalingComboBox : public QComboBox { + Q_OBJECT + +public: + OBSPreviewScalingComboBox(QWidget *parent = nullptr) : QComboBox(parent) + { + } + + inline void SetCanvasSize(uint32_t width, uint32_t height) + { + canvas_width = width; + canvas_height = height; + }; + inline void SetOutputSize(uint32_t width, uint32_t height) + { + output_width = width; + output_height = height; + }; + void UpdateAllText(); + +public slots: + void PreviewScaleChanged(float scale); + void PreviewFixedScalingChanged(bool fixed); + void CanvasResized(uint32_t width, uint32_t height); + void OutputResized(uint32_t width, uint32_t height); + +private: + uint32_t canvas_width = 0; + uint32_t canvas_height = 0; + + uint32_t output_width = 0; + uint32_t output_height = 0; + + float previewScale = 0.0f; + + bool fixedScaling = false; + + bool scaleOutputEnabled = false; + void SetScaleOutputEnabled(bool show); + + void UpdateCanvasText(); + void UpdateOutputText(); + void UpdateScaledText(); + void UpdateSelection(); +}; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 04e71bfffedd29..f131f6f18caeb8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -464,6 +464,7 @@ OBSBasic::OBSBasic(QWidget *parent) ResizePreview(ovi.base_width, ovi.base_height); UpdateContextBarVisibility(); + UpdatePreviewScrollbars(); dpi = devicePixelRatioF(); }; dpi = devicePixelRatioF(); @@ -471,6 +472,30 @@ OBSBasic::OBSBasic(QWidget *parent) connect(windowHandle(), &QWindow::screenChanged, displayResize); connect(ui->preview, &OBSQTDisplay::DisplayResized, displayResize); + /* TODO: Move these into window-basic-preview */ + /* Preview Scaling label */ + connect(ui->preview, &OBSBasicPreview::scalingChanged, + ui->previewScalePercent, + &OBSPreviewScalingLabel::PreviewScaleChanged); + + /* Preview Scaling dropdown */ + connect(ui->preview, &OBSBasicPreview::scalingChanged, + ui->previewScalingMode, + &OBSPreviewScalingComboBox::PreviewScaleChanged); + + connect(ui->preview, &OBSBasicPreview::fixedScalingChanged, + ui->previewScalingMode, + &OBSPreviewScalingComboBox::PreviewFixedScalingChanged); + + connect(ui->previewScalingMode, + &OBSPreviewScalingComboBox::currentIndexChanged, this, + &OBSBasic::PreviewScalingModeChanged); + + connect(this, &OBSBasic::CanvasResized, ui->previewScalingMode, + &OBSPreviewScalingComboBox::CanvasResized); + connect(this, &OBSBasic::OutputResized, ui->previewScalingMode, + &OBSPreviewScalingComboBox::OutputResized); + delete shortcutFilter; shortcutFilter = CreateShortcutFilter(); installEventFilter(shortcutFilter); @@ -1386,6 +1411,7 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file) ui->preview->SetScrollingOffset(scrollOffX, scrollOffY); } ui->preview->SetFixedScaling(fixedScaling); + emit ui->preview->DisplayResized(); if (vcamEnabled) { @@ -2176,6 +2202,7 @@ void OBSBasic::OBSInit() InitOBSCallbacks(); InitHotkeys(); + ui->preview->Init(); /* hack to prevent elgato from loading its own QtNetwork that it tries * to ship with */ @@ -4924,6 +4951,9 @@ int OBSBasic::ResetVideo() obs_set_video_levels(sdr_white_level, hdr_nominal_peak_level); OBSBasicStats::InitializeValues(); OBSProjector::UpdateMultiviewProjectors(); + + emit CanvasResized(ovi.base_width, ovi.base_height); + emit OutputResized(ovi.output_width, ovi.output_height); } return ret; @@ -5013,8 +5043,10 @@ void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy) obs_get_video_info(&ovi); if (isFixedScaling) { - ui->preview->ClampScrollingOffsets(); previewScale = ui->preview->GetScalingAmount(); + + ui->preview->ClampScrollingOffsets(); + GetCenterPosFromFixedScale( int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2, @@ -5031,6 +5063,8 @@ void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy) previewX, previewY, previewScale); } + ui->preview->SetScalingAmount(previewScale); + previewX += float(PREVIEW_EDGE_SIZE); previewY += float(PREVIEW_EDGE_SIZE); } @@ -9169,7 +9203,7 @@ void OBSBasic::on_actionHorizontalCenter_triggered() void OBSBasic::EnablePreviewDisplay(bool enable) { obs_display_set_enabled(ui->preview->GetDisplay(), enable); - ui->preview->setVisible(enable); + ui->previewContainer->setVisible(enable); ui->previewDisabledWidget->setVisible(!enable); } @@ -9765,6 +9799,7 @@ void OBSBasic::on_actionScaleWindow_triggered() { ui->preview->SetFixedScaling(false); ui->preview->ResetScrollingOffset(); + emit ui->preview->DisplayResized(); } @@ -9772,6 +9807,7 @@ void OBSBasic::on_actionScaleCanvas_triggered() { ui->preview->SetFixedScaling(true); ui->preview->SetScalingLevel(0); + emit ui->preview->DisplayResized(); } @@ -9785,8 +9821,8 @@ void OBSBasic::on_actionScaleOutput_triggered() // log base ZOOM_SENSITIVITY of x = log(x) / log(ZOOM_SENSITIVITY) int32_t approxScalingLevel = int32_t(round(log(scalingAmount) / log(ZOOM_SENSITIVITY))); - ui->preview->SetScalingLevel(approxScalingLevel); - ui->preview->SetScalingAmount(scalingAmount); + ui->preview->SetScalingLevelAndAmount(approxScalingLevel, + scalingAmount); emit ui->preview->DisplayResized(); } @@ -11093,3 +11129,36 @@ void OBSBasic::OnEvent(enum obs_frontend_event event) if (api) api->on_event(event); } + +void OBSBasic::UpdatePreviewScrollbars() +{ + if (!ui->preview->IsFixedScaling()) { + ui->previewXScrollBar->setRange(0, 0); + ui->previewYScrollBar->setRange(0, 0); + } +} + +void OBSBasic::on_previewXScrollBar_valueChanged(int value) +{ + emit PreviewXScrollBarMoved(value); +} + +void OBSBasic::on_previewYScrollBar_valueChanged(int value) +{ + emit PreviewYScrollBarMoved(value); +} + +void OBSBasic::PreviewScalingModeChanged(int value) +{ + switch (value) { + case 0: + on_actionScaleWindow_triggered(); + break; + case 1: + on_actionScaleCanvas_triggered(); + break; + case 2: + on_actionScaleOutput_triggered(); + break; + }; +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index de11ea29d06862..aaca4023784015 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -670,6 +670,7 @@ class OBSBasic : public OBSMainWindow { std::string lastReplay; void UpdatePreviewOverflowSettings(); + void UpdatePreviewScrollbars(); bool streamingStarting = false; @@ -810,6 +811,11 @@ private slots: void AudioMixerPasteFilters(); void SourcePasteFilters(OBSSource source, OBSSource dstSource); + void on_previewXScrollBar_valueChanged(int value); + void on_previewYScrollBar_valueChanged(int value); + + void PreviewScalingModeChanged(int value); + void ColorChange(); SourceTreeItem *GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem); @@ -1292,6 +1298,12 @@ public slots: /* Studio Mode signal */ void PreviewProgramModeChanged(bool enabled); + void CanvasResized(uint32_t width, uint32_t height); + void OutputResized(uint32_t width, uint32_t height); + + /* Preview signals */ + void PreviewXScrollBarMoved(int value); + void PreviewYScrollBarMoved(int value); private: std::unique_ptr ui; diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 848ce2ad8e2c9c..7d6ced49b97de8 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -39,6 +39,15 @@ OBSBasicPreview::~OBSBasicPreview() obs_leave_graphics(); } +void OBSBasicPreview::Init() +{ + OBSBasic *main = OBSBasic::Get(); + connect(main, &OBSBasic::PreviewXScrollBarMoved, this, + &OBSBasicPreview::XScrollBarMoved); + connect(main, &OBSBasic::PreviewYScrollBarMoved, this, + &OBSBasicPreview::YScrollBarMoved); +} + vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event) { OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); @@ -2327,7 +2336,21 @@ void OBSBasicPreview::SetScalingAmount(float newScalingAmountVal) { scrollingOffset.x *= newScalingAmountVal / scalingAmount; scrollingOffset.y *= newScalingAmountVal / scalingAmount; + + if (scalingAmount == newScalingAmountVal) + return; + scalingAmount = newScalingAmountVal; + emit scalingChanged(scalingAmount); +} + +void OBSBasicPreview::SetScalingLevelAndAmount(int32_t newScalingLevelVal, + float newScalingAmountVal) +{ + newScalingLevelVal = std::clamp(newScalingLevelVal, -MAX_SCALING_LEVEL, + MAX_SCALING_LEVEL); + scalingLevel = newScalingLevelVal; + SetScalingAmount(newScalingAmountVal); } OBSBasicPreview *OBSBasicPreview::Get() @@ -2689,4 +2712,65 @@ void OBSBasicPreview::ClampScrollingOffsets() scrollingOffset.x = std::clamp(scrollingOffset.x, -offset.x, offset.x); scrollingOffset.y = std::clamp(scrollingOffset.y, -offset.y, offset.y); + + UpdateXScrollBar(offset.x); + UpdateYScrollBar(offset.y); +} + +void OBSBasicPreview::XScrollBarMoved(int value) +{ + updatingXScrollBar = true; + scrollingOffset.x = float(-value); + + emit DisplayResized(); + updatingXScrollBar = false; +} + +void OBSBasicPreview::YScrollBarMoved(int value) +{ + updatingYScrollBar = true; + scrollingOffset.y = float(-value); + + emit DisplayResized(); + updatingYScrollBar = false; +} + +void OBSBasicPreview::UpdateXScrollBar(float cx) +{ + if (updatingXScrollBar) + return; + + OBSBasic *main = OBSBasic::Get(); + + if (!main->ui->previewXScrollBar->isVisible()) + return; + + main->ui->previewXScrollBar->setRange(int(-cx), int(cx)); + + QSize targetSize = GetPixelSize(this); + main->ui->previewXScrollBar->setPageStep(targetSize.width() / + std::min(scalingAmount, 1.0f)); + + QSignalBlocker sig(main->ui->previewXScrollBar); + main->ui->previewXScrollBar->setValue(int(-scrollingOffset.x)); +} + +void OBSBasicPreview::UpdateYScrollBar(float cy) +{ + if (updatingYScrollBar) + return; + + OBSBasic *main = OBSBasic::Get(); + + if (!main->ui->previewYScrollBar->isVisible()) + return; + + main->ui->previewYScrollBar->setRange(int(-cy), int(cy)); + + QSize targetSize = GetPixelSize(this); + main->ui->previewYScrollBar->setPageStep(targetSize.height() / + std::min(scalingAmount, 1.0f)); + + QSignalBlocker sig(main->ui->previewYScrollBar); + main->ui->previewYScrollBar->setValue(int(-scrollingOffset.y)); } diff --git a/UI/window-basic-preview.hpp b/UI/window-basic-preview.hpp index 132df402acbb29..719da894e5743c 100644 --- a/UI/window-basic-preview.hpp +++ b/UI/window-basic-preview.hpp @@ -8,6 +8,7 @@ #include #include "qt-display.hpp" #include "obs-app.hpp" +#include "preview-controls.hpp" class OBSBasic; class QMouseEvent; @@ -18,8 +19,8 @@ class QMouseEvent; #define ITEM_BOTTOM (1 << 3) #define ITEM_ROT (1 << 4) -#define MAX_SCALING_LEVEL 20 -#define MAX_SCALING_AMOUNT 10.0f +#define MAX_SCALING_LEVEL 32 +#define MAX_SCALING_AMOUNT 8.0f #define ZOOM_SENSITIVITY pow(MAX_SCALING_AMOUNT, 1.0f / MAX_SCALING_LEVEL) #define SPACER_LABEL_MARGIN 6.0f @@ -81,6 +82,8 @@ class OBSBasicPreview : public OBSQTDisplay { int32_t scalingLevel = 0; float scalingAmount = 1.0f; float groupRot = 0.0f; + bool updatingXScrollBar = false; + bool updatingYScrollBar = false; std::vector hoveredPreviewItems; std::vector selectedItems; @@ -124,11 +127,17 @@ class OBSBasicPreview : public OBSQTDisplay { OBSDataAutoRelease wrapper = nullptr; bool changed; +private slots: + void XScrollBarMoved(int value); + void YScrollBarMoved(int value); + public: OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags = Qt::WindowFlags()); ~OBSBasicPreview(); + void Init(); + static OBSBasicPreview *Get(); virtual void keyPressEvent(QKeyEvent *event) override; @@ -150,12 +159,18 @@ class OBSBasicPreview : public OBSQTDisplay { inline void SetFixedScaling(bool newFixedScalingVal) { + if (fixedScaling == newFixedScalingVal) + return; + fixedScaling = newFixedScalingVal; + emit fixedScalingChanged(fixedScaling); } inline bool IsFixedScaling() const { return fixedScaling; } void SetScalingLevel(int32_t newScalingLevelVal); void SetScalingAmount(float newScalingAmountVal); + void SetScalingLevelAndAmount(int32_t newScalingLevelVal, + float newScalingAmountVal); inline int32_t GetScalingLevel() const { return scalingLevel; } inline float GetScalingAmount() const { return scalingAmount; } @@ -198,4 +213,10 @@ class OBSBasicPreview : public OBSQTDisplay { void DrawSpacingHelpers(); void ClampScrollingOffsets(); + void UpdateXScrollBar(float cx); + void UpdateYScrollBar(float cy); + +signals: + void scalingChanged(float scalingAmount); + void fixedScalingChanged(bool isFixed); }; From 3d2654f71bfc2b648d4c12fa917b60d56dc09cee Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 4 Jul 2023 17:47:16 +0200 Subject: [PATCH 0410/1073] UI: Add UUID to file-based list widgets List widgets are currently used as playlists in source properties, but only contain the file paths and no other identifying information. This can lead to files being added multiple times, so when changes to list order occurs, plugins cannot uniquely identify which duplicate item was actually changed (because they're only identified by the path). By adding a UUID to the user data role of a list item, an additional unique information is added that allows plugins to de-duplicate list items. --- shared/properties-view/properties-view.cpp | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/shared/properties-view/properties-view.cpp b/shared/properties-view/properties-view.cpp index ad861ea7ee52d3..c71cbaab801db2 100644 --- a/shared/properties-view/properties-view.cpp +++ b/shared/properties-view/properties-view.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "double-slider.hpp" #include "spinbox-ignorewheel.hpp" #include "properties-view.hpp" @@ -740,6 +741,14 @@ void OBSPropertiesView::AddEditableList(obs_property_t *prop, QListWidgetItem *const list_item = list->item((int)i); list_item->setSelected(obs_data_get_bool(item, "selected")); list_item->setHidden(obs_data_get_bool(item, "hidden")); + QString uuid = QT_UTF8(obs_data_get_string(item, "uuid")); + /* for backwards compatibility */ + if (uuid.isEmpty()) { + uuid = QUuid::createUuid().toString( + QUuid::WithoutBraces); + obs_data_set_string(item, "uuid", uuid.toUtf8()); + } + list_item->setData(Qt::UserRole, uuid); } WidgetInfo *info = new WidgetInfo(this, prop, list); @@ -2024,6 +2033,9 @@ void WidgetInfo::EditableListChanged() OBSDataAutoRelease arrayItem = obs_data_create(); obs_data_set_string(arrayItem, "value", QT_TO_UTF8(item->text())); + obs_data_set_string( + arrayItem, "uuid", + QT_TO_UTF8(item->data(Qt::UserRole).toString())); obs_data_set_bool(arrayItem, "selected", item->isSelected()); obs_data_set_bool(arrayItem, "hidden", item->isHidden()); obs_data_array_push_back(array, arrayItem); @@ -2293,7 +2305,11 @@ void WidgetInfo::EditListAddText() if (text.isEmpty()) return; - list->addItem(text); + QListWidgetItem *item = new QListWidgetItem(text); + item->setData(Qt::UserRole, + QUuid::createUuid().toString(QUuid::WithoutBraces)); + list->addItem(item); + EditableListChanged(); } @@ -2318,7 +2334,13 @@ void WidgetInfo::EditListAddFiles() if (files.count() == 0) return; - list->addItems(files); + for (QString file : files) { + QListWidgetItem *item = new QListWidgetItem(file); + item->setData(Qt::UserRole, QUuid::createUuid().toString( + QUuid::WithoutBraces)); + list->addItem(item); + } + EditableListChanged(); } @@ -2341,7 +2363,11 @@ void WidgetInfo::EditListAddDir() if (dir.isEmpty()) return; - list->addItem(dir); + QListWidgetItem *item = new QListWidgetItem(dir); + item->setData(Qt::UserRole, + QUuid::createUuid().toString(QUuid::WithoutBraces)); + list->addItem(item); + EditableListChanged(); } From d7adbf1e2436e4300e4b47b8a5f892cf68cb2ed6 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 19 Aug 2024 04:55:09 +0200 Subject: [PATCH 0411/1073] libobs: Add NULL check to encoder deprecation warning --- libobs/obs-encoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 841ff050b1c4c3..44c394a880aafe 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -123,7 +123,7 @@ create_encoder(const char *id, enum obs_encoder_type type, const char *name, blog(LOG_DEBUG, "encoder '%s' (%s) created", name, id); - if (ei->caps & OBS_ENCODER_CAP_DEPRECATED) { + if (ei && ei->caps & OBS_ENCODER_CAP_DEPRECATED) { blog(LOG_WARNING, "Encoder ID '%s' is deprecated and may be removed in a future version.", id); From 3312c2567d7785bdac02c31150b4223d83607b18 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 19 Aug 2024 04:57:57 +0200 Subject: [PATCH 0412/1073] obs-nvenc: Fix nvenc availability check always returning true --- plugins/obs-nvenc/nvenc-helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-nvenc/nvenc-helpers.c b/plugins/obs-nvenc/nvenc-helpers.c index a07d6ff274585e..455831e7dbf041 100644 --- a/plugins/obs-nvenc/nvenc-helpers.c +++ b/plugins/obs-nvenc/nvenc-helpers.c @@ -381,7 +381,7 @@ static bool nvenc_check(void) dstr_free(&caps_str); os_process_args_destroy(args); - return true; + return success; } static const char *nvenc_check_name = "nvenc_check"; From 2c57f4564cb1d52ca75ab698db2c172e2969eb6f Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 03:50:36 +0200 Subject: [PATCH 0413/1073] libobs: Switch to full reference counting for encoders Removes the "destroy_on_stop" hack that predates refcounting. Ensures outputs hold strong references to all their encoders. --- libobs/obs-encoder.c | 27 +-------------------------- libobs/obs-internal.h | 2 -- libobs/obs-output.c | 22 ++++++++++++++++------ 3 files changed, 17 insertions(+), 34 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 44c394a880aafe..dee7683dcde3fa 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -386,7 +386,7 @@ static inline void free_audio_buffers(struct obs_encoder *encoder) } } -static void obs_encoder_actually_destroy(obs_encoder_t *encoder) +void obs_encoder_destroy(obs_encoder_t *encoder) { if (encoder) { pthread_mutex_lock(&encoder->outputs_mutex); @@ -427,28 +427,6 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder) } } -/* does not actually destroy the encoder until all connections to it have been - * removed. (full reference counting really would have been superfluous) */ -void obs_encoder_destroy(obs_encoder_t *encoder) -{ - if (encoder) { - bool destroy; - - obs_context_data_remove(&encoder->context); - - pthread_mutex_lock(&encoder->init_mutex); - pthread_mutex_lock(&encoder->callbacks_mutex); - destroy = encoder->callbacks.num == 0; - if (!destroy) - encoder->destroy_on_stop = true; - pthread_mutex_unlock(&encoder->callbacks_mutex); - pthread_mutex_unlock(&encoder->init_mutex); - - if (destroy) - obs_encoder_actually_destroy(encoder); - } -} - const char *obs_encoder_get_name(const obs_encoder_t *encoder) { return obs_encoder_valid(encoder, "obs_encoder_get_name") @@ -823,9 +801,6 @@ void obs_encoder_stop(obs_encoder_t *encoder, struct obs_encoder_group *group = encoder->encoder_group; - if (encoder->destroy_on_stop) - obs_encoder_actually_destroy(encoder); - /* Destroying the group all the way back here prevents a race * where destruction of the group can prematurely destroy the * encoder within internal functions. This is the point where it diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 1e71d956f84fdf..8ee31c394f73cc 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -1326,8 +1326,6 @@ struct obs_encoder { pthread_mutex_t outputs_mutex; DARRAY(obs_output_t *) outputs; - bool destroy_on_stop; - /* stores the video/audio media output pointer. video_t *or audio_t **/ void *media; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 6394d7c1af4672..b900a7a088ac6e 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -290,6 +290,7 @@ void obs_output_destroy(obs_output_t *output) if (output->video_encoders[i]) { obs_encoder_remove_output( output->video_encoders[i], output); + obs_encoder_release(output->video_encoders[i]); } if (output->caption_tracks[i]) { destroy_caption_track( @@ -301,6 +302,7 @@ void obs_output_destroy(obs_output_t *output) if (output->audio_encoders[i]) { obs_encoder_remove_output( output->audio_encoders[i], output); + obs_encoder_release(output->audio_encoders[i]); } } @@ -970,14 +972,18 @@ void obs_output_remove_encoder_internal(struct obs_output *output, if (encoder->info.type == OBS_ENCODER_VIDEO) { for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { obs_encoder_t *video = output->video_encoders[i]; - if (video == encoder) + if (video == encoder) { output->video_encoders[i] = NULL; + obs_encoder_release(video); + } } } else if (encoder->info.type == OBS_ENCODER_AUDIO) { for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) { obs_encoder_t *audio = output->audio_encoders[i]; - if (audio == encoder) + if (audio == encoder) { output->audio_encoders[i] = NULL; + obs_encoder_release(audio); + } } } } @@ -1041,8 +1047,10 @@ void obs_output_set_video_encoder2(obs_output_t *output, obs_encoder_t *encoder, return; obs_encoder_remove_output(output->video_encoders[idx], output); - obs_encoder_add_output(encoder, output); - output->video_encoders[idx] = encoder; + obs_encoder_release(output->video_encoders[idx]); + + output->video_encoders[idx] = obs_encoder_get_ref(encoder); + obs_encoder_add_output(output->video_encoders[idx], output); destroy_caption_track(&output->caption_tracks[idx]); if (encoder != NULL) { @@ -1104,8 +1112,10 @@ void obs_output_set_audio_encoder(obs_output_t *output, obs_encoder_t *encoder, return; obs_encoder_remove_output(output->audio_encoders[idx], output); - obs_encoder_add_output(encoder, output); - output->audio_encoders[idx] = encoder; + obs_encoder_release(output->audio_encoders[idx]); + + output->audio_encoders[idx] = obs_encoder_get_ref(encoder); + obs_encoder_add_output(output->audio_encoders[idx], output); } obs_encoder_t *obs_output_get_video_encoder2(const obs_output_t *output, From c422a336fcc7cdefc4fb853f79f54c5b89360b70 Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 12 Aug 2024 04:09:18 +0200 Subject: [PATCH 0414/1073] libobs: Use weak reference for paired encoders --- libobs/obs-encoder.c | 27 +++++++++++++++++++++------ libobs/obs-internal.h | 2 +- libobs/obs-output.c | 8 ++++++-- libobs/obs-video-gpu-encode.c | 20 ++++++++++++++------ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index dee7683dcde3fa..77698ae44a5c09 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -690,12 +690,17 @@ void obs_encoder_shutdown(obs_encoder_t *encoder) if (encoder->context.data) { encoder->info.destroy(encoder->context.data); encoder->context.data = NULL; - da_free(encoder->paired_encoders); encoder->first_received = false; encoder->offset_usec = 0; encoder->start_ts = 0; encoder->frame_rate_divisor_counter = 0; maybe_clear_encoder_core_video_mix(encoder); + + for (size_t i = 0; i < encoder->paired_encoders.num; i++) { + obs_weak_encoder_release( + encoder->paired_encoders.array[i]); + } + da_free(encoder->paired_encoders); } obs_encoder_set_last_error(encoder, NULL); pthread_mutex_unlock(&encoder->init_mutex); @@ -1427,7 +1432,6 @@ static void receive_video(void *param, struct video_data *frame) profile_start(receive_video_name); struct obs_encoder *encoder = param; - struct obs_encoder **paired = encoder->paired_encoders.array; struct encoder_frame enc_frame; if (encoder->encoder_group && !encoder->start_ts) { @@ -1442,10 +1446,18 @@ static void receive_video(void *param, struct video_data *frame) if (!encoder->first_received && encoder->paired_encoders.num) { for (size_t i = 0; i < encoder->paired_encoders.num; i++) { - if (!paired[i]->first_received || - paired[i]->first_raw_ts > frame->timestamp) { + obs_encoder_t *paired = obs_weak_encoder_get_encoder( + encoder->paired_encoders.array[i]); + if (!paired) + continue; + + if (!paired->first_received || + paired->first_raw_ts > frame->timestamp) { + obs_encoder_release(paired); goto wait_for_audio; } + + obs_encoder_release(paired); } } @@ -1535,8 +1547,10 @@ static bool buffer_audio(struct obs_encoder *encoder, struct audio_data *data) struct obs_encoder *paired_encoder = NULL; /* Audio encoders can only be paired to one video encoder */ - if (encoder->paired_encoders.num) - paired_encoder = encoder->paired_encoders.array[0]; + if (encoder->paired_encoders.num) { + paired_encoder = obs_weak_encoder_get_encoder( + encoder->paired_encoders.array[0]); + } if (!encoder->start_ts && paired_encoder) { uint64_t end_ts = data->timestamp; @@ -1577,6 +1591,7 @@ static bool buffer_audio(struct obs_encoder *encoder, struct audio_data *data) fail: push_back_audio(encoder, data, size, offset_size); + obs_encoder_release(paired_encoder); profile_end(buffer_audio_name); return success; diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 8ee31c394f73cc..cc28381b3ebfe6 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -1315,7 +1315,7 @@ struct obs_encoder { * up at the specific timestamp. if this is the audio encoder, * it waits until it's ready to sync up with video */ bool first_received; - DARRAY(struct obs_encoder *) paired_encoders; + DARRAY(struct obs_weak_encoder *) paired_encoders; int64_t offset_usec; uint64_t first_raw_ts; uint64_t start_ts; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index b900a7a088ac6e..4266239b479aa6 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -2656,8 +2656,12 @@ static inline void pair_encoders(obs_output_t *output) pthread_mutex_lock(&audio->init_mutex); if (!audio->active && !audio->paired_encoders.num) { - da_push_back(video->paired_encoders, &audio); - da_push_back(audio->paired_encoders, &video); + obs_weak_encoder_t *weak_audio = + obs_encoder_get_weak_encoder(audio); + obs_weak_encoder_t *weak_video = + obs_encoder_get_weak_encoder(video); + da_push_back(video->paired_encoders, &weak_audio); + da_push_back(audio->paired_encoders, &weak_video); } pthread_mutex_unlock(&audio->init_mutex); } diff --git a/libobs/obs-video-gpu-encode.c b/libobs/obs-video-gpu-encode.c index 96bd081ad8ba18..bb42be80cdf871 100644 --- a/libobs/obs-video-gpu-encode.c +++ b/libobs/obs-video-gpu-encode.c @@ -82,7 +82,7 @@ static void *gpu_encode_thread(void *data) uint32_t skip = 0; obs_encoder_t *encoder = encoders.array[i]; - struct obs_encoder **paired = + obs_weak_encoder_t **paired = encoder->paired_encoders.array; size_t num_paired = encoder->paired_encoders.num; @@ -105,13 +105,21 @@ static void *gpu_encode_thread(void *data) if (!encoder->first_received && num_paired) { bool wait_for_audio = false; - for (size_t idx = 0; idx < num_paired; idx++) { - if (!paired[idx]->first_received || - paired[idx]->first_raw_ts > - timestamp) { + for (size_t idx = 0; + !wait_for_audio && idx < num_paired; + idx++) { + obs_encoder_t *enc = + obs_weak_encoder_get_encoder( + paired[idx]); + if (!enc) + continue; + + if (!enc->first_received || + enc->first_raw_ts > timestamp) { wait_for_audio = true; - break; } + + obs_encoder_release(enc); } if (wait_for_audio) From 117ee9cf449bc570b3fa2c89d84239e9157f3174 Mon Sep 17 00:00:00 2001 From: EBK21 Date: Fri, 26 Jul 2024 10:24:41 +0000 Subject: [PATCH 0415/1073] win-update: Use correct winhttp proxy type Legacy one does not provide correct result on some setups, use new one on 10+ . --- UI/win-update/updater/http.cpp | 2 +- UI/win-update/updater/updater.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/win-update/updater/http.cpp b/UI/win-update/updater/http.cpp index 5f81d81de9e6aa..63353b8cb6c822 100644 --- a/UI/win-update/updater/http.cpp +++ b/UI/win-update/updater/http.cpp @@ -76,7 +76,7 @@ bool HTTPPostData(const wchar_t *url, const BYTE *data, int dataLen, * connect to server */ hSession = WinHttpOpen(L"OBS Studio Updater/3.0", - WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) { diff --git a/UI/win-update/updater/updater.cpp b/UI/win-update/updater/updater.cpp index a18cb2141f2d6b..e505dd60ceadd3 100644 --- a/UI/win-update/updater/updater.cpp +++ b/UI/win-update/updater/updater.cpp @@ -379,7 +379,7 @@ bool DownloadWorkerThread() const DWORD compressionFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; HttpHandle hSession = WinHttpOpen(L"OBS Studio Updater/3.0", - WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) { @@ -1138,7 +1138,7 @@ static bool UpdateVSRedists() const DWORD compressionFlags = WINHTTP_DECOMPRESSION_FLAG_ALL; HttpHandle hSession = WinHttpOpen(L"OBS Studio Updater/3.0", - WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) { From c89cef3aa5033347dc82e623ccda38b5316a3a1a Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Sun, 11 Aug 2024 16:16:50 +0200 Subject: [PATCH 0416/1073] cmake: Fix malformed CMake package location on Windows CMake doesn't expect CMake package files to be separated in subdirectories on Windows and instead expects all files to be put in a single directory `cmake` found within one of the PREFIX paths. Also fixes circular dependency in w32-pthreads CMake package config file. --- cmake/common/helpers_common.cmake | 6 +++++- deps/w32-pthreads/cmake/w32-pthreadsConfig.cmake.in | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index 0f040b2f058eca..ade057e1d516a8 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -292,7 +292,11 @@ function(target_export target) set(package_destination "Frameworks/${target}.framework/Resources/cmake") set(include_destination "Frameworks/${target}.framework/Headers") else() - set(package_destination "${OBS_CMAKE_DESTINATION}/${target}") + if(OS_WINDOWS) + set(package_destination "${OBS_CMAKE_DESTINATION}") + else() + set(package_destination "${OBS_CMAKE_DESTINATION}/${target}") + endif() set(include_destination "${OBS_INCLUDE_DESTINATION}") endif() diff --git a/deps/w32-pthreads/cmake/w32-pthreadsConfig.cmake.in b/deps/w32-pthreads/cmake/w32-pthreadsConfig.cmake.in index 38bbde7b37fb35..ce5516a464b04a 100644 --- a/deps/w32-pthreads/cmake/w32-pthreadsConfig.cmake.in +++ b/deps/w32-pthreads/cmake/w32-pthreadsConfig.cmake.in @@ -1,4 +1,4 @@ @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@PROJECT_NAME@") +check_required_components("w32-pthreads") From f2b5a01a88f03184212e44de7fe059b31685a765 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 17 Aug 2024 20:08:09 +0200 Subject: [PATCH 0417/1073] cmake: Remove WITH_MESSAGE from obs-qsv11 --- plugins/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 67d842328ce107..11f3b165c94df6 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -68,7 +68,6 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) obs-qsv11 PLATFORMS WINDOWS LINUX ARCHITECTURES x64 x86_64 - WITH_MESSAGE ) add_obs_plugin(obs-text PLATFORMS WINDOWS) add_obs_plugin(obs-transitions) From 5bbb5e08c9d9ccb2dca0d4d90a724dd3b8bb48d2 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 2 Aug 2024 20:06:12 +0200 Subject: [PATCH 0418/1073] libobs: Add shared interface libraries to shared project directory The ComPtr, WinHandle, and obfuscate source files are shared with multiple sub-projects and thus need to be moved into the shared directory to uncouple their availability from libobs itself. The same applies to d3d8-api, inject-library, and hook-config (from win-capture), as well as comutils (from the virtualcam-module). --- shared/obs-d3d8-api/.clang-format | 3 + shared/obs-d3d8-api/CMakeLists.txt | 6 + shared/obs-d3d8-api/d3d8.h | 1279 +++++++++++++ shared/obs-d3d8-api/d3d8caps.h | 364 ++++ shared/obs-d3d8-api/d3d8types.h | 1690 +++++++++++++++++ shared/obs-hook-config/CMakeLists.txt | 6 + shared/obs-hook-config/graphics-hook-info.h | 143 ++ shared/obs-hook-config/graphics-hook-ver.h | 25 + shared/obs-hook-config/hook-helpers.h | 50 + shared/obs-inject-library/CMakeLists.txt | 11 + shared/obs-inject-library/inject-library.c | 149 ++ shared/obs-inject-library/inject-library.h | 20 + shared/obs-shared-memory-queue/CMakeLists.txt | 11 + .../shared-memory-queue.c | 225 +++ .../shared-memory-queue.h | 37 + shared/obs-tiny-nv12-scale/CMakeLists.txt | 6 + shared/obs-tiny-nv12-scale/tiny-nv12-scale.c | 207 ++ shared/obs-tiny-nv12-scale/tiny-nv12-scale.h | 34 + 18 files changed, 4266 insertions(+) create mode 100644 shared/obs-d3d8-api/.clang-format create mode 100644 shared/obs-d3d8-api/CMakeLists.txt create mode 100644 shared/obs-d3d8-api/d3d8.h create mode 100644 shared/obs-d3d8-api/d3d8caps.h create mode 100644 shared/obs-d3d8-api/d3d8types.h create mode 100644 shared/obs-hook-config/CMakeLists.txt create mode 100644 shared/obs-hook-config/graphics-hook-info.h create mode 100644 shared/obs-hook-config/graphics-hook-ver.h create mode 100644 shared/obs-hook-config/hook-helpers.h create mode 100644 shared/obs-inject-library/CMakeLists.txt create mode 100644 shared/obs-inject-library/inject-library.c create mode 100644 shared/obs-inject-library/inject-library.h create mode 100644 shared/obs-shared-memory-queue/CMakeLists.txt create mode 100644 shared/obs-shared-memory-queue/shared-memory-queue.c create mode 100644 shared/obs-shared-memory-queue/shared-memory-queue.h create mode 100644 shared/obs-tiny-nv12-scale/CMakeLists.txt create mode 100644 shared/obs-tiny-nv12-scale/tiny-nv12-scale.c create mode 100644 shared/obs-tiny-nv12-scale/tiny-nv12-scale.h diff --git a/shared/obs-d3d8-api/.clang-format b/shared/obs-d3d8-api/.clang-format new file mode 100644 index 00000000000000..6420a46881e054 --- /dev/null +++ b/shared/obs-d3d8-api/.clang-format @@ -0,0 +1,3 @@ +Language: Cpp +SortIncludes: false +DisableFormat: true diff --git a/shared/obs-d3d8-api/CMakeLists.txt b/shared/obs-d3d8-api/CMakeLists.txt new file mode 100644 index 00000000000000..35546f25537be6 --- /dev/null +++ b/shared/obs-d3d8-api/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +add_library(d3d8-api INTERFACE) +add_library(OBS::d3d8-api ALIAS d3d8-api) +target_sources(d3d8-api INTERFACE d3d8.h d3d8caps.h d3d8types.h) +target_include_directories(d3d8-api INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/obs-d3d8-api/d3d8.h b/shared/obs-d3d8-api/d3d8.h new file mode 100644 index 00000000000000..adf91ebf0b7ebd --- /dev/null +++ b/shared/obs-d3d8-api/d3d8.h @@ -0,0 +1,1279 @@ +/*==========================================================================; + * + * Copyright (C) Microsoft Corporation. All Rights Reserved. + * + * File: d3d8.h + * Content: Direct3D include file + * + ****************************************************************************/ + +#ifndef _D3D8_H_ +#define _D3D8_H_ + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0800 +#endif //DIRECT3D_VERSION + +// include this file content only if compiling for DX8 interfaces +#if(DIRECT3D_VERSION >= 0x0800) + + +/* This identifier is passed to Direct3DCreate8 in order to ensure that an + * application was built against the correct header files. This number is + * incremented whenever a header (or other) change would require applications + * to be rebuilt. If the version doesn't match, Direct3DCreate8 will fail. + * (The number itself has no meaning.)*/ + +#define D3D_SDK_VERSION 220 + + +#include + +#define COM_NO_WINDOWS_H +#include + +#include + +#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500) + #define HMONITOR_DECLARED + DECLARE_HANDLE(HMONITOR); +#endif + +#define D3DAPI WINAPI + +/* + * Interface IID's + */ +#if defined( _WIN32 ) && !defined( _NO_COM) + +/* IID_IDirect3D8 */ +/* {1DD9E8DA-1C77-4d40-B0CF-98FEFDFF9512} */ +DEFINE_GUID(IID_IDirect3D8, 0x1dd9e8da, 0x1c77, 0x4d40, 0xb0, 0xcf, 0x98, 0xfe, 0xfd, 0xff, 0x95, 0x12); + +/* IID_IDirect3DDevice8 */ +/* {7385E5DF-8FE8-41D5-86B6-D7B48547B6CF} */ +DEFINE_GUID(IID_IDirect3DDevice8, 0x7385e5df, 0x8fe8, 0x41d5, 0x86, 0xb6, 0xd7, 0xb4, 0x85, 0x47, 0xb6, 0xcf); + +/* IID_IDirect3DResource8 */ +/* {1B36BB7B-09B7-410a-B445-7D1430D7B33F} */ +DEFINE_GUID(IID_IDirect3DResource8, 0x1b36bb7b, 0x9b7, 0x410a, 0xb4, 0x45, 0x7d, 0x14, 0x30, 0xd7, 0xb3, 0x3f); + +/* IID_IDirect3DBaseTexture8 */ +/* {B4211CFA-51B9-4a9f-AB78-DB99B2BB678E} */ +DEFINE_GUID(IID_IDirect3DBaseTexture8, 0xb4211cfa, 0x51b9, 0x4a9f, 0xab, 0x78, 0xdb, 0x99, 0xb2, 0xbb, 0x67, 0x8e); + +/* IID_IDirect3DTexture8 */ +/* {E4CDD575-2866-4f01-B12E-7EECE1EC9358} */ +DEFINE_GUID(IID_IDirect3DTexture8, 0xe4cdd575, 0x2866, 0x4f01, 0xb1, 0x2e, 0x7e, 0xec, 0xe1, 0xec, 0x93, 0x58); + +/* IID_IDirect3DCubeTexture8 */ +/* {3EE5B968-2ACA-4c34-8BB5-7E0C3D19B750} */ +DEFINE_GUID(IID_IDirect3DCubeTexture8, 0x3ee5b968, 0x2aca, 0x4c34, 0x8b, 0xb5, 0x7e, 0x0c, 0x3d, 0x19, 0xb7, 0x50); + +/* IID_IDirect3DVolumeTexture8 */ +/* {4B8AAAFA-140F-42ba-9131-597EAFAA2EAD} */ +DEFINE_GUID(IID_IDirect3DVolumeTexture8, 0x4b8aaafa, 0x140f, 0x42ba, 0x91, 0x31, 0x59, 0x7e, 0xaf, 0xaa, 0x2e, 0xad); + +/* IID_IDirect3DVertexBuffer8 */ +/* {8AEEEAC7-05F9-44d4-B591-000B0DF1CB95} */ +DEFINE_GUID(IID_IDirect3DVertexBuffer8, 0x8aeeeac7, 0x05f9, 0x44d4, 0xb5, 0x91, 0x00, 0x0b, 0x0d, 0xf1, 0xcb, 0x95); + +/* IID_IDirect3DIndexBuffer8 */ +/* {0E689C9A-053D-44a0-9D92-DB0E3D750F86} */ +DEFINE_GUID(IID_IDirect3DIndexBuffer8, 0x0e689c9a, 0x053d, 0x44a0, 0x9d, 0x92, 0xdb, 0x0e, 0x3d, 0x75, 0x0f, 0x86); + +/* IID_IDirect3DSurface8 */ +/* {B96EEBCA-B326-4ea5-882F-2FF5BAE021DD} */ +DEFINE_GUID(IID_IDirect3DSurface8, 0xb96eebca, 0xb326, 0x4ea5, 0x88, 0x2f, 0x2f, 0xf5, 0xba, 0xe0, 0x21, 0xdd); + +/* IID_IDirect3DVolume8 */ +/* {BD7349F5-14F1-42e4-9C79-972380DB40C0} */ +DEFINE_GUID(IID_IDirect3DVolume8, 0xbd7349f5, 0x14f1, 0x42e4, 0x9c, 0x79, 0x97, 0x23, 0x80, 0xdb, 0x40, 0xc0); + +/* IID_IDirect3DSwapChain8 */ +/* {928C088B-76B9-4C6B-A536-A590853876CD} */ +DEFINE_GUID(IID_IDirect3DSwapChain8, 0x928c088b, 0x76b9, 0x4c6b, 0xa5, 0x36, 0xa5, 0x90, 0x85, 0x38, 0x76, 0xcd); + +#endif + +#ifdef __cplusplus + +interface IDirect3D8; +interface IDirect3DDevice8; + +interface IDirect3DResource8; +interface IDirect3DBaseTexture8; +interface IDirect3DTexture8; +interface IDirect3DVolumeTexture8; +interface IDirect3DCubeTexture8; + +interface IDirect3DVertexBuffer8; +interface IDirect3DIndexBuffer8; + +interface IDirect3DSurface8; +interface IDirect3DVolume8; + +interface IDirect3DSwapChain8; + +#endif + + +typedef interface IDirect3D8 IDirect3D8; +typedef interface IDirect3DDevice8 IDirect3DDevice8; +typedef interface IDirect3DResource8 IDirect3DResource8; +typedef interface IDirect3DBaseTexture8 IDirect3DBaseTexture8; +typedef interface IDirect3DTexture8 IDirect3DTexture8; +typedef interface IDirect3DVolumeTexture8 IDirect3DVolumeTexture8; +typedef interface IDirect3DCubeTexture8 IDirect3DCubeTexture8; +typedef interface IDirect3DVertexBuffer8 IDirect3DVertexBuffer8; +typedef interface IDirect3DIndexBuffer8 IDirect3DIndexBuffer8; +typedef interface IDirect3DSurface8 IDirect3DSurface8; +typedef interface IDirect3DVolume8 IDirect3DVolume8; +typedef interface IDirect3DSwapChain8 IDirect3DSwapChain8; + +#include "d3d8types.h" +#include "d3d8caps.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * DLL Function for creating a Direct3D8 object. This object supports + * enumeration and allows the creation of Direct3DDevice8 objects. + * Pass the value of the constant D3D_SDK_VERSION to this function, so + * that the run-time can validate that your application was compiled + * against the right headers. + */ + +IDirect3D8 * WINAPI Direct3DCreate8(UINT SDKVersion); + + +/* + * Direct3D interfaces + */ + + + + + + +#undef INTERFACE +#define INTERFACE IDirect3D8 + +DECLARE_INTERFACE_(IDirect3D8, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3D8 methods ***/ + STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE; + STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE; + STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER8* pIdentifier) PURE; + STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter) PURE; + STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,UINT Mode,D3DDISPLAYMODE* pMode) PURE; + STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE; + STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE CheckType,D3DFORMAT DisplayFormat,D3DFORMAT BackBufferFormat,BOOL Windowed) PURE; + STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE; + STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType) PURE; + STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE; + STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS8* pCaps) PURE; + STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE; + STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice8** ppReturnedDeviceInterface) PURE; +}; + +typedef struct IDirect3D8 *LPDIRECT3D8, *PDIRECT3D8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3D8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3D8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3D8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3D8_RegisterSoftwareDevice(p,a) (p)->lpVtbl->RegisterSoftwareDevice(p,a) +#define IDirect3D8_GetAdapterCount(p) (p)->lpVtbl->GetAdapterCount(p) +#define IDirect3D8_GetAdapterIdentifier(p,a,b,c) (p)->lpVtbl->GetAdapterIdentifier(p,a,b,c) +#define IDirect3D8_GetAdapterModeCount(p,a) (p)->lpVtbl->GetAdapterModeCount(p,a) +#define IDirect3D8_EnumAdapterModes(p,a,b,c) (p)->lpVtbl->EnumAdapterModes(p,a,b,c) +#define IDirect3D8_GetAdapterDisplayMode(p,a,b) (p)->lpVtbl->GetAdapterDisplayMode(p,a,b) +#define IDirect3D8_CheckDeviceType(p,a,b,c,d,e) (p)->lpVtbl->CheckDeviceType(p,a,b,c,d,e) +#define IDirect3D8_CheckDeviceFormat(p,a,b,c,d,e,f) (p)->lpVtbl->CheckDeviceFormat(p,a,b,c,d,e,f) +#define IDirect3D8_CheckDeviceMultiSampleType(p,a,b,c,d,e) (p)->lpVtbl->CheckDeviceMultiSampleType(p,a,b,c,d,e) +#define IDirect3D8_CheckDepthStencilMatch(p,a,b,c,d,e) (p)->lpVtbl->CheckDepthStencilMatch(p,a,b,c,d,e) +#define IDirect3D8_GetDeviceCaps(p,a,b,c) (p)->lpVtbl->GetDeviceCaps(p,a,b,c) +#define IDirect3D8_GetAdapterMonitor(p,a) (p)->lpVtbl->GetAdapterMonitor(p,a) +#define IDirect3D8_CreateDevice(p,a,b,c,d,e,f) (p)->lpVtbl->CreateDevice(p,a,b,c,d,e,f) +#else +#define IDirect3D8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3D8_AddRef(p) (p)->AddRef() +#define IDirect3D8_Release(p) (p)->Release() +#define IDirect3D8_RegisterSoftwareDevice(p,a) (p)->RegisterSoftwareDevice(a) +#define IDirect3D8_GetAdapterCount(p) (p)->GetAdapterCount() +#define IDirect3D8_GetAdapterIdentifier(p,a,b,c) (p)->GetAdapterIdentifier(a,b,c) +#define IDirect3D8_GetAdapterModeCount(p,a) (p)->GetAdapterModeCount(a) +#define IDirect3D8_EnumAdapterModes(p,a,b,c) (p)->EnumAdapterModes(a,b,c) +#define IDirect3D8_GetAdapterDisplayMode(p,a,b) (p)->GetAdapterDisplayMode(a,b) +#define IDirect3D8_CheckDeviceType(p,a,b,c,d,e) (p)->CheckDeviceType(a,b,c,d,e) +#define IDirect3D8_CheckDeviceFormat(p,a,b,c,d,e,f) (p)->CheckDeviceFormat(a,b,c,d,e,f) +#define IDirect3D8_CheckDeviceMultiSampleType(p,a,b,c,d,e) (p)->CheckDeviceMultiSampleType(a,b,c,d,e) +#define IDirect3D8_CheckDepthStencilMatch(p,a,b,c,d,e) (p)->CheckDepthStencilMatch(a,b,c,d,e) +#define IDirect3D8_GetDeviceCaps(p,a,b,c) (p)->GetDeviceCaps(a,b,c) +#define IDirect3D8_GetAdapterMonitor(p,a) (p)->GetAdapterMonitor(a) +#define IDirect3D8_CreateDevice(p,a,b,c,d,e,f) (p)->CreateDevice(a,b,c,d,e,f) +#endif + + + + + + + + + + + + + + + + + + + +#undef INTERFACE +#define INTERFACE IDirect3DDevice8 + +DECLARE_INTERFACE_(IDirect3DDevice8, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DDevice8 methods ***/ + STDMETHOD(TestCooperativeLevel)(THIS) PURE; + STDMETHOD_(UINT, GetAvailableTextureMem)(THIS) PURE; + STDMETHOD(ResourceManagerDiscardBytes)(THIS_ DWORD Bytes) PURE; + STDMETHOD(GetDirect3D)(THIS_ IDirect3D8** ppD3D8) PURE; + STDMETHOD(GetDeviceCaps)(THIS_ D3DCAPS8* pCaps) PURE; + STDMETHOD(GetDisplayMode)(THIS_ D3DDISPLAYMODE* pMode) PURE; + STDMETHOD(GetCreationParameters)(THIS_ D3DDEVICE_CREATION_PARAMETERS *pParameters) PURE; + STDMETHOD(SetCursorProperties)(THIS_ UINT XHotSpot,UINT YHotSpot,IDirect3DSurface8* pCursorBitmap) PURE; + STDMETHOD_(void, SetCursorPosition)(THIS_ int X,int Y,DWORD Flags) PURE; + STDMETHOD_(BOOL, ShowCursor)(THIS_ BOOL bShow) PURE; + STDMETHOD(CreateAdditionalSwapChain)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DSwapChain8** pSwapChain) PURE; + STDMETHOD(Reset)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters) PURE; + STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) PURE; + STDMETHOD(GetBackBuffer)(THIS_ UINT BackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface8** ppBackBuffer) PURE; + STDMETHOD(GetRasterStatus)(THIS_ D3DRASTER_STATUS* pRasterStatus) PURE; + STDMETHOD_(void, SetGammaRamp)(THIS_ DWORD Flags,CONST D3DGAMMARAMP* pRamp) PURE; + STDMETHOD_(void, GetGammaRamp)(THIS_ D3DGAMMARAMP* pRamp) PURE; + STDMETHOD(CreateTexture)(THIS_ UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture8** ppTexture) PURE; + STDMETHOD(CreateVolumeTexture)(THIS_ UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture8** ppVolumeTexture) PURE; + STDMETHOD(CreateCubeTexture)(THIS_ UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture8** ppCubeTexture) PURE; + STDMETHOD(CreateVertexBuffer)(THIS_ UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer8** ppVertexBuffer) PURE; + STDMETHOD(CreateIndexBuffer)(THIS_ UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer8** ppIndexBuffer) PURE; + STDMETHOD(CreateRenderTarget)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,BOOL Lockable,IDirect3DSurface8** ppSurface) PURE; + STDMETHOD(CreateDepthStencilSurface)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,IDirect3DSurface8** ppSurface) PURE; + STDMETHOD(CreateImageSurface)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,IDirect3DSurface8** ppSurface) PURE; + STDMETHOD(CopyRects)(THIS_ IDirect3DSurface8* pSourceSurface,CONST RECT* pSourceRectsArray,UINT cRects,IDirect3DSurface8* pDestinationSurface,CONST POINT* pDestPointsArray) PURE; + STDMETHOD(UpdateTexture)(THIS_ IDirect3DBaseTexture8* pSourceTexture,IDirect3DBaseTexture8* pDestinationTexture) PURE; + STDMETHOD(GetFrontBuffer)(THIS_ IDirect3DSurface8* pDestSurface) PURE; + STDMETHOD(SetRenderTarget)(THIS_ IDirect3DSurface8* pRenderTarget,IDirect3DSurface8* pNewZStencil) PURE; + STDMETHOD(GetRenderTarget)(THIS_ IDirect3DSurface8** ppRenderTarget) PURE; + STDMETHOD(GetDepthStencilSurface)(THIS_ IDirect3DSurface8** ppZStencilSurface) PURE; + STDMETHOD(BeginScene)(THIS) PURE; + STDMETHOD(EndScene)(THIS) PURE; + STDMETHOD(Clear)(THIS_ DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) PURE; + STDMETHOD(SetTransform)(THIS_ D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) PURE; + STDMETHOD(GetTransform)(THIS_ D3DTRANSFORMSTATETYPE State,D3DMATRIX* pMatrix) PURE; + STDMETHOD(MultiplyTransform)(THIS_ D3DTRANSFORMSTATETYPE,CONST D3DMATRIX*) PURE; + STDMETHOD(SetViewport)(THIS_ CONST D3DVIEWPORT8* pViewport) PURE; + STDMETHOD(GetViewport)(THIS_ D3DVIEWPORT8* pViewport) PURE; + STDMETHOD(SetMaterial)(THIS_ CONST D3DMATERIAL8* pMaterial) PURE; + STDMETHOD(GetMaterial)(THIS_ D3DMATERIAL8* pMaterial) PURE; + STDMETHOD(SetLight)(THIS_ DWORD Index,CONST D3DLIGHT8*) PURE; + STDMETHOD(GetLight)(THIS_ DWORD Index,D3DLIGHT8*) PURE; + STDMETHOD(LightEnable)(THIS_ DWORD Index,BOOL Enable) PURE; + STDMETHOD(GetLightEnable)(THIS_ DWORD Index,BOOL* pEnable) PURE; + STDMETHOD(SetClipPlane)(THIS_ DWORD Index,CONST float* pPlane) PURE; + STDMETHOD(GetClipPlane)(THIS_ DWORD Index,float* pPlane) PURE; + STDMETHOD(SetRenderState)(THIS_ D3DRENDERSTATETYPE State,DWORD Value) PURE; + STDMETHOD(GetRenderState)(THIS_ D3DRENDERSTATETYPE State,DWORD* pValue) PURE; + STDMETHOD(BeginStateBlock)(THIS) PURE; + STDMETHOD(EndStateBlock)(THIS_ DWORD* pToken) PURE; + STDMETHOD(ApplyStateBlock)(THIS_ DWORD Token) PURE; + STDMETHOD(CaptureStateBlock)(THIS_ DWORD Token) PURE; + STDMETHOD(DeleteStateBlock)(THIS_ DWORD Token) PURE; + STDMETHOD(CreateStateBlock)(THIS_ D3DSTATEBLOCKTYPE Type,DWORD* pToken) PURE; + STDMETHOD(SetClipStatus)(THIS_ CONST D3DCLIPSTATUS8* pClipStatus) PURE; + STDMETHOD(GetClipStatus)(THIS_ D3DCLIPSTATUS8* pClipStatus) PURE; + STDMETHOD(GetTexture)(THIS_ DWORD Stage,IDirect3DBaseTexture8** ppTexture) PURE; + STDMETHOD(SetTexture)(THIS_ DWORD Stage,IDirect3DBaseTexture8* pTexture) PURE; + STDMETHOD(GetTextureStageState)(THIS_ DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD* pValue) PURE; + STDMETHOD(SetTextureStageState)(THIS_ DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) PURE; + STDMETHOD(ValidateDevice)(THIS_ DWORD* pNumPasses) PURE; + STDMETHOD(GetInfo)(THIS_ DWORD DevInfoID,void* pDevInfoStruct,DWORD DevInfoStructSize) PURE; + STDMETHOD(SetPaletteEntries)(THIS_ UINT PaletteNumber,CONST PALETTEENTRY* pEntries) PURE; + STDMETHOD(GetPaletteEntries)(THIS_ UINT PaletteNumber,PALETTEENTRY* pEntries) PURE; + STDMETHOD(SetCurrentTexturePalette)(THIS_ UINT PaletteNumber) PURE; + STDMETHOD(GetCurrentTexturePalette)(THIS_ UINT *PaletteNumber) PURE; + STDMETHOD(DrawPrimitive)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) PURE; + STDMETHOD(DrawIndexedPrimitive)(THIS_ D3DPRIMITIVETYPE,UINT minIndex,UINT NumVertices,UINT startIndex,UINT primCount) PURE; + STDMETHOD(DrawPrimitiveUP)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCount,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) PURE; + STDMETHOD(DrawIndexedPrimitiveUP)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertexIndices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) PURE; + STDMETHOD(ProcessVertices)(THIS_ UINT SrcStartIndex,UINT DestIndex,UINT VertexCount,IDirect3DVertexBuffer8* pDestBuffer,DWORD Flags) PURE; + STDMETHOD(CreateVertexShader)(THIS_ CONST DWORD* pDeclaration,CONST DWORD* pFunction,DWORD* pHandle,DWORD Usage) PURE; + STDMETHOD(SetVertexShader)(THIS_ DWORD Handle) PURE; + STDMETHOD(GetVertexShader)(THIS_ DWORD* pHandle) PURE; + STDMETHOD(DeleteVertexShader)(THIS_ DWORD Handle) PURE; + STDMETHOD(SetVertexShaderConstant)(THIS_ DWORD Register,CONST void* pConstantData,DWORD ConstantCount) PURE; + STDMETHOD(GetVertexShaderConstant)(THIS_ DWORD Register,void* pConstantData,DWORD ConstantCount) PURE; + STDMETHOD(GetVertexShaderDeclaration)(THIS_ DWORD Handle,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(GetVertexShaderFunction)(THIS_ DWORD Handle,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(SetStreamSource)(THIS_ UINT StreamNumber,IDirect3DVertexBuffer8* pStreamData,UINT Stride) PURE; + STDMETHOD(GetStreamSource)(THIS_ UINT StreamNumber,IDirect3DVertexBuffer8** ppStreamData,UINT* pStride) PURE; + STDMETHOD(SetIndices)(THIS_ IDirect3DIndexBuffer8* pIndexData,UINT BaseVertexIndex) PURE; + STDMETHOD(GetIndices)(THIS_ IDirect3DIndexBuffer8** ppIndexData,UINT* pBaseVertexIndex) PURE; + STDMETHOD(CreatePixelShader)(THIS_ CONST DWORD* pFunction,DWORD* pHandle) PURE; + STDMETHOD(SetPixelShader)(THIS_ DWORD Handle) PURE; + STDMETHOD(GetPixelShader)(THIS_ DWORD* pHandle) PURE; + STDMETHOD(DeletePixelShader)(THIS_ DWORD Handle) PURE; + STDMETHOD(SetPixelShaderConstant)(THIS_ DWORD Register,CONST void* pConstantData,DWORD ConstantCount) PURE; + STDMETHOD(GetPixelShaderConstant)(THIS_ DWORD Register,void* pConstantData,DWORD ConstantCount) PURE; + STDMETHOD(GetPixelShaderFunction)(THIS_ DWORD Handle,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(DrawRectPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DRECTPATCH_INFO* pRectPatchInfo) PURE; + STDMETHOD(DrawTriPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) PURE; + STDMETHOD(DeletePatch)(THIS_ UINT Handle) PURE; +}; + +typedef struct IDirect3DDevice8 *LPDIRECT3DDEVICE8, *PDIRECT3DDEVICE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DDevice8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DDevice8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DDevice8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DDevice8_TestCooperativeLevel(p) (p)->lpVtbl->TestCooperativeLevel(p) +#define IDirect3DDevice8_GetAvailableTextureMem(p) (p)->lpVtbl->GetAvailableTextureMem(p) +#define IDirect3DDevice8_ResourceManagerDiscardBytes(p,a) (p)->lpVtbl->ResourceManagerDiscardBytes(p,a) +#define IDirect3DDevice8_GetDirect3D(p,a) (p)->lpVtbl->GetDirect3D(p,a) +#define IDirect3DDevice8_GetDeviceCaps(p,a) (p)->lpVtbl->GetDeviceCaps(p,a) +#define IDirect3DDevice8_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a) +#define IDirect3DDevice8_GetCreationParameters(p,a) (p)->lpVtbl->GetCreationParameters(p,a) +#define IDirect3DDevice8_SetCursorProperties(p,a,b,c) (p)->lpVtbl->SetCursorProperties(p,a,b,c) +#define IDirect3DDevice8_SetCursorPosition(p,a,b,c) (p)->lpVtbl->SetCursorPosition(p,a,b,c) +#define IDirect3DDevice8_ShowCursor(p,a) (p)->lpVtbl->ShowCursor(p,a) +#define IDirect3DDevice8_CreateAdditionalSwapChain(p,a,b) (p)->lpVtbl->CreateAdditionalSwapChain(p,a,b) +#define IDirect3DDevice8_Reset(p,a) (p)->lpVtbl->Reset(p,a) +#define IDirect3DDevice8_Present(p,a,b,c,d) (p)->lpVtbl->Present(p,a,b,c,d) +#define IDirect3DDevice8_GetBackBuffer(p,a,b,c) (p)->lpVtbl->GetBackBuffer(p,a,b,c) +#define IDirect3DDevice8_GetRasterStatus(p,a) (p)->lpVtbl->GetRasterStatus(p,a) +#define IDirect3DDevice8_SetGammaRamp(p,a,b) (p)->lpVtbl->SetGammaRamp(p,a,b) +#define IDirect3DDevice8_GetGammaRamp(p,a) (p)->lpVtbl->GetGammaRamp(p,a) +#define IDirect3DDevice8_CreateTexture(p,a,b,c,d,e,f,g) (p)->lpVtbl->CreateTexture(p,a,b,c,d,e,f,g) +#define IDirect3DDevice8_CreateVolumeTexture(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->CreateVolumeTexture(p,a,b,c,d,e,f,g,h) +#define IDirect3DDevice8_CreateCubeTexture(p,a,b,c,d,e,f) (p)->lpVtbl->CreateCubeTexture(p,a,b,c,d,e,f) +#define IDirect3DDevice8_CreateVertexBuffer(p,a,b,c,d,e) (p)->lpVtbl->CreateVertexBuffer(p,a,b,c,d,e) +#define IDirect3DDevice8_CreateIndexBuffer(p,a,b,c,d,e) (p)->lpVtbl->CreateIndexBuffer(p,a,b,c,d,e) +#define IDirect3DDevice8_CreateRenderTarget(p,a,b,c,d,e,f) (p)->lpVtbl->CreateRenderTarget(p,a,b,c,d,e,f) +#define IDirect3DDevice8_CreateDepthStencilSurface(p,a,b,c,d,e) (p)->lpVtbl->CreateDepthStencilSurface(p,a,b,c,d,e) +#define IDirect3DDevice8_CreateImageSurface(p,a,b,c,d) (p)->lpVtbl->CreateImageSurface(p,a,b,c,d) +#define IDirect3DDevice8_CopyRects(p,a,b,c,d,e) (p)->lpVtbl->CopyRects(p,a,b,c,d,e) +#define IDirect3DDevice8_UpdateTexture(p,a,b) (p)->lpVtbl->UpdateTexture(p,a,b) +#define IDirect3DDevice8_GetFrontBuffer(p,a) (p)->lpVtbl->GetFrontBuffer(p,a) +#define IDirect3DDevice8_SetRenderTarget(p,a,b) (p)->lpVtbl->SetRenderTarget(p,a,b) +#define IDirect3DDevice8_GetRenderTarget(p,a) (p)->lpVtbl->GetRenderTarget(p,a) +#define IDirect3DDevice8_GetDepthStencilSurface(p,a) (p)->lpVtbl->GetDepthStencilSurface(p,a) +#define IDirect3DDevice8_BeginScene(p) (p)->lpVtbl->BeginScene(p) +#define IDirect3DDevice8_EndScene(p) (p)->lpVtbl->EndScene(p) +#define IDirect3DDevice8_Clear(p,a,b,c,d,e,f) (p)->lpVtbl->Clear(p,a,b,c,d,e,f) +#define IDirect3DDevice8_SetTransform(p,a,b) (p)->lpVtbl->SetTransform(p,a,b) +#define IDirect3DDevice8_GetTransform(p,a,b) (p)->lpVtbl->GetTransform(p,a,b) +#define IDirect3DDevice8_MultiplyTransform(p,a,b) (p)->lpVtbl->MultiplyTransform(p,a,b) +#define IDirect3DDevice8_SetViewport(p,a) (p)->lpVtbl->SetViewport(p,a) +#define IDirect3DDevice8_GetViewport(p,a) (p)->lpVtbl->GetViewport(p,a) +#define IDirect3DDevice8_SetMaterial(p,a) (p)->lpVtbl->SetMaterial(p,a) +#define IDirect3DDevice8_GetMaterial(p,a) (p)->lpVtbl->GetMaterial(p,a) +#define IDirect3DDevice8_SetLight(p,a,b) (p)->lpVtbl->SetLight(p,a,b) +#define IDirect3DDevice8_GetLight(p,a,b) (p)->lpVtbl->GetLight(p,a,b) +#define IDirect3DDevice8_LightEnable(p,a,b) (p)->lpVtbl->LightEnable(p,a,b) +#define IDirect3DDevice8_GetLightEnable(p,a,b) (p)->lpVtbl->GetLightEnable(p,a,b) +#define IDirect3DDevice8_SetClipPlane(p,a,b) (p)->lpVtbl->SetClipPlane(p,a,b) +#define IDirect3DDevice8_GetClipPlane(p,a,b) (p)->lpVtbl->GetClipPlane(p,a,b) +#define IDirect3DDevice8_SetRenderState(p,a,b) (p)->lpVtbl->SetRenderState(p,a,b) +#define IDirect3DDevice8_GetRenderState(p,a,b) (p)->lpVtbl->GetRenderState(p,a,b) +#define IDirect3DDevice8_BeginStateBlock(p) (p)->lpVtbl->BeginStateBlock(p) +#define IDirect3DDevice8_EndStateBlock(p,a) (p)->lpVtbl->EndStateBlock(p,a) +#define IDirect3DDevice8_ApplyStateBlock(p,a) (p)->lpVtbl->ApplyStateBlock(p,a) +#define IDirect3DDevice8_CaptureStateBlock(p,a) (p)->lpVtbl->CaptureStateBlock(p,a) +#define IDirect3DDevice8_DeleteStateBlock(p,a) (p)->lpVtbl->DeleteStateBlock(p,a) +#define IDirect3DDevice8_CreateStateBlock(p,a,b) (p)->lpVtbl->CreateStateBlock(p,a,b) +#define IDirect3DDevice8_SetClipStatus(p,a) (p)->lpVtbl->SetClipStatus(p,a) +#define IDirect3DDevice8_GetClipStatus(p,a) (p)->lpVtbl->GetClipStatus(p,a) +#define IDirect3DDevice8_GetTexture(p,a,b) (p)->lpVtbl->GetTexture(p,a,b) +#define IDirect3DDevice8_SetTexture(p,a,b) (p)->lpVtbl->SetTexture(p,a,b) +#define IDirect3DDevice8_GetTextureStageState(p,a,b,c) (p)->lpVtbl->GetTextureStageState(p,a,b,c) +#define IDirect3DDevice8_SetTextureStageState(p,a,b,c) (p)->lpVtbl->SetTextureStageState(p,a,b,c) +#define IDirect3DDevice8_ValidateDevice(p,a) (p)->lpVtbl->ValidateDevice(p,a) +#define IDirect3DDevice8_GetInfo(p,a,b,c) (p)->lpVtbl->GetInfo(p,a,b,c) +#define IDirect3DDevice8_SetPaletteEntries(p,a,b) (p)->lpVtbl->SetPaletteEntries(p,a,b) +#define IDirect3DDevice8_GetPaletteEntries(p,a,b) (p)->lpVtbl->GetPaletteEntries(p,a,b) +#define IDirect3DDevice8_SetCurrentTexturePalette(p,a) (p)->lpVtbl->SetCurrentTexturePalette(p,a) +#define IDirect3DDevice8_GetCurrentTexturePalette(p,a) (p)->lpVtbl->GetCurrentTexturePalette(p,a) +#define IDirect3DDevice8_DrawPrimitive(p,a,b,c) (p)->lpVtbl->DrawPrimitive(p,a,b,c) +#define IDirect3DDevice8_DrawIndexedPrimitive(p,a,b,c,d,e) (p)->lpVtbl->DrawIndexedPrimitive(p,a,b,c,d,e) +#define IDirect3DDevice8_DrawPrimitiveUP(p,a,b,c,d) (p)->lpVtbl->DrawPrimitiveUP(p,a,b,c,d) +#define IDirect3DDevice8_DrawIndexedPrimitiveUP(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->DrawIndexedPrimitiveUP(p,a,b,c,d,e,f,g,h) +#define IDirect3DDevice8_ProcessVertices(p,a,b,c,d,e) (p)->lpVtbl->ProcessVertices(p,a,b,c,d,e) +#define IDirect3DDevice8_CreateVertexShader(p,a,b,c,d) (p)->lpVtbl->CreateVertexShader(p,a,b,c,d) +#define IDirect3DDevice8_SetVertexShader(p,a) (p)->lpVtbl->SetVertexShader(p,a) +#define IDirect3DDevice8_GetVertexShader(p,a) (p)->lpVtbl->GetVertexShader(p,a) +#define IDirect3DDevice8_DeleteVertexShader(p,a) (p)->lpVtbl->DeleteVertexShader(p,a) +#define IDirect3DDevice8_SetVertexShaderConstant(p,a,b,c) (p)->lpVtbl->SetVertexShaderConstant(p,a,b,c) +#define IDirect3DDevice8_GetVertexShaderConstant(p,a,b,c) (p)->lpVtbl->GetVertexShaderConstant(p,a,b,c) +#define IDirect3DDevice8_GetVertexShaderDeclaration(p,a,b,c) (p)->lpVtbl->GetVertexShaderDeclaration(p,a,b,c) +#define IDirect3DDevice8_GetVertexShaderFunction(p,a,b,c) (p)->lpVtbl->GetVertexShaderFunction(p,a,b,c) +#define IDirect3DDevice8_SetStreamSource(p,a,b,c) (p)->lpVtbl->SetStreamSource(p,a,b,c) +#define IDirect3DDevice8_GetStreamSource(p,a,b,c) (p)->lpVtbl->GetStreamSource(p,a,b,c) +#define IDirect3DDevice8_SetIndices(p,a,b) (p)->lpVtbl->SetIndices(p,a,b) +#define IDirect3DDevice8_GetIndices(p,a,b) (p)->lpVtbl->GetIndices(p,a,b) +#define IDirect3DDevice8_CreatePixelShader(p,a,b) (p)->lpVtbl->CreatePixelShader(p,a,b) +#define IDirect3DDevice8_SetPixelShader(p,a) (p)->lpVtbl->SetPixelShader(p,a) +#define IDirect3DDevice8_GetPixelShader(p,a) (p)->lpVtbl->GetPixelShader(p,a) +#define IDirect3DDevice8_DeletePixelShader(p,a) (p)->lpVtbl->DeletePixelShader(p,a) +#define IDirect3DDevice8_SetPixelShaderConstant(p,a,b,c) (p)->lpVtbl->SetPixelShaderConstant(p,a,b,c) +#define IDirect3DDevice8_GetPixelShaderConstant(p,a,b,c) (p)->lpVtbl->GetPixelShaderConstant(p,a,b,c) +#define IDirect3DDevice8_GetPixelShaderFunction(p,a,b,c) (p)->lpVtbl->GetPixelShaderFunction(p,a,b,c) +#define IDirect3DDevice8_DrawRectPatch(p,a,b,c) (p)->lpVtbl->DrawRectPatch(p,a,b,c) +#define IDirect3DDevice8_DrawTriPatch(p,a,b,c) (p)->lpVtbl->DrawTriPatch(p,a,b,c) +#define IDirect3DDevice8_DeletePatch(p,a) (p)->lpVtbl->DeletePatch(p,a) +#else +#define IDirect3DDevice8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DDevice8_AddRef(p) (p)->AddRef() +#define IDirect3DDevice8_Release(p) (p)->Release() +#define IDirect3DDevice8_TestCooperativeLevel(p) (p)->TestCooperativeLevel() +#define IDirect3DDevice8_GetAvailableTextureMem(p) (p)->GetAvailableTextureMem() +#define IDirect3DDevice8_ResourceManagerDiscardBytes(p,a) (p)->ResourceManagerDiscardBytes(a) +#define IDirect3DDevice8_GetDirect3D(p,a) (p)->GetDirect3D(a) +#define IDirect3DDevice8_GetDeviceCaps(p,a) (p)->GetDeviceCaps(a) +#define IDirect3DDevice8_GetDisplayMode(p,a) (p)->GetDisplayMode(a) +#define IDirect3DDevice8_GetCreationParameters(p,a) (p)->GetCreationParameters(a) +#define IDirect3DDevice8_SetCursorProperties(p,a,b,c) (p)->SetCursorProperties(a,b,c) +#define IDirect3DDevice8_SetCursorPosition(p,a,b,c) (p)->SetCursorPosition(a,b,c) +#define IDirect3DDevice8_ShowCursor(p,a) (p)->ShowCursor(a) +#define IDirect3DDevice8_CreateAdditionalSwapChain(p,a,b) (p)->CreateAdditionalSwapChain(a,b) +#define IDirect3DDevice8_Reset(p,a) (p)->Reset(a) +#define IDirect3DDevice8_Present(p,a,b,c,d) (p)->Present(a,b,c,d) +#define IDirect3DDevice8_GetBackBuffer(p,a,b,c) (p)->GetBackBuffer(a,b,c) +#define IDirect3DDevice8_GetRasterStatus(p,a) (p)->GetRasterStatus(a) +#define IDirect3DDevice8_SetGammaRamp(p,a,b) (p)->SetGammaRamp(a,b) +#define IDirect3DDevice8_GetGammaRamp(p,a) (p)->GetGammaRamp(a) +#define IDirect3DDevice8_CreateTexture(p,a,b,c,d,e,f,g) (p)->CreateTexture(a,b,c,d,e,f,g) +#define IDirect3DDevice8_CreateVolumeTexture(p,a,b,c,d,e,f,g,h) (p)->CreateVolumeTexture(a,b,c,d,e,f,g,h) +#define IDirect3DDevice8_CreateCubeTexture(p,a,b,c,d,e,f) (p)->CreateCubeTexture(a,b,c,d,e,f) +#define IDirect3DDevice8_CreateVertexBuffer(p,a,b,c,d,e) (p)->CreateVertexBuffer(a,b,c,d,e) +#define IDirect3DDevice8_CreateIndexBuffer(p,a,b,c,d,e) (p)->CreateIndexBuffer(a,b,c,d,e) +#define IDirect3DDevice8_CreateRenderTarget(p,a,b,c,d,e,f) (p)->CreateRenderTarget(a,b,c,d,e,f) +#define IDirect3DDevice8_CreateDepthStencilSurface(p,a,b,c,d,e) (p)->CreateDepthStencilSurface(a,b,c,d,e) +#define IDirect3DDevice8_CreateImageSurface(p,a,b,c,d) (p)->CreateImageSurface(a,b,c,d) +#define IDirect3DDevice8_CopyRects(p,a,b,c,d,e) (p)->CopyRects(a,b,c,d,e) +#define IDirect3DDevice8_UpdateTexture(p,a,b) (p)->UpdateTexture(a,b) +#define IDirect3DDevice8_GetFrontBuffer(p,a) (p)->GetFrontBuffer(a) +#define IDirect3DDevice8_SetRenderTarget(p,a,b) (p)->SetRenderTarget(a,b) +#define IDirect3DDevice8_GetRenderTarget(p,a) (p)->GetRenderTarget(a) +#define IDirect3DDevice8_GetDepthStencilSurface(p,a) (p)->GetDepthStencilSurface(a) +#define IDirect3DDevice8_BeginScene(p) (p)->BeginScene() +#define IDirect3DDevice8_EndScene(p) (p)->EndScene() +#define IDirect3DDevice8_Clear(p,a,b,c,d,e,f) (p)->Clear(a,b,c,d,e,f) +#define IDirect3DDevice8_SetTransform(p,a,b) (p)->SetTransform(a,b) +#define IDirect3DDevice8_GetTransform(p,a,b) (p)->GetTransform(a,b) +#define IDirect3DDevice8_MultiplyTransform(p,a,b) (p)->MultiplyTransform(a,b) +#define IDirect3DDevice8_SetViewport(p,a) (p)->SetViewport(a) +#define IDirect3DDevice8_GetViewport(p,a) (p)->GetViewport(a) +#define IDirect3DDevice8_SetMaterial(p,a) (p)->SetMaterial(a) +#define IDirect3DDevice8_GetMaterial(p,a) (p)->GetMaterial(a) +#define IDirect3DDevice8_SetLight(p,a,b) (p)->SetLight(a,b) +#define IDirect3DDevice8_GetLight(p,a,b) (p)->GetLight(a,b) +#define IDirect3DDevice8_LightEnable(p,a,b) (p)->LightEnable(a,b) +#define IDirect3DDevice8_GetLightEnable(p,a,b) (p)->GetLightEnable(a,b) +#define IDirect3DDevice8_SetClipPlane(p,a,b) (p)->SetClipPlane(a,b) +#define IDirect3DDevice8_GetClipPlane(p,a,b) (p)->GetClipPlane(a,b) +#define IDirect3DDevice8_SetRenderState(p,a,b) (p)->SetRenderState(a,b) +#define IDirect3DDevice8_GetRenderState(p,a,b) (p)->GetRenderState(a,b) +#define IDirect3DDevice8_BeginStateBlock(p) (p)->BeginStateBlock() +#define IDirect3DDevice8_EndStateBlock(p,a) (p)->EndStateBlock(a) +#define IDirect3DDevice8_ApplyStateBlock(p,a) (p)->ApplyStateBlock(a) +#define IDirect3DDevice8_CaptureStateBlock(p,a) (p)->CaptureStateBlock(a) +#define IDirect3DDevice8_DeleteStateBlock(p,a) (p)->DeleteStateBlock(a) +#define IDirect3DDevice8_CreateStateBlock(p,a,b) (p)->CreateStateBlock(a,b) +#define IDirect3DDevice8_SetClipStatus(p,a) (p)->SetClipStatus(a) +#define IDirect3DDevice8_GetClipStatus(p,a) (p)->GetClipStatus(a) +#define IDirect3DDevice8_GetTexture(p,a,b) (p)->GetTexture(a,b) +#define IDirect3DDevice8_SetTexture(p,a,b) (p)->SetTexture(a,b) +#define IDirect3DDevice8_GetTextureStageState(p,a,b,c) (p)->GetTextureStageState(a,b,c) +#define IDirect3DDevice8_SetTextureStageState(p,a,b,c) (p)->SetTextureStageState(a,b,c) +#define IDirect3DDevice8_ValidateDevice(p,a) (p)->ValidateDevice(a) +#define IDirect3DDevice8_GetInfo(p,a,b,c) (p)->GetInfo(a,b,c) +#define IDirect3DDevice8_SetPaletteEntries(p,a,b) (p)->SetPaletteEntries(a,b) +#define IDirect3DDevice8_GetPaletteEntries(p,a,b) (p)->GetPaletteEntries(a,b) +#define IDirect3DDevice8_SetCurrentTexturePalette(p,a) (p)->SetCurrentTexturePalette(a) +#define IDirect3DDevice8_GetCurrentTexturePalette(p,a) (p)->GetCurrentTexturePalette(a) +#define IDirect3DDevice8_DrawPrimitive(p,a,b,c) (p)->DrawPrimitive(a,b,c) +#define IDirect3DDevice8_DrawIndexedPrimitive(p,a,b,c,d,e) (p)->DrawIndexedPrimitive(a,b,c,d,e) +#define IDirect3DDevice8_DrawPrimitiveUP(p,a,b,c,d) (p)->DrawPrimitiveUP(a,b,c,d) +#define IDirect3DDevice8_DrawIndexedPrimitiveUP(p,a,b,c,d,e,f,g,h) (p)->DrawIndexedPrimitiveUP(a,b,c,d,e,f,g,h) +#define IDirect3DDevice8_ProcessVertices(p,a,b,c,d,e) (p)->ProcessVertices(a,b,c,d,e) +#define IDirect3DDevice8_CreateVertexShader(p,a,b,c,d) (p)->CreateVertexShader(a,b,c,d) +#define IDirect3DDevice8_SetVertexShader(p,a) (p)->SetVertexShader(a) +#define IDirect3DDevice8_GetVertexShader(p,a) (p)->GetVertexShader(a) +#define IDirect3DDevice8_DeleteVertexShader(p,a) (p)->DeleteVertexShader(a) +#define IDirect3DDevice8_SetVertexShaderConstant(p,a,b,c) (p)->SetVertexShaderConstant(a,b,c) +#define IDirect3DDevice8_GetVertexShaderConstant(p,a,b,c) (p)->GetVertexShaderConstant(a,b,c) +#define IDirect3DDevice8_GetVertexShaderDeclaration(p,a,b,c) (p)->GetVertexShaderDeclaration(a,b,c) +#define IDirect3DDevice8_GetVertexShaderFunction(p,a,b,c) (p)->GetVertexShaderFunction(a,b,c) +#define IDirect3DDevice8_SetStreamSource(p,a,b,c) (p)->SetStreamSource(a,b,c) +#define IDirect3DDevice8_GetStreamSource(p,a,b,c) (p)->GetStreamSource(a,b,c) +#define IDirect3DDevice8_SetIndices(p,a,b) (p)->SetIndices(a,b) +#define IDirect3DDevice8_GetIndices(p,a,b) (p)->GetIndices(a,b) +#define IDirect3DDevice8_CreatePixelShader(p,a,b) (p)->CreatePixelShader(a,b) +#define IDirect3DDevice8_SetPixelShader(p,a) (p)->SetPixelShader(a) +#define IDirect3DDevice8_GetPixelShader(p,a) (p)->GetPixelShader(a) +#define IDirect3DDevice8_DeletePixelShader(p,a) (p)->DeletePixelShader(a) +#define IDirect3DDevice8_SetPixelShaderConstant(p,a,b,c) (p)->SetPixelShaderConstant(a,b,c) +#define IDirect3DDevice8_GetPixelShaderConstant(p,a,b,c) (p)->GetPixelShaderConstant(a,b,c) +#define IDirect3DDevice8_GetPixelShaderFunction(p,a,b,c) (p)->GetPixelShaderFunction(a,b,c) +#define IDirect3DDevice8_DrawRectPatch(p,a,b,c) (p)->DrawRectPatch(a,b,c) +#define IDirect3DDevice8_DrawTriPatch(p,a,b,c) (p)->DrawTriPatch(a,b,c) +#define IDirect3DDevice8_DeletePatch(p,a) (p)->DeletePatch(a) +#endif + + + +#undef INTERFACE +#define INTERFACE IDirect3DSwapChain8 + +DECLARE_INTERFACE_(IDirect3DSwapChain8, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DSwapChain8 methods ***/ + STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) PURE; + STDMETHOD(GetBackBuffer)(THIS_ UINT BackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface8** ppBackBuffer) PURE; +}; + +typedef struct IDirect3DSwapChain8 *LPDIRECT3DSWAPCHAIN8, *PDIRECT3DSWAPCHAIN8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DSwapChain8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DSwapChain8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DSwapChain8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DSwapChain8_Present(p,a,b,c,d) (p)->lpVtbl->Present(p,a,b,c,d) +#define IDirect3DSwapChain8_GetBackBuffer(p,a,b,c) (p)->lpVtbl->GetBackBuffer(p,a,b,c) +#else +#define IDirect3DSwapChain8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DSwapChain8_AddRef(p) (p)->AddRef() +#define IDirect3DSwapChain8_Release(p) (p)->Release() +#define IDirect3DSwapChain8_Present(p,a,b,c,d) (p)->Present(a,b,c,d) +#define IDirect3DSwapChain8_GetBackBuffer(p,a,b,c) (p)->GetBackBuffer(a,b,c) +#endif + + + +#undef INTERFACE +#define INTERFACE IDirect3DResource8 + +DECLARE_INTERFACE_(IDirect3DResource8, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DResource8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; +}; + +typedef struct IDirect3DResource8 *LPDIRECT3DRESOURCE8, *PDIRECT3DRESOURCE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DResource8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DResource8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DResource8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DResource8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DResource8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DResource8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DResource8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DResource8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DResource8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DResource8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DResource8_GetType(p) (p)->lpVtbl->GetType(p) +#else +#define IDirect3DResource8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DResource8_AddRef(p) (p)->AddRef() +#define IDirect3DResource8_Release(p) (p)->Release() +#define IDirect3DResource8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DResource8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DResource8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DResource8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DResource8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DResource8_GetPriority(p) (p)->GetPriority() +#define IDirect3DResource8_PreLoad(p) (p)->PreLoad() +#define IDirect3DResource8_GetType(p) (p)->GetType() +#endif + + + + +#undef INTERFACE +#define INTERFACE IDirect3DBaseTexture8 + +DECLARE_INTERFACE_(IDirect3DBaseTexture8, IDirect3DResource8) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DResource8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; + STDMETHOD_(DWORD, GetLOD)(THIS) PURE; + STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; +}; + +typedef struct IDirect3DBaseTexture8 *LPDIRECT3DBASETEXTURE8, *PDIRECT3DBASETEXTURE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DBaseTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DBaseTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DBaseTexture8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DBaseTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DBaseTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DBaseTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DBaseTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DBaseTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DBaseTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DBaseTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DBaseTexture8_GetType(p) (p)->lpVtbl->GetType(p) +#define IDirect3DBaseTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) +#define IDirect3DBaseTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) +#define IDirect3DBaseTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) +#else +#define IDirect3DBaseTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DBaseTexture8_AddRef(p) (p)->AddRef() +#define IDirect3DBaseTexture8_Release(p) (p)->Release() +#define IDirect3DBaseTexture8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DBaseTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DBaseTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DBaseTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DBaseTexture8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DBaseTexture8_GetPriority(p) (p)->GetPriority() +#define IDirect3DBaseTexture8_PreLoad(p) (p)->PreLoad() +#define IDirect3DBaseTexture8_GetType(p) (p)->GetType() +#define IDirect3DBaseTexture8_SetLOD(p,a) (p)->SetLOD(a) +#define IDirect3DBaseTexture8_GetLOD(p) (p)->GetLOD() +#define IDirect3DBaseTexture8_GetLevelCount(p) (p)->GetLevelCount() +#endif + + + + + +#undef INTERFACE +#define INTERFACE IDirect3DTexture8 + +DECLARE_INTERFACE_(IDirect3DTexture8, IDirect3DBaseTexture8) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DBaseTexture8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; + STDMETHOD_(DWORD, GetLOD)(THIS) PURE; + STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; + STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DSURFACE_DESC *pDesc) PURE; + STDMETHOD(GetSurfaceLevel)(THIS_ UINT Level,IDirect3DSurface8** ppSurfaceLevel) PURE; + STDMETHOD(LockRect)(THIS_ UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) PURE; + STDMETHOD(UnlockRect)(THIS_ UINT Level) PURE; + STDMETHOD(AddDirtyRect)(THIS_ CONST RECT* pDirtyRect) PURE; +}; + +typedef struct IDirect3DTexture8 *LPDIRECT3DTEXTURE8, *PDIRECT3DTEXTURE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DTexture8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DTexture8_GetType(p) (p)->lpVtbl->GetType(p) +#define IDirect3DTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) +#define IDirect3DTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) +#define IDirect3DTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) +#define IDirect3DTexture8_GetLevelDesc(p,a,b) (p)->lpVtbl->GetLevelDesc(p,a,b) +#define IDirect3DTexture8_GetSurfaceLevel(p,a,b) (p)->lpVtbl->GetSurfaceLevel(p,a,b) +#define IDirect3DTexture8_LockRect(p,a,b,c,d) (p)->lpVtbl->LockRect(p,a,b,c,d) +#define IDirect3DTexture8_UnlockRect(p,a) (p)->lpVtbl->UnlockRect(p,a) +#define IDirect3DTexture8_AddDirtyRect(p,a) (p)->lpVtbl->AddDirtyRect(p,a) +#else +#define IDirect3DTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DTexture8_AddRef(p) (p)->AddRef() +#define IDirect3DTexture8_Release(p) (p)->Release() +#define IDirect3DTexture8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DTexture8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DTexture8_GetPriority(p) (p)->GetPriority() +#define IDirect3DTexture8_PreLoad(p) (p)->PreLoad() +#define IDirect3DTexture8_GetType(p) (p)->GetType() +#define IDirect3DTexture8_SetLOD(p,a) (p)->SetLOD(a) +#define IDirect3DTexture8_GetLOD(p) (p)->GetLOD() +#define IDirect3DTexture8_GetLevelCount(p) (p)->GetLevelCount() +#define IDirect3DTexture8_GetLevelDesc(p,a,b) (p)->GetLevelDesc(a,b) +#define IDirect3DTexture8_GetSurfaceLevel(p,a,b) (p)->GetSurfaceLevel(a,b) +#define IDirect3DTexture8_LockRect(p,a,b,c,d) (p)->LockRect(a,b,c,d) +#define IDirect3DTexture8_UnlockRect(p,a) (p)->UnlockRect(a) +#define IDirect3DTexture8_AddDirtyRect(p,a) (p)->AddDirtyRect(a) +#endif + + + + + +#undef INTERFACE +#define INTERFACE IDirect3DVolumeTexture8 + +DECLARE_INTERFACE_(IDirect3DVolumeTexture8, IDirect3DBaseTexture8) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DBaseTexture8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; + STDMETHOD_(DWORD, GetLOD)(THIS) PURE; + STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; + STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DVOLUME_DESC *pDesc) PURE; + STDMETHOD(GetVolumeLevel)(THIS_ UINT Level,IDirect3DVolume8** ppVolumeLevel) PURE; + STDMETHOD(LockBox)(THIS_ UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) PURE; + STDMETHOD(UnlockBox)(THIS_ UINT Level) PURE; + STDMETHOD(AddDirtyBox)(THIS_ CONST D3DBOX* pDirtyBox) PURE; +}; + +typedef struct IDirect3DVolumeTexture8 *LPDIRECT3DVOLUMETEXTURE8, *PDIRECT3DVOLUMETEXTURE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DVolumeTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DVolumeTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DVolumeTexture8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DVolumeTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DVolumeTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DVolumeTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DVolumeTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DVolumeTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DVolumeTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DVolumeTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DVolumeTexture8_GetType(p) (p)->lpVtbl->GetType(p) +#define IDirect3DVolumeTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) +#define IDirect3DVolumeTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) +#define IDirect3DVolumeTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) +#define IDirect3DVolumeTexture8_GetLevelDesc(p,a,b) (p)->lpVtbl->GetLevelDesc(p,a,b) +#define IDirect3DVolumeTexture8_GetVolumeLevel(p,a,b) (p)->lpVtbl->GetVolumeLevel(p,a,b) +#define IDirect3DVolumeTexture8_LockBox(p,a,b,c,d) (p)->lpVtbl->LockBox(p,a,b,c,d) +#define IDirect3DVolumeTexture8_UnlockBox(p,a) (p)->lpVtbl->UnlockBox(p,a) +#define IDirect3DVolumeTexture8_AddDirtyBox(p,a) (p)->lpVtbl->AddDirtyBox(p,a) +#else +#define IDirect3DVolumeTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DVolumeTexture8_AddRef(p) (p)->AddRef() +#define IDirect3DVolumeTexture8_Release(p) (p)->Release() +#define IDirect3DVolumeTexture8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DVolumeTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DVolumeTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DVolumeTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DVolumeTexture8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DVolumeTexture8_GetPriority(p) (p)->GetPriority() +#define IDirect3DVolumeTexture8_PreLoad(p) (p)->PreLoad() +#define IDirect3DVolumeTexture8_GetType(p) (p)->GetType() +#define IDirect3DVolumeTexture8_SetLOD(p,a) (p)->SetLOD(a) +#define IDirect3DVolumeTexture8_GetLOD(p) (p)->GetLOD() +#define IDirect3DVolumeTexture8_GetLevelCount(p) (p)->GetLevelCount() +#define IDirect3DVolumeTexture8_GetLevelDesc(p,a,b) (p)->GetLevelDesc(a,b) +#define IDirect3DVolumeTexture8_GetVolumeLevel(p,a,b) (p)->GetVolumeLevel(a,b) +#define IDirect3DVolumeTexture8_LockBox(p,a,b,c,d) (p)->LockBox(a,b,c,d) +#define IDirect3DVolumeTexture8_UnlockBox(p,a) (p)->UnlockBox(a) +#define IDirect3DVolumeTexture8_AddDirtyBox(p,a) (p)->AddDirtyBox(a) +#endif + + + + + +#undef INTERFACE +#define INTERFACE IDirect3DCubeTexture8 + +DECLARE_INTERFACE_(IDirect3DCubeTexture8, IDirect3DBaseTexture8) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DBaseTexture8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; + STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; + STDMETHOD_(DWORD, GetLOD)(THIS) PURE; + STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; + STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DSURFACE_DESC *pDesc) PURE; + STDMETHOD(GetCubeMapSurface)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface8** ppCubeMapSurface) PURE; + STDMETHOD(LockRect)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) PURE; + STDMETHOD(UnlockRect)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level) PURE; + STDMETHOD(AddDirtyRect)(THIS_ D3DCUBEMAP_FACES FaceType,CONST RECT* pDirtyRect) PURE; +}; + +typedef struct IDirect3DCubeTexture8 *LPDIRECT3DCUBETEXTURE8, *PDIRECT3DCUBETEXTURE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DCubeTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DCubeTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DCubeTexture8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DCubeTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DCubeTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DCubeTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DCubeTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DCubeTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DCubeTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DCubeTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DCubeTexture8_GetType(p) (p)->lpVtbl->GetType(p) +#define IDirect3DCubeTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) +#define IDirect3DCubeTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) +#define IDirect3DCubeTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) +#define IDirect3DCubeTexture8_GetLevelDesc(p,a,b) (p)->lpVtbl->GetLevelDesc(p,a,b) +#define IDirect3DCubeTexture8_GetCubeMapSurface(p,a,b,c) (p)->lpVtbl->GetCubeMapSurface(p,a,b,c) +#define IDirect3DCubeTexture8_LockRect(p,a,b,c,d,e) (p)->lpVtbl->LockRect(p,a,b,c,d,e) +#define IDirect3DCubeTexture8_UnlockRect(p,a,b) (p)->lpVtbl->UnlockRect(p,a,b) +#define IDirect3DCubeTexture8_AddDirtyRect(p,a,b) (p)->lpVtbl->AddDirtyRect(p,a,b) +#else +#define IDirect3DCubeTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DCubeTexture8_AddRef(p) (p)->AddRef() +#define IDirect3DCubeTexture8_Release(p) (p)->Release() +#define IDirect3DCubeTexture8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DCubeTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DCubeTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DCubeTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DCubeTexture8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DCubeTexture8_GetPriority(p) (p)->GetPriority() +#define IDirect3DCubeTexture8_PreLoad(p) (p)->PreLoad() +#define IDirect3DCubeTexture8_GetType(p) (p)->GetType() +#define IDirect3DCubeTexture8_SetLOD(p,a) (p)->SetLOD(a) +#define IDirect3DCubeTexture8_GetLOD(p) (p)->GetLOD() +#define IDirect3DCubeTexture8_GetLevelCount(p) (p)->GetLevelCount() +#define IDirect3DCubeTexture8_GetLevelDesc(p,a,b) (p)->GetLevelDesc(a,b) +#define IDirect3DCubeTexture8_GetCubeMapSurface(p,a,b,c) (p)->GetCubeMapSurface(a,b,c) +#define IDirect3DCubeTexture8_LockRect(p,a,b,c,d,e) (p)->LockRect(a,b,c,d,e) +#define IDirect3DCubeTexture8_UnlockRect(p,a,b) (p)->UnlockRect(a,b) +#define IDirect3DCubeTexture8_AddDirtyRect(p,a,b) (p)->AddDirtyRect(a,b) +#endif + + + + +#undef INTERFACE +#define INTERFACE IDirect3DVertexBuffer8 + +DECLARE_INTERFACE_(IDirect3DVertexBuffer8, IDirect3DResource8) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DResource8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; + STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,BYTE** ppbData,DWORD Flags) PURE; + STDMETHOD(Unlock)(THIS) PURE; + STDMETHOD(GetDesc)(THIS_ D3DVERTEXBUFFER_DESC *pDesc) PURE; +}; + +typedef struct IDirect3DVertexBuffer8 *LPDIRECT3DVERTEXBUFFER8, *PDIRECT3DVERTEXBUFFER8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DVertexBuffer8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DVertexBuffer8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DVertexBuffer8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DVertexBuffer8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DVertexBuffer8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DVertexBuffer8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DVertexBuffer8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DVertexBuffer8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DVertexBuffer8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DVertexBuffer8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DVertexBuffer8_GetType(p) (p)->lpVtbl->GetType(p) +#define IDirect3DVertexBuffer8_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirect3DVertexBuffer8_Unlock(p) (p)->lpVtbl->Unlock(p) +#define IDirect3DVertexBuffer8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) +#else +#define IDirect3DVertexBuffer8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DVertexBuffer8_AddRef(p) (p)->AddRef() +#define IDirect3DVertexBuffer8_Release(p) (p)->Release() +#define IDirect3DVertexBuffer8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DVertexBuffer8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DVertexBuffer8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DVertexBuffer8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DVertexBuffer8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DVertexBuffer8_GetPriority(p) (p)->GetPriority() +#define IDirect3DVertexBuffer8_PreLoad(p) (p)->PreLoad() +#define IDirect3DVertexBuffer8_GetType(p) (p)->GetType() +#define IDirect3DVertexBuffer8_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirect3DVertexBuffer8_Unlock(p) (p)->Unlock() +#define IDirect3DVertexBuffer8_GetDesc(p,a) (p)->GetDesc(a) +#endif + + + + +#undef INTERFACE +#define INTERFACE IDirect3DIndexBuffer8 + +DECLARE_INTERFACE_(IDirect3DIndexBuffer8, IDirect3DResource8) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DResource8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; + STDMETHOD_(DWORD, GetPriority)(THIS) PURE; + STDMETHOD_(void, PreLoad)(THIS) PURE; + STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; + STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,BYTE** ppbData,DWORD Flags) PURE; + STDMETHOD(Unlock)(THIS) PURE; + STDMETHOD(GetDesc)(THIS_ D3DINDEXBUFFER_DESC *pDesc) PURE; +}; + +typedef struct IDirect3DIndexBuffer8 *LPDIRECT3DINDEXBUFFER8, *PDIRECT3DINDEXBUFFER8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DIndexBuffer8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DIndexBuffer8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DIndexBuffer8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DIndexBuffer8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DIndexBuffer8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DIndexBuffer8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DIndexBuffer8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DIndexBuffer8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) +#define IDirect3DIndexBuffer8_GetPriority(p) (p)->lpVtbl->GetPriority(p) +#define IDirect3DIndexBuffer8_PreLoad(p) (p)->lpVtbl->PreLoad(p) +#define IDirect3DIndexBuffer8_GetType(p) (p)->lpVtbl->GetType(p) +#define IDirect3DIndexBuffer8_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirect3DIndexBuffer8_Unlock(p) (p)->lpVtbl->Unlock(p) +#define IDirect3DIndexBuffer8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) +#else +#define IDirect3DIndexBuffer8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DIndexBuffer8_AddRef(p) (p)->AddRef() +#define IDirect3DIndexBuffer8_Release(p) (p)->Release() +#define IDirect3DIndexBuffer8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DIndexBuffer8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DIndexBuffer8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DIndexBuffer8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DIndexBuffer8_SetPriority(p,a) (p)->SetPriority(a) +#define IDirect3DIndexBuffer8_GetPriority(p) (p)->GetPriority() +#define IDirect3DIndexBuffer8_PreLoad(p) (p)->PreLoad() +#define IDirect3DIndexBuffer8_GetType(p) (p)->GetType() +#define IDirect3DIndexBuffer8_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirect3DIndexBuffer8_Unlock(p) (p)->Unlock() +#define IDirect3DIndexBuffer8_GetDesc(p,a) (p)->GetDesc(a) +#endif + + + + +#undef INTERFACE +#define INTERFACE IDirect3DSurface8 + +DECLARE_INTERFACE_(IDirect3DSurface8, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DSurface8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD(GetContainer)(THIS_ REFIID riid,void** ppContainer) PURE; + STDMETHOD(GetDesc)(THIS_ D3DSURFACE_DESC *pDesc) PURE; + STDMETHOD(LockRect)(THIS_ D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) PURE; + STDMETHOD(UnlockRect)(THIS) PURE; +}; + +typedef struct IDirect3DSurface8 *LPDIRECT3DSURFACE8, *PDIRECT3DSURFACE8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DSurface8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DSurface8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DSurface8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DSurface8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DSurface8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DSurface8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DSurface8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DSurface8_GetContainer(p,a,b) (p)->lpVtbl->GetContainer(p,a,b) +#define IDirect3DSurface8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) +#define IDirect3DSurface8_LockRect(p,a,b,c) (p)->lpVtbl->LockRect(p,a,b,c) +#define IDirect3DSurface8_UnlockRect(p) (p)->lpVtbl->UnlockRect(p) +#else +#define IDirect3DSurface8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DSurface8_AddRef(p) (p)->AddRef() +#define IDirect3DSurface8_Release(p) (p)->Release() +#define IDirect3DSurface8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DSurface8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DSurface8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DSurface8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DSurface8_GetContainer(p,a,b) (p)->GetContainer(a,b) +#define IDirect3DSurface8_GetDesc(p,a) (p)->GetDesc(a) +#define IDirect3DSurface8_LockRect(p,a,b,c) (p)->LockRect(a,b,c) +#define IDirect3DSurface8_UnlockRect(p) (p)->UnlockRect() +#endif + + + + +#undef INTERFACE +#define INTERFACE IDirect3DVolume8 + +DECLARE_INTERFACE_(IDirect3DVolume8, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirect3DVolume8 methods ***/ + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; + STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; + STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; + STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; + STDMETHOD(GetContainer)(THIS_ REFIID riid,void** ppContainer) PURE; + STDMETHOD(GetDesc)(THIS_ D3DVOLUME_DESC *pDesc) PURE; + STDMETHOD(LockBox)(THIS_ D3DLOCKED_BOX * pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) PURE; + STDMETHOD(UnlockBox)(THIS) PURE; +}; + +typedef struct IDirect3DVolume8 *LPDIRECT3DVOLUME8, *PDIRECT3DVOLUME8; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DVolume8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirect3DVolume8_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DVolume8_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DVolume8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) +#define IDirect3DVolume8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) +#define IDirect3DVolume8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) +#define IDirect3DVolume8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) +#define IDirect3DVolume8_GetContainer(p,a,b) (p)->lpVtbl->GetContainer(p,a,b) +#define IDirect3DVolume8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) +#define IDirect3DVolume8_LockBox(p,a,b,c) (p)->lpVtbl->LockBox(p,a,b,c) +#define IDirect3DVolume8_UnlockBox(p) (p)->lpVtbl->UnlockBox(p) +#else +#define IDirect3DVolume8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirect3DVolume8_AddRef(p) (p)->AddRef() +#define IDirect3DVolume8_Release(p) (p)->Release() +#define IDirect3DVolume8_GetDevice(p,a) (p)->GetDevice(a) +#define IDirect3DVolume8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) +#define IDirect3DVolume8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) +#define IDirect3DVolume8_FreePrivateData(p,a) (p)->FreePrivateData(a) +#define IDirect3DVolume8_GetContainer(p,a,b) (p)->GetContainer(a,b) +#define IDirect3DVolume8_GetDesc(p,a) (p)->GetDesc(a) +#define IDirect3DVolume8_LockBox(p,a,b,c) (p)->LockBox(a,b,c) +#define IDirect3DVolume8_UnlockBox(p) (p)->UnlockBox() +#endif + +/**************************************************************************** + * Flags for SetPrivateData method on all D3D8 interfaces + * + * The passed pointer is an IUnknown ptr. The SizeOfData argument to SetPrivateData + * must be set to sizeof(IUnknown*). Direct3D will call AddRef through this + * pointer and Release when the private data is destroyed. The data will be + * destroyed when another SetPrivateData with the same GUID is set, when + * FreePrivateData is called, or when the D3D8 object is freed. + ****************************************************************************/ +#define D3DSPD_IUNKNOWN 0x00000001L + +/**************************************************************************** + * + * Parameter for IDirect3D8 Enum and GetCaps8 functions to get the info for + * the current mode only. + * + ****************************************************************************/ + +#define D3DCURRENT_DISPLAY_MODE 0x00EFFFFFL + +/**************************************************************************** + * + * Flags for IDirect3D8::CreateDevice's BehaviorFlags + * + ****************************************************************************/ + +#define D3DCREATE_FPU_PRESERVE 0x00000002L +#define D3DCREATE_MULTITHREADED 0x00000004L + +#define D3DCREATE_PUREDEVICE 0x00000010L +#define D3DCREATE_SOFTWARE_VERTEXPROCESSING 0x00000020L +#define D3DCREATE_HARDWARE_VERTEXPROCESSING 0x00000040L +#define D3DCREATE_MIXED_VERTEXPROCESSING 0x00000080L + +#define D3DCREATE_DISABLE_DRIVER_MANAGEMENT 0x00000100L + + +/**************************************************************************** + * + * Parameter for IDirect3D8::CreateDevice's iAdapter + * + ****************************************************************************/ + +#define D3DADAPTER_DEFAULT 0 + +/**************************************************************************** + * + * Flags for IDirect3D8::EnumAdapters + * + ****************************************************************************/ + +#define D3DENUM_NO_WHQL_LEVEL 0x00000002L + +/**************************************************************************** + * + * Maximum number of back-buffers supported in DX8 + * + ****************************************************************************/ + +#define D3DPRESENT_BACK_BUFFERS_MAX 3L + +/**************************************************************************** + * + * Flags for IDirect3DDevice8::SetGammaRamp + * + ****************************************************************************/ + +#define D3DSGR_NO_CALIBRATION 0x00000000L +#define D3DSGR_CALIBRATE 0x00000001L + +/**************************************************************************** + * + * Flags for IDirect3DDevice8::SetCursorPosition + * + ****************************************************************************/ + +#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L + +/**************************************************************************** + * + * Flags for DrawPrimitive/DrawIndexedPrimitive + * Also valid for Begin/BeginIndexed + * Also valid for VertexBuffer::CreateVertexBuffer + ****************************************************************************/ + + +/* + * DirectDraw error codes + */ +#define _FACD3D 0x876 +#define MAKE_D3DHRESULT( code ) MAKE_HRESULT( 1, _FACD3D, code ) + +/* + * Direct3D Errors + */ +#define D3D_OK S_OK + +#define D3DERR_WRONGTEXTUREFORMAT MAKE_D3DHRESULT(2072) +#define D3DERR_UNSUPPORTEDCOLOROPERATION MAKE_D3DHRESULT(2073) +#define D3DERR_UNSUPPORTEDCOLORARG MAKE_D3DHRESULT(2074) +#define D3DERR_UNSUPPORTEDALPHAOPERATION MAKE_D3DHRESULT(2075) +#define D3DERR_UNSUPPORTEDALPHAARG MAKE_D3DHRESULT(2076) +#define D3DERR_TOOMANYOPERATIONS MAKE_D3DHRESULT(2077) +#define D3DERR_CONFLICTINGTEXTUREFILTER MAKE_D3DHRESULT(2078) +#define D3DERR_UNSUPPORTEDFACTORVALUE MAKE_D3DHRESULT(2079) +#define D3DERR_CONFLICTINGRENDERSTATE MAKE_D3DHRESULT(2081) +#define D3DERR_UNSUPPORTEDTEXTUREFILTER MAKE_D3DHRESULT(2082) +#define D3DERR_CONFLICTINGTEXTUREPALETTE MAKE_D3DHRESULT(2086) +#define D3DERR_DRIVERINTERNALERROR MAKE_D3DHRESULT(2087) + +#define D3DERR_NOTFOUND MAKE_D3DHRESULT(2150) +#define D3DERR_MOREDATA MAKE_D3DHRESULT(2151) +#define D3DERR_DEVICELOST MAKE_D3DHRESULT(2152) +#define D3DERR_DEVICENOTRESET MAKE_D3DHRESULT(2153) +#define D3DERR_NOTAVAILABLE MAKE_D3DHRESULT(2154) +#define D3DERR_OUTOFVIDEOMEMORY MAKE_D3DHRESULT(380) +#define D3DERR_INVALIDDEVICE MAKE_D3DHRESULT(2155) +#define D3DERR_INVALIDCALL MAKE_D3DHRESULT(2156) +#define D3DERR_DRIVERINVALIDCALL MAKE_D3DHRESULT(2157) + +#ifdef __cplusplus +}; +#endif + +#endif /* (DIRECT3D_VERSION >= 0x0800) */ +#endif /* _D3D_H_ */ + diff --git a/shared/obs-d3d8-api/d3d8caps.h b/shared/obs-d3d8-api/d3d8caps.h new file mode 100644 index 00000000000000..6af8e6c434c06b --- /dev/null +++ b/shared/obs-d3d8-api/d3d8caps.h @@ -0,0 +1,364 @@ +/*==========================================================================; + * + * Copyright (C) Microsoft Corporation. All Rights Reserved. + * + * File: d3d8caps.h + * Content: Direct3D capabilities include file + * + ***************************************************************************/ + +#ifndef _D3D8CAPS_H +#define _D3D8CAPS_H + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0800 +#endif //DIRECT3D_VERSION + +// include this file content only if compiling for DX8 interfaces +#if(DIRECT3D_VERSION >= 0x0800) + +#if defined(_X86_) || defined(_IA64_) +#pragma pack(4) +#endif + +typedef struct _D3DCAPS8 +{ + /* Device Info */ + D3DDEVTYPE DeviceType; + UINT AdapterOrdinal; + + /* Caps from DX7 Draw */ + DWORD Caps; + DWORD Caps2; + DWORD Caps3; + DWORD PresentationIntervals; + + /* Cursor Caps */ + DWORD CursorCaps; + + /* 3D Device Caps */ + DWORD DevCaps; + + DWORD PrimitiveMiscCaps; + DWORD RasterCaps; + DWORD ZCmpCaps; + DWORD SrcBlendCaps; + DWORD DestBlendCaps; + DWORD AlphaCmpCaps; + DWORD ShadeCaps; + DWORD TextureCaps; + DWORD TextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DTexture8's + DWORD CubeTextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DCubeTexture8's + DWORD VolumeTextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DVolumeTexture8's + DWORD TextureAddressCaps; // D3DPTADDRESSCAPS for IDirect3DTexture8's + DWORD VolumeTextureAddressCaps; // D3DPTADDRESSCAPS for IDirect3DVolumeTexture8's + + DWORD LineCaps; // D3DLINECAPS + + DWORD MaxTextureWidth, MaxTextureHeight; + DWORD MaxVolumeExtent; + + DWORD MaxTextureRepeat; + DWORD MaxTextureAspectRatio; + DWORD MaxAnisotropy; + float MaxVertexW; + + float GuardBandLeft; + float GuardBandTop; + float GuardBandRight; + float GuardBandBottom; + + float ExtentsAdjust; + DWORD StencilCaps; + + DWORD FVFCaps; + DWORD TextureOpCaps; + DWORD MaxTextureBlendStages; + DWORD MaxSimultaneousTextures; + + DWORD VertexProcessingCaps; + DWORD MaxActiveLights; + DWORD MaxUserClipPlanes; + DWORD MaxVertexBlendMatrices; + DWORD MaxVertexBlendMatrixIndex; + + float MaxPointSize; + + DWORD MaxPrimitiveCount; // max number of primitives per DrawPrimitive call + DWORD MaxVertexIndex; + DWORD MaxStreams; + DWORD MaxStreamStride; // max stride for SetStreamSource + + DWORD VertexShaderVersion; + DWORD MaxVertexShaderConst; // number of vertex shader constant registers + + DWORD PixelShaderVersion; + float MaxPixelShaderValue; // max value of pixel shader arithmetic component + +} D3DCAPS8; + +// +// BIT DEFINES FOR D3DCAPS8 DWORD MEMBERS +// + +// +// Caps +// +#define D3DCAPS_READ_SCANLINE 0x00020000L + +// +// Caps2 +// +#define D3DCAPS2_NO2DDURING3DSCENE 0x00000002L +#define D3DCAPS2_FULLSCREENGAMMA 0x00020000L +#define D3DCAPS2_CANRENDERWINDOWED 0x00080000L +#define D3DCAPS2_CANCALIBRATEGAMMA 0x00100000L +#define D3DCAPS2_RESERVED 0x02000000L +#define D3DCAPS2_CANMANAGERESOURCE 0x10000000L +#define D3DCAPS2_DYNAMICTEXTURES 0x20000000L + +// +// Caps3 +// +#define D3DCAPS3_RESERVED 0x8000001fL + +// Indicates that the device can respect the ALPHABLENDENABLE render state +// when fullscreen while using the FLIP or DISCARD swap effect. +// COPY and COPYVSYNC swap effects work whether or not this flag is set. +#define D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD 0x00000020L + +// +// PresentationIntervals +// +#define D3DPRESENT_INTERVAL_DEFAULT 0x00000000L +#define D3DPRESENT_INTERVAL_ONE 0x00000001L +#define D3DPRESENT_INTERVAL_TWO 0x00000002L +#define D3DPRESENT_INTERVAL_THREE 0x00000004L +#define D3DPRESENT_INTERVAL_FOUR 0x00000008L +#define D3DPRESENT_INTERVAL_IMMEDIATE 0x80000000L + +// +// CursorCaps +// +// Driver supports HW color cursor in at least hi-res modes(height >=400) +#define D3DCURSORCAPS_COLOR 0x00000001L +// Driver supports HW cursor also in low-res modes(height < 400) +#define D3DCURSORCAPS_LOWRES 0x00000002L + +// +// DevCaps +// +#define D3DDEVCAPS_EXECUTESYSTEMMEMORY 0x00000010L /* Device can use execute buffers from system memory */ +#define D3DDEVCAPS_EXECUTEVIDEOMEMORY 0x00000020L /* Device can use execute buffers from video memory */ +#define D3DDEVCAPS_TLVERTEXSYSTEMMEMORY 0x00000040L /* Device can use TL buffers from system memory */ +#define D3DDEVCAPS_TLVERTEXVIDEOMEMORY 0x00000080L /* Device can use TL buffers from video memory */ +#define D3DDEVCAPS_TEXTURESYSTEMMEMORY 0x00000100L /* Device can texture from system memory */ +#define D3DDEVCAPS_TEXTUREVIDEOMEMORY 0x00000200L /* Device can texture from device memory */ +#define D3DDEVCAPS_DRAWPRIMTLVERTEX 0x00000400L /* Device can draw TLVERTEX primitives */ +#define D3DDEVCAPS_CANRENDERAFTERFLIP 0x00000800L /* Device can render without waiting for flip to complete */ +#define D3DDEVCAPS_TEXTURENONLOCALVIDMEM 0x00001000L /* Device can texture from nonlocal video memory */ +#define D3DDEVCAPS_DRAWPRIMITIVES2 0x00002000L /* Device can support DrawPrimitives2 */ +#define D3DDEVCAPS_SEPARATETEXTUREMEMORIES 0x00004000L /* Device is texturing from separate memory pools */ +#define D3DDEVCAPS_DRAWPRIMITIVES2EX 0x00008000L /* Device can support Extended DrawPrimitives2 i.e. DX7 compliant driver*/ +#define D3DDEVCAPS_HWTRANSFORMANDLIGHT 0x00010000L /* Device can support transformation and lighting in hardware and DRAWPRIMITIVES2EX must be also */ +#define D3DDEVCAPS_CANBLTSYSTONONLOCAL 0x00020000L /* Device supports a Tex Blt from system memory to non-local vidmem */ +#define D3DDEVCAPS_HWRASTERIZATION 0x00080000L /* Device has HW acceleration for rasterization */ +#define D3DDEVCAPS_PUREDEVICE 0x00100000L /* Device supports D3DCREATE_PUREDEVICE */ +#define D3DDEVCAPS_QUINTICRTPATCHES 0x00200000L /* Device supports quintic Beziers and BSplines */ +#define D3DDEVCAPS_RTPATCHES 0x00400000L /* Device supports Rect and Tri patches */ +#define D3DDEVCAPS_RTPATCHHANDLEZERO 0x00800000L /* Indicates that RT Patches may be drawn efficiently using handle 0 */ +#define D3DDEVCAPS_NPATCHES 0x01000000L /* Device supports N-Patches */ + +// +// PrimitiveMiscCaps +// +#define D3DPMISCCAPS_MASKZ 0x00000002L +#define D3DPMISCCAPS_LINEPATTERNREP 0x00000004L +#define D3DPMISCCAPS_CULLNONE 0x00000010L +#define D3DPMISCCAPS_CULLCW 0x00000020L +#define D3DPMISCCAPS_CULLCCW 0x00000040L +#define D3DPMISCCAPS_COLORWRITEENABLE 0x00000080L +#define D3DPMISCCAPS_CLIPPLANESCALEDPOINTS 0x00000100L /* Device correctly clips scaled points to clip planes */ +#define D3DPMISCCAPS_CLIPTLVERTS 0x00000200L /* device will clip post-transformed vertex primitives */ +#define D3DPMISCCAPS_TSSARGTEMP 0x00000400L /* device supports D3DTA_TEMP for temporary register */ +#define D3DPMISCCAPS_BLENDOP 0x00000800L /* device supports D3DRS_BLENDOP */ +#define D3DPMISCCAPS_NULLREFERENCE 0x00001000L /* Reference Device that doesnt render */ + +// +// LineCaps +// +#define D3DLINECAPS_TEXTURE 0x00000001L +#define D3DLINECAPS_ZTEST 0x00000002L +#define D3DLINECAPS_BLEND 0x00000004L +#define D3DLINECAPS_ALPHACMP 0x00000008L +#define D3DLINECAPS_FOG 0x00000010L + +// +// RasterCaps +// +#define D3DPRASTERCAPS_DITHER 0x00000001L +#define D3DPRASTERCAPS_PAT 0x00000008L +#define D3DPRASTERCAPS_ZTEST 0x00000010L +#define D3DPRASTERCAPS_FOGVERTEX 0x00000080L +#define D3DPRASTERCAPS_FOGTABLE 0x00000100L +#define D3DPRASTERCAPS_ANTIALIASEDGES 0x00001000L +#define D3DPRASTERCAPS_MIPMAPLODBIAS 0x00002000L +#define D3DPRASTERCAPS_ZBIAS 0x00004000L +#define D3DPRASTERCAPS_ZBUFFERLESSHSR 0x00008000L +#define D3DPRASTERCAPS_FOGRANGE 0x00010000L +#define D3DPRASTERCAPS_ANISOTROPY 0x00020000L +#define D3DPRASTERCAPS_WBUFFER 0x00040000L +#define D3DPRASTERCAPS_WFOG 0x00100000L +#define D3DPRASTERCAPS_ZFOG 0x00200000L +#define D3DPRASTERCAPS_COLORPERSPECTIVE 0x00400000L /* Device iterates colors perspective correct */ +#define D3DPRASTERCAPS_STRETCHBLTMULTISAMPLE 0x00800000L + +// +// ZCmpCaps, AlphaCmpCaps +// +#define D3DPCMPCAPS_NEVER 0x00000001L +#define D3DPCMPCAPS_LESS 0x00000002L +#define D3DPCMPCAPS_EQUAL 0x00000004L +#define D3DPCMPCAPS_LESSEQUAL 0x00000008L +#define D3DPCMPCAPS_GREATER 0x00000010L +#define D3DPCMPCAPS_NOTEQUAL 0x00000020L +#define D3DPCMPCAPS_GREATEREQUAL 0x00000040L +#define D3DPCMPCAPS_ALWAYS 0x00000080L + +// +// SourceBlendCaps, DestBlendCaps +// +#define D3DPBLENDCAPS_ZERO 0x00000001L +#define D3DPBLENDCAPS_ONE 0x00000002L +#define D3DPBLENDCAPS_SRCCOLOR 0x00000004L +#define D3DPBLENDCAPS_INVSRCCOLOR 0x00000008L +#define D3DPBLENDCAPS_SRCALPHA 0x00000010L +#define D3DPBLENDCAPS_INVSRCALPHA 0x00000020L +#define D3DPBLENDCAPS_DESTALPHA 0x00000040L +#define D3DPBLENDCAPS_INVDESTALPHA 0x00000080L +#define D3DPBLENDCAPS_DESTCOLOR 0x00000100L +#define D3DPBLENDCAPS_INVDESTCOLOR 0x00000200L +#define D3DPBLENDCAPS_SRCALPHASAT 0x00000400L +#define D3DPBLENDCAPS_BOTHSRCALPHA 0x00000800L +#define D3DPBLENDCAPS_BOTHINVSRCALPHA 0x00001000L + +// +// ShadeCaps +// +#define D3DPSHADECAPS_COLORGOURAUDRGB 0x00000008L +#define D3DPSHADECAPS_SPECULARGOURAUDRGB 0x00000200L +#define D3DPSHADECAPS_ALPHAGOURAUDBLEND 0x00004000L +#define D3DPSHADECAPS_FOGGOURAUD 0x00080000L + +// +// TextureCaps +// +#define D3DPTEXTURECAPS_PERSPECTIVE 0x00000001L /* Perspective-correct texturing is supported */ +#define D3DPTEXTURECAPS_POW2 0x00000002L /* Power-of-2 texture dimensions are required - applies to non-Cube/Volume textures only. */ +#define D3DPTEXTURECAPS_ALPHA 0x00000004L /* Alpha in texture pixels is supported */ +#define D3DPTEXTURECAPS_SQUAREONLY 0x00000020L /* Only square textures are supported */ +#define D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE 0x00000040L /* Texture indices are not scaled by the texture size prior to interpolation */ +#define D3DPTEXTURECAPS_ALPHAPALETTE 0x00000080L /* Device can draw alpha from texture palettes */ +// Device can use non-POW2 textures if: +// 1) D3DTEXTURE_ADDRESS is set to CLAMP for this texture's stage +// 2) D3DRS_WRAP(N) is zero for this texture's coordinates +// 3) mip mapping is not enabled (use magnification filter only) +#define D3DPTEXTURECAPS_NONPOW2CONDITIONAL 0x00000100L +#define D3DPTEXTURECAPS_PROJECTED 0x00000400L /* Device can do D3DTTFF_PROJECTED */ +#define D3DPTEXTURECAPS_CUBEMAP 0x00000800L /* Device can do cubemap textures */ +#define D3DPTEXTURECAPS_VOLUMEMAP 0x00002000L /* Device can do volume textures */ +#define D3DPTEXTURECAPS_MIPMAP 0x00004000L /* Device can do mipmapped textures */ +#define D3DPTEXTURECAPS_MIPVOLUMEMAP 0x00008000L /* Device can do mipmapped volume textures */ +#define D3DPTEXTURECAPS_MIPCUBEMAP 0x00010000L /* Device can do mipmapped cube maps */ +#define D3DPTEXTURECAPS_CUBEMAP_POW2 0x00020000L /* Device requires that cubemaps be power-of-2 dimension */ +#define D3DPTEXTURECAPS_VOLUMEMAP_POW2 0x00040000L /* Device requires that volume maps be power-of-2 dimension */ + +// +// TextureFilterCaps +// +#define D3DPTFILTERCAPS_MINFPOINT 0x00000100L /* Min Filter */ +#define D3DPTFILTERCAPS_MINFLINEAR 0x00000200L +#define D3DPTFILTERCAPS_MINFANISOTROPIC 0x00000400L +#define D3DPTFILTERCAPS_MIPFPOINT 0x00010000L /* Mip Filter */ +#define D3DPTFILTERCAPS_MIPFLINEAR 0x00020000L +#define D3DPTFILTERCAPS_MAGFPOINT 0x01000000L /* Mag Filter */ +#define D3DPTFILTERCAPS_MAGFLINEAR 0x02000000L +#define D3DPTFILTERCAPS_MAGFANISOTROPIC 0x04000000L +#define D3DPTFILTERCAPS_MAGFAFLATCUBIC 0x08000000L +#define D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC 0x10000000L + +// +// TextureAddressCaps +// +#define D3DPTADDRESSCAPS_WRAP 0x00000001L +#define D3DPTADDRESSCAPS_MIRROR 0x00000002L +#define D3DPTADDRESSCAPS_CLAMP 0x00000004L +#define D3DPTADDRESSCAPS_BORDER 0x00000008L +#define D3DPTADDRESSCAPS_INDEPENDENTUV 0x00000010L +#define D3DPTADDRESSCAPS_MIRRORONCE 0x00000020L + +// +// StencilCaps +// +#define D3DSTENCILCAPS_KEEP 0x00000001L +#define D3DSTENCILCAPS_ZERO 0x00000002L +#define D3DSTENCILCAPS_REPLACE 0x00000004L +#define D3DSTENCILCAPS_INCRSAT 0x00000008L +#define D3DSTENCILCAPS_DECRSAT 0x00000010L +#define D3DSTENCILCAPS_INVERT 0x00000020L +#define D3DSTENCILCAPS_INCR 0x00000040L +#define D3DSTENCILCAPS_DECR 0x00000080L + +// +// TextureOpCaps +// +#define D3DTEXOPCAPS_DISABLE 0x00000001L +#define D3DTEXOPCAPS_SELECTARG1 0x00000002L +#define D3DTEXOPCAPS_SELECTARG2 0x00000004L +#define D3DTEXOPCAPS_MODULATE 0x00000008L +#define D3DTEXOPCAPS_MODULATE2X 0x00000010L +#define D3DTEXOPCAPS_MODULATE4X 0x00000020L +#define D3DTEXOPCAPS_ADD 0x00000040L +#define D3DTEXOPCAPS_ADDSIGNED 0x00000080L +#define D3DTEXOPCAPS_ADDSIGNED2X 0x00000100L +#define D3DTEXOPCAPS_SUBTRACT 0x00000200L +#define D3DTEXOPCAPS_ADDSMOOTH 0x00000400L +#define D3DTEXOPCAPS_BLENDDIFFUSEALPHA 0x00000800L +#define D3DTEXOPCAPS_BLENDTEXTUREALPHA 0x00001000L +#define D3DTEXOPCAPS_BLENDFACTORALPHA 0x00002000L +#define D3DTEXOPCAPS_BLENDTEXTUREALPHAPM 0x00004000L +#define D3DTEXOPCAPS_BLENDCURRENTALPHA 0x00008000L +#define D3DTEXOPCAPS_PREMODULATE 0x00010000L +#define D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR 0x00020000L +#define D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA 0x00040000L +#define D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR 0x00080000L +#define D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA 0x00100000L +#define D3DTEXOPCAPS_BUMPENVMAP 0x00200000L +#define D3DTEXOPCAPS_BUMPENVMAPLUMINANCE 0x00400000L +#define D3DTEXOPCAPS_DOTPRODUCT3 0x00800000L +#define D3DTEXOPCAPS_MULTIPLYADD 0x01000000L +#define D3DTEXOPCAPS_LERP 0x02000000L + +// +// FVFCaps +// +#define D3DFVFCAPS_TEXCOORDCOUNTMASK 0x0000ffffL /* mask for texture coordinate count field */ +#define D3DFVFCAPS_DONOTSTRIPELEMENTS 0x00080000L /* Device prefers that vertex elements not be stripped */ +#define D3DFVFCAPS_PSIZE 0x00100000L /* Device can receive point size */ + +// +// VertexProcessingCaps +// +#define D3DVTXPCAPS_TEXGEN 0x00000001L /* device can do texgen */ +#define D3DVTXPCAPS_MATERIALSOURCE7 0x00000002L /* device can do DX7-level colormaterialsource ops */ +#define D3DVTXPCAPS_DIRECTIONALLIGHTS 0x00000008L /* device can do directional lights */ +#define D3DVTXPCAPS_POSITIONALLIGHTS 0x00000010L /* device can do positional lights (includes point and spot) */ +#define D3DVTXPCAPS_LOCALVIEWER 0x00000020L /* device can do local viewer */ +#define D3DVTXPCAPS_TWEENING 0x00000040L /* device can do vertex tweening */ +#define D3DVTXPCAPS_NO_VSDT_UBYTE4 0x00000080L /* device does not support D3DVSDT_UBYTE4 */ + +#pragma pack() + +#endif /* (DIRECT3D_VERSION >= 0x0800) */ +#endif /* _D3D8CAPS_H_ */ + diff --git a/shared/obs-d3d8-api/d3d8types.h b/shared/obs-d3d8-api/d3d8types.h new file mode 100644 index 00000000000000..9774619e5a7d23 --- /dev/null +++ b/shared/obs-d3d8-api/d3d8types.h @@ -0,0 +1,1690 @@ +/*==========================================================================; + * + * Copyright (C) Microsoft Corporation. All Rights Reserved. + * + * File: d3d8types.h + * Content: Direct3D capabilities include file + * + ***************************************************************************/ + +#ifndef _D3D8TYPES_H_ +#define _D3D8TYPES_H_ + +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0800 +#endif //DIRECT3D_VERSION + +// include this file content only if compiling for DX8 interfaces +#if(DIRECT3D_VERSION >= 0x0800) + +#include + +#if _MSC_VER >= 1200 +#pragma warning(push) +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4201) // anonymous unions warning +#endif + +#if defined(_X86_) || defined(_IA64_) +#pragma pack(4) +#endif + +// D3DCOLOR is equivalent to D3DFMT_A8R8G8B8 +#ifndef D3DCOLOR_DEFINED +typedef DWORD D3DCOLOR; +#define D3DCOLOR_DEFINED +#endif + +// maps unsigned 8 bits/channel to D3DCOLOR +#define D3DCOLOR_ARGB(a,r,g,b) \ + ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) +#define D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b) +#define D3DCOLOR_XRGB(r,g,b) D3DCOLOR_ARGB(0xff,r,g,b) + +// maps floating point channels (0.f to 1.f range) to D3DCOLOR +#define D3DCOLOR_COLORVALUE(r,g,b,a) \ + D3DCOLOR_RGBA((DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f),(DWORD)((a)*255.f)) + + +#ifndef D3DVECTOR_DEFINED +typedef struct _D3DVECTOR { + float x; + float y; + float z; +} D3DVECTOR; +#define D3DVECTOR_DEFINED +#endif + +#ifndef D3DCOLORVALUE_DEFINED +typedef struct _D3DCOLORVALUE { + float r; + float g; + float b; + float a; +} D3DCOLORVALUE; +#define D3DCOLORVALUE_DEFINED +#endif + +#ifndef D3DRECT_DEFINED +typedef struct _D3DRECT { + LONG x1; + LONG y1; + LONG x2; + LONG y2; +} D3DRECT; +#define D3DRECT_DEFINED +#endif + +#ifndef D3DMATRIX_DEFINED +typedef struct _D3DMATRIX { + union { + struct { + float _11, _12, _13, _14; + float _21, _22, _23, _24; + float _31, _32, _33, _34; + float _41, _42, _43, _44; + + }; + float m[4][4]; + }; +} D3DMATRIX; +#define D3DMATRIX_DEFINED +#endif + +typedef struct _D3DVIEWPORT8 { + DWORD X; + DWORD Y; /* Viewport Top left */ + DWORD Width; + DWORD Height; /* Viewport Dimensions */ + float MinZ; /* Min/max of clip Volume */ + float MaxZ; +} D3DVIEWPORT8; + +/* + * Values for clip fields. + */ + +// Max number of user clipping planes, supported in D3D. +#define D3DMAXUSERCLIPPLANES 32 + +// These bits could be ORed together to use with D3DRS_CLIPPLANEENABLE +// +#define D3DCLIPPLANE0 (1 << 0) +#define D3DCLIPPLANE1 (1 << 1) +#define D3DCLIPPLANE2 (1 << 2) +#define D3DCLIPPLANE3 (1 << 3) +#define D3DCLIPPLANE4 (1 << 4) +#define D3DCLIPPLANE5 (1 << 5) + +// The following bits are used in the ClipUnion and ClipIntersection +// members of the D3DCLIPSTATUS8 +// + +#define D3DCS_LEFT 0x00000001L +#define D3DCS_RIGHT 0x00000002L +#define D3DCS_TOP 0x00000004L +#define D3DCS_BOTTOM 0x00000008L +#define D3DCS_FRONT 0x00000010L +#define D3DCS_BACK 0x00000020L +#define D3DCS_PLANE0 0x00000040L +#define D3DCS_PLANE1 0x00000080L +#define D3DCS_PLANE2 0x00000100L +#define D3DCS_PLANE3 0x00000200L +#define D3DCS_PLANE4 0x00000400L +#define D3DCS_PLANE5 0x00000800L + +#define D3DCS_ALL (D3DCS_LEFT | \ + D3DCS_RIGHT | \ + D3DCS_TOP | \ + D3DCS_BOTTOM | \ + D3DCS_FRONT | \ + D3DCS_BACK | \ + D3DCS_PLANE0 | \ + D3DCS_PLANE1 | \ + D3DCS_PLANE2 | \ + D3DCS_PLANE3 | \ + D3DCS_PLANE4 | \ + D3DCS_PLANE5) + +typedef struct _D3DCLIPSTATUS8 { + DWORD ClipUnion; + DWORD ClipIntersection; +} D3DCLIPSTATUS8; + +typedef struct _D3DMATERIAL8 { + D3DCOLORVALUE Diffuse; /* Diffuse color RGBA */ + D3DCOLORVALUE Ambient; /* Ambient color RGB */ + D3DCOLORVALUE Specular; /* Specular 'shininess' */ + D3DCOLORVALUE Emissive; /* Emissive color RGB */ + float Power; /* Sharpness if specular highlight */ +} D3DMATERIAL8; + +typedef enum _D3DLIGHTTYPE { + D3DLIGHT_POINT = 1, + D3DLIGHT_SPOT = 2, + D3DLIGHT_DIRECTIONAL = 3, + D3DLIGHT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DLIGHTTYPE; + +typedef struct _D3DLIGHT8 { + D3DLIGHTTYPE Type; /* Type of light source */ + D3DCOLORVALUE Diffuse; /* Diffuse color of light */ + D3DCOLORVALUE Specular; /* Specular color of light */ + D3DCOLORVALUE Ambient; /* Ambient color of light */ + D3DVECTOR Position; /* Position in world space */ + D3DVECTOR Direction; /* Direction in world space */ + float Range; /* Cutoff range */ + float Falloff; /* Falloff */ + float Attenuation0; /* Constant attenuation */ + float Attenuation1; /* Linear attenuation */ + float Attenuation2; /* Quadratic attenuation */ + float Theta; /* Inner angle of spotlight cone */ + float Phi; /* Outer angle of spotlight cone */ +} D3DLIGHT8; + +/* + * Options for clearing + */ +#define D3DCLEAR_TARGET 0x00000001l /* Clear target surface */ +#define D3DCLEAR_ZBUFFER 0x00000002l /* Clear target z buffer */ +#define D3DCLEAR_STENCIL 0x00000004l /* Clear stencil planes */ + +/* + * The following defines the rendering states + */ + +typedef enum _D3DSHADEMODE { + D3DSHADE_FLAT = 1, + D3DSHADE_GOURAUD = 2, + D3DSHADE_PHONG = 3, + D3DSHADE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DSHADEMODE; + +typedef enum _D3DFILLMODE { + D3DFILL_POINT = 1, + D3DFILL_WIREFRAME = 2, + D3DFILL_SOLID = 3, + D3DFILL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DFILLMODE; + +typedef struct _D3DLINEPATTERN { + WORD wRepeatFactor; + WORD wLinePattern; +} D3DLINEPATTERN; + +typedef enum _D3DBLEND { + D3DBLEND_ZERO = 1, + D3DBLEND_ONE = 2, + D3DBLEND_SRCCOLOR = 3, + D3DBLEND_INVSRCCOLOR = 4, + D3DBLEND_SRCALPHA = 5, + D3DBLEND_INVSRCALPHA = 6, + D3DBLEND_DESTALPHA = 7, + D3DBLEND_INVDESTALPHA = 8, + D3DBLEND_DESTCOLOR = 9, + D3DBLEND_INVDESTCOLOR = 10, + D3DBLEND_SRCALPHASAT = 11, + D3DBLEND_BOTHSRCALPHA = 12, + D3DBLEND_BOTHINVSRCALPHA = 13, + D3DBLEND_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DBLEND; + +typedef enum _D3DBLENDOP { + D3DBLENDOP_ADD = 1, + D3DBLENDOP_SUBTRACT = 2, + D3DBLENDOP_REVSUBTRACT = 3, + D3DBLENDOP_MIN = 4, + D3DBLENDOP_MAX = 5, + D3DBLENDOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DBLENDOP; + +typedef enum _D3DTEXTUREADDRESS { + D3DTADDRESS_WRAP = 1, + D3DTADDRESS_MIRROR = 2, + D3DTADDRESS_CLAMP = 3, + D3DTADDRESS_BORDER = 4, + D3DTADDRESS_MIRRORONCE = 5, + D3DTADDRESS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREADDRESS; + +typedef enum _D3DCULL { + D3DCULL_NONE = 1, + D3DCULL_CW = 2, + D3DCULL_CCW = 3, + D3DCULL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DCULL; + +typedef enum _D3DCMPFUNC { + D3DCMP_NEVER = 1, + D3DCMP_LESS = 2, + D3DCMP_EQUAL = 3, + D3DCMP_LESSEQUAL = 4, + D3DCMP_GREATER = 5, + D3DCMP_NOTEQUAL = 6, + D3DCMP_GREATEREQUAL = 7, + D3DCMP_ALWAYS = 8, + D3DCMP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DCMPFUNC; + +typedef enum _D3DSTENCILOP { + D3DSTENCILOP_KEEP = 1, + D3DSTENCILOP_ZERO = 2, + D3DSTENCILOP_REPLACE = 3, + D3DSTENCILOP_INCRSAT = 4, + D3DSTENCILOP_DECRSAT = 5, + D3DSTENCILOP_INVERT = 6, + D3DSTENCILOP_INCR = 7, + D3DSTENCILOP_DECR = 8, + D3DSTENCILOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DSTENCILOP; + +typedef enum _D3DFOGMODE { + D3DFOG_NONE = 0, + D3DFOG_EXP = 1, + D3DFOG_EXP2 = 2, + D3DFOG_LINEAR = 3, + D3DFOG_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DFOGMODE; + +typedef enum _D3DZBUFFERTYPE { + D3DZB_FALSE = 0, + D3DZB_TRUE = 1, // Z buffering + D3DZB_USEW = 2, // W buffering + D3DZB_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DZBUFFERTYPE; + +// Primitives supported by draw-primitive API +typedef enum _D3DPRIMITIVETYPE { + D3DPT_POINTLIST = 1, + D3DPT_LINELIST = 2, + D3DPT_LINESTRIP = 3, + D3DPT_TRIANGLELIST = 4, + D3DPT_TRIANGLESTRIP = 5, + D3DPT_TRIANGLEFAN = 6, + D3DPT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DPRIMITIVETYPE; + +typedef enum _D3DTRANSFORMSTATETYPE { + D3DTS_VIEW = 2, + D3DTS_PROJECTION = 3, + D3DTS_TEXTURE0 = 16, + D3DTS_TEXTURE1 = 17, + D3DTS_TEXTURE2 = 18, + D3DTS_TEXTURE3 = 19, + D3DTS_TEXTURE4 = 20, + D3DTS_TEXTURE5 = 21, + D3DTS_TEXTURE6 = 22, + D3DTS_TEXTURE7 = 23, + D3DTS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTRANSFORMSTATETYPE; + +#define D3DTS_WORLDMATRIX(index) (D3DTRANSFORMSTATETYPE)(index + 256) +#define D3DTS_WORLD D3DTS_WORLDMATRIX(0) +#define D3DTS_WORLD1 D3DTS_WORLDMATRIX(1) +#define D3DTS_WORLD2 D3DTS_WORLDMATRIX(2) +#define D3DTS_WORLD3 D3DTS_WORLDMATRIX(3) + +typedef enum _D3DRENDERSTATETYPE { + D3DRS_ZENABLE = 7, /* D3DZBUFFERTYPE (or TRUE/FALSE for legacy) */ + D3DRS_FILLMODE = 8, /* D3DFILLMODE */ + D3DRS_SHADEMODE = 9, /* D3DSHADEMODE */ + D3DRS_LINEPATTERN = 10, /* D3DLINEPATTERN */ + D3DRS_ZWRITEENABLE = 14, /* TRUE to enable z writes */ + D3DRS_ALPHATESTENABLE = 15, /* TRUE to enable alpha tests */ + D3DRS_LASTPIXEL = 16, /* TRUE for last-pixel on lines */ + D3DRS_SRCBLEND = 19, /* D3DBLEND */ + D3DRS_DESTBLEND = 20, /* D3DBLEND */ + D3DRS_CULLMODE = 22, /* D3DCULL */ + D3DRS_ZFUNC = 23, /* D3DCMPFUNC */ + D3DRS_ALPHAREF = 24, /* D3DFIXED */ + D3DRS_ALPHAFUNC = 25, /* D3DCMPFUNC */ + D3DRS_DITHERENABLE = 26, /* TRUE to enable dithering */ + D3DRS_ALPHABLENDENABLE = 27, /* TRUE to enable alpha blending */ + D3DRS_FOGENABLE = 28, /* TRUE to enable fog blending */ + D3DRS_SPECULARENABLE = 29, /* TRUE to enable specular */ + D3DRS_ZVISIBLE = 30, /* TRUE to enable z checking */ + D3DRS_FOGCOLOR = 34, /* D3DCOLOR */ + D3DRS_FOGTABLEMODE = 35, /* D3DFOGMODE */ + D3DRS_FOGSTART = 36, /* Fog start (for both vertex and pixel fog) */ + D3DRS_FOGEND = 37, /* Fog end */ + D3DRS_FOGDENSITY = 38, /* Fog density */ + D3DRS_EDGEANTIALIAS = 40, /* TRUE to enable edge antialiasing */ + D3DRS_ZBIAS = 47, /* LONG Z bias */ + D3DRS_RANGEFOGENABLE = 48, /* Enables range-based fog */ + D3DRS_STENCILENABLE = 52, /* BOOL enable/disable stenciling */ + D3DRS_STENCILFAIL = 53, /* D3DSTENCILOP to do if stencil test fails */ + D3DRS_STENCILZFAIL = 54, /* D3DSTENCILOP to do if stencil test passes and Z test fails */ + D3DRS_STENCILPASS = 55, /* D3DSTENCILOP to do if both stencil and Z tests pass */ + D3DRS_STENCILFUNC = 56, /* D3DCMPFUNC fn. Stencil Test passes if ((ref & mask) stencilfn (stencil & mask)) is true */ + D3DRS_STENCILREF = 57, /* Reference value used in stencil test */ + D3DRS_STENCILMASK = 58, /* Mask value used in stencil test */ + D3DRS_STENCILWRITEMASK = 59, /* Write mask applied to values written to stencil buffer */ + D3DRS_TEXTUREFACTOR = 60, /* D3DCOLOR used for multi-texture blend */ + D3DRS_WRAP0 = 128, /* wrap for 1st texture coord. set */ + D3DRS_WRAP1 = 129, /* wrap for 2nd texture coord. set */ + D3DRS_WRAP2 = 130, /* wrap for 3rd texture coord. set */ + D3DRS_WRAP3 = 131, /* wrap for 4th texture coord. set */ + D3DRS_WRAP4 = 132, /* wrap for 5th texture coord. set */ + D3DRS_WRAP5 = 133, /* wrap for 6th texture coord. set */ + D3DRS_WRAP6 = 134, /* wrap for 7th texture coord. set */ + D3DRS_WRAP7 = 135, /* wrap for 8th texture coord. set */ + D3DRS_CLIPPING = 136, + D3DRS_LIGHTING = 137, + D3DRS_AMBIENT = 139, + D3DRS_FOGVERTEXMODE = 140, + D3DRS_COLORVERTEX = 141, + D3DRS_LOCALVIEWER = 142, + D3DRS_NORMALIZENORMALS = 143, + D3DRS_DIFFUSEMATERIALSOURCE = 145, + D3DRS_SPECULARMATERIALSOURCE = 146, + D3DRS_AMBIENTMATERIALSOURCE = 147, + D3DRS_EMISSIVEMATERIALSOURCE = 148, + D3DRS_VERTEXBLEND = 151, + D3DRS_CLIPPLANEENABLE = 152, + D3DRS_SOFTWAREVERTEXPROCESSING = 153, + D3DRS_POINTSIZE = 154, /* float point size */ + D3DRS_POINTSIZE_MIN = 155, /* float point size min threshold */ + D3DRS_POINTSPRITEENABLE = 156, /* BOOL point texture coord control */ + D3DRS_POINTSCALEENABLE = 157, /* BOOL point size scale enable */ + D3DRS_POINTSCALE_A = 158, /* float point attenuation A value */ + D3DRS_POINTSCALE_B = 159, /* float point attenuation B value */ + D3DRS_POINTSCALE_C = 160, /* float point attenuation C value */ + D3DRS_MULTISAMPLEANTIALIAS = 161, // BOOL - set to do FSAA with multisample buffer + D3DRS_MULTISAMPLEMASK = 162, // DWORD - per-sample enable/disable + D3DRS_PATCHEDGESTYLE = 163, // Sets whether patch edges will use float style tessellation + D3DRS_PATCHSEGMENTS = 164, // Number of segments per edge when drawing patches + D3DRS_DEBUGMONITORTOKEN = 165, // DEBUG ONLY - token to debug monitor + D3DRS_POINTSIZE_MAX = 166, /* float point size max threshold */ + D3DRS_INDEXEDVERTEXBLENDENABLE = 167, + D3DRS_COLORWRITEENABLE = 168, // per-channel write enable + D3DRS_TWEENFACTOR = 170, // float tween factor + D3DRS_BLENDOP = 171, // D3DBLENDOP setting + D3DRS_POSITIONORDER = 172, // NPatch position interpolation order. D3DORDER_LINEAR or D3DORDER_CUBIC (default) + D3DRS_NORMALORDER = 173, // NPatch normal interpolation order. D3DORDER_LINEAR (default) or D3DORDER_QUADRATIC + + D3DRS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DRENDERSTATETYPE; + +// Values for material source +typedef enum _D3DMATERIALCOLORSOURCE +{ + D3DMCS_MATERIAL = 0, // Color from material is used + D3DMCS_COLOR1 = 1, // Diffuse vertex color is used + D3DMCS_COLOR2 = 2, // Specular vertex color is used + D3DMCS_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DMATERIALCOLORSOURCE; + +// Bias to apply to the texture coordinate set to apply a wrap to. +#define D3DRENDERSTATE_WRAPBIAS 128UL + +/* Flags to construct the WRAP render states */ +#define D3DWRAP_U 0x00000001L +#define D3DWRAP_V 0x00000002L +#define D3DWRAP_W 0x00000004L + +/* Flags to construct the WRAP render states for 1D thru 4D texture coordinates */ +#define D3DWRAPCOORD_0 0x00000001L // same as D3DWRAP_U +#define D3DWRAPCOORD_1 0x00000002L // same as D3DWRAP_V +#define D3DWRAPCOORD_2 0x00000004L // same as D3DWRAP_W +#define D3DWRAPCOORD_3 0x00000008L + +/* Flags to construct D3DRS_COLORWRITEENABLE */ +#define D3DCOLORWRITEENABLE_RED (1L<<0) +#define D3DCOLORWRITEENABLE_GREEN (1L<<1) +#define D3DCOLORWRITEENABLE_BLUE (1L<<2) +#define D3DCOLORWRITEENABLE_ALPHA (1L<<3) + +/* + * State enumerants for per-stage texture processing. + */ +typedef enum _D3DTEXTURESTAGESTATETYPE +{ + D3DTSS_COLOROP = 1, /* D3DTEXTUREOP - per-stage blending controls for color channels */ + D3DTSS_COLORARG1 = 2, /* D3DTA_* (texture arg) */ + D3DTSS_COLORARG2 = 3, /* D3DTA_* (texture arg) */ + D3DTSS_ALPHAOP = 4, /* D3DTEXTUREOP - per-stage blending controls for alpha channel */ + D3DTSS_ALPHAARG1 = 5, /* D3DTA_* (texture arg) */ + D3DTSS_ALPHAARG2 = 6, /* D3DTA_* (texture arg) */ + D3DTSS_BUMPENVMAT00 = 7, /* float (bump mapping matrix) */ + D3DTSS_BUMPENVMAT01 = 8, /* float (bump mapping matrix) */ + D3DTSS_BUMPENVMAT10 = 9, /* float (bump mapping matrix) */ + D3DTSS_BUMPENVMAT11 = 10, /* float (bump mapping matrix) */ + D3DTSS_TEXCOORDINDEX = 11, /* identifies which set of texture coordinates index this texture */ + D3DTSS_ADDRESSU = 13, /* D3DTEXTUREADDRESS for U coordinate */ + D3DTSS_ADDRESSV = 14, /* D3DTEXTUREADDRESS for V coordinate */ + D3DTSS_BORDERCOLOR = 15, /* D3DCOLOR */ + D3DTSS_MAGFILTER = 16, /* D3DTEXTUREFILTER filter to use for magnification */ + D3DTSS_MINFILTER = 17, /* D3DTEXTUREFILTER filter to use for minification */ + D3DTSS_MIPFILTER = 18, /* D3DTEXTUREFILTER filter to use between mipmaps during minification */ + D3DTSS_MIPMAPLODBIAS = 19, /* float Mipmap LOD bias */ + D3DTSS_MAXMIPLEVEL = 20, /* DWORD 0..(n-1) LOD index of largest map to use (0 == largest) */ + D3DTSS_MAXANISOTROPY = 21, /* DWORD maximum anisotropy */ + D3DTSS_BUMPENVLSCALE = 22, /* float scale for bump map luminance */ + D3DTSS_BUMPENVLOFFSET = 23, /* float offset for bump map luminance */ + D3DTSS_TEXTURETRANSFORMFLAGS = 24, /* D3DTEXTURETRANSFORMFLAGS controls texture transform */ + D3DTSS_ADDRESSW = 25, /* D3DTEXTUREADDRESS for W coordinate */ + D3DTSS_COLORARG0 = 26, /* D3DTA_* third arg for triadic ops */ + D3DTSS_ALPHAARG0 = 27, /* D3DTA_* third arg for triadic ops */ + D3DTSS_RESULTARG = 28, /* D3DTA_* arg for result (CURRENT or TEMP) */ + D3DTSS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTURESTAGESTATETYPE; + +// Values, used with D3DTSS_TEXCOORDINDEX, to specify that the vertex data(position +// and normal in the camera space) should be taken as texture coordinates +// Low 16 bits are used to specify texture coordinate index, to take the WRAP mode from +// +#define D3DTSS_TCI_PASSTHRU 0x00000000 +#define D3DTSS_TCI_CAMERASPACENORMAL 0x00010000 +#define D3DTSS_TCI_CAMERASPACEPOSITION 0x00020000 +#define D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR 0x00030000 + +/* + * Enumerations for COLOROP and ALPHAOP texture blending operations set in + * texture processing stage controls in D3DTSS. + */ +typedef enum _D3DTEXTUREOP +{ + // Control + D3DTOP_DISABLE = 1, // disables stage + D3DTOP_SELECTARG1 = 2, // the default + D3DTOP_SELECTARG2 = 3, + + // Modulate + D3DTOP_MODULATE = 4, // multiply args together + D3DTOP_MODULATE2X = 5, // multiply and 1 bit + D3DTOP_MODULATE4X = 6, // multiply and 2 bits + + // Add + D3DTOP_ADD = 7, // add arguments together + D3DTOP_ADDSIGNED = 8, // add with -0.5 bias + D3DTOP_ADDSIGNED2X = 9, // as above but left 1 bit + D3DTOP_SUBTRACT = 10, // Arg1 - Arg2, with no saturation + D3DTOP_ADDSMOOTH = 11, // add 2 args, subtract product + // Arg1 + Arg2 - Arg1*Arg2 + // = Arg1 + (1-Arg1)*Arg2 + + // Linear alpha blend: Arg1*(Alpha) + Arg2*(1-Alpha) + D3DTOP_BLENDDIFFUSEALPHA = 12, // iterated alpha + D3DTOP_BLENDTEXTUREALPHA = 13, // texture alpha + D3DTOP_BLENDFACTORALPHA = 14, // alpha from D3DRS_TEXTUREFACTOR + + // Linear alpha blend with pre-multiplied arg1 input: Arg1 + Arg2*(1-Alpha) + D3DTOP_BLENDTEXTUREALPHAPM = 15, // texture alpha + D3DTOP_BLENDCURRENTALPHA = 16, // by alpha of current color + + // Specular mapping + D3DTOP_PREMODULATE = 17, // modulate with next texture before use + D3DTOP_MODULATEALPHA_ADDCOLOR = 18, // Arg1.RGB + Arg1.A*Arg2.RGB + // COLOROP only + D3DTOP_MODULATECOLOR_ADDALPHA = 19, // Arg1.RGB*Arg2.RGB + Arg1.A + // COLOROP only + D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20, // (1-Arg1.A)*Arg2.RGB + Arg1.RGB + // COLOROP only + D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21, // (1-Arg1.RGB)*Arg2.RGB + Arg1.A + // COLOROP only + + // Bump mapping + D3DTOP_BUMPENVMAP = 22, // per pixel env map perturbation + D3DTOP_BUMPENVMAPLUMINANCE = 23, // with luminance channel + + // This can do either diffuse or specular bump mapping with correct input. + // Performs the function (Arg1.R*Arg2.R + Arg1.G*Arg2.G + Arg1.B*Arg2.B) + // where each component has been scaled and offset to make it signed. + // The result is replicated into all four (including alpha) channels. + // This is a valid COLOROP only. + D3DTOP_DOTPRODUCT3 = 24, + + // Triadic ops + D3DTOP_MULTIPLYADD = 25, // Arg0 + Arg1*Arg2 + D3DTOP_LERP = 26, // (Arg0)*Arg1 + (1-Arg0)*Arg2 + + D3DTOP_FORCE_DWORD = 0x7fffffff, +} D3DTEXTUREOP; + +/* + * Values for COLORARG0,1,2, ALPHAARG0,1,2, and RESULTARG texture blending + * operations set in texture processing stage controls in D3DRENDERSTATE. + */ +#define D3DTA_SELECTMASK 0x0000000f // mask for arg selector +#define D3DTA_DIFFUSE 0x00000000 // select diffuse color (read only) +#define D3DTA_CURRENT 0x00000001 // select stage destination register (read/write) +#define D3DTA_TEXTURE 0x00000002 // select texture color (read only) +#define D3DTA_TFACTOR 0x00000003 // select D3DRS_TEXTUREFACTOR (read only) +#define D3DTA_SPECULAR 0x00000004 // select specular color (read only) +#define D3DTA_TEMP 0x00000005 // select temporary register color (read/write) +#define D3DTA_COMPLEMENT 0x00000010 // take 1.0 - x (read modifier) +#define D3DTA_ALPHAREPLICATE 0x00000020 // replicate alpha to color components (read modifier) + +// +// Values for D3DTSS_***FILTER texture stage states +// +typedef enum _D3DTEXTUREFILTERTYPE +{ + D3DTEXF_NONE = 0, // filtering disabled (valid for mip filter only) + D3DTEXF_POINT = 1, // nearest + D3DTEXF_LINEAR = 2, // linear interpolation + D3DTEXF_ANISOTROPIC = 3, // anisotropic + D3DTEXF_FLATCUBIC = 4, // cubic + D3DTEXF_GAUSSIANCUBIC = 5, // different cubic kernel + D3DTEXF_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DTEXTUREFILTERTYPE; + +/* Bits for Flags in ProcessVertices call */ + +#define D3DPV_DONOTCOPYDATA (1 << 0) + +//------------------------------------------------------------------- + +// Flexible vertex format bits +// +#define D3DFVF_RESERVED0 0x001 +#define D3DFVF_POSITION_MASK 0x00E +#define D3DFVF_XYZ 0x002 +#define D3DFVF_XYZRHW 0x004 +#define D3DFVF_XYZB1 0x006 +#define D3DFVF_XYZB2 0x008 +#define D3DFVF_XYZB3 0x00a +#define D3DFVF_XYZB4 0x00c +#define D3DFVF_XYZB5 0x00e + +#define D3DFVF_NORMAL 0x010 +#define D3DFVF_PSIZE 0x020 +#define D3DFVF_DIFFUSE 0x040 +#define D3DFVF_SPECULAR 0x080 + +#define D3DFVF_TEXCOUNT_MASK 0xf00 +#define D3DFVF_TEXCOUNT_SHIFT 8 +#define D3DFVF_TEX0 0x000 +#define D3DFVF_TEX1 0x100 +#define D3DFVF_TEX2 0x200 +#define D3DFVF_TEX3 0x300 +#define D3DFVF_TEX4 0x400 +#define D3DFVF_TEX5 0x500 +#define D3DFVF_TEX6 0x600 +#define D3DFVF_TEX7 0x700 +#define D3DFVF_TEX8 0x800 + +#define D3DFVF_LASTBETA_UBYTE4 0x1000 + +#define D3DFVF_RESERVED2 0xE000 // 4 reserved bits + +//--------------------------------------------------------------------- +// Vertex Shaders +// + +/* + +Vertex Shader Declaration + +The declaration portion of a vertex shader defines the static external +interface of the shader. The information in the declaration includes: + +- Assignments of vertex shader input registers to data streams. These +assignments bind a specific vertex register to a single component within a +vertex stream. A vertex stream element is identified by a byte offset +within the stream and a type. The type specifies the arithmetic data type +plus the dimensionality (1, 2, 3, or 4 values). Stream data which is +less than 4 values are always expanded out to 4 values with zero or more +0.F values and one 1.F value. + +- Assignment of vertex shader input registers to implicit data from the +primitive tessellator. This controls the loading of vertex data which is +not loaded from a stream, but rather is generated during primitive +tessellation prior to the vertex shader. + +- Loading data into the constant memory at the time a shader is set as the +current shader. Each token specifies values for one or more contiguous 4 +DWORD constant registers. This allows the shader to update an arbitrary +subset of the constant memory, overwriting the device state (which +contains the current values of the constant memory). Note that these +values can be subsequently overwritten (between DrawPrimitive calls) +during the time a shader is bound to a device via the +SetVertexShaderConstant method. + + +Declaration arrays are single-dimensional arrays of DWORDs composed of +multiple tokens each of which is one or more DWORDs. The single-DWORD +token value 0xFFFFFFFF is a special token used to indicate the end of the +declaration array. The single DWORD token value 0x00000000 is a NOP token +with is ignored during the declaration parsing. Note that 0x00000000 is a +valid value for DWORDs following the first DWORD for multiple word tokens. + +[31:29] TokenType + 0x0 - NOP (requires all DWORD bits to be zero) + 0x1 - stream selector + 0x2 - stream data definition (map to vertex input memory) + 0x3 - vertex input memory from tessellator + 0x4 - constant memory from shader + 0x5 - extension + 0x6 - reserved + 0x7 - end-of-array (requires all DWORD bits to be 1) + +NOP Token (single DWORD token) + [31:29] 0x0 + [28:00] 0x0 + +Stream Selector (single DWORD token) + [31:29] 0x1 + [28] indicates whether this is a tessellator stream + [27:04] 0x0 + [03:00] stream selector (0..15) + +Stream Data Definition (single DWORD token) + Vertex Input Register Load + [31:29] 0x2 + [28] 0x0 + [27:20] 0x0 + [19:16] type (dimensionality and data type) + [15:04] 0x0 + [03:00] vertex register address (0..15) + Data Skip (no register load) + [31:29] 0x2 + [28] 0x1 + [27:20] 0x0 + [19:16] count of DWORDS to skip over (0..15) + [15:00] 0x0 + Vertex Input Memory from Tessellator Data (single DWORD token) + [31:29] 0x3 + [28] indicates whether data is normals or u/v + [27:24] 0x0 + [23:20] vertex register address (0..15) + [19:16] type (dimensionality) + [15:04] 0x0 + [03:00] vertex register address (0..15) + +Constant Memory from Shader (multiple DWORD token) + [31:29] 0x4 + [28:25] count of 4*DWORD constants to load (0..15) + [24:07] 0x0 + [06:00] constant memory address (0..95) + +Extension Token (single or multiple DWORD token) + [31:29] 0x5 + [28:24] count of additional DWORDs in token (0..31) + [23:00] extension-specific information + +End-of-array token (single DWORD token) + [31:29] 0x7 + [28:00] 0x1fffffff + +The stream selector token must be immediately followed by a contiguous set of stream data definition tokens. This token sequence fully defines that stream, including the set of elements within the stream, the order in which the elements appear, the type of each element, and the vertex register into which to load an element. +Streams are allowed to include data which is not loaded into a vertex register, thus allowing data which is not used for this shader to exist in the vertex stream. This skipped data is defined only by a count of DWORDs to skip over, since the type information is irrelevant. +The token sequence: +Stream Select: stream=0 +Stream Data Definition (Load): type=FLOAT3; register=3 +Stream Data Definition (Load): type=FLOAT3; register=4 +Stream Data Definition (Skip): count=2 +Stream Data Definition (Load): type=FLOAT2; register=7 + +defines stream zero to consist of 4 elements, 3 of which are loaded into registers and the fourth skipped over. Register 3 is loaded with the first three DWORDs in each vertex interpreted as FLOAT data. Register 4 is loaded with the 4th, 5th, and 6th DWORDs interpreted as FLOAT data. The next two DWORDs (7th and 8th) are skipped over and not loaded into any vertex input register. Register 7 is loaded with the 9th and 10th DWORDS interpreted as FLOAT data. +Placing of tokens other than NOPs between the Stream Selector and Stream Data Definition tokens is disallowed. + +*/ + +typedef enum _D3DVSD_TOKENTYPE +{ + D3DVSD_TOKEN_NOP = 0, // NOP or extension + D3DVSD_TOKEN_STREAM, // stream selector + D3DVSD_TOKEN_STREAMDATA, // stream data definition (map to vertex input memory) + D3DVSD_TOKEN_TESSELLATOR, // vertex input memory from tessellator + D3DVSD_TOKEN_CONSTMEM, // constant memory from shader + D3DVSD_TOKEN_EXT, // extension + D3DVSD_TOKEN_END = 7, // end-of-array (requires all DWORD bits to be 1) + D3DVSD_FORCE_DWORD = 0x7fffffff,// force 32-bit size enum +} D3DVSD_TOKENTYPE; + +#define D3DVSD_TOKENTYPESHIFT 29 +#define D3DVSD_TOKENTYPEMASK (7 << D3DVSD_TOKENTYPESHIFT) + +#define D3DVSD_STREAMNUMBERSHIFT 0 +#define D3DVSD_STREAMNUMBERMASK (0xF << D3DVSD_STREAMNUMBERSHIFT) + +#define D3DVSD_DATALOADTYPESHIFT 28 +#define D3DVSD_DATALOADTYPEMASK (0x1 << D3DVSD_DATALOADTYPESHIFT) + +#define D3DVSD_DATATYPESHIFT 16 +#define D3DVSD_DATATYPEMASK (0xF << D3DVSD_DATATYPESHIFT) + +#define D3DVSD_SKIPCOUNTSHIFT 16 +#define D3DVSD_SKIPCOUNTMASK (0xF << D3DVSD_SKIPCOUNTSHIFT) + +#define D3DVSD_VERTEXREGSHIFT 0 +#define D3DVSD_VERTEXREGMASK (0x1F << D3DVSD_VERTEXREGSHIFT) + +#define D3DVSD_VERTEXREGINSHIFT 20 +#define D3DVSD_VERTEXREGINMASK (0xF << D3DVSD_VERTEXREGINSHIFT) + +#define D3DVSD_CONSTCOUNTSHIFT 25 +#define D3DVSD_CONSTCOUNTMASK (0xF << D3DVSD_CONSTCOUNTSHIFT) + +#define D3DVSD_CONSTADDRESSSHIFT 0 +#define D3DVSD_CONSTADDRESSMASK (0x7F << D3DVSD_CONSTADDRESSSHIFT) + +#define D3DVSD_CONSTRSSHIFT 16 +#define D3DVSD_CONSTRSMASK (0x1FFF << D3DVSD_CONSTRSSHIFT) + +#define D3DVSD_EXTCOUNTSHIFT 24 +#define D3DVSD_EXTCOUNTMASK (0x1F << D3DVSD_EXTCOUNTSHIFT) + +#define D3DVSD_EXTINFOSHIFT 0 +#define D3DVSD_EXTINFOMASK (0xFFFFFF << D3DVSD_EXTINFOSHIFT) + +#define D3DVSD_MAKETOKENTYPE(tokenType) ((tokenType << D3DVSD_TOKENTYPESHIFT) & D3DVSD_TOKENTYPEMASK) + +// macros for generation of CreateVertexShader Declaration token array + +// Set current stream +// _StreamNumber [0..(MaxStreams-1)] stream to get data from +// +#define D3DVSD_STREAM( _StreamNumber ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAM) | (_StreamNumber)) + +// Set tessellator stream +// +#define D3DVSD_STREAMTESSSHIFT 28 +#define D3DVSD_STREAMTESSMASK (1 << D3DVSD_STREAMTESSSHIFT) +#define D3DVSD_STREAM_TESS( ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAM) | (D3DVSD_STREAMTESSMASK)) + +// bind single vertex register to vertex element from vertex stream +// +// _VertexRegister [0..15] address of the vertex register +// _Type [D3DVSDT_*] dimensionality and arithmetic data type + +#define D3DVSD_REG( _VertexRegister, _Type ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAMDATA) | \ + ((_Type) << D3DVSD_DATATYPESHIFT) | (_VertexRegister)) + +// Skip _DWORDCount DWORDs in vertex +// +#define D3DVSD_SKIP( _DWORDCount ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAMDATA) | 0x10000000 | \ + ((_DWORDCount) << D3DVSD_SKIPCOUNTSHIFT)) + +// load data into vertex shader constant memory +// +// _ConstantAddress [0..95] - address of constant array to begin filling data +// _Count [0..15] - number of constant vectors to load (4 DWORDs each) +// followed by 4*_Count DWORDS of data +// +#define D3DVSD_CONST( _ConstantAddress, _Count ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_CONSTMEM) | \ + ((_Count) << D3DVSD_CONSTCOUNTSHIFT) | (_ConstantAddress)) + +// enable tessellator generated normals +// +// _VertexRegisterIn [0..15] address of vertex register whose input stream +// will be used in normal computation +// _VertexRegisterOut [0..15] address of vertex register to output the normal to +// +#define D3DVSD_TESSNORMAL( _VertexRegisterIn, _VertexRegisterOut ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_TESSELLATOR) | \ + ((_VertexRegisterIn) << D3DVSD_VERTEXREGINSHIFT) | \ + ((0x02) << D3DVSD_DATATYPESHIFT) | (_VertexRegisterOut)) + +// enable tessellator generated surface parameters +// +// _VertexRegister [0..15] address of vertex register to output parameters +// +#define D3DVSD_TESSUV( _VertexRegister ) \ + (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_TESSELLATOR) | 0x10000000 | \ + ((0x01) << D3DVSD_DATATYPESHIFT) | (_VertexRegister)) + +// Generates END token +// +#define D3DVSD_END() 0xFFFFFFFF + +// Generates NOP token +#define D3DVSD_NOP() 0x00000000 + +// bit declarations for _Type fields +#define D3DVSDT_FLOAT1 0x00 // 1D float expanded to (value, 0., 0., 1.) +#define D3DVSDT_FLOAT2 0x01 // 2D float expanded to (value, value, 0., 1.) +#define D3DVSDT_FLOAT3 0x02 // 3D float expanded to (value, value, value, 1.) +#define D3DVSDT_FLOAT4 0x03 // 4D float +#define D3DVSDT_D3DCOLOR 0x04 // 4D packed unsigned bytes mapped to 0. to 1. range + // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) +#define D3DVSDT_UBYTE4 0x05 // 4D unsigned byte +#define D3DVSDT_SHORT2 0x06 // 2D signed short expanded to (value, value, 0., 1.) +#define D3DVSDT_SHORT4 0x07 // 4D signed short + +// assignments of vertex input registers for fixed function vertex shader +// +#define D3DVSDE_POSITION 0 +#define D3DVSDE_BLENDWEIGHT 1 +#define D3DVSDE_BLENDINDICES 2 +#define D3DVSDE_NORMAL 3 +#define D3DVSDE_PSIZE 4 +#define D3DVSDE_DIFFUSE 5 +#define D3DVSDE_SPECULAR 6 +#define D3DVSDE_TEXCOORD0 7 +#define D3DVSDE_TEXCOORD1 8 +#define D3DVSDE_TEXCOORD2 9 +#define D3DVSDE_TEXCOORD3 10 +#define D3DVSDE_TEXCOORD4 11 +#define D3DVSDE_TEXCOORD5 12 +#define D3DVSDE_TEXCOORD6 13 +#define D3DVSDE_TEXCOORD7 14 +#define D3DVSDE_POSITION2 15 +#define D3DVSDE_NORMAL2 16 + +// Maximum supported number of texture coordinate sets +#define D3DDP_MAXTEXCOORD 8 + + +// +// Instruction Token Bit Definitions +// +#define D3DSI_OPCODE_MASK 0x0000FFFF + +typedef enum _D3DSHADER_INSTRUCTION_OPCODE_TYPE +{ + D3DSIO_NOP = 0, // PS/VS + D3DSIO_MOV , // PS/VS + D3DSIO_ADD , // PS/VS + D3DSIO_SUB , // PS + D3DSIO_MAD , // PS/VS + D3DSIO_MUL , // PS/VS + D3DSIO_RCP , // VS + D3DSIO_RSQ , // VS + D3DSIO_DP3 , // PS/VS + D3DSIO_DP4 , // PS/VS + D3DSIO_MIN , // VS + D3DSIO_MAX , // VS + D3DSIO_SLT , // VS + D3DSIO_SGE , // VS + D3DSIO_EXP , // VS + D3DSIO_LOG , // VS + D3DSIO_LIT , // VS + D3DSIO_DST , // VS + D3DSIO_LRP , // PS + D3DSIO_FRC , // VS + D3DSIO_M4x4 , // VS + D3DSIO_M4x3 , // VS + D3DSIO_M3x4 , // VS + D3DSIO_M3x3 , // VS + D3DSIO_M3x2 , // VS + + D3DSIO_TEXCOORD = 64, // PS + D3DSIO_TEXKILL , // PS + D3DSIO_TEX , // PS + D3DSIO_TEXBEM , // PS + D3DSIO_TEXBEML , // PS + D3DSIO_TEXREG2AR , // PS + D3DSIO_TEXREG2GB , // PS + D3DSIO_TEXM3x2PAD , // PS + D3DSIO_TEXM3x2TEX , // PS + D3DSIO_TEXM3x3PAD , // PS + D3DSIO_TEXM3x3TEX , // PS + D3DSIO_TEXM3x3DIFF , // PS + D3DSIO_TEXM3x3SPEC , // PS + D3DSIO_TEXM3x3VSPEC , // PS + D3DSIO_EXPP , // VS + D3DSIO_LOGP , // VS + D3DSIO_CND , // PS + D3DSIO_DEF , // PS + D3DSIO_TEXREG2RGB , // PS + D3DSIO_TEXDP3TEX , // PS + D3DSIO_TEXM3x2DEPTH , // PS + D3DSIO_TEXDP3 , // PS + D3DSIO_TEXM3x3 , // PS + D3DSIO_TEXDEPTH , // PS + D3DSIO_CMP , // PS + D3DSIO_BEM , // PS + + D3DSIO_PHASE = 0xFFFD, + D3DSIO_COMMENT = 0xFFFE, + D3DSIO_END = 0xFFFF, + + D3DSIO_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum +} D3DSHADER_INSTRUCTION_OPCODE_TYPE; + +// +// Co-Issue Instruction Modifier - if set then this instruction is to be +// issued in parallel with the previous instruction(s) for which this bit +// is not set. +// +#define D3DSI_COISSUE 0x40000000 + +// +// Parameter Token Bit Definitions +// +#define D3DSP_REGNUM_MASK 0x00001FFF + +// destination parameter write mask +#define D3DSP_WRITEMASK_0 0x00010000 // Component 0 (X;Red) +#define D3DSP_WRITEMASK_1 0x00020000 // Component 1 (Y;Green) +#define D3DSP_WRITEMASK_2 0x00040000 // Component 2 (Z;Blue) +#define D3DSP_WRITEMASK_3 0x00080000 // Component 3 (W;Alpha) +#define D3DSP_WRITEMASK_ALL 0x000F0000 // All Components + +// destination parameter modifiers +#define D3DSP_DSTMOD_SHIFT 20 +#define D3DSP_DSTMOD_MASK 0x00F00000 + +typedef enum _D3DSHADER_PARAM_DSTMOD_TYPE +{ + D3DSPDM_NONE = 0<>8)&0xFF) +#define D3DSHADER_VERSION_MINOR(_Version) (((_Version)>>0)&0xFF) + +// destination/source parameter register type +#define D3DSI_COMMENTSIZE_SHIFT 16 +#define D3DSI_COMMENTSIZE_MASK 0x7FFF0000 +#define D3DSHADER_COMMENT(_DWordSize) \ + ((((_DWordSize)<= 1200 +#pragma warning(pop) +#else +#ifdef _MSC_VER +#pragma warning(default:4201) +#endif +#endif + +#endif /* (DIRECT3D_VERSION >= 0x0800) */ +#endif /* _D3D8TYPES(P)_H_ */ + diff --git a/shared/obs-hook-config/CMakeLists.txt b/shared/obs-hook-config/CMakeLists.txt new file mode 100644 index 00000000000000..347657cc563a52 --- /dev/null +++ b/shared/obs-hook-config/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +add_library(hook-config INTERFACE) +add_library(OBS::hook-config ALIAS hook-config) +target_sources(hook-config INTERFACE graphics-hook-ver.h graphics-hook-info.h hook-helpers.h) +target_include_directories(hook-config INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/obs-hook-config/graphics-hook-info.h b/shared/obs-hook-config/graphics-hook-info.h new file mode 100644 index 00000000000000..f449afa98f1d1f --- /dev/null +++ b/shared/obs-hook-config/graphics-hook-info.h @@ -0,0 +1,143 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "hook-helpers.h" + +#define EVENT_CAPTURE_RESTART L"CaptureHook_Restart" +#define EVENT_CAPTURE_STOP L"CaptureHook_Stop" + +#define EVENT_HOOK_READY L"CaptureHook_HookReady" +#define EVENT_HOOK_EXIT L"CaptureHook_Exit" + +#define EVENT_HOOK_INIT L"CaptureHook_Initialize" + +#define WINDOW_HOOK_KEEPALIVE L"CaptureHook_KeepAlive" + +#define MUTEX_TEXTURE1 L"CaptureHook_TextureMutex1" +#define MUTEX_TEXTURE2 L"CaptureHook_TextureMutex2" + +#define SHMEM_HOOK_INFO L"CaptureHook_HookInfo" +#define SHMEM_TEXTURE L"CaptureHook_Texture" + +#define PIPE_NAME "CaptureHook_Pipe" + +#pragma pack(push, 8) + +struct d3d8_offsets { + uint32_t present; +}; + +struct d3d9_offsets { + uint32_t present; + uint32_t present_ex; + uint32_t present_swap; + uint32_t d3d9_clsoff; + uint32_t is_d3d9ex_clsoff; +}; + +struct d3d12_offsets { + uint32_t execute_command_lists; +}; + +struct dxgi_offsets { + uint32_t present; + uint32_t resize; + + uint32_t present1; +}; + +struct dxgi_offsets2 { + uint32_t release; +}; + +struct ddraw_offsets { + uint32_t surface_create; + uint32_t surface_restore; + uint32_t surface_release; + uint32_t surface_unlock; + uint32_t surface_blt; + uint32_t surface_flip; + uint32_t surface_set_palette; + uint32_t palette_set_entries; +}; + +struct shmem_data { + volatile int last_tex; + uint32_t tex1_offset; + uint32_t tex2_offset; +}; + +struct shtex_data { + uint32_t tex_handle; +}; + +enum capture_type { + CAPTURE_TYPE_MEMORY, + CAPTURE_TYPE_TEXTURE, +}; + +struct graphics_offsets { + struct d3d8_offsets d3d8; + struct d3d9_offsets d3d9; + struct dxgi_offsets dxgi; + struct ddraw_offsets ddraw; + struct dxgi_offsets2 dxgi2; + struct d3d12_offsets d3d12; +}; + +struct hook_info { + /* hook version */ + uint32_t hook_ver_major; + uint32_t hook_ver_minor; + + /* capture info */ + enum capture_type type; + uint32_t window; + uint32_t format; + uint32_t cx; + uint32_t cy; + uint32_t UNUSED_base_cx; + uint32_t UNUSED_base_cy; + uint32_t pitch; + uint32_t map_id; + uint32_t map_size; + bool flip; + + /* additional options */ + uint64_t frame_interval; + bool UNUSED_use_scale; + bool force_shmem; + bool capture_overlay; + bool allow_srgb_alias; + + /* hook addresses */ + struct graphics_offsets offsets; + + uint32_t reserved[126]; +}; +static_assert(sizeof(struct hook_info) == 648, "ABI compatibility"); + +#pragma pack(pop) + +#define GC_MAPPING_FLAGS (FILE_MAP_READ | FILE_MAP_WRITE) + +static inline HANDLE create_hook_info(DWORD id) +{ + HANDLE handle = NULL; + + wchar_t new_name[64]; + const int len = swprintf(new_name, _countof(new_name), + SHMEM_HOOK_INFO L"%lu", id); + if (len > 0) { + handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, + sizeof(struct hook_info), new_name); + } + + return handle; +} diff --git a/shared/obs-hook-config/graphics-hook-ver.h b/shared/obs-hook-config/graphics-hook-ver.h new file mode 100644 index 00000000000000..fb1fca3c66ba83 --- /dev/null +++ b/shared/obs-hook-config/graphics-hook-ver.h @@ -0,0 +1,25 @@ +/* DO NOT MODIFY THIS FILE WITHOUT CONSULTING LAIN. OTHERWISE, LAIN WILL DO + * EVERYTHING IN HER POWER TO MAKE YOUR LIFE MISERABLE FROM THEREON OUT WITH A + * LEVEL OF DETERMINATION UNLIKE ANYTHING ANYONE HAS EVER SEEN IN THE HISTORY + * OF MANKIND. + * + * YES, THAT MEANS YOU READING THIS RIGHT NOW. + * + * IF YOU HAVE A FORK AND FEEL YOU NEED TO MODIFY THIS, SUBMIT A PULL REQUEST + * AND WAIT UNTIL IT HAS BEEN MERGED AND FULLY RELEASED IN THE CORE PROJECT + * BEFORE USING IT. + * + * THIS IS YOUR ONLY WARNING. */ + +#define HOOK_VER_MAJOR 1 +#define HOOK_VER_MINOR 8 +#define HOOK_VER_PATCH 3 + +#ifndef STRINGIFY +#define STRINGIFY(s) #s +#endif + +#define MAKE_VERSION_NAME(major, minor, patch) \ + STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch) ".0" +#define HOOK_VERSION_NAME \ + MAKE_VERSION_NAME(HOOK_VER_MAJOR, HOOK_VER_MINOR, HOOK_VER_PATCH) diff --git a/shared/obs-hook-config/hook-helpers.h b/shared/obs-hook-config/hook-helpers.h new file mode 100644 index 00000000000000..98b211c8761a90 --- /dev/null +++ b/shared/obs-hook-config/hook-helpers.h @@ -0,0 +1,50 @@ +#pragma once + +#if !defined(__cplusplus) && !defined(inline) +#define inline __inline +#endif + +#define GC_EVENT_FLAGS (EVENT_MODIFY_STATE | SYNCHRONIZE) +#define GC_MUTEX_FLAGS (SYNCHRONIZE) + +static inline HANDLE create_event(const wchar_t *name) +{ + return CreateEventW(NULL, false, false, name); +} + +static inline HANDLE open_event(const wchar_t *name) +{ + return OpenEventW(GC_EVENT_FLAGS, false, name); +} + +static inline HANDLE create_mutex(const wchar_t *name) +{ + return CreateMutexW(NULL, false, name); +} + +static inline HANDLE open_mutex(const wchar_t *name) +{ + return OpenMutexW(GC_MUTEX_FLAGS, false, name); +} + +static inline HANDLE create_event_plus_id(const wchar_t *name, DWORD id) +{ + wchar_t new_name[64]; + _snwprintf(new_name, 64, L"%s%lu", name, id); + return create_event(new_name); +} + +static inline HANDLE create_mutex_plus_id(const wchar_t *name, DWORD id) +{ + wchar_t new_name[64]; + _snwprintf(new_name, 64, L"%s%lu", name, id); + return create_mutex(new_name); +} + +static inline bool object_signalled(HANDLE event) +{ + if (!event) + return false; + + return WaitForSingleObject(event, 0) == WAIT_OBJECT_0; +} diff --git a/shared/obs-inject-library/CMakeLists.txt b/shared/obs-inject-library/CMakeLists.txt new file mode 100644 index 00000000000000..b80f43118e4649 --- /dev/null +++ b/shared/obs-inject-library/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +if(NOT TARGET OBS::obfuscate) + add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") +endif() + +add_library(inject-library INTERFACE) +add_library(OBS::inject-library ALIAS inject-library) +target_sources(inject-library INTERFACE inject-library.c inject-library.h) +target_link_libraries(inject-library INTERFACE OBS::obfuscate) +target_include_directories(inject-library INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/obs-inject-library/inject-library.c b/shared/obs-inject-library/inject-library.c new file mode 100644 index 00000000000000..6e9bdfc75a2fc6 --- /dev/null +++ b/shared/obs-inject-library/inject-library.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include "inject-library.h" + +typedef HANDLE(WINAPI *create_remote_thread_t)(HANDLE, LPSECURITY_ATTRIBUTES, + SIZE_T, LPTHREAD_START_ROUTINE, + LPVOID, DWORD, LPDWORD); +typedef BOOL(WINAPI *write_process_memory_t)(HANDLE, LPVOID, LPCVOID, SIZE_T, + SIZE_T *); +typedef LPVOID(WINAPI *virtual_alloc_ex_t)(HANDLE, LPVOID, SIZE_T, DWORD, + DWORD); +typedef BOOL(WINAPI *virtual_free_ex_t)(HANDLE, LPVOID, SIZE_T, DWORD); + +int inject_library_obf(HANDLE process, const wchar_t *dll, + const char *create_remote_thread_obf, uint64_t obf1, + const char *write_process_memory_obf, uint64_t obf2, + const char *virtual_alloc_ex_obf, uint64_t obf3, + const char *virtual_free_ex_obf, uint64_t obf4, + const char *load_library_w_obf, uint64_t obf5) +{ + int ret = INJECT_ERROR_UNLIKELY_FAIL; + DWORD last_error = 0; + bool success = false; + size_t written_size; + DWORD thread_id; + HANDLE thread = NULL; + size_t size; + void *mem; + + /* -------------------------------- */ + + HMODULE kernel32 = GetModuleHandleW(L"KERNEL32"); + create_remote_thread_t create_remote_thread; + write_process_memory_t write_process_memory; + virtual_alloc_ex_t virtual_alloc_ex; + virtual_free_ex_t virtual_free_ex; + FARPROC load_library_w; + + create_remote_thread = (create_remote_thread_t)ms_get_obfuscated_func( + kernel32, create_remote_thread_obf, obf1); + write_process_memory = (write_process_memory_t)ms_get_obfuscated_func( + kernel32, write_process_memory_obf, obf2); + virtual_alloc_ex = (virtual_alloc_ex_t)ms_get_obfuscated_func( + kernel32, virtual_alloc_ex_obf, obf3); + virtual_free_ex = (virtual_free_ex_t)ms_get_obfuscated_func( + kernel32, virtual_free_ex_obf, obf4); + load_library_w = (FARPROC)ms_get_obfuscated_func( + kernel32, load_library_w_obf, obf5); + + /* -------------------------------- */ + + size = (wcslen(dll) + 1) * sizeof(wchar_t); + mem = virtual_alloc_ex(process, NULL, size, MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + if (!mem) { + goto fail; + } + + success = write_process_memory(process, mem, dll, size, &written_size); + if (!success) { + goto fail; + } + + thread = create_remote_thread(process, NULL, 0, + (LPTHREAD_START_ROUTINE)load_library_w, + mem, 0, &thread_id); + if (!thread) { + goto fail; + } + + if (WaitForSingleObject(thread, 4000) == WAIT_OBJECT_0) { + DWORD code; + GetExitCodeThread(thread, &code); + ret = (code != 0) ? 0 : INJECT_ERROR_INJECT_FAILED; + + SetLastError(0); + } + +fail: + if (ret == INJECT_ERROR_UNLIKELY_FAIL) { + last_error = GetLastError(); + } + if (thread) { + CloseHandle(thread); + } + if (mem) { + virtual_free_ex(process, mem, 0, MEM_RELEASE); + } + if (last_error != 0) { + SetLastError(last_error); + } + + return ret; +} + +/* ------------------------------------------------------------------------- */ + +typedef HHOOK(WINAPI *set_windows_hook_ex_t)(int, HOOKPROC, HINSTANCE, DWORD); + +#define RETRY_INTERVAL_MS 500 +#define TOTAL_RETRY_TIME_MS 4000 +#define RETRY_COUNT (TOTAL_RETRY_TIME_MS / RETRY_INTERVAL_MS) + +int inject_library_safe_obf(DWORD thread_id, const wchar_t *dll, + const char *set_windows_hook_ex_obf, uint64_t obf1) +{ + HMODULE user32 = GetModuleHandleW(L"USER32"); + set_windows_hook_ex_t set_windows_hook_ex; + HMODULE lib = LoadLibraryW(dll); + HOOKPROC proc; + HHOOK hook; + size_t i; + + if (!lib || !user32) { + return INJECT_ERROR_UNLIKELY_FAIL; + } + +#ifdef _WIN64 + proc = (HOOKPROC)GetProcAddress(lib, "dummy_debug_proc"); +#else + proc = (HOOKPROC)GetProcAddress(lib, "_dummy_debug_proc@12"); +#endif + + if (!proc) { + return INJECT_ERROR_UNLIKELY_FAIL; + } + + set_windows_hook_ex = (set_windows_hook_ex_t)ms_get_obfuscated_func( + user32, set_windows_hook_ex_obf, obf1); + + hook = set_windows_hook_ex(WH_GETMESSAGE, proc, lib, thread_id); + if (!hook) { + return GetLastError(); + } + + /* SetWindowsHookEx does not inject the library in to the target + * process unless the event associated with it has occurred, so + * repeatedly send the hook message to start the hook at small + * intervals to signal to SetWindowsHookEx to process the message and + * therefore inject the library in to the target process. Repeating + * this is mostly just a precaution. */ + + for (i = 0; i < RETRY_COUNT; i++) { + Sleep(RETRY_INTERVAL_MS); + PostThreadMessage(thread_id, WM_USER + 432, 0, (LPARAM)hook); + } + return 0; +} diff --git a/shared/obs-inject-library/inject-library.h b/shared/obs-inject-library/inject-library.h new file mode 100644 index 00000000000000..f65a129bdd4cf4 --- /dev/null +++ b/shared/obs-inject-library/inject-library.h @@ -0,0 +1,20 @@ +#include +#include + +#define INJECT_ERROR_INJECT_FAILED -1 +#define INJECT_ERROR_INVALID_PARAMS -2 +#define INJECT_ERROR_OPEN_PROCESS_FAIL -3 +#define INJECT_ERROR_UNLIKELY_FAIL -4 + +extern int inject_library_obf(HANDLE process, const wchar_t *dll, + const char *create_remote_thread_obf, + uint64_t obf1, + const char *write_process_memory_obf, + uint64_t obf2, const char *virtual_alloc_ex_obf, + uint64_t obf3, const char *virtual_free_ex_obf, + uint64_t obf4, const char *load_library_w_obf, + uint64_t obf5); + +extern int inject_library_safe_obf(DWORD thread_id, const wchar_t *dll, + const char *set_windows_hook_ex_obf, + uint64_t obf1); diff --git a/shared/obs-shared-memory-queue/CMakeLists.txt b/shared/obs-shared-memory-queue/CMakeLists.txt new file mode 100644 index 00000000000000..2cc7538f021e67 --- /dev/null +++ b/shared/obs-shared-memory-queue/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +if(NOT TARGET OBS::tiny-nv12-scale) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-tiny-nv12-scale" obs-tiny-nv12-scale) +endif() + +add_library(obs-shared-memory-queue INTERFACE) +add_library(OBS::shared-memory-queue ALIAS obs-shared-memory-queue) +target_sources(obs-shared-memory-queue INTERFACE shared-memory-queue.c shared-memory-queue.h) +target_include_directories(obs-shared-memory-queue INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(obs-shared-memory-queue INTERFACE OBS::tiny-nv12-scale) diff --git a/shared/obs-shared-memory-queue/shared-memory-queue.c b/shared/obs-shared-memory-queue/shared-memory-queue.c new file mode 100644 index 00000000000000..f1cdbf7ae078f3 --- /dev/null +++ b/shared/obs-shared-memory-queue/shared-memory-queue.c @@ -0,0 +1,225 @@ +#include +#include "shared-memory-queue.h" +#include "tiny-nv12-scale.h" + +#define VIDEO_NAME L"OBSVirtualCamVideo" + +enum queue_type { + SHARED_QUEUE_TYPE_VIDEO, +}; + +struct queue_header { + volatile uint32_t write_idx; + volatile uint32_t read_idx; + volatile uint32_t state; + + uint32_t offsets[3]; + + uint32_t type; + + uint32_t cx; + uint32_t cy; + uint64_t interval; + + uint32_t reserved[8]; +}; + +struct video_queue { + HANDLE handle; + bool ready_to_read; + struct queue_header *header; + uint64_t *ts[3]; + uint8_t *frame[3]; + long last_inc; + int dup_counter; + bool is_writer; +}; + +#define ALIGN_SIZE(size, align) size = (((size) + (align - 1)) & (~(align - 1))) +#define FRAME_HEADER_SIZE 32 + +video_queue_t *video_queue_create(uint32_t cx, uint32_t cy, uint64_t interval) +{ + struct video_queue vq = {0}; + struct video_queue *pvq; + DWORD frame_size = cx * cy * 3 / 2; + uint32_t offset_frame[3]; + DWORD size; + + size = sizeof(struct queue_header); + + ALIGN_SIZE(size, 32); + + offset_frame[0] = size; + size += frame_size + FRAME_HEADER_SIZE; + ALIGN_SIZE(size, 32); + + offset_frame[1] = size; + size += frame_size + FRAME_HEADER_SIZE; + ALIGN_SIZE(size, 32); + + offset_frame[2] = size; + size += frame_size + FRAME_HEADER_SIZE; + ALIGN_SIZE(size, 32); + + struct queue_header header = {0}; + + header.state = SHARED_QUEUE_STATE_STARTING; + header.cx = cx; + header.cy = cy; + header.interval = interval; + vq.is_writer = true; + + for (size_t i = 0; i < 3; i++) { + uint32_t off = offset_frame[i]; + header.offsets[i] = off; + } + + /* fail if already in use */ + vq.handle = OpenFileMappingW(FILE_MAP_READ, false, VIDEO_NAME); + if (vq.handle) { + CloseHandle(vq.handle); + return NULL; + } + + vq.handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, size, VIDEO_NAME); + if (!vq.handle) { + return NULL; + } + + vq.header = (struct queue_header *)MapViewOfFile( + vq.handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (!vq.header) { + CloseHandle(vq.handle); + return NULL; + } + memcpy(vq.header, &header, sizeof(header)); + + for (size_t i = 0; i < 3; i++) { + uint32_t off = offset_frame[i]; + vq.ts[i] = (uint64_t *)(((uint8_t *)vq.header) + off); + vq.frame[i] = ((uint8_t *)vq.header) + off + FRAME_HEADER_SIZE; + } + pvq = malloc(sizeof(vq)); + if (!pvq) { + CloseHandle(vq.handle); + return NULL; + } + memcpy(pvq, &vq, sizeof(vq)); + return pvq; +} + +video_queue_t *video_queue_open() +{ + struct video_queue vq = {0}; + + vq.handle = OpenFileMappingW(FILE_MAP_READ, false, VIDEO_NAME); + if (!vq.handle) { + return NULL; + } + + vq.header = (struct queue_header *)MapViewOfFile( + vq.handle, FILE_MAP_READ, 0, 0, 0); + if (!vq.header) { + CloseHandle(vq.handle); + return NULL; + } + + struct video_queue *pvq = malloc(sizeof(vq)); + if (!pvq) { + CloseHandle(vq.handle); + return NULL; + } + memcpy(pvq, &vq, sizeof(vq)); + return pvq; +} + +void video_queue_close(video_queue_t *vq) +{ + if (!vq) { + return; + } + if (vq->is_writer) { + vq->header->state = SHARED_QUEUE_STATE_STOPPING; + } + + UnmapViewOfFile(vq->header); + CloseHandle(vq->handle); + free(vq); +} + +void video_queue_get_info(video_queue_t *vq, uint32_t *cx, uint32_t *cy, + uint64_t *interval) +{ + struct queue_header *qh = vq->header; + *cx = qh->cx; + *cy = qh->cy; + *interval = qh->interval; +} + +#define get_idx(inc) ((unsigned long)inc % 3) + +void video_queue_write(video_queue_t *vq, uint8_t **data, uint32_t *linesize, + uint64_t timestamp) +{ + struct queue_header *qh = vq->header; + long inc = ++qh->write_idx; + + unsigned long idx = get_idx(inc); + size_t size = linesize[0] * qh->cy; + + *vq->ts[idx] = timestamp; + memcpy(vq->frame[idx], data[0], size); + memcpy(vq->frame[idx] + size, data[1], size / 2); + + qh->read_idx = inc; + qh->state = SHARED_QUEUE_STATE_READY; +} + +enum queue_state video_queue_state(video_queue_t *vq) +{ + if (!vq) { + return SHARED_QUEUE_STATE_INVALID; + } + + enum queue_state state = (enum queue_state)vq->header->state; + if (!vq->ready_to_read && state == SHARED_QUEUE_STATE_READY) { + for (size_t i = 0; i < 3; i++) { + size_t off = vq->header->offsets[i]; + vq->ts[i] = (uint64_t *)(((uint8_t *)vq->header) + off); + vq->frame[i] = ((uint8_t *)vq->header) + off + + FRAME_HEADER_SIZE; + } + vq->ready_to_read = true; + } + + return state; +} + +bool video_queue_read(video_queue_t *vq, nv12_scale_t *scale, void *dst, + uint64_t *ts) +{ + struct queue_header *qh = vq->header; + long inc = qh->read_idx; + + if (qh->state == SHARED_QUEUE_STATE_STOPPING) { + return false; + } + + if (inc == vq->last_inc) { + if (++vq->dup_counter == 10) { + return false; + } + } else { + vq->dup_counter = 0; + vq->last_inc = inc; + } + + unsigned long idx = get_idx(inc); + + *ts = *vq->ts[idx]; + + nv12_do_scale(scale, dst, vq->frame[idx]); + return true; +} diff --git a/shared/obs-shared-memory-queue/shared-memory-queue.h b/shared/obs-shared-memory-queue/shared-memory-queue.h new file mode 100644 index 00000000000000..5e643529976e55 --- /dev/null +++ b/shared/obs-shared-memory-queue/shared-memory-queue.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct video_queue; +struct nv12_scale; +typedef struct video_queue video_queue_t; +typedef struct nv12_scale nv12_scale_t; + +enum queue_state { + SHARED_QUEUE_STATE_INVALID, + SHARED_QUEUE_STATE_STARTING, + SHARED_QUEUE_STATE_READY, + SHARED_QUEUE_STATE_STOPPING, +}; + +extern video_queue_t *video_queue_create(uint32_t cx, uint32_t cy, + uint64_t interval); +extern video_queue_t *video_queue_open(); +extern void video_queue_close(video_queue_t *vq); + +extern void video_queue_get_info(video_queue_t *vq, uint32_t *cx, uint32_t *cy, + uint64_t *interval); +extern void video_queue_write(video_queue_t *vq, uint8_t **data, + uint32_t *linesize, uint64_t timestamp); +extern enum queue_state video_queue_state(video_queue_t *vq); +extern bool video_queue_read(video_queue_t *vq, nv12_scale_t *scale, void *dst, + uint64_t *ts); + +#ifdef __cplusplus +} +#endif diff --git a/shared/obs-tiny-nv12-scale/CMakeLists.txt b/shared/obs-tiny-nv12-scale/CMakeLists.txt new file mode 100644 index 00000000000000..4b25249756c5da --- /dev/null +++ b/shared/obs-tiny-nv12-scale/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +add_library(obs-tiny-nv12-scale INTERFACE) +add_library(OBS::tiny-nv12-scale ALIAS obs-tiny-nv12-scale) +target_sources(obs-tiny-nv12-scale INTERFACE tiny-nv12-scale.c tiny-nv12-scale.h) +target_include_directories(obs-tiny-nv12-scale INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/shared/obs-tiny-nv12-scale/tiny-nv12-scale.c b/shared/obs-tiny-nv12-scale/tiny-nv12-scale.c new file mode 100644 index 00000000000000..697267b09c50ce --- /dev/null +++ b/shared/obs-tiny-nv12-scale/tiny-nv12-scale.c @@ -0,0 +1,207 @@ +#include +#include "tiny-nv12-scale.h" + +/* TODO: optimize this stuff later, or replace with something better. it's + * kind of garbage. although normally it shouldn't be called that often. plus + * it's nearest neighbor so not really a huge deal. at the very least it + * should be sse2 at some point. */ + +void nv12_scale_init(nv12_scale_t *s, enum target_format format, int dst_cx, + int dst_cy, int src_cx, int src_cy) +{ + s->format = format; + + s->src_cx = src_cx; + s->src_cy = src_cy; + + s->dst_cx = dst_cx; + s->dst_cy = dst_cy; +} + +static void nv12_scale_nearest(nv12_scale_t *s, uint8_t *dst_start, + const uint8_t *src) +{ + register uint8_t *dst = dst_start; + const int src_cx = s->src_cx; + const int src_cy = s->src_cy; + const int dst_cx = s->dst_cx; + const int dst_cy = s->dst_cy; + + /* lum */ + for (int y = 0; y < dst_cy; y++) { + const int src_line = y * src_cy / dst_cy * s->src_cx; + + for (int x = 0; x < dst_cx; x++) { + const int src_x = x * src_cx / dst_cx; + + *(dst++) = src[src_line + src_x]; + } + } + + src += src_cx * src_cy; + + /* uv */ + const int dst_cx_d2 = dst_cx / 2; + const int dst_cy_d2 = dst_cy / 2; + + for (int y = 0; y < dst_cy_d2; y++) { + const int src_line = y * src_cy / dst_cy * src_cx; + + for (int x = 0; x < dst_cx_d2; x++) { + const int src_x = x * src_cx / dst_cx * 2; + const int pos = src_line + src_x; + + *(dst++) = src[pos]; + *(dst++) = src[pos + 1]; + } + } +} + +static void nv12_scale_nearest_to_i420(nv12_scale_t *s, uint8_t *dst_start, + const uint8_t *src) +{ + register uint8_t *dst = dst_start; + const int src_cx = s->src_cx; + const int src_cy = s->src_cy; + const int dst_cx = s->dst_cx; + const int dst_cy = s->dst_cy; + const int size = src_cx * src_cy; + + /* lum */ + for (int y = 0; y < dst_cy; y++) { + const int src_line = y * src_cy / dst_cy * s->src_cx; + + for (int x = 0; x < dst_cx; x++) { + const int src_x = x * src_cx / dst_cx; + + *(dst++) = src[src_line + src_x]; + } + } + + src += size; + + /* uv */ + const int dst_cx_d2 = dst_cx / 2; + const int dst_cy_d2 = dst_cy / 2; + + register uint8_t *dst2 = dst + dst_cx * dst_cy / 4; + + for (int y = 0; y < dst_cy_d2; y++) { + const int src_line = y * src_cy / dst_cy * src_cx; + + for (int x = 0; x < dst_cx_d2; x++) { + const int src_x = x * src_cx / dst_cx * 2; + const int pos = src_line + src_x; + + *(dst++) = src[pos]; + *(dst2++) = src[pos + 1]; + } + } +} + +static void nv12_convert_to_i420(nv12_scale_t *s, uint8_t *dst_start, + const uint8_t *src_start) +{ + const int size = s->src_cx * s->src_cy; + const int size_d4 = size / 4; + + memcpy(dst_start, src_start, size); + + register uint8_t *dst1 = dst_start + size; + register uint8_t *dst2 = dst1 + size_d4; + register uint8_t *dst_end = dst2 + size_d4; + register const uint8_t *src = src_start + size; + + while (dst2 < dst_end) { + *(dst1++) = *(src++); + *(dst2++) = *(src++); + } +} + +static void nv12_scale_nearest_to_yuy2(nv12_scale_t *s, uint8_t *dst_start, + const uint8_t *src) +{ + register uint8_t *dst = dst_start; + const int src_cx = s->src_cx; + const int src_cy = s->src_cy; + const int dst_cx = s->dst_cx; + const int dst_cy = s->dst_cy; + const int src_cx_d2 = src_cx / 2; + const int src_cy_d2 = src_cy / 2; + const int dst_cx_d2 = dst_cx / 2; + const int dst_cy_d2 = dst_cy / 2; + const int size = src_cx * src_cy; + + const uint8_t *src_uv = src + size; + + register int uv_flip = 0; + + for (int y = 0; y < dst_cy; y++) { + const int src_line = y * src_cy / dst_cy * s->src_cx; + const int src_line_d2 = + y / 2 * src_cy_d2 / dst_cy_d2 * s->src_cx; + + for (int x = 0; x < dst_cx; x++) { + const int src_x = x * src_cx / dst_cx; + const int src_x_d2 = x / 2 * src_cx_d2 / dst_cx_d2; + const int pos = src_line + src_x; + const int pos_uv = src_line_d2 + src_x_d2 * 2 + uv_flip; + + *(dst++) = src[pos]; + *(dst++) = src_uv[pos_uv]; + + uv_flip ^= 1; + } + } +} + +static void nv12_convert_to_yuy2(nv12_scale_t *s, uint8_t *dst_start, + const uint8_t *src_start) +{ + const int size = s->src_cx * s->src_cy; + const int size_dst_line = s->src_cx * 2; + + register const uint8_t *src_y = src_start; + register const uint8_t *src_uv = src_y + size; + register uint8_t *dst = dst_start; + register uint8_t *dst_end = dst + size * 2; + + while (dst < dst_end) { + register uint8_t *dst_line_end = dst + size_dst_line; + const uint8_t *src_uv_start = src_uv; + while (dst < dst_line_end) { + *(dst++) = *(src_y++); + *(dst++) = *(src_uv++); + *(dst++) = *(src_y++); + *(dst++) = *(src_uv++); + } + + dst_line_end = dst + size_dst_line; + src_uv = src_uv_start; + while (dst < dst_line_end) { + *(dst++) = *(src_y++); + *(dst++) = *(src_uv++); + *(dst++) = *(src_y++); + *(dst++) = *(src_uv++); + } + } +} + +void nv12_do_scale(nv12_scale_t *s, uint8_t *dst, const uint8_t *src) +{ + if (s->src_cx == s->dst_cx && s->src_cy == s->dst_cy) { + if (s->format == TARGET_FORMAT_I420) + nv12_convert_to_i420(s, dst, src); + else if (s->format == TARGET_FORMAT_YUY2) + nv12_convert_to_yuy2(s, dst, src); + else + memcpy(dst, src, s->src_cx * s->src_cy * 3 / 2); + } else { + if (s->format == TARGET_FORMAT_I420) + nv12_scale_nearest_to_i420(s, dst, src); + else if (s->format == TARGET_FORMAT_YUY2) + nv12_scale_nearest_to_yuy2(s, dst, src); + else + nv12_scale_nearest(s, dst, src); + } +} diff --git a/shared/obs-tiny-nv12-scale/tiny-nv12-scale.h b/shared/obs-tiny-nv12-scale/tiny-nv12-scale.h new file mode 100644 index 00000000000000..fb8bc580aa6b44 --- /dev/null +++ b/shared/obs-tiny-nv12-scale/tiny-nv12-scale.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum target_format { + TARGET_FORMAT_NV12, + TARGET_FORMAT_I420, + TARGET_FORMAT_YUY2, +}; + +struct nv12_scale { + enum target_format format; + + int src_cx; + int src_cy; + + int dst_cx; + int dst_cy; +}; + +typedef struct nv12_scale nv12_scale_t; + +extern void nv12_scale_init(nv12_scale_t *s, enum target_format format, + int dst_cx, int dst_cy, int src_cx, int src_cy); +extern void nv12_do_scale(nv12_scale_t *s, uint8_t *dst, const uint8_t *src); + +#ifdef __cplusplus +} +#endif From d81fcd70e067c4b7fa1c3268acdf8b0866cfe16f Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 2 Aug 2024 20:28:11 +0200 Subject: [PATCH 0419/1073] cmake: Update cross-platform build project management for Windows Enables creation of x64 and x86 child projects when building on ARM64 and decouples functionality from legacy_check function --- .gitmodules | 2 +- CMakeLists.txt | 8 +- build-aux/.run-format.zsh | 4 +- cmake/32bit/projects.cmake | 63 ------ cmake/common/buildspec_common.cmake | 3 + cmake/common/helpers_common.cmake | 2 +- cmake/windows/architecture.cmake | 97 +++++++++ cmake/windows/buildspec.cmake | 2 +- cmake/windows/defaults.cmake | 2 +- cmake/windows/helpers.cmake | 33 ++- deps/libdshowcapture/CMakeLists.txt | 43 ++++ .../libdshowcapture/src | 0 libobs/cmake/32bit-build.cmake | 16 -- libobs/cmake/os-windows.cmake | 45 ++-- plugins/win-capture/CMakeLists.txt | 21 +- .../get-graphics-offsets/CMakeLists.txt | 49 ++++- .../cmake/32bit-build.cmake | 9 - .../get-graphics-offsets/cmake/32bit.cmake | 6 - .../win-capture/graphics-hook/CMakeLists.txt | 64 ++++-- .../graphics-hook/cmake/32bit-build.cmake | 13 -- .../graphics-hook/cmake/32bit.cmake | 6 - .../graphics-hook/graphics-hook.rc | 2 +- .../win-capture/inject-helper/CMakeLists.txt | 39 +++- .../inject-helper/cmake/32bit-build.cmake | 9 - .../inject-helper/cmake/32bit.cmake | 6 - plugins/win-dshow/CMakeLists.txt | 44 ++-- plugins/win-dshow/cmake/legacy.cmake | 205 ++++++++++-------- .../virtualcam-module/CMakeLists.txt | 195 ++++++++++------- .../virtualcam-module/cmake/32bit-build.cmake | 49 ----- .../virtualcam-module/cmake/32bit.cmake | 6 - .../virtualcam-module/virtualcam-filter.hpp | 8 +- plugins/win-dshow/win-dshow-encoder.cpp | 4 + plugins/win-dshow/win-dshow.cpp | 4 + 33 files changed, 602 insertions(+), 457 deletions(-) delete mode 100644 cmake/32bit/projects.cmake create mode 100644 cmake/windows/architecture.cmake create mode 100644 deps/libdshowcapture/CMakeLists.txt rename plugins/win-dshow/libdshowcapture => deps/libdshowcapture/src (100%) delete mode 100644 libobs/cmake/32bit-build.cmake delete mode 100644 plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake delete mode 100644 plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake delete mode 100644 plugins/win-capture/graphics-hook/cmake/32bit-build.cmake delete mode 100644 plugins/win-capture/graphics-hook/cmake/32bit.cmake delete mode 100644 plugins/win-capture/inject-helper/cmake/32bit-build.cmake delete mode 100644 plugins/win-capture/inject-helper/cmake/32bit.cmake delete mode 100644 plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake delete mode 100644 plugins/win-dshow/virtualcam-module/cmake/32bit.cmake diff --git a/.gitmodules b/.gitmodules index 7f21ac7eda1cf6..180e3fe06b69a5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "plugins/win-dshow/libdshowcapture"] - path = plugins/win-dshow/libdshowcapture + path = deps/libdshowcapture/src url = https://github.com/obsproject/libdshowcapture.git [submodule "plugins/obs-browser"] path = plugins/obs-browser diff --git a/CMakeLists.txt b/CMakeLists.txt index f4cc48c10ebf23..70e029723a9d8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,11 @@ if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Darwin)" OR OBS_CMAKE_VERSION VERSION_GREATE project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) - if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Windows)" AND CMAKE_SIZEOF_VOID_P EQUAL 4) - include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/32bit/projects.cmake") - return() + if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/architecture.cmake") + if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + return() + endif() endif() include(compilerconfig) diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh index 11e9e64c2de6f6..26c92f8f12b99c 100755 --- a/build-aux/.run-format.zsh +++ b/build-aux/.run-format.zsh @@ -56,7 +56,7 @@ invoke_formatter() { if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared)/**/*.(c|cpp|h|hpp|m|mm)(.N)) - source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|libdshowcapture)/*}) local -a format_args=(-style=file -fallback-style=none) if (( _loglevel > 2 )) format_args+=(--verbose) @@ -104,7 +104,7 @@ invoke_formatter() { if (( ! #source_files )) source_files=(CMakeLists.txt (libobs|libobs-*|UI|plugins|deps|shared|cmake|test)/**/(CMakeLists.txt|*.cmake)(.N)) - source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|libdshowcapture)/*}) source_files=(${source_files:#(cmake/Modules/*|*/legacy.cmake)}) check_files() { diff --git a/cmake/32bit/projects.cmake b/cmake/32bit/projects.cmake deleted file mode 100644 index 54978c87fae5f8..00000000000000 --- a/cmake/32bit/projects.cmake +++ /dev/null @@ -1,63 +0,0 @@ -# OBS CMake 32-bit slice module - -include_guard(GLOBAL) - -include(compilerconfig) - -# legacy_check: Helper macro to automatically include available 32-bit build script -macro(legacy_check) - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/32bit-build.cmake") - include(cmake/32bit-build.cmake) - endif() - return() -endmacro() - -# target_disable_feature: Stub macro for 32-bit projects -macro(target_disable_feature) -endmacro() - -# target_disable: Stub macro for 32-bit projects -macro(target_disable) -endmacro() - -# check_uuid: Helper function to check for valid UUID -function(check_uuid uuid_string return_value) - set(valid_uuid TRUE) - # gersemi: off - set(uuid_token_lengths 8 4 4 4 12) - # gersemi: on - set(token_num 0) - - string(REPLACE "-" ";" uuid_tokens ${uuid_string}) - list(LENGTH uuid_tokens uuid_num_tokens) - - if(uuid_num_tokens EQUAL 5) - message(DEBUG "UUID ${uuid_string} is valid with 5 tokens.") - foreach(uuid_token IN LISTS uuid_tokens) - list(GET uuid_token_lengths ${token_num} uuid_target_length) - string(LENGTH "${uuid_token}" uuid_actual_length) - if(uuid_actual_length EQUAL uuid_target_length) - string(REGEX MATCH "[0-9a-fA-F]+" uuid_hex_match ${uuid_token}) - if(NOT uuid_hex_match STREQUAL uuid_token) - set(valid_uuid FALSE) - break() - endif() - else() - set(valid_uuid FALSE) - break() - endif() - math(EXPR token_num "${token_num}+1") - endforeach() - else() - set(valid_uuid FALSE) - endif() - message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") - set(${return_value} ${valid_uuid} PARENT_SCOPE) -endfunction() - -if(OS_WINDOWS) - include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/buildspec.cmake") - add_subdirectory(libobs) - add_subdirectory(plugins/win-capture) - add_subdirectory(plugins/win-dshow) -endif() diff --git a/cmake/common/buildspec_common.cmake b/cmake/common/buildspec_common.cmake index 5798efa6d9c3e3..18d1431476e2d2 100644 --- a/cmake/common/buildspec_common.cmake +++ b/cmake/common/buildspec_common.cmake @@ -51,6 +51,9 @@ function(_check_dependencies) string(JSON dependency_data GET ${buildspec} dependencies) foreach(dependency IN LISTS dependencies_list) + if(dependency STREQUAL cef AND NOT ENABLE_BROWSER) + continue() + endif() if(dependency STREQUAL cef AND arch STREQUAL universal) if(CMAKE_OSX_ARCHITECTURES MATCHES ".+;.+") continue() diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index ade057e1d516a8..2fd493d34428c4 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -138,7 +138,7 @@ function(_handle_generator_expression_dependency library) if(TARGET ${gen_target}) set(${var_FOUND_VAR} "${gen_target}") endif() - elseif(library MATCHES "\\$<.*Qt6::EntryPointPrivate>" OR library MATCHES "\\$<.*Qt6::QDarwin.+PermissionPlugin>") + elseif(library MATCHES "\\$<.*Qt6::(EntryPointPrivate|QDarwin.*PermissionPlugin)>") set(${var_FOUND_VAR} "${var_FOUND_VAR}-SKIP") else() # Unknown or unimplemented generator expression found. Abort script run to either add to ignore list or implement diff --git a/cmake/windows/architecture.cmake b/cmake/windows/architecture.cmake new file mode 100644 index 00000000000000..941e6697e3c422 --- /dev/null +++ b/cmake/windows/architecture.cmake @@ -0,0 +1,97 @@ +# OBS CMake Windows Architecture Helper + +include_guard(GLOBAL) + +include(compilerconfig) + +if(NOT DEFINED OBS_PARENT_ARCHITECTURE) + if(CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") + set(OBS_PARENT_ARCHITECTURE ${CMAKE_GENERATOR_PLATFORM}) + else() + message(FATAL_ERROR "Unsupported generator platform for Windows builds: ${CMAKE_GENERATOR_PLATFORM}!") + endif() +endif() + +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + if(OBS_PARENT_ARCHITECTURE STREQUAL x64) + execute_process( + COMMAND + "${CMAKE_COMMAND}" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A Win32 -G + "${CMAKE_GENERATOR}" -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DOBS_CMAKE_VERSION:STRING=3.0.0 + -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID} -DCMAKE_MESSAGE_LOG_LEVEL:STRING=${CMAKE_MESSAGE_LOG_LEVEL} + -DENABLE_CCACHE:BOOL=${ENABLE_CCACHE} -DOBS_PARENT_ARCHITECTURE:STRING=x64 + RESULT_VARIABLE _process_result + COMMAND_ERROR_IS_FATAL ANY + ) + endif() +else() + # legacy_check: Stub macro for child architecture builds + macro(legacy_check) + endmacro() + + # target_disable_feature: Stub macro for child architecture builds + macro(target_disable_feature) + endmacro() + + # target_disable: Stub macro for child architecture builds + macro(target_disable) + endmacro() + + # target_add_resource: Stub macro for child architecture builds + macro(target_add_resource) + endmacro() + + # target_export: Stub macro for child architecture builds + macro(target_export) + endmacro() + + # set_target_properties_obs: Stub macro for child architecture builds + macro(set_target_properties_obs) + set_target_properties(${ARGV}) + endmacro() + + # check_uuid: Helper function to check for valid UUID + function(check_uuid uuid_string return_value) + set(valid_uuid TRUE) + # gersemi: off + set(uuid_token_lengths 8 4 4 4 12) + # gersemi: on + set(token_num 0) + + string(REPLACE "-" ";" uuid_tokens ${uuid_string}) + list(LENGTH uuid_tokens uuid_num_tokens) + + if(uuid_num_tokens EQUAL 5) + message(DEBUG "UUID ${uuid_string} is valid with 5 tokens.") + foreach(uuid_token IN LISTS uuid_tokens) + list(GET uuid_token_lengths ${token_num} uuid_target_length) + string(LENGTH "${uuid_token}" uuid_actual_length) + if(uuid_actual_length EQUAL uuid_target_length) + string(REGEX MATCH "[0-9a-fA-F]+" uuid_hex_match ${uuid_token}) + if(NOT uuid_hex_match STREQUAL uuid_token) + set(valid_uuid FALSE) + break() + endif() + else() + set(valid_uuid FALSE) + break() + endif() + math(EXPR token_num "${token_num}+1") + endforeach() + else() + set(valid_uuid FALSE) + endif() + message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") + set(${return_value} ${valid_uuid} PARENT_SCOPE) + endfunction() + + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/buildspec.cmake") + + add_subdirectory(libobs) + add_subdirectory(plugins/win-capture/get-graphics-offsets) + add_subdirectory(plugins/win-capture/graphics-hook) + add_subdirectory(plugins/win-capture/inject-helper) + add_subdirectory(plugins/win-dshow/virtualcam-module) + + return() +endif() diff --git a/cmake/windows/buildspec.cmake b/cmake/windows/buildspec.cmake index 72a2e0c6d614f7..b15cbb2e2b8113 100644 --- a/cmake/windows/buildspec.cmake +++ b/cmake/windows/buildspec.cmake @@ -18,7 +18,7 @@ function(_check_dependencies_windows) set(arch x86) set(dependencies_list prebuilt) else() - set(arch ${CMAKE_GENERATOR_PLATFORM}) + string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" arch) set(dependencies_list prebuilt qt6 cef) endif() set(platform windows-${arch}) diff --git a/cmake/windows/defaults.cmake b/cmake/windows/defaults.cmake index 137f0415eae200..a9c4c1b1ac2d79 100644 --- a/cmake/windows/defaults.cmake +++ b/cmake/windows/defaults.cmake @@ -24,7 +24,7 @@ set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE) include(buildspec) include(cpackconfig) -if(CMAKE_SIZEOF_VOID_P EQUAL 8) +if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) execute_process( COMMAND "${CMAKE_COMMAND}" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A Win32 -G diff --git a/cmake/windows/helpers.cmake b/cmake/windows/helpers.cmake index f9b2e940b7e8d7..7c0abc271e1764 100644 --- a/cmake/windows/helpers.cmake +++ b/cmake/windows/helpers.cmake @@ -26,7 +26,7 @@ function(set_target_properties_obs target) elseif(target STREQUAL inject-helper OR target STREQUAL get-graphics-offsets) set(OBS_EXECUTABLE_DESTINATION "${OBS_DATA_DESTINATION}/obs-plugins/win-capture") - _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION} 32BIT) + _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION} x86) endif() _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION}) @@ -66,11 +66,11 @@ function(set_target_properties_obs target) target_add_resource(graphics-hook "${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan64.json" "${target_destination}") target_add_resource(graphics-hook "${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan32.json" "${target_destination}") - _target_install_obs(${target} DESTINATION ${target_destination} 32BIT) + _target_install_obs(${target} DESTINATION ${target_destination} x86) elseif(target STREQUAL obs-virtualcam-module) set(target_destination "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow") - _target_install_obs(${target} DESTINATION ${target_destination} 32BIT) + _target_install_obs(${target} DESTINATION ${target_destination} x86) else() set(target_destination "${OBS_PLUGIN_DESTINATION}") endif() @@ -171,12 +171,12 @@ endfunction() # _target_install_obs: Helper function to install build artifacts to rundir and install location function(_target_install_obs target) - set(options "32BIT") + set(options "x86" "x64") set(oneValueArgs "DESTINATION" "LIBRARY_DESTINATION" "HEADER_DESTINATION") set(multiValueArgs "") cmake_parse_arguments(PARSE_ARGV 0 _TIO "${options}" "${oneValueArgs}" "${multiValueArgs}") - if(_TIO_32BIT) + if(_TIO_x86) get_target_property(target_type ${target} TYPE) if(target_type STREQUAL EXECUTABLE) set(suffix exe) @@ -189,7 +189,7 @@ function(_target_install_obs target) set(32bit_project_path "${OBS_SOURCE_DIR}/build_x86/${project_path}") set(target_file "${32bit_project_path}/$/${target}32.${suffix}") set(target_pdb_file "${32bit_project_path}/$/${target}32.pdb") - set(comment "Copy ${target} (32-bit) to destination") + set(comment "Copy ${target} (x86) to destination") install( FILES "${32bit_project_path}/$/${target}32.${suffix}" @@ -197,6 +197,27 @@ function(_target_install_obs target) COMPONENT Runtime OPTIONAL ) + elseif(_TIO_x64) + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL EXECUTABLE) + set(suffix exe) + else() + set(suffix dll) + endif() + + cmake_path(RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR BASE_DIRECTORY "${OBS_SOURCE_DIR}" OUTPUT_VARIABLE project_path) + + set(32bit_project_path "${OBS_SOURCE_DIR}/build_x64/${project_path}") + set(target_file "${32bit_project_path}/$/${target}64.${suffix}") + set(target_pdb_file "${32bit_project_path}/$/${target}64.pdb") + set(comment "Copy ${target} (x64) to destination") + + install( + FILES "${32bit_project_path}/$/${target}64.${suffix}" + DESTINATION "${_TIO_DESTINATION}" + COMPONENT Runtime + OPTIONAL + ) else() set(target_file "$") set(target_pdb_file "$") diff --git a/deps/libdshowcapture/CMakeLists.txt b/deps/libdshowcapture/CMakeLists.txt new file mode 100644 index 00000000000000..8e7cd93e444864 --- /dev/null +++ b/deps/libdshowcapture/CMakeLists.txt @@ -0,0 +1,43 @@ +add_library(libdshowcapture INTERFACE) +add_library(OBS::libdshowcapture ALIAS libdshowcapture) + +target_sources( + libdshowcapture + INTERFACE src/dshowcapture.hpp + src/source/capture-filter.cpp + src/source/capture-filter.hpp + src/source/device-vendor.cpp + src/source/device.cpp + src/source/device.hpp + src/source/dshow-base.cpp + src/source/dshow-base.hpp + src/source/dshow-demux.cpp + src/source/dshow-demux.hpp + src/source/dshow-device-defs.hpp + src/source/dshow-encoded-device.cpp + src/source/dshow-enum.cpp + src/source/dshow-enum.hpp + src/source/dshow-formats.cpp + src/source/dshow-formats.hpp + src/source/dshow-media-type.cpp + src/source/dshow-media-type.hpp + src/source/dshowcapture.cpp + src/source/dshowencode.cpp + src/source/encoder.cpp + src/source/encoder.hpp + src/source/external/IVideoCaptureFilter.h + src/source/log.cpp + src/source/log.hpp + src/source/output-filter.cpp + src/source/output-filter.hpp + src/external/capture-device-support/Library/EGAVResult.cpp + src/external/capture-device-support/Library/ElgatoUVCDevice.cpp + src/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp + src/external/capture-device-support/SampleCode/DriverInterface.cpp) + +target_include_directories( + libdshowcapture INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src" + "${CMAKE_CURRENT_SOURCE_DIR}/src/external/capture-device-support/Library") + +target_compile_definitions(libdshowcapture INTERFACE _UP_WINDOWS=1) +target_compile_options(libdshowcapture INTERFACE /wd4018) diff --git a/plugins/win-dshow/libdshowcapture b/deps/libdshowcapture/src similarity index 100% rename from plugins/win-dshow/libdshowcapture rename to deps/libdshowcapture/src diff --git a/libobs/cmake/32bit-build.cmake b/libobs/cmake/32bit-build.cmake deleted file mode 100644 index 9fb054b9eafb5f..00000000000000 --- a/libobs/cmake/32bit-build.cmake +++ /dev/null @@ -1,16 +0,0 @@ -if(OS_WINDOWS) - add_library(obs-obfuscate INTERFACE) - add_library(OBS::obfuscate ALIAS obs-obfuscate) - target_sources(obs-obfuscate INTERFACE util/windows/obfuscate.c util/windows/obfuscate.h) - target_include_directories(obs-obfuscate INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - add_library(obs-comutils INTERFACE) - add_library(OBS::COMutils ALIAS obs-comutils) - target_sources(obs-comutils INTERFACE util/windows/ComPtr.hpp) - target_include_directories(obs-comutils INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - add_library(obs-winhandle INTERFACE) - add_library(OBS::winhandle ALIAS obs-winhandle) - target_sources(obs-winhandle INTERFACE util/windows/WinHandle.hpp) - target_include_directories(obs-winhandle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") -endif() diff --git a/libobs/cmake/os-windows.cmake b/libobs/cmake/os-windows.cmake index caa8d25d06309c..2f56e9fd823de5 100644 --- a/libobs/cmake/os-windows.cmake +++ b/libobs/cmake/os-windows.cmake @@ -1,23 +1,36 @@ -if(NOT TARGET OBS::w32-pthreads) - add_subdirectory("${CMAKE_SOURCE_DIR}/deps/w32-pthreads" "${CMAKE_BINARY_DIR}/deps/w32-pthreads") +if(NOT TARGET OBS::obfuscate) + add_library(obs-obfuscate INTERFACE) + add_library(OBS::obfuscate ALIAS obs-obfuscate) + target_sources(obs-obfuscate INTERFACE util/windows/obfuscate.c util/windows/obfuscate.h) + target_include_directories(obs-obfuscate INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") endif() -configure_file(cmake/windows/obs-module.rc.in libobs.rc) +if(NOT TARGET OBS::comutils) + add_library(obs-comutils INTERFACE) + add_library(OBS::COMutils ALIAS obs-comutils) + target_sources(obs-comutils INTERFACE util/windows/ComPtr.hpp) + target_include_directories(obs-comutils INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +endif() -add_library(obs-obfuscate INTERFACE) -add_library(OBS::obfuscate ALIAS obs-obfuscate) -target_sources(obs-obfuscate INTERFACE util/windows/obfuscate.c util/windows/obfuscate.h) -target_include_directories(obs-obfuscate INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +if(NOT TARGET OBS::winhandle) + add_library(obs-winhandle INTERFACE) + add_library(OBS::winhandle ALIAS obs-winhandle) + target_sources(obs-winhandle INTERFACE util/windows/WinHandle.hpp) + target_include_directories(obs-winhandle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +endif() -add_library(obs-comutils INTERFACE) -add_library(OBS::COMutils ALIAS obs-comutils) -target_sources(obs-comutils INTERFACE util/windows/ComPtr.hpp) -target_include_directories(obs-comutils INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +if(NOT TARGET OBS::threading-windows) + add_library(obs-threading-windows INTERFACE) + add_library(OBS::threading-windows ALIAS obs-threading-windows) + target_sources(obs-threading-windows INTERFACE util/threading-windows.h) + target_include_directories(obs-threading-windows INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +endif() -add_library(obs-winhandle INTERFACE) -add_library(OBS::winhandle ALIAS obs-winhandle) -target_sources(obs-winhandle INTERFACE util/windows/WinHandle.hpp) -target_include_directories(obs-winhandle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +if(NOT TARGET OBS::w32-pthreads) + add_subdirectory("${CMAKE_SOURCE_DIR}/deps/w32-pthreads" "${CMAKE_BINARY_DIR}/deps/w32-pthreads") +endif() + +configure_file(cmake/windows/obs-module.rc.in libobs.rc) target_sources( libobs @@ -37,8 +50,6 @@ target_sources( util/windows/device-enum.c util/windows/device-enum.h util/windows/HRError.hpp - util/windows/obfuscate.c - util/windows/obfuscate.h util/windows/win-registry.h util/windows/win-version.h util/windows/window-helpers.c diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index e354ede6fbe6cc..0c4a9d670ddc80 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -1,23 +1,14 @@ cmake_minimum_required(VERSION 3.24...3.25) -if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) - add_library(hook-config INTERFACE) - add_library(OBS::hook-config ALIAS hook-config) - target_sources(hook-config INTERFACE graphics-hook-ver.h graphics-hook-info.h hook-helpers.h) - target_include_directories(hook-config INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - add_library(inject-library INTERFACE) - add_library(OBS::inject-library ALIAS inject-library) - target_sources(inject-library INTERFACE inject-library.c inject-library.h) - target_include_directories(inject-library INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") +legacy_check() - add_library(d3d8-api INTERFACE) - add_library(OBS::d3d8-api ALIAS d3d8-api) - target_sources(d3d8-api INTERFACE d3d8-api/d3d8.h d3d8-api/d3d8caps.h d3d8-api/d3d8types.h) - target_include_directories(d3d8-api INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/d3d8-api") +if(NOT TARGET OBS::obfuscate) + add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") endif() -legacy_check() +if(NOT TARGET OBS::inject-library) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-inject-library" "${CMAKE_BINARY_DIR}/shared/obs-inject-library") +endif() option(ENABLE_COMPAT_UPDATES "Checks for capture compatibility data updates" ON) diff --git a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt index a99df6c6ffe3b9..cdb6c3393a1b04 100644 --- a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt +++ b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt @@ -1,23 +1,48 @@ cmake_minimum_required(VERSION 3.24...3.25) -add_library(_get-graphics-offsets INTERFACE) +legacy_check() + +if(NOT TARGET OBS::d3d8-api) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-d3d8-api" obs-d3d8-api) +endif() + +if(NOT TARGET OBS::hook-config) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-hook-config" obs-hook-config) +endif() + +add_executable(get-graphics-offsets) target_sources( - _get-graphics-offsets - INTERFACE d3d8-offsets.cpp d3d9-offsets.cpp dxgi-offsets.cpp get-graphics-offsets.c get-graphics-offsets.h + get-graphics-offsets + PRIVATE d3d8-offsets.cpp d3d9-offsets.cpp dxgi-offsets.cpp get-graphics-offsets.c get-graphics-offsets.h ) target_link_libraries( - _get-graphics-offsets - INTERFACE OBS::hook-config OBS::d3d8-api d3d9.lib dxgi.lib d3d11.lib + get-graphics-offsets + PRIVATE OBS::hook-config OBS::d3d8-api d3d9.lib dxgi.lib d3d11.lib ) -legacy_check() - -add_executable(get-graphics-offsets) -target_link_libraries(get-graphics-offsets PRIVATE _get-graphics-offsets) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + add_custom_command( + TARGET get-graphics-offsets + POST_BUILD + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t get-graphics-offsets + COMMENT "Build x86 get-graphics-offsets" + ) + endif() -include(cmake/32bit.cmake) + add_dependencies(win-capture get-graphics-offsets) +endif() -set_target_properties_obs(get-graphics-offsets PROPERTIES FOLDER plugins/win-capture OUTPUT_NAME get-graphics-offsets64 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + set(_OUTPUT_NAME get-graphics-offsets64) +else() + set(_OUTPUT_NAME get-graphics-offsets32) +endif() -add_dependencies(win-capture get-graphics-offsets) +set_target_properties_obs( + get-graphics-offsets PROPERTIES + FOLDER plugins/win-capture + OUTPUT_NAME ${_OUTPUT_NAME} + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake b/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake deleted file mode 100644 index 76865eaea7e596..00000000000000 --- a/plugins/win-capture/get-graphics-offsets/cmake/32bit-build.cmake +++ /dev/null @@ -1,9 +0,0 @@ -project(get-graphics-offsets) - -add_executable(get-graphics-offsets) -target_link_libraries(get-graphics-offsets PRIVATE _get-graphics-offsets) - -set_target_properties( - get-graphics-offsets - PROPERTIES OUTPUT_NAME get-graphics-offsets32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" -) diff --git a/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake b/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake deleted file mode 100644 index 44f757657b57d1..00000000000000 --- a/plugins/win-capture/get-graphics-offsets/cmake/32bit.cmake +++ /dev/null @@ -1,6 +0,0 @@ -add_custom_command( - TARGET get-graphics-offsets - POST_BUILD - COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t get-graphics-offsets - COMMENT "Build 32-bit get-graphics-offsets" -) diff --git a/plugins/win-capture/graphics-hook/CMakeLists.txt b/plugins/win-capture/graphics-hook/CMakeLists.txt index 9434e3e5387918..3f2ce5e613140f 100644 --- a/plugins/win-capture/graphics-hook/CMakeLists.txt +++ b/plugins/win-capture/graphics-hook/CMakeLists.txt @@ -1,21 +1,32 @@ cmake_minimum_required(VERSION 3.24...3.25) +legacy_check() + find_package(Detours REQUIRED) find_package(Vulkan REQUIRED) if(NOT TARGET OBS::ipc-util) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" ipc-util) endif() if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") endif() -add_library(_graphics-hook INTERFACE) +if(NOT TARGET OBS::d3d8-api) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-d3d8-api" obs-d3d8-api) +endif() + +if(NOT TARGET OBS::hook-config) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-hook-config" obs-hook-config) +endif() + +add_library(graphics-hook MODULE) +add_library(OBS::graphics-hook ALIAS graphics-hook) target_sources( - _graphics-hook - INTERFACE + graphics-hook + PRIVATE d3d10-capture.cpp d3d11-capture.cpp d3d12-capture.cpp @@ -30,30 +41,43 @@ target_sources( graphics-hook.rc ) -target_compile_definitions(_graphics-hook INTERFACE COMPILE_D3D12_HOOK) +target_compile_definitions(graphics-hook PRIVATE COMPILE_D3D12_HOOK) target_link_libraries( - _graphics-hook - INTERFACE OBS::d3d8-api OBS::hook-config OBS::ipc-util OBS::obfuscate Detours::Detours dxguid + graphics-hook + PRIVATE OBS::d3d8-api OBS::hook-config OBS::ipc-util OBS::obfuscate Detours::Detours dxguid ) -target_link_options(_graphics-hook INTERFACE /IGNORE:4099) +target_link_options(graphics-hook PRIVATE /IGNORE:4099) if(TARGET Vulkan::Vulkan) - target_sources(_graphics-hook INTERFACE vulkan-capture.c vulkan-capture.h) - target_link_libraries(_graphics-hook INTERFACE Vulkan::Vulkan) - target_compile_definitions(_graphics-hook INTERFACE COMPILE_VULKAN_HOOK) + target_sources(graphics-hook PRIVATE vulkan-capture.c vulkan-capture.h) + target_link_libraries(graphics-hook PRIVATE Vulkan::Vulkan) + target_compile_definitions(graphics-hook PRIVATE COMPILE_VULKAN_HOOK) endif() -legacy_check() - -add_library(graphics-hook MODULE) -add_library(OBS::graphics-hook ALIAS graphics-hook) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + add_custom_command( + TARGET graphics-hook + POST_BUILD + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t graphics-hook + COMMENT "Build x86 graphics-hook" + ) + endif() -target_link_libraries(graphics-hook PRIVATE _graphics-hook) - -include(cmake/32bit.cmake) + add_dependencies(win-capture graphics-hook) +endif() -set_target_properties_obs(graphics-hook PROPERTIES FOLDER "plugins/win-capture" OUTPUT_NAME graphics-hook64 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + set(_OUTPUT_NAME graphics-hook64) +else() + set(_OUTPUT_NAME graphics-hook32) +endif() -add_dependencies(win-capture graphics-hook) +set_target_properties_obs( + graphics-hook PROPERTIES + FOLDER "plugins/win-capture" + OUTPUT_NAME ${_OUTPUT_NAME} + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake b/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake deleted file mode 100644 index 7ff387fb9e87e9..00000000000000 --- a/plugins/win-capture/graphics-hook/cmake/32bit-build.cmake +++ /dev/null @@ -1,13 +0,0 @@ -project(graphics-hook) - -if(NOT TARGET OBS::obfuscate) - add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") -endif() - -add_library(graphics-hook MODULE) -target_link_libraries(graphics-hook PRIVATE _graphics-hook) - -set_target_properties( - graphics-hook - PROPERTIES OUTPUT_NAME graphics-hook32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" -) diff --git a/plugins/win-capture/graphics-hook/cmake/32bit.cmake b/plugins/win-capture/graphics-hook/cmake/32bit.cmake deleted file mode 100644 index bf332e6652ed67..00000000000000 --- a/plugins/win-capture/graphics-hook/cmake/32bit.cmake +++ /dev/null @@ -1,6 +0,0 @@ -add_custom_command( - TARGET graphics-hook - POST_BUILD - COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t graphics-hook - COMMENT "Build 32-bit graphics-hook" -) diff --git a/plugins/win-capture/graphics-hook/graphics-hook.rc b/plugins/win-capture/graphics-hook/graphics-hook.rc index 3ea6376a2bfb73..96643df9374f14 100644 --- a/plugins/win-capture/graphics-hook/graphics-hook.rc +++ b/plugins/win-capture/graphics-hook/graphics-hook.rc @@ -11,7 +11,7 @@ * * THIS IS YOUR ONLY WARNING. */ -#include "../graphics-hook-ver.h" +#include 1 VERSIONINFO FILEVERSION HOOK_VER_MAJOR,HOOK_VER_MINOR,HOOK_VER_PATCH,0 diff --git a/plugins/win-capture/inject-helper/CMakeLists.txt b/plugins/win-capture/inject-helper/CMakeLists.txt index 4d6b4b787ce6c5..49c52011581345 100644 --- a/plugins/win-capture/inject-helper/CMakeLists.txt +++ b/plugins/win-capture/inject-helper/CMakeLists.txt @@ -1,22 +1,43 @@ cmake_minimum_required(VERSION 3.24...3.25) +legacy_check() + if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") endif() -add_library(_inject-helper INTERFACE) +if(NOT TARGET OBS::inject-library) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-inject-library" obs-inject-library) +endif() -target_sources(_inject-helper INTERFACE inject-helper.c) +add_executable(inject-helper) -target_link_libraries(_inject-helper INTERFACE OBS::inject-library OBS::obfuscate) +target_sources(inject-helper PRIVATE inject-helper.c) -legacy_check() +target_link_libraries(inject-helper PRIVATE OBS::inject-library OBS::obfuscate) -add_executable(inject-helper) -target_link_libraries(inject-helper PRIVATE _inject-helper) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + add_custom_command( + TARGET inject-helper + POST_BUILD + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t inject-helper + COMMENT "Build x86 inject-helper" + ) + endif() -include(cmake/32bit.cmake) + add_dependencies(win-capture inject-helper) +endif() -set_target_properties_obs(inject-helper PROPERTIES FOLDER plugins/win-capture OUTPUT_NAME inject-helper64 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + set(_OUTPUT_NAME inject-helper64) +else() + set(_OUTPUT_NAME inject-helper32) +endif() -add_dependencies(win-capture inject-helper) +set_target_properties_obs( + inject-helper PROPERTIES + FOLDER plugins/win-capture + OUTPUT_NAME ${_OUTPUT_NAME} + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-capture/inject-helper/cmake/32bit-build.cmake b/plugins/win-capture/inject-helper/cmake/32bit-build.cmake deleted file mode 100644 index d6fc2a3aade9f5..00000000000000 --- a/plugins/win-capture/inject-helper/cmake/32bit-build.cmake +++ /dev/null @@ -1,9 +0,0 @@ -project(inject-helper) - -add_executable(inject-helper) -target_link_libraries(inject-helper PRIVATE _inject-helper) - -set_target_properties( - inject-helper - PROPERTIES OUTPUT_NAME inject-helper32 MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" -) diff --git a/plugins/win-capture/inject-helper/cmake/32bit.cmake b/plugins/win-capture/inject-helper/cmake/32bit.cmake deleted file mode 100644 index a7b3be3217d2c3..00000000000000 --- a/plugins/win-capture/inject-helper/cmake/32bit.cmake +++ /dev/null @@ -1,6 +0,0 @@ -add_custom_command( - TARGET inject-helper - POST_BUILD - COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t inject-helper - COMMENT "Build 32-bit inject-helper" -) diff --git a/plugins/win-dshow/CMakeLists.txt b/plugins/win-dshow/CMakeLists.txt index a01c6a10fab4cc..f89429f0c01e2d 100644 --- a/plugins/win-dshow/CMakeLists.txt +++ b/plugins/win-dshow/CMakeLists.txt @@ -1,19 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) - add_library(obs-virtualcam-interface INTERFACE) - add_library(OBS::virtualcam-interface ALIAS obs-virtualcam-interface) - - target_sources( - obs-virtualcam-interface - INTERFACE shared-memory-queue.c shared-memory-queue.h tiny-nv12-scale.c tiny-nv12-scale.h - ) - target_include_directories(obs-virtualcam-interface INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - include(cmake/libdshowcapture.cmake) - add_subdirectory(virtualcam-module) -endif() - legacy_check() find_package(FFmpeg REQUIRED avcodec avutil) @@ -21,6 +7,22 @@ find_package(FFmpeg REQUIRED avcodec avutil) add_library(win-dshow MODULE) add_library(OBS::dshow ALIAS win-dshow) +if(NOT TARGET OBS::tiny-nv12-scale) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-tiny-nv12-scale" obs-tiny-nv12-scale) +endif() + +if(NOT TARGET OBS::shared-memory-queue) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-shared-memory-queue" obs-shared-memory-queue) +endif() + +if(NOT TARGET OBS::libdshowcapture) + add_subdirectory("${CMAKE_SOURCE_DIR}/deps/libdshowcapture" libdshowcapture) +endif() + +if(NOT TARGET OBS::winhandle) + add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") +endif() + target_sources( win-dshow PRIVATE dshow-plugin.cpp encode-dstr.hpp ffmpeg-decode.c ffmpeg-decode.h win-dshow-encoder.cpp win-dshow.cpp @@ -31,7 +33,17 @@ target_sources(win-dshow PRIVATE win-dshow.rc) target_link_libraries( win-dshow - PRIVATE OBS::libobs OBS::w32-pthreads OBS::libdshowcapture FFmpeg::avcodec FFmpeg::avutil strmiids winmm + PRIVATE + OBS::libobs + OBS::w32-pthreads + OBS::libdshowcapture + OBS::tiny-nv12-scale + OBS::shared-memory-queue + OBS::winhandle + FFmpeg::avcodec + FFmpeg::avutil + strmiids + winmm ) if(TARGET OBS::virtualcam AND TARGET OBS::virtualcam-guid) @@ -43,3 +55,5 @@ if(TARGET OBS::virtualcam AND TARGET OBS::virtualcam-guid) endif() set_target_properties_obs(win-dshow PROPERTIES FOLDER plugins/win-dshow PREFIX "") + +add_subdirectory(virtualcam-module) diff --git a/plugins/win-dshow/cmake/legacy.cmake b/plugins/win-dshow/cmake/legacy.cmake index 2e83c29bc1efc5..86566a91036eb6 100644 --- a/plugins/win-dshow/cmake/legacy.cmake +++ b/plugins/win-dshow/cmake/legacy.cmake @@ -10,9 +10,7 @@ if(NOT ENABLE_VIRTUALCAM) endif() if(ENABLE_VIRTUALCAM AND NOT VIRTUALCAM_GUID) - set(VIRTUALCAM_GUID - "" - CACHE STRING "Virtual Camera GUID" FORCE) + set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) mark_as_advanced(VIRTUALCAM_GUID) endif() @@ -23,8 +21,10 @@ find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil) add_library(win-dshow MODULE) add_library(OBS::dshow ALIAS win-dshow) -target_sources(win-dshow PRIVATE encode-dstr.hpp win-dshow.cpp win-dshow-encoder.cpp dshow-plugin.cpp ffmpeg-decode.c - ffmpeg-decode.h) +target_sources( + win-dshow + PRIVATE encode-dstr.hpp win-dshow.cpp win-dshow-encoder.cpp dshow-plugin.cpp ffmpeg-decode.c ffmpeg-decode.h +) add_library(libdshowcapture-external INTERFACE) add_library(libdshowcapture INTERFACE) @@ -33,44 +33,51 @@ add_library(OBS::libdshowcapture ALIAS libdshowcapture) target_sources( libdshowcapture-external - INTERFACE libdshowcapture/external/capture-device-support/Library/EGAVResult.cpp - libdshowcapture/external/capture-device-support/Library/ElgatoUVCDevice.cpp - libdshowcapture/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp - libdshowcapture/external/capture-device-support/SampleCode/DriverInterface.cpp) + INTERFACE + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.cpp +) target_sources( libdshowcapture - INTERFACE libdshowcapture/dshowcapture.hpp - libdshowcapture/source/capture-filter.cpp - libdshowcapture/source/capture-filter.hpp - libdshowcapture/source/output-filter.cpp - libdshowcapture/source/output-filter.hpp - libdshowcapture/source/dshowcapture.cpp - libdshowcapture/source/dshowencode.cpp - libdshowcapture/source/device.cpp - libdshowcapture/source/device.hpp - libdshowcapture/source/device-vendor.cpp - libdshowcapture/source/encoder.cpp - libdshowcapture/source/encoder.hpp - libdshowcapture/source/dshow-base.cpp - libdshowcapture/source/dshow-base.hpp - libdshowcapture/source/dshow-demux.cpp - libdshowcapture/source/dshow-demux.hpp - libdshowcapture/source/dshow-device-defs.hpp - libdshowcapture/source/dshow-enum.cpp - libdshowcapture/source/dshow-enum.hpp - libdshowcapture/source/dshow-formats.cpp - libdshowcapture/source/dshow-formats.hpp - libdshowcapture/source/dshow-media-type.cpp - libdshowcapture/source/dshow-encoded-device.cpp - libdshowcapture/source/dshow-media-type.hpp - libdshowcapture/source/log.cpp - libdshowcapture/source/log.hpp - libdshowcapture/source/external/IVideoCaptureFilter.h) + INTERFACE + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshowcapture.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/capture-filter.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/capture-filter.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/output-filter.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/output-filter.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshowcapture.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshowencode.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/device.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/device.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/device-vendor.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/encoder.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/encoder.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-base.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-base.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-demux.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-demux.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-device-defs.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-enum.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-enum.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-formats.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-formats.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-media-type.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-encoded-device.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-media-type.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/log.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/log.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/external/IVideoCaptureFilter.h +) target_include_directories( - libdshowcapture INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture - ${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture/external/capture-device-support/Library) + libdshowcapture + INTERFACE + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library +) target_compile_definitions(libdshowcapture-external INTERFACE _UP_WINDOWS=1) target_compile_definitions(libdshowcapture INTERFACE _UP_WINDOWS=1) @@ -82,8 +89,10 @@ configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in win-dsh target_sources(win-dshow PRIVATE win-dshow.rc) -target_compile_definitions(win-dshow PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS - OBS_LEGACY) +target_compile_definitions( + win-dshow + PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS OBS_LEGACY +) set(VIRTUALCAM_AVAILABLE OFF) if(ENABLE_VIRTUALCAM) @@ -109,11 +118,13 @@ if(ENABLE_VIRTUALCAM) string(LENGTH ${GUID_VALS_DATA3} GUID_VALS_DATA3_LENGTH) string(LENGTH ${GUID_VALS_DATA4} GUID_VALS_DATA4_LENGTH) string(LENGTH ${GUID_VALS_DATA5} GUID_VALS_DATA5_LENGTH) - if(GUID_VALS_DATA1_LENGTH EQUAL 8 - AND GUID_VALS_DATA2_LENGTH EQUAL 4 - AND GUID_VALS_DATA3_LENGTH EQUAL 4 - AND GUID_VALS_DATA4_LENGTH EQUAL 4 - AND GUID_VALS_DATA5_LENGTH EQUAL 12) + if( + GUID_VALS_DATA1_LENGTH EQUAL 8 + AND GUID_VALS_DATA2_LENGTH EQUAL 4 + AND GUID_VALS_DATA3_LENGTH EQUAL 4 + AND GUID_VALS_DATA4_LENGTH EQUAL 4 + AND GUID_VALS_DATA5_LENGTH EQUAL 12 + ) set(GUID_VAL01 ${GUID_VALS_DATA1}) set(GUID_VAL02 ${GUID_VALS_DATA2}) set(GUID_VAL03 ${GUID_VALS_DATA3}) @@ -139,70 +150,82 @@ endif() target_link_libraries( win-dshow - PRIVATE OBS::libobs - OBS::w32-pthreads - OBS::libdshowcapture-external - OBS::libdshowcapture - setupapi - strmiids - ksuser - winmm - wmcodecdspuuid - FFmpeg::avcodec - FFmpeg::avutil) + PRIVATE + OBS::libobs + OBS::w32-pthreads + OBS::libdshowcapture-external + OBS::libdshowcapture + setupapi + strmiids + ksuser + winmm + wmcodecdspuuid + FFmpeg::avcodec + FFmpeg::avutil +) source_group( "libdshowcapture-external\\Source Files" - FILES libdshowcapture/external/capture-device-support/Library/EGAVResult.cpp - libdshowcapture/external/capture-device-support/Library/ElgatoUVCDevice.cpp - libdshowcapture/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp - libdshowcapture/external/capture-device-support/SampleCode/DriverInterface.cpp) + FILES + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.cpp +) source_group( "libdshowcapture\\Source Files" - FILES libdshowcapture/source/capture-filter.cpp - libdshowcapture/source/output-filter.cpp - libdshowcapture/source/dshowcapture.cpp - libdshowcapture/source/dshowencode.cpp - libdshowcapture/source/device.cpp - libdshowcapture/source/device-vendor.cpp - libdshowcapture/source/encoder.cpp - libdshowcapture/source/dshow-base.cpp - libdshowcapture/source/dshow-demux.cpp - libdshowcapture/source/dshow-enum.cpp - libdshowcapture/source/dshow-formats.cpp - libdshowcapture/source/dshow-media-type.cpp - libdshowcapture/source/dshow-encoded-device.cpp - libdshowcapture/source/log.cpp) + FILES + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/capture-filter.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/output-filter.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshowcapture.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshowencode.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/device.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/device-vendor.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/encoder.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-base.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-demux.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-enum.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-formats.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-media-type.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-encoded-device.cpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/log.cpp +) source_group( "libdshowcapture\\Header Files" - FILES libdshowcapture/dshowcapture.hpp - libdshowcapture/source/capture-filter.hpp - libdshowcapture/source/output-filter.hpp - libdshowcapture/source/device.hpp - libdshowcapture/source/encoder.hpp - libdshowcapture/source/dshow-base.hpp - libdshowcapture/source/dshow-demux.hpp - libdshowcapture/source/dshow-device-defs.hpp - libdshowcapture/source/dshow-enum.hpp - libdshowcapture/source/dshow-formats.hpp - libdshowcapture/source/dshow-media-type.hpp - libdshowcapture/source/log.hpp - libdshowcapture/source/external/IVideoCaptureFilter.h) + FILES + libdshowcapture/dshowcapture.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/capture-filter.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/output-filter.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/device.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/encoder.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-base.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-demux.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-device-defs.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-enum.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-formats.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-media-type.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/log.hpp + ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/IVideoCaptureFilter.h +) set_target_properties(win-dshow PROPERTIES FOLDER "plugins/win-dshow") setup_plugin_target(win-dshow) if(ENABLE_VIRTUALCAM AND VIRTUALCAM_AVAILABLE) - target_sources(win-dshow PRIVATE tiny-nv12-scale.c tiny-nv12-scale.h shared-memory-queue.c shared-memory-queue.h - virtualcam.c) + target_sources( + win-dshow + PRIVATE tiny-nv12-scale.c tiny-nv12-scale.h shared-memory-queue.c shared-memory-queue.h virtualcam.c + ) target_compile_definitions(win-dshow PRIVATE VIRTUALCAM_AVAILABLE) target_include_directories(win-dshow PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/virtualcam-module/virtualcam-guid.h.in - ${CMAKE_CURRENT_BINARY_DIR}/config/virtualcam-guid.h) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/virtualcam-module/virtualcam-guid.h.in + ${CMAKE_CURRENT_BINARY_DIR}/config/virtualcam-guid.h + ) target_sources(win-dshow PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config/virtualcam-guid.h) diff --git a/plugins/win-dshow/virtualcam-module/CMakeLists.txt b/plugins/win-dshow/virtualcam-module/CMakeLists.txt index 413d8fa5c6c6f2..916c27d30c731d 100644 --- a/plugins/win-dshow/virtualcam-module/CMakeLists.txt +++ b/plugins/win-dshow/virtualcam-module/CMakeLists.txt @@ -1,89 +1,131 @@ cmake_minimum_required(VERSION 3.24...3.25) -if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) - option(ENABLE_VIRTUALCAM "Enable Windows Virtual Camera" ON) - if(NOT ENABLE_VIRTUALCAM) - target_disable_feature(obs-virtualcam-module "Virtual Camera Support") - target_disable(obs-virtualcam-module) - return() - endif() +legacy_check() + +option(ENABLE_VIRTUALCAM "Enable Windows Virtual Camera" ON) +if(NOT ENABLE_VIRTUALCAM) + target_disable_feature(obs-virtualcam-module "Virtual Camera Support") + target_disable(obs-virtualcam-module) + return() +endif() + +if(NOT VIRTUALCAM_GUID) + set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) + mark_as_advanced(VIRTUALCAM_GUID) - if(NOT VIRTUALCAM_GUID) - set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) - mark_as_advanced(VIRTUALCAM_GUID) + message(WARNING "Empty Virtual Camera GUID set.") + target_disable_feature(obs-virtualcam-module "Virtual Camera Support (empty GUID)") + return() +else() + set(VALID_GUID FALSE) + check_uuid(${VIRTUALCAM_GUID} VALID_GUID) - message(WARNING "Empty Virtual Camera GUID set.") - target_disable_feature(obs-virtualcam-module "Virtual Camera Support (empty GUID)") + if(NOT VALID_GUID) + message(WARNING "Invalid Virtual Camera GUID set.") + target_disable_feature(obs-virtualcam-module "Virtual Camera Support (invalid GUID)") return() - else() - set(VALID_GUID FALSE) - check_uuid(${VIRTUALCAM_GUID} VALID_GUID) - - if(NOT VALID_GUID) - message(WARNING "Invalid Virtual Camera GUID set.") - target_disable_feature(obs-virtualcam-module "Virtual Camera Support (invalid GUID)") - return() - endif() - - # DirectShow API requires separate GUID tokens - string(REPLACE "-" ";" GUID_VALS ${VIRTUALCAM_GUID}) - list(GET GUID_VALS 0 GUID_VALS_DATA1) - list(GET GUID_VALS 1 GUID_VALS_DATA2) - list(GET GUID_VALS 2 GUID_VALS_DATA3) - list(GET GUID_VALS 3 GUID_VALS_DATA4) - list(GET GUID_VALS 4 GUID_VALS_DATA5) - set(GUID_VAL01 ${GUID_VALS_DATA1}) - set(GUID_VAL02 ${GUID_VALS_DATA2}) - set(GUID_VAL03 ${GUID_VALS_DATA3}) - string(SUBSTRING ${GUID_VALS_DATA4} 0 2 GUID_VAL04) - string(SUBSTRING ${GUID_VALS_DATA4} 2 2 GUID_VAL05) - string(SUBSTRING ${GUID_VALS_DATA5} 0 2 GUID_VAL06) - string(SUBSTRING ${GUID_VALS_DATA5} 2 2 GUID_VAL07) - string(SUBSTRING ${GUID_VALS_DATA5} 4 2 GUID_VAL08) - string(SUBSTRING ${GUID_VALS_DATA5} 6 2 GUID_VAL09) - string(SUBSTRING ${GUID_VALS_DATA5} 8 2 GUID_VAL10) - string(SUBSTRING ${GUID_VALS_DATA5} 10 2 GUID_VAL11) endif() - add_library(obs-virtualcam-guid INTERFACE) - add_library(OBS::virtualcam-guid ALIAS obs-virtualcam-guid) - - configure_file(virtualcam-guid.h.in virtualcam-guid.h) - target_sources(obs-virtualcam-guid INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-guid.h") - target_include_directories(obs-virtualcam-guid INTERFACE "${CMAKE_CURRENT_BINARY_DIR}") - - add_library(_virtualcam INTERFACE) - - configure_file(cmake/windows/obs-module.rc.in virtualcam-module.rc) - target_sources( - _virtualcam - INTERFACE - ${CMAKE_CURRENT_BINARY_DIR}/virtualcam-module.rc - placeholder.cpp - sleepto.c - sleepto.h - virtualcam-filter.cpp - virtualcam-filter.hpp - virtualcam-module.cpp - ) - target_include_directories(_virtualcam INTERFACE "${CMAKE_CURRENT_BINARY_DIR}") - target_compile_definitions(_virtualcam INTERFACE VIRTUALCAM_AVAILABLE) - - target_link_libraries( - _virtualcam - INTERFACE OBS::virtualcam-interface OBS::virtualcam-guid OBS::libdshowcapture OBS::winhandle gdiplus strmiids winmm - ) + # DirectShow API requires separate GUID tokens + string(REPLACE "-" ";" GUID_VALS ${VIRTUALCAM_GUID}) + list(GET GUID_VALS 0 GUID_VALS_DATA1) + list(GET GUID_VALS 1 GUID_VALS_DATA2) + list(GET GUID_VALS 2 GUID_VALS_DATA3) + list(GET GUID_VALS 3 GUID_VALS_DATA4) + list(GET GUID_VALS 4 GUID_VALS_DATA5) + set(GUID_VAL01 ${GUID_VALS_DATA1}) + set(GUID_VAL02 ${GUID_VALS_DATA2}) + set(GUID_VAL03 ${GUID_VALS_DATA3}) + string(SUBSTRING ${GUID_VALS_DATA4} 0 2 GUID_VAL04) + string(SUBSTRING ${GUID_VALS_DATA4} 2 2 GUID_VAL05) + string(SUBSTRING ${GUID_VALS_DATA5} 0 2 GUID_VAL06) + string(SUBSTRING ${GUID_VALS_DATA5} 2 2 GUID_VAL07) + string(SUBSTRING ${GUID_VALS_DATA5} 4 2 GUID_VAL08) + string(SUBSTRING ${GUID_VALS_DATA5} 6 2 GUID_VAL09) + string(SUBSTRING ${GUID_VALS_DATA5} 8 2 GUID_VAL10) + string(SUBSTRING ${GUID_VALS_DATA5} 10 2 GUID_VAL11) endif() -legacy_check() +if(NOT TARGET OBS::winhandle) + add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") +endif() + +if(NOT TARGET OBS::threading-windows) + add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") +endif() + +if(NOT TARGET OBS::tiny-nv12-scale) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-tiny-nv12-scale" obs-tiny-nv12-scale) +endif() + +if(NOT TARGET OBS::shared-memory-queue) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-shared-memory-queue" obs-shared-memory-queue) +endif() + +if(NOT TARGET OBS::libdshowcapture) + add_subdirectory("${CMAKE_SOURCE_DIR}/deps/libdshowcapture" libdshowcapture) +endif() + +add_library(obs-virtualcam-guid INTERFACE) +add_library(OBS::virtualcam-guid ALIAS obs-virtualcam-guid) + +configure_file(virtualcam-guid.h.in virtualcam-guid.h) +target_sources(obs-virtualcam-guid INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-guid.h") +target_include_directories(obs-virtualcam-guid INTERFACE "${CMAKE_CURRENT_BINARY_DIR}") add_library(obs-virtualcam-module MODULE) add_library(OBS::virtualcam ALIAS obs-virtualcam-module) -target_sources(obs-virtualcam-module PRIVATE cmake/windows/virtualcam-module64.def) -target_link_libraries(obs-virtualcam-module PRIVATE _virtualcam) +configure_file(cmake/windows/obs-module.rc.in virtualcam-module.rc) +target_sources( + obs-virtualcam-module + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/virtualcam-module.rc + placeholder.cpp + sleepto.c + sleepto.h + virtualcam-filter.cpp + virtualcam-filter.hpp + virtualcam-module.cpp +) + +target_include_directories(obs-virtualcam-module PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") +target_compile_definitions(obs-virtualcam-module PRIVATE VIRTUALCAM_AVAILABLE) + +target_link_libraries( + obs-virtualcam-module + PRIVATE + OBS::tiny-nv12-scale + OBS::shared-memory-queue + OBS::virtualcam-guid + OBS::libdshowcapture + OBS::winhandle + OBS::threading-windows + gdiplus + strmiids + winmm +) + +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + add_custom_command( + TARGET obs-virtualcam-module + POST_BUILD + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t obs-virtualcam-module + COMMENT "Build x86 obs-virtualcam-module" + ) + endif() + + add_dependencies(win-dshow obs-virtualcam-module) +endif() -set_property(TARGET obs-virtualcam-module PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) + set(_OUTPUT_NAME virtualcam-module64) +else() + set(_OUTPUT_NAME virtualcam-module32) +endif() + +target_sources(obs-virtualcam-module PRIVATE cmake/windows/${_OUTPUT_NAME}.def) configure_file(virtualcam-install.bat.in virtualcam-install.bat) target_add_resource(obs-virtualcam-module "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-install.bat" @@ -95,6 +137,9 @@ target_add_resource(obs-virtualcam-module "${CMAKE_CURRENT_BINARY_DIR}/virtualca "${OBS_DATA_DESTINATION}/obs-plugins/win-dshow" ) -include(cmake/32bit.cmake) - -set_target_properties_obs(obs-virtualcam-module PROPERTIES FOLDER plugins/win-dshow OUTPUT_NAME obs-virtualcam-module64) +set_target_properties_obs( + obs-virtualcam-module PROPERTIES + FOLDER plugins/win-dshow + OUTPUT_NAME obs-${_OUTPUT_NAME} + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) diff --git a/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake b/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake deleted file mode 100644 index fe748a6fecef55..00000000000000 --- a/plugins/win-dshow/virtualcam-module/cmake/32bit-build.cmake +++ /dev/null @@ -1,49 +0,0 @@ -option(ENABLE_VIRTUALCAM "Enable Windows Virtual Camera" ON) -if(NOT ENABLE_VIRTUALCAM) - return() -endif() - -if(NOT VIRTUALCAM_GUID) - set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) - mark_as_advanced(VIRTUALCAM_GUID) - - message(WARNING "Empty Virtual Camera GUID set.") - return() -else() - set(VALID_GUID FALSE) - check_uuid(${VIRTUALCAM_GUID} VALID_GUID) - - if(NOT VALID_GUID) - message(WARNING "Invalid Virtual Camera GUID set.") - return() - endif() - - # DirectShow API requires separate GUID tokens - string(REPLACE "-" ";" GUID_VALS ${VIRTUALCAM_GUID}) - list(GET GUID_VALS 0 GUID_VALS_DATA1) - list(GET GUID_VALS 1 GUID_VALS_DATA2) - list(GET GUID_VALS 2 GUID_VALS_DATA3) - list(GET GUID_VALS 3 GUID_VALS_DATA4) - list(GET GUID_VALS 4 GUID_VALS_DATA5) - set(GUID_VAL01 ${GUID_VALS_DATA1}) - set(GUID_VAL02 ${GUID_VALS_DATA2}) - set(GUID_VAL03 ${GUID_VALS_DATA3}) - string(SUBSTRING ${GUID_VALS_DATA4} 0 2 GUID_VAL04) - string(SUBSTRING ${GUID_VALS_DATA4} 2 2 GUID_VAL05) - string(SUBSTRING ${GUID_VALS_DATA5} 0 2 GUID_VAL06) - string(SUBSTRING ${GUID_VALS_DATA5} 2 2 GUID_VAL07) - string(SUBSTRING ${GUID_VALS_DATA5} 4 2 GUID_VAL08) - string(SUBSTRING ${GUID_VALS_DATA5} 6 2 GUID_VAL09) - string(SUBSTRING ${GUID_VALS_DATA5} 8 2 GUID_VAL10) - string(SUBSTRING ${GUID_VALS_DATA5} 10 2 GUID_VAL11) -endif() - -add_library(obs-virtualcam-module MODULE) -add_library(OBS::virtualcam ALIAS obs-virtualcam-module) - -target_sources(obs-virtualcam-module PRIVATE cmake/windows/virtualcam-module32.def) -target_link_libraries(obs-virtualcam-module PRIVATE _virtualcam) - -set_property(TARGET obs-virtualcam-module PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - -set_property(TARGET obs-virtualcam-module PROPERTY OUTPUT_NAME obs-virtualcam-module32) diff --git a/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake b/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake deleted file mode 100644 index 5ea1cf3e8ec7cd..00000000000000 --- a/plugins/win-dshow/virtualcam-module/cmake/32bit.cmake +++ /dev/null @@ -1,6 +0,0 @@ -add_custom_command( - TARGET obs-virtualcam-module - POST_BUILD - COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_SOURCE_DIR}/build_x86 --config $ -t obs-virtualcam-module - COMMENT "Build 32-bit obs-virtualcam" -) diff --git a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp index 66e43318a57797..09d0c9c35f6e86 100644 --- a/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp +++ b/plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp @@ -7,15 +7,15 @@ #ifdef OBS_LEGACY #include "../shared-memory-queue.h" #include "../tiny-nv12-scale.h" -#include "../libdshowcapture/source/output-filter.hpp" -#include "../libdshowcapture/source/dshow-formats.hpp" +#include "../../../deps/libdshowcapture/src/source/output-filter.hpp" +#include "../../../deps/libdshowcapture/src/source/dshow-formats.hpp" #include "../../../libobs/util/windows/WinHandle.hpp" #include "../../../libobs/util/threading-windows.h" #else #include #include -#include -#include +#include +#include #include #include #endif diff --git a/plugins/win-dshow/win-dshow-encoder.cpp b/plugins/win-dshow/win-dshow-encoder.cpp index 937110f2a69d5a..4fc8fb30daf502 100644 --- a/plugins/win-dshow/win-dshow-encoder.cpp +++ b/plugins/win-dshow/win-dshow-encoder.cpp @@ -2,7 +2,11 @@ #include #include #include +#ifdef OBS_LEGACY #include "libdshowcapture/dshowcapture.hpp" +#else +#include +#endif using namespace DShow; using namespace std; diff --git a/plugins/win-dshow/win-dshow.cpp b/plugins/win-dshow/win-dshow.cpp index 547665655f2b73..438158c721d336 100644 --- a/plugins/win-dshow/win-dshow.cpp +++ b/plugins/win-dshow/win-dshow.cpp @@ -6,7 +6,11 @@ #include #include #include +#ifdef OBS_LEGACY #include "libdshowcapture/dshowcapture.hpp" +#else +#include +#endif #include "ffmpeg-decode.h" #include "encode-dstr.hpp" From af2aa9b6ff261e5f13c2bbea9a839a5bb332d70e Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 2 Aug 2024 20:16:16 +0200 Subject: [PATCH 0420/1073] shared: Remove shared interface libraries from their original locations --- plugins/win-capture/d3d8-api/.clang-format | 3 - plugins/win-capture/d3d8-api/d3d8.h | 1279 --------------- plugins/win-capture/d3d8-api/d3d8caps.h | 364 ----- plugins/win-capture/d3d8-api/d3d8types.h | 1690 -------------------- plugins/win-capture/graphics-hook-info.h | 143 -- plugins/win-capture/graphics-hook-ver.h | 25 - plugins/win-capture/hook-helpers.h | 50 - plugins/win-capture/inject-library.c | 153 -- plugins/win-capture/inject-library.h | 20 - 9 files changed, 3727 deletions(-) delete mode 100644 plugins/win-capture/d3d8-api/.clang-format delete mode 100644 plugins/win-capture/d3d8-api/d3d8.h delete mode 100644 plugins/win-capture/d3d8-api/d3d8caps.h delete mode 100644 plugins/win-capture/d3d8-api/d3d8types.h delete mode 100644 plugins/win-capture/graphics-hook-info.h delete mode 100644 plugins/win-capture/graphics-hook-ver.h delete mode 100644 plugins/win-capture/hook-helpers.h delete mode 100644 plugins/win-capture/inject-library.c delete mode 100644 plugins/win-capture/inject-library.h diff --git a/plugins/win-capture/d3d8-api/.clang-format b/plugins/win-capture/d3d8-api/.clang-format deleted file mode 100644 index 6420a46881e054..00000000000000 --- a/plugins/win-capture/d3d8-api/.clang-format +++ /dev/null @@ -1,3 +0,0 @@ -Language: Cpp -SortIncludes: false -DisableFormat: true diff --git a/plugins/win-capture/d3d8-api/d3d8.h b/plugins/win-capture/d3d8-api/d3d8.h deleted file mode 100644 index adf91ebf0b7ebd..00000000000000 --- a/plugins/win-capture/d3d8-api/d3d8.h +++ /dev/null @@ -1,1279 +0,0 @@ -/*==========================================================================; - * - * Copyright (C) Microsoft Corporation. All Rights Reserved. - * - * File: d3d8.h - * Content: Direct3D include file - * - ****************************************************************************/ - -#ifndef _D3D8_H_ -#define _D3D8_H_ - -#ifndef DIRECT3D_VERSION -#define DIRECT3D_VERSION 0x0800 -#endif //DIRECT3D_VERSION - -// include this file content only if compiling for DX8 interfaces -#if(DIRECT3D_VERSION >= 0x0800) - - -/* This identifier is passed to Direct3DCreate8 in order to ensure that an - * application was built against the correct header files. This number is - * incremented whenever a header (or other) change would require applications - * to be rebuilt. If the version doesn't match, Direct3DCreate8 will fail. - * (The number itself has no meaning.)*/ - -#define D3D_SDK_VERSION 220 - - -#include - -#define COM_NO_WINDOWS_H -#include - -#include - -#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500) - #define HMONITOR_DECLARED - DECLARE_HANDLE(HMONITOR); -#endif - -#define D3DAPI WINAPI - -/* - * Interface IID's - */ -#if defined( _WIN32 ) && !defined( _NO_COM) - -/* IID_IDirect3D8 */ -/* {1DD9E8DA-1C77-4d40-B0CF-98FEFDFF9512} */ -DEFINE_GUID(IID_IDirect3D8, 0x1dd9e8da, 0x1c77, 0x4d40, 0xb0, 0xcf, 0x98, 0xfe, 0xfd, 0xff, 0x95, 0x12); - -/* IID_IDirect3DDevice8 */ -/* {7385E5DF-8FE8-41D5-86B6-D7B48547B6CF} */ -DEFINE_GUID(IID_IDirect3DDevice8, 0x7385e5df, 0x8fe8, 0x41d5, 0x86, 0xb6, 0xd7, 0xb4, 0x85, 0x47, 0xb6, 0xcf); - -/* IID_IDirect3DResource8 */ -/* {1B36BB7B-09B7-410a-B445-7D1430D7B33F} */ -DEFINE_GUID(IID_IDirect3DResource8, 0x1b36bb7b, 0x9b7, 0x410a, 0xb4, 0x45, 0x7d, 0x14, 0x30, 0xd7, 0xb3, 0x3f); - -/* IID_IDirect3DBaseTexture8 */ -/* {B4211CFA-51B9-4a9f-AB78-DB99B2BB678E} */ -DEFINE_GUID(IID_IDirect3DBaseTexture8, 0xb4211cfa, 0x51b9, 0x4a9f, 0xab, 0x78, 0xdb, 0x99, 0xb2, 0xbb, 0x67, 0x8e); - -/* IID_IDirect3DTexture8 */ -/* {E4CDD575-2866-4f01-B12E-7EECE1EC9358} */ -DEFINE_GUID(IID_IDirect3DTexture8, 0xe4cdd575, 0x2866, 0x4f01, 0xb1, 0x2e, 0x7e, 0xec, 0xe1, 0xec, 0x93, 0x58); - -/* IID_IDirect3DCubeTexture8 */ -/* {3EE5B968-2ACA-4c34-8BB5-7E0C3D19B750} */ -DEFINE_GUID(IID_IDirect3DCubeTexture8, 0x3ee5b968, 0x2aca, 0x4c34, 0x8b, 0xb5, 0x7e, 0x0c, 0x3d, 0x19, 0xb7, 0x50); - -/* IID_IDirect3DVolumeTexture8 */ -/* {4B8AAAFA-140F-42ba-9131-597EAFAA2EAD} */ -DEFINE_GUID(IID_IDirect3DVolumeTexture8, 0x4b8aaafa, 0x140f, 0x42ba, 0x91, 0x31, 0x59, 0x7e, 0xaf, 0xaa, 0x2e, 0xad); - -/* IID_IDirect3DVertexBuffer8 */ -/* {8AEEEAC7-05F9-44d4-B591-000B0DF1CB95} */ -DEFINE_GUID(IID_IDirect3DVertexBuffer8, 0x8aeeeac7, 0x05f9, 0x44d4, 0xb5, 0x91, 0x00, 0x0b, 0x0d, 0xf1, 0xcb, 0x95); - -/* IID_IDirect3DIndexBuffer8 */ -/* {0E689C9A-053D-44a0-9D92-DB0E3D750F86} */ -DEFINE_GUID(IID_IDirect3DIndexBuffer8, 0x0e689c9a, 0x053d, 0x44a0, 0x9d, 0x92, 0xdb, 0x0e, 0x3d, 0x75, 0x0f, 0x86); - -/* IID_IDirect3DSurface8 */ -/* {B96EEBCA-B326-4ea5-882F-2FF5BAE021DD} */ -DEFINE_GUID(IID_IDirect3DSurface8, 0xb96eebca, 0xb326, 0x4ea5, 0x88, 0x2f, 0x2f, 0xf5, 0xba, 0xe0, 0x21, 0xdd); - -/* IID_IDirect3DVolume8 */ -/* {BD7349F5-14F1-42e4-9C79-972380DB40C0} */ -DEFINE_GUID(IID_IDirect3DVolume8, 0xbd7349f5, 0x14f1, 0x42e4, 0x9c, 0x79, 0x97, 0x23, 0x80, 0xdb, 0x40, 0xc0); - -/* IID_IDirect3DSwapChain8 */ -/* {928C088B-76B9-4C6B-A536-A590853876CD} */ -DEFINE_GUID(IID_IDirect3DSwapChain8, 0x928c088b, 0x76b9, 0x4c6b, 0xa5, 0x36, 0xa5, 0x90, 0x85, 0x38, 0x76, 0xcd); - -#endif - -#ifdef __cplusplus - -interface IDirect3D8; -interface IDirect3DDevice8; - -interface IDirect3DResource8; -interface IDirect3DBaseTexture8; -interface IDirect3DTexture8; -interface IDirect3DVolumeTexture8; -interface IDirect3DCubeTexture8; - -interface IDirect3DVertexBuffer8; -interface IDirect3DIndexBuffer8; - -interface IDirect3DSurface8; -interface IDirect3DVolume8; - -interface IDirect3DSwapChain8; - -#endif - - -typedef interface IDirect3D8 IDirect3D8; -typedef interface IDirect3DDevice8 IDirect3DDevice8; -typedef interface IDirect3DResource8 IDirect3DResource8; -typedef interface IDirect3DBaseTexture8 IDirect3DBaseTexture8; -typedef interface IDirect3DTexture8 IDirect3DTexture8; -typedef interface IDirect3DVolumeTexture8 IDirect3DVolumeTexture8; -typedef interface IDirect3DCubeTexture8 IDirect3DCubeTexture8; -typedef interface IDirect3DVertexBuffer8 IDirect3DVertexBuffer8; -typedef interface IDirect3DIndexBuffer8 IDirect3DIndexBuffer8; -typedef interface IDirect3DSurface8 IDirect3DSurface8; -typedef interface IDirect3DVolume8 IDirect3DVolume8; -typedef interface IDirect3DSwapChain8 IDirect3DSwapChain8; - -#include "d3d8types.h" -#include "d3d8caps.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * DLL Function for creating a Direct3D8 object. This object supports - * enumeration and allows the creation of Direct3DDevice8 objects. - * Pass the value of the constant D3D_SDK_VERSION to this function, so - * that the run-time can validate that your application was compiled - * against the right headers. - */ - -IDirect3D8 * WINAPI Direct3DCreate8(UINT SDKVersion); - - -/* - * Direct3D interfaces - */ - - - - - - -#undef INTERFACE -#define INTERFACE IDirect3D8 - -DECLARE_INTERFACE_(IDirect3D8, IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3D8 methods ***/ - STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE; - STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE; - STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER8* pIdentifier) PURE; - STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter) PURE; - STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,UINT Mode,D3DDISPLAYMODE* pMode) PURE; - STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE; - STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE CheckType,D3DFORMAT DisplayFormat,D3DFORMAT BackBufferFormat,BOOL Windowed) PURE; - STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE; - STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType) PURE; - STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE; - STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS8* pCaps) PURE; - STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE; - STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice8** ppReturnedDeviceInterface) PURE; -}; - -typedef struct IDirect3D8 *LPDIRECT3D8, *PDIRECT3D8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3D8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3D8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3D8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3D8_RegisterSoftwareDevice(p,a) (p)->lpVtbl->RegisterSoftwareDevice(p,a) -#define IDirect3D8_GetAdapterCount(p) (p)->lpVtbl->GetAdapterCount(p) -#define IDirect3D8_GetAdapterIdentifier(p,a,b,c) (p)->lpVtbl->GetAdapterIdentifier(p,a,b,c) -#define IDirect3D8_GetAdapterModeCount(p,a) (p)->lpVtbl->GetAdapterModeCount(p,a) -#define IDirect3D8_EnumAdapterModes(p,a,b,c) (p)->lpVtbl->EnumAdapterModes(p,a,b,c) -#define IDirect3D8_GetAdapterDisplayMode(p,a,b) (p)->lpVtbl->GetAdapterDisplayMode(p,a,b) -#define IDirect3D8_CheckDeviceType(p,a,b,c,d,e) (p)->lpVtbl->CheckDeviceType(p,a,b,c,d,e) -#define IDirect3D8_CheckDeviceFormat(p,a,b,c,d,e,f) (p)->lpVtbl->CheckDeviceFormat(p,a,b,c,d,e,f) -#define IDirect3D8_CheckDeviceMultiSampleType(p,a,b,c,d,e) (p)->lpVtbl->CheckDeviceMultiSampleType(p,a,b,c,d,e) -#define IDirect3D8_CheckDepthStencilMatch(p,a,b,c,d,e) (p)->lpVtbl->CheckDepthStencilMatch(p,a,b,c,d,e) -#define IDirect3D8_GetDeviceCaps(p,a,b,c) (p)->lpVtbl->GetDeviceCaps(p,a,b,c) -#define IDirect3D8_GetAdapterMonitor(p,a) (p)->lpVtbl->GetAdapterMonitor(p,a) -#define IDirect3D8_CreateDevice(p,a,b,c,d,e,f) (p)->lpVtbl->CreateDevice(p,a,b,c,d,e,f) -#else -#define IDirect3D8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3D8_AddRef(p) (p)->AddRef() -#define IDirect3D8_Release(p) (p)->Release() -#define IDirect3D8_RegisterSoftwareDevice(p,a) (p)->RegisterSoftwareDevice(a) -#define IDirect3D8_GetAdapterCount(p) (p)->GetAdapterCount() -#define IDirect3D8_GetAdapterIdentifier(p,a,b,c) (p)->GetAdapterIdentifier(a,b,c) -#define IDirect3D8_GetAdapterModeCount(p,a) (p)->GetAdapterModeCount(a) -#define IDirect3D8_EnumAdapterModes(p,a,b,c) (p)->EnumAdapterModes(a,b,c) -#define IDirect3D8_GetAdapterDisplayMode(p,a,b) (p)->GetAdapterDisplayMode(a,b) -#define IDirect3D8_CheckDeviceType(p,a,b,c,d,e) (p)->CheckDeviceType(a,b,c,d,e) -#define IDirect3D8_CheckDeviceFormat(p,a,b,c,d,e,f) (p)->CheckDeviceFormat(a,b,c,d,e,f) -#define IDirect3D8_CheckDeviceMultiSampleType(p,a,b,c,d,e) (p)->CheckDeviceMultiSampleType(a,b,c,d,e) -#define IDirect3D8_CheckDepthStencilMatch(p,a,b,c,d,e) (p)->CheckDepthStencilMatch(a,b,c,d,e) -#define IDirect3D8_GetDeviceCaps(p,a,b,c) (p)->GetDeviceCaps(a,b,c) -#define IDirect3D8_GetAdapterMonitor(p,a) (p)->GetAdapterMonitor(a) -#define IDirect3D8_CreateDevice(p,a,b,c,d,e,f) (p)->CreateDevice(a,b,c,d,e,f) -#endif - - - - - - - - - - - - - - - - - - - -#undef INTERFACE -#define INTERFACE IDirect3DDevice8 - -DECLARE_INTERFACE_(IDirect3DDevice8, IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DDevice8 methods ***/ - STDMETHOD(TestCooperativeLevel)(THIS) PURE; - STDMETHOD_(UINT, GetAvailableTextureMem)(THIS) PURE; - STDMETHOD(ResourceManagerDiscardBytes)(THIS_ DWORD Bytes) PURE; - STDMETHOD(GetDirect3D)(THIS_ IDirect3D8** ppD3D8) PURE; - STDMETHOD(GetDeviceCaps)(THIS_ D3DCAPS8* pCaps) PURE; - STDMETHOD(GetDisplayMode)(THIS_ D3DDISPLAYMODE* pMode) PURE; - STDMETHOD(GetCreationParameters)(THIS_ D3DDEVICE_CREATION_PARAMETERS *pParameters) PURE; - STDMETHOD(SetCursorProperties)(THIS_ UINT XHotSpot,UINT YHotSpot,IDirect3DSurface8* pCursorBitmap) PURE; - STDMETHOD_(void, SetCursorPosition)(THIS_ int X,int Y,DWORD Flags) PURE; - STDMETHOD_(BOOL, ShowCursor)(THIS_ BOOL bShow) PURE; - STDMETHOD(CreateAdditionalSwapChain)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DSwapChain8** pSwapChain) PURE; - STDMETHOD(Reset)(THIS_ D3DPRESENT_PARAMETERS* pPresentationParameters) PURE; - STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) PURE; - STDMETHOD(GetBackBuffer)(THIS_ UINT BackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface8** ppBackBuffer) PURE; - STDMETHOD(GetRasterStatus)(THIS_ D3DRASTER_STATUS* pRasterStatus) PURE; - STDMETHOD_(void, SetGammaRamp)(THIS_ DWORD Flags,CONST D3DGAMMARAMP* pRamp) PURE; - STDMETHOD_(void, GetGammaRamp)(THIS_ D3DGAMMARAMP* pRamp) PURE; - STDMETHOD(CreateTexture)(THIS_ UINT Width,UINT Height,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DTexture8** ppTexture) PURE; - STDMETHOD(CreateVolumeTexture)(THIS_ UINT Width,UINT Height,UINT Depth,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DVolumeTexture8** ppVolumeTexture) PURE; - STDMETHOD(CreateCubeTexture)(THIS_ UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture8** ppCubeTexture) PURE; - STDMETHOD(CreateVertexBuffer)(THIS_ UINT Length,DWORD Usage,DWORD FVF,D3DPOOL Pool,IDirect3DVertexBuffer8** ppVertexBuffer) PURE; - STDMETHOD(CreateIndexBuffer)(THIS_ UINT Length,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DIndexBuffer8** ppIndexBuffer) PURE; - STDMETHOD(CreateRenderTarget)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,BOOL Lockable,IDirect3DSurface8** ppSurface) PURE; - STDMETHOD(CreateDepthStencilSurface)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,D3DMULTISAMPLE_TYPE MultiSample,IDirect3DSurface8** ppSurface) PURE; - STDMETHOD(CreateImageSurface)(THIS_ UINT Width,UINT Height,D3DFORMAT Format,IDirect3DSurface8** ppSurface) PURE; - STDMETHOD(CopyRects)(THIS_ IDirect3DSurface8* pSourceSurface,CONST RECT* pSourceRectsArray,UINT cRects,IDirect3DSurface8* pDestinationSurface,CONST POINT* pDestPointsArray) PURE; - STDMETHOD(UpdateTexture)(THIS_ IDirect3DBaseTexture8* pSourceTexture,IDirect3DBaseTexture8* pDestinationTexture) PURE; - STDMETHOD(GetFrontBuffer)(THIS_ IDirect3DSurface8* pDestSurface) PURE; - STDMETHOD(SetRenderTarget)(THIS_ IDirect3DSurface8* pRenderTarget,IDirect3DSurface8* pNewZStencil) PURE; - STDMETHOD(GetRenderTarget)(THIS_ IDirect3DSurface8** ppRenderTarget) PURE; - STDMETHOD(GetDepthStencilSurface)(THIS_ IDirect3DSurface8** ppZStencilSurface) PURE; - STDMETHOD(BeginScene)(THIS) PURE; - STDMETHOD(EndScene)(THIS) PURE; - STDMETHOD(Clear)(THIS_ DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil) PURE; - STDMETHOD(SetTransform)(THIS_ D3DTRANSFORMSTATETYPE State,CONST D3DMATRIX* pMatrix) PURE; - STDMETHOD(GetTransform)(THIS_ D3DTRANSFORMSTATETYPE State,D3DMATRIX* pMatrix) PURE; - STDMETHOD(MultiplyTransform)(THIS_ D3DTRANSFORMSTATETYPE,CONST D3DMATRIX*) PURE; - STDMETHOD(SetViewport)(THIS_ CONST D3DVIEWPORT8* pViewport) PURE; - STDMETHOD(GetViewport)(THIS_ D3DVIEWPORT8* pViewport) PURE; - STDMETHOD(SetMaterial)(THIS_ CONST D3DMATERIAL8* pMaterial) PURE; - STDMETHOD(GetMaterial)(THIS_ D3DMATERIAL8* pMaterial) PURE; - STDMETHOD(SetLight)(THIS_ DWORD Index,CONST D3DLIGHT8*) PURE; - STDMETHOD(GetLight)(THIS_ DWORD Index,D3DLIGHT8*) PURE; - STDMETHOD(LightEnable)(THIS_ DWORD Index,BOOL Enable) PURE; - STDMETHOD(GetLightEnable)(THIS_ DWORD Index,BOOL* pEnable) PURE; - STDMETHOD(SetClipPlane)(THIS_ DWORD Index,CONST float* pPlane) PURE; - STDMETHOD(GetClipPlane)(THIS_ DWORD Index,float* pPlane) PURE; - STDMETHOD(SetRenderState)(THIS_ D3DRENDERSTATETYPE State,DWORD Value) PURE; - STDMETHOD(GetRenderState)(THIS_ D3DRENDERSTATETYPE State,DWORD* pValue) PURE; - STDMETHOD(BeginStateBlock)(THIS) PURE; - STDMETHOD(EndStateBlock)(THIS_ DWORD* pToken) PURE; - STDMETHOD(ApplyStateBlock)(THIS_ DWORD Token) PURE; - STDMETHOD(CaptureStateBlock)(THIS_ DWORD Token) PURE; - STDMETHOD(DeleteStateBlock)(THIS_ DWORD Token) PURE; - STDMETHOD(CreateStateBlock)(THIS_ D3DSTATEBLOCKTYPE Type,DWORD* pToken) PURE; - STDMETHOD(SetClipStatus)(THIS_ CONST D3DCLIPSTATUS8* pClipStatus) PURE; - STDMETHOD(GetClipStatus)(THIS_ D3DCLIPSTATUS8* pClipStatus) PURE; - STDMETHOD(GetTexture)(THIS_ DWORD Stage,IDirect3DBaseTexture8** ppTexture) PURE; - STDMETHOD(SetTexture)(THIS_ DWORD Stage,IDirect3DBaseTexture8* pTexture) PURE; - STDMETHOD(GetTextureStageState)(THIS_ DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD* pValue) PURE; - STDMETHOD(SetTextureStageState)(THIS_ DWORD Stage,D3DTEXTURESTAGESTATETYPE Type,DWORD Value) PURE; - STDMETHOD(ValidateDevice)(THIS_ DWORD* pNumPasses) PURE; - STDMETHOD(GetInfo)(THIS_ DWORD DevInfoID,void* pDevInfoStruct,DWORD DevInfoStructSize) PURE; - STDMETHOD(SetPaletteEntries)(THIS_ UINT PaletteNumber,CONST PALETTEENTRY* pEntries) PURE; - STDMETHOD(GetPaletteEntries)(THIS_ UINT PaletteNumber,PALETTEENTRY* pEntries) PURE; - STDMETHOD(SetCurrentTexturePalette)(THIS_ UINT PaletteNumber) PURE; - STDMETHOD(GetCurrentTexturePalette)(THIS_ UINT *PaletteNumber) PURE; - STDMETHOD(DrawPrimitive)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT StartVertex,UINT PrimitiveCount) PURE; - STDMETHOD(DrawIndexedPrimitive)(THIS_ D3DPRIMITIVETYPE,UINT minIndex,UINT NumVertices,UINT startIndex,UINT primCount) PURE; - STDMETHOD(DrawPrimitiveUP)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT PrimitiveCount,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) PURE; - STDMETHOD(DrawIndexedPrimitiveUP)(THIS_ D3DPRIMITIVETYPE PrimitiveType,UINT MinVertexIndex,UINT NumVertexIndices,UINT PrimitiveCount,CONST void* pIndexData,D3DFORMAT IndexDataFormat,CONST void* pVertexStreamZeroData,UINT VertexStreamZeroStride) PURE; - STDMETHOD(ProcessVertices)(THIS_ UINT SrcStartIndex,UINT DestIndex,UINT VertexCount,IDirect3DVertexBuffer8* pDestBuffer,DWORD Flags) PURE; - STDMETHOD(CreateVertexShader)(THIS_ CONST DWORD* pDeclaration,CONST DWORD* pFunction,DWORD* pHandle,DWORD Usage) PURE; - STDMETHOD(SetVertexShader)(THIS_ DWORD Handle) PURE; - STDMETHOD(GetVertexShader)(THIS_ DWORD* pHandle) PURE; - STDMETHOD(DeleteVertexShader)(THIS_ DWORD Handle) PURE; - STDMETHOD(SetVertexShaderConstant)(THIS_ DWORD Register,CONST void* pConstantData,DWORD ConstantCount) PURE; - STDMETHOD(GetVertexShaderConstant)(THIS_ DWORD Register,void* pConstantData,DWORD ConstantCount) PURE; - STDMETHOD(GetVertexShaderDeclaration)(THIS_ DWORD Handle,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(GetVertexShaderFunction)(THIS_ DWORD Handle,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(SetStreamSource)(THIS_ UINT StreamNumber,IDirect3DVertexBuffer8* pStreamData,UINT Stride) PURE; - STDMETHOD(GetStreamSource)(THIS_ UINT StreamNumber,IDirect3DVertexBuffer8** ppStreamData,UINT* pStride) PURE; - STDMETHOD(SetIndices)(THIS_ IDirect3DIndexBuffer8* pIndexData,UINT BaseVertexIndex) PURE; - STDMETHOD(GetIndices)(THIS_ IDirect3DIndexBuffer8** ppIndexData,UINT* pBaseVertexIndex) PURE; - STDMETHOD(CreatePixelShader)(THIS_ CONST DWORD* pFunction,DWORD* pHandle) PURE; - STDMETHOD(SetPixelShader)(THIS_ DWORD Handle) PURE; - STDMETHOD(GetPixelShader)(THIS_ DWORD* pHandle) PURE; - STDMETHOD(DeletePixelShader)(THIS_ DWORD Handle) PURE; - STDMETHOD(SetPixelShaderConstant)(THIS_ DWORD Register,CONST void* pConstantData,DWORD ConstantCount) PURE; - STDMETHOD(GetPixelShaderConstant)(THIS_ DWORD Register,void* pConstantData,DWORD ConstantCount) PURE; - STDMETHOD(GetPixelShaderFunction)(THIS_ DWORD Handle,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(DrawRectPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DRECTPATCH_INFO* pRectPatchInfo) PURE; - STDMETHOD(DrawTriPatch)(THIS_ UINT Handle,CONST float* pNumSegs,CONST D3DTRIPATCH_INFO* pTriPatchInfo) PURE; - STDMETHOD(DeletePatch)(THIS_ UINT Handle) PURE; -}; - -typedef struct IDirect3DDevice8 *LPDIRECT3DDEVICE8, *PDIRECT3DDEVICE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DDevice8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DDevice8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DDevice8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DDevice8_TestCooperativeLevel(p) (p)->lpVtbl->TestCooperativeLevel(p) -#define IDirect3DDevice8_GetAvailableTextureMem(p) (p)->lpVtbl->GetAvailableTextureMem(p) -#define IDirect3DDevice8_ResourceManagerDiscardBytes(p,a) (p)->lpVtbl->ResourceManagerDiscardBytes(p,a) -#define IDirect3DDevice8_GetDirect3D(p,a) (p)->lpVtbl->GetDirect3D(p,a) -#define IDirect3DDevice8_GetDeviceCaps(p,a) (p)->lpVtbl->GetDeviceCaps(p,a) -#define IDirect3DDevice8_GetDisplayMode(p,a) (p)->lpVtbl->GetDisplayMode(p,a) -#define IDirect3DDevice8_GetCreationParameters(p,a) (p)->lpVtbl->GetCreationParameters(p,a) -#define IDirect3DDevice8_SetCursorProperties(p,a,b,c) (p)->lpVtbl->SetCursorProperties(p,a,b,c) -#define IDirect3DDevice8_SetCursorPosition(p,a,b,c) (p)->lpVtbl->SetCursorPosition(p,a,b,c) -#define IDirect3DDevice8_ShowCursor(p,a) (p)->lpVtbl->ShowCursor(p,a) -#define IDirect3DDevice8_CreateAdditionalSwapChain(p,a,b) (p)->lpVtbl->CreateAdditionalSwapChain(p,a,b) -#define IDirect3DDevice8_Reset(p,a) (p)->lpVtbl->Reset(p,a) -#define IDirect3DDevice8_Present(p,a,b,c,d) (p)->lpVtbl->Present(p,a,b,c,d) -#define IDirect3DDevice8_GetBackBuffer(p,a,b,c) (p)->lpVtbl->GetBackBuffer(p,a,b,c) -#define IDirect3DDevice8_GetRasterStatus(p,a) (p)->lpVtbl->GetRasterStatus(p,a) -#define IDirect3DDevice8_SetGammaRamp(p,a,b) (p)->lpVtbl->SetGammaRamp(p,a,b) -#define IDirect3DDevice8_GetGammaRamp(p,a) (p)->lpVtbl->GetGammaRamp(p,a) -#define IDirect3DDevice8_CreateTexture(p,a,b,c,d,e,f,g) (p)->lpVtbl->CreateTexture(p,a,b,c,d,e,f,g) -#define IDirect3DDevice8_CreateVolumeTexture(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->CreateVolumeTexture(p,a,b,c,d,e,f,g,h) -#define IDirect3DDevice8_CreateCubeTexture(p,a,b,c,d,e,f) (p)->lpVtbl->CreateCubeTexture(p,a,b,c,d,e,f) -#define IDirect3DDevice8_CreateVertexBuffer(p,a,b,c,d,e) (p)->lpVtbl->CreateVertexBuffer(p,a,b,c,d,e) -#define IDirect3DDevice8_CreateIndexBuffer(p,a,b,c,d,e) (p)->lpVtbl->CreateIndexBuffer(p,a,b,c,d,e) -#define IDirect3DDevice8_CreateRenderTarget(p,a,b,c,d,e,f) (p)->lpVtbl->CreateRenderTarget(p,a,b,c,d,e,f) -#define IDirect3DDevice8_CreateDepthStencilSurface(p,a,b,c,d,e) (p)->lpVtbl->CreateDepthStencilSurface(p,a,b,c,d,e) -#define IDirect3DDevice8_CreateImageSurface(p,a,b,c,d) (p)->lpVtbl->CreateImageSurface(p,a,b,c,d) -#define IDirect3DDevice8_CopyRects(p,a,b,c,d,e) (p)->lpVtbl->CopyRects(p,a,b,c,d,e) -#define IDirect3DDevice8_UpdateTexture(p,a,b) (p)->lpVtbl->UpdateTexture(p,a,b) -#define IDirect3DDevice8_GetFrontBuffer(p,a) (p)->lpVtbl->GetFrontBuffer(p,a) -#define IDirect3DDevice8_SetRenderTarget(p,a,b) (p)->lpVtbl->SetRenderTarget(p,a,b) -#define IDirect3DDevice8_GetRenderTarget(p,a) (p)->lpVtbl->GetRenderTarget(p,a) -#define IDirect3DDevice8_GetDepthStencilSurface(p,a) (p)->lpVtbl->GetDepthStencilSurface(p,a) -#define IDirect3DDevice8_BeginScene(p) (p)->lpVtbl->BeginScene(p) -#define IDirect3DDevice8_EndScene(p) (p)->lpVtbl->EndScene(p) -#define IDirect3DDevice8_Clear(p,a,b,c,d,e,f) (p)->lpVtbl->Clear(p,a,b,c,d,e,f) -#define IDirect3DDevice8_SetTransform(p,a,b) (p)->lpVtbl->SetTransform(p,a,b) -#define IDirect3DDevice8_GetTransform(p,a,b) (p)->lpVtbl->GetTransform(p,a,b) -#define IDirect3DDevice8_MultiplyTransform(p,a,b) (p)->lpVtbl->MultiplyTransform(p,a,b) -#define IDirect3DDevice8_SetViewport(p,a) (p)->lpVtbl->SetViewport(p,a) -#define IDirect3DDevice8_GetViewport(p,a) (p)->lpVtbl->GetViewport(p,a) -#define IDirect3DDevice8_SetMaterial(p,a) (p)->lpVtbl->SetMaterial(p,a) -#define IDirect3DDevice8_GetMaterial(p,a) (p)->lpVtbl->GetMaterial(p,a) -#define IDirect3DDevice8_SetLight(p,a,b) (p)->lpVtbl->SetLight(p,a,b) -#define IDirect3DDevice8_GetLight(p,a,b) (p)->lpVtbl->GetLight(p,a,b) -#define IDirect3DDevice8_LightEnable(p,a,b) (p)->lpVtbl->LightEnable(p,a,b) -#define IDirect3DDevice8_GetLightEnable(p,a,b) (p)->lpVtbl->GetLightEnable(p,a,b) -#define IDirect3DDevice8_SetClipPlane(p,a,b) (p)->lpVtbl->SetClipPlane(p,a,b) -#define IDirect3DDevice8_GetClipPlane(p,a,b) (p)->lpVtbl->GetClipPlane(p,a,b) -#define IDirect3DDevice8_SetRenderState(p,a,b) (p)->lpVtbl->SetRenderState(p,a,b) -#define IDirect3DDevice8_GetRenderState(p,a,b) (p)->lpVtbl->GetRenderState(p,a,b) -#define IDirect3DDevice8_BeginStateBlock(p) (p)->lpVtbl->BeginStateBlock(p) -#define IDirect3DDevice8_EndStateBlock(p,a) (p)->lpVtbl->EndStateBlock(p,a) -#define IDirect3DDevice8_ApplyStateBlock(p,a) (p)->lpVtbl->ApplyStateBlock(p,a) -#define IDirect3DDevice8_CaptureStateBlock(p,a) (p)->lpVtbl->CaptureStateBlock(p,a) -#define IDirect3DDevice8_DeleteStateBlock(p,a) (p)->lpVtbl->DeleteStateBlock(p,a) -#define IDirect3DDevice8_CreateStateBlock(p,a,b) (p)->lpVtbl->CreateStateBlock(p,a,b) -#define IDirect3DDevice8_SetClipStatus(p,a) (p)->lpVtbl->SetClipStatus(p,a) -#define IDirect3DDevice8_GetClipStatus(p,a) (p)->lpVtbl->GetClipStatus(p,a) -#define IDirect3DDevice8_GetTexture(p,a,b) (p)->lpVtbl->GetTexture(p,a,b) -#define IDirect3DDevice8_SetTexture(p,a,b) (p)->lpVtbl->SetTexture(p,a,b) -#define IDirect3DDevice8_GetTextureStageState(p,a,b,c) (p)->lpVtbl->GetTextureStageState(p,a,b,c) -#define IDirect3DDevice8_SetTextureStageState(p,a,b,c) (p)->lpVtbl->SetTextureStageState(p,a,b,c) -#define IDirect3DDevice8_ValidateDevice(p,a) (p)->lpVtbl->ValidateDevice(p,a) -#define IDirect3DDevice8_GetInfo(p,a,b,c) (p)->lpVtbl->GetInfo(p,a,b,c) -#define IDirect3DDevice8_SetPaletteEntries(p,a,b) (p)->lpVtbl->SetPaletteEntries(p,a,b) -#define IDirect3DDevice8_GetPaletteEntries(p,a,b) (p)->lpVtbl->GetPaletteEntries(p,a,b) -#define IDirect3DDevice8_SetCurrentTexturePalette(p,a) (p)->lpVtbl->SetCurrentTexturePalette(p,a) -#define IDirect3DDevice8_GetCurrentTexturePalette(p,a) (p)->lpVtbl->GetCurrentTexturePalette(p,a) -#define IDirect3DDevice8_DrawPrimitive(p,a,b,c) (p)->lpVtbl->DrawPrimitive(p,a,b,c) -#define IDirect3DDevice8_DrawIndexedPrimitive(p,a,b,c,d,e) (p)->lpVtbl->DrawIndexedPrimitive(p,a,b,c,d,e) -#define IDirect3DDevice8_DrawPrimitiveUP(p,a,b,c,d) (p)->lpVtbl->DrawPrimitiveUP(p,a,b,c,d) -#define IDirect3DDevice8_DrawIndexedPrimitiveUP(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->DrawIndexedPrimitiveUP(p,a,b,c,d,e,f,g,h) -#define IDirect3DDevice8_ProcessVertices(p,a,b,c,d,e) (p)->lpVtbl->ProcessVertices(p,a,b,c,d,e) -#define IDirect3DDevice8_CreateVertexShader(p,a,b,c,d) (p)->lpVtbl->CreateVertexShader(p,a,b,c,d) -#define IDirect3DDevice8_SetVertexShader(p,a) (p)->lpVtbl->SetVertexShader(p,a) -#define IDirect3DDevice8_GetVertexShader(p,a) (p)->lpVtbl->GetVertexShader(p,a) -#define IDirect3DDevice8_DeleteVertexShader(p,a) (p)->lpVtbl->DeleteVertexShader(p,a) -#define IDirect3DDevice8_SetVertexShaderConstant(p,a,b,c) (p)->lpVtbl->SetVertexShaderConstant(p,a,b,c) -#define IDirect3DDevice8_GetVertexShaderConstant(p,a,b,c) (p)->lpVtbl->GetVertexShaderConstant(p,a,b,c) -#define IDirect3DDevice8_GetVertexShaderDeclaration(p,a,b,c) (p)->lpVtbl->GetVertexShaderDeclaration(p,a,b,c) -#define IDirect3DDevice8_GetVertexShaderFunction(p,a,b,c) (p)->lpVtbl->GetVertexShaderFunction(p,a,b,c) -#define IDirect3DDevice8_SetStreamSource(p,a,b,c) (p)->lpVtbl->SetStreamSource(p,a,b,c) -#define IDirect3DDevice8_GetStreamSource(p,a,b,c) (p)->lpVtbl->GetStreamSource(p,a,b,c) -#define IDirect3DDevice8_SetIndices(p,a,b) (p)->lpVtbl->SetIndices(p,a,b) -#define IDirect3DDevice8_GetIndices(p,a,b) (p)->lpVtbl->GetIndices(p,a,b) -#define IDirect3DDevice8_CreatePixelShader(p,a,b) (p)->lpVtbl->CreatePixelShader(p,a,b) -#define IDirect3DDevice8_SetPixelShader(p,a) (p)->lpVtbl->SetPixelShader(p,a) -#define IDirect3DDevice8_GetPixelShader(p,a) (p)->lpVtbl->GetPixelShader(p,a) -#define IDirect3DDevice8_DeletePixelShader(p,a) (p)->lpVtbl->DeletePixelShader(p,a) -#define IDirect3DDevice8_SetPixelShaderConstant(p,a,b,c) (p)->lpVtbl->SetPixelShaderConstant(p,a,b,c) -#define IDirect3DDevice8_GetPixelShaderConstant(p,a,b,c) (p)->lpVtbl->GetPixelShaderConstant(p,a,b,c) -#define IDirect3DDevice8_GetPixelShaderFunction(p,a,b,c) (p)->lpVtbl->GetPixelShaderFunction(p,a,b,c) -#define IDirect3DDevice8_DrawRectPatch(p,a,b,c) (p)->lpVtbl->DrawRectPatch(p,a,b,c) -#define IDirect3DDevice8_DrawTriPatch(p,a,b,c) (p)->lpVtbl->DrawTriPatch(p,a,b,c) -#define IDirect3DDevice8_DeletePatch(p,a) (p)->lpVtbl->DeletePatch(p,a) -#else -#define IDirect3DDevice8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DDevice8_AddRef(p) (p)->AddRef() -#define IDirect3DDevice8_Release(p) (p)->Release() -#define IDirect3DDevice8_TestCooperativeLevel(p) (p)->TestCooperativeLevel() -#define IDirect3DDevice8_GetAvailableTextureMem(p) (p)->GetAvailableTextureMem() -#define IDirect3DDevice8_ResourceManagerDiscardBytes(p,a) (p)->ResourceManagerDiscardBytes(a) -#define IDirect3DDevice8_GetDirect3D(p,a) (p)->GetDirect3D(a) -#define IDirect3DDevice8_GetDeviceCaps(p,a) (p)->GetDeviceCaps(a) -#define IDirect3DDevice8_GetDisplayMode(p,a) (p)->GetDisplayMode(a) -#define IDirect3DDevice8_GetCreationParameters(p,a) (p)->GetCreationParameters(a) -#define IDirect3DDevice8_SetCursorProperties(p,a,b,c) (p)->SetCursorProperties(a,b,c) -#define IDirect3DDevice8_SetCursorPosition(p,a,b,c) (p)->SetCursorPosition(a,b,c) -#define IDirect3DDevice8_ShowCursor(p,a) (p)->ShowCursor(a) -#define IDirect3DDevice8_CreateAdditionalSwapChain(p,a,b) (p)->CreateAdditionalSwapChain(a,b) -#define IDirect3DDevice8_Reset(p,a) (p)->Reset(a) -#define IDirect3DDevice8_Present(p,a,b,c,d) (p)->Present(a,b,c,d) -#define IDirect3DDevice8_GetBackBuffer(p,a,b,c) (p)->GetBackBuffer(a,b,c) -#define IDirect3DDevice8_GetRasterStatus(p,a) (p)->GetRasterStatus(a) -#define IDirect3DDevice8_SetGammaRamp(p,a,b) (p)->SetGammaRamp(a,b) -#define IDirect3DDevice8_GetGammaRamp(p,a) (p)->GetGammaRamp(a) -#define IDirect3DDevice8_CreateTexture(p,a,b,c,d,e,f,g) (p)->CreateTexture(a,b,c,d,e,f,g) -#define IDirect3DDevice8_CreateVolumeTexture(p,a,b,c,d,e,f,g,h) (p)->CreateVolumeTexture(a,b,c,d,e,f,g,h) -#define IDirect3DDevice8_CreateCubeTexture(p,a,b,c,d,e,f) (p)->CreateCubeTexture(a,b,c,d,e,f) -#define IDirect3DDevice8_CreateVertexBuffer(p,a,b,c,d,e) (p)->CreateVertexBuffer(a,b,c,d,e) -#define IDirect3DDevice8_CreateIndexBuffer(p,a,b,c,d,e) (p)->CreateIndexBuffer(a,b,c,d,e) -#define IDirect3DDevice8_CreateRenderTarget(p,a,b,c,d,e,f) (p)->CreateRenderTarget(a,b,c,d,e,f) -#define IDirect3DDevice8_CreateDepthStencilSurface(p,a,b,c,d,e) (p)->CreateDepthStencilSurface(a,b,c,d,e) -#define IDirect3DDevice8_CreateImageSurface(p,a,b,c,d) (p)->CreateImageSurface(a,b,c,d) -#define IDirect3DDevice8_CopyRects(p,a,b,c,d,e) (p)->CopyRects(a,b,c,d,e) -#define IDirect3DDevice8_UpdateTexture(p,a,b) (p)->UpdateTexture(a,b) -#define IDirect3DDevice8_GetFrontBuffer(p,a) (p)->GetFrontBuffer(a) -#define IDirect3DDevice8_SetRenderTarget(p,a,b) (p)->SetRenderTarget(a,b) -#define IDirect3DDevice8_GetRenderTarget(p,a) (p)->GetRenderTarget(a) -#define IDirect3DDevice8_GetDepthStencilSurface(p,a) (p)->GetDepthStencilSurface(a) -#define IDirect3DDevice8_BeginScene(p) (p)->BeginScene() -#define IDirect3DDevice8_EndScene(p) (p)->EndScene() -#define IDirect3DDevice8_Clear(p,a,b,c,d,e,f) (p)->Clear(a,b,c,d,e,f) -#define IDirect3DDevice8_SetTransform(p,a,b) (p)->SetTransform(a,b) -#define IDirect3DDevice8_GetTransform(p,a,b) (p)->GetTransform(a,b) -#define IDirect3DDevice8_MultiplyTransform(p,a,b) (p)->MultiplyTransform(a,b) -#define IDirect3DDevice8_SetViewport(p,a) (p)->SetViewport(a) -#define IDirect3DDevice8_GetViewport(p,a) (p)->GetViewport(a) -#define IDirect3DDevice8_SetMaterial(p,a) (p)->SetMaterial(a) -#define IDirect3DDevice8_GetMaterial(p,a) (p)->GetMaterial(a) -#define IDirect3DDevice8_SetLight(p,a,b) (p)->SetLight(a,b) -#define IDirect3DDevice8_GetLight(p,a,b) (p)->GetLight(a,b) -#define IDirect3DDevice8_LightEnable(p,a,b) (p)->LightEnable(a,b) -#define IDirect3DDevice8_GetLightEnable(p,a,b) (p)->GetLightEnable(a,b) -#define IDirect3DDevice8_SetClipPlane(p,a,b) (p)->SetClipPlane(a,b) -#define IDirect3DDevice8_GetClipPlane(p,a,b) (p)->GetClipPlane(a,b) -#define IDirect3DDevice8_SetRenderState(p,a,b) (p)->SetRenderState(a,b) -#define IDirect3DDevice8_GetRenderState(p,a,b) (p)->GetRenderState(a,b) -#define IDirect3DDevice8_BeginStateBlock(p) (p)->BeginStateBlock() -#define IDirect3DDevice8_EndStateBlock(p,a) (p)->EndStateBlock(a) -#define IDirect3DDevice8_ApplyStateBlock(p,a) (p)->ApplyStateBlock(a) -#define IDirect3DDevice8_CaptureStateBlock(p,a) (p)->CaptureStateBlock(a) -#define IDirect3DDevice8_DeleteStateBlock(p,a) (p)->DeleteStateBlock(a) -#define IDirect3DDevice8_CreateStateBlock(p,a,b) (p)->CreateStateBlock(a,b) -#define IDirect3DDevice8_SetClipStatus(p,a) (p)->SetClipStatus(a) -#define IDirect3DDevice8_GetClipStatus(p,a) (p)->GetClipStatus(a) -#define IDirect3DDevice8_GetTexture(p,a,b) (p)->GetTexture(a,b) -#define IDirect3DDevice8_SetTexture(p,a,b) (p)->SetTexture(a,b) -#define IDirect3DDevice8_GetTextureStageState(p,a,b,c) (p)->GetTextureStageState(a,b,c) -#define IDirect3DDevice8_SetTextureStageState(p,a,b,c) (p)->SetTextureStageState(a,b,c) -#define IDirect3DDevice8_ValidateDevice(p,a) (p)->ValidateDevice(a) -#define IDirect3DDevice8_GetInfo(p,a,b,c) (p)->GetInfo(a,b,c) -#define IDirect3DDevice8_SetPaletteEntries(p,a,b) (p)->SetPaletteEntries(a,b) -#define IDirect3DDevice8_GetPaletteEntries(p,a,b) (p)->GetPaletteEntries(a,b) -#define IDirect3DDevice8_SetCurrentTexturePalette(p,a) (p)->SetCurrentTexturePalette(a) -#define IDirect3DDevice8_GetCurrentTexturePalette(p,a) (p)->GetCurrentTexturePalette(a) -#define IDirect3DDevice8_DrawPrimitive(p,a,b,c) (p)->DrawPrimitive(a,b,c) -#define IDirect3DDevice8_DrawIndexedPrimitive(p,a,b,c,d,e) (p)->DrawIndexedPrimitive(a,b,c,d,e) -#define IDirect3DDevice8_DrawPrimitiveUP(p,a,b,c,d) (p)->DrawPrimitiveUP(a,b,c,d) -#define IDirect3DDevice8_DrawIndexedPrimitiveUP(p,a,b,c,d,e,f,g,h) (p)->DrawIndexedPrimitiveUP(a,b,c,d,e,f,g,h) -#define IDirect3DDevice8_ProcessVertices(p,a,b,c,d,e) (p)->ProcessVertices(a,b,c,d,e) -#define IDirect3DDevice8_CreateVertexShader(p,a,b,c,d) (p)->CreateVertexShader(a,b,c,d) -#define IDirect3DDevice8_SetVertexShader(p,a) (p)->SetVertexShader(a) -#define IDirect3DDevice8_GetVertexShader(p,a) (p)->GetVertexShader(a) -#define IDirect3DDevice8_DeleteVertexShader(p,a) (p)->DeleteVertexShader(a) -#define IDirect3DDevice8_SetVertexShaderConstant(p,a,b,c) (p)->SetVertexShaderConstant(a,b,c) -#define IDirect3DDevice8_GetVertexShaderConstant(p,a,b,c) (p)->GetVertexShaderConstant(a,b,c) -#define IDirect3DDevice8_GetVertexShaderDeclaration(p,a,b,c) (p)->GetVertexShaderDeclaration(a,b,c) -#define IDirect3DDevice8_GetVertexShaderFunction(p,a,b,c) (p)->GetVertexShaderFunction(a,b,c) -#define IDirect3DDevice8_SetStreamSource(p,a,b,c) (p)->SetStreamSource(a,b,c) -#define IDirect3DDevice8_GetStreamSource(p,a,b,c) (p)->GetStreamSource(a,b,c) -#define IDirect3DDevice8_SetIndices(p,a,b) (p)->SetIndices(a,b) -#define IDirect3DDevice8_GetIndices(p,a,b) (p)->GetIndices(a,b) -#define IDirect3DDevice8_CreatePixelShader(p,a,b) (p)->CreatePixelShader(a,b) -#define IDirect3DDevice8_SetPixelShader(p,a) (p)->SetPixelShader(a) -#define IDirect3DDevice8_GetPixelShader(p,a) (p)->GetPixelShader(a) -#define IDirect3DDevice8_DeletePixelShader(p,a) (p)->DeletePixelShader(a) -#define IDirect3DDevice8_SetPixelShaderConstant(p,a,b,c) (p)->SetPixelShaderConstant(a,b,c) -#define IDirect3DDevice8_GetPixelShaderConstant(p,a,b,c) (p)->GetPixelShaderConstant(a,b,c) -#define IDirect3DDevice8_GetPixelShaderFunction(p,a,b,c) (p)->GetPixelShaderFunction(a,b,c) -#define IDirect3DDevice8_DrawRectPatch(p,a,b,c) (p)->DrawRectPatch(a,b,c) -#define IDirect3DDevice8_DrawTriPatch(p,a,b,c) (p)->DrawTriPatch(a,b,c) -#define IDirect3DDevice8_DeletePatch(p,a) (p)->DeletePatch(a) -#endif - - - -#undef INTERFACE -#define INTERFACE IDirect3DSwapChain8 - -DECLARE_INTERFACE_(IDirect3DSwapChain8, IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DSwapChain8 methods ***/ - STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) PURE; - STDMETHOD(GetBackBuffer)(THIS_ UINT BackBuffer,D3DBACKBUFFER_TYPE Type,IDirect3DSurface8** ppBackBuffer) PURE; -}; - -typedef struct IDirect3DSwapChain8 *LPDIRECT3DSWAPCHAIN8, *PDIRECT3DSWAPCHAIN8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DSwapChain8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DSwapChain8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DSwapChain8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DSwapChain8_Present(p,a,b,c,d) (p)->lpVtbl->Present(p,a,b,c,d) -#define IDirect3DSwapChain8_GetBackBuffer(p,a,b,c) (p)->lpVtbl->GetBackBuffer(p,a,b,c) -#else -#define IDirect3DSwapChain8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DSwapChain8_AddRef(p) (p)->AddRef() -#define IDirect3DSwapChain8_Release(p) (p)->Release() -#define IDirect3DSwapChain8_Present(p,a,b,c,d) (p)->Present(a,b,c,d) -#define IDirect3DSwapChain8_GetBackBuffer(p,a,b,c) (p)->GetBackBuffer(a,b,c) -#endif - - - -#undef INTERFACE -#define INTERFACE IDirect3DResource8 - -DECLARE_INTERFACE_(IDirect3DResource8, IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DResource8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; -}; - -typedef struct IDirect3DResource8 *LPDIRECT3DRESOURCE8, *PDIRECT3DRESOURCE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DResource8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DResource8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DResource8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DResource8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DResource8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DResource8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DResource8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DResource8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DResource8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DResource8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DResource8_GetType(p) (p)->lpVtbl->GetType(p) -#else -#define IDirect3DResource8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DResource8_AddRef(p) (p)->AddRef() -#define IDirect3DResource8_Release(p) (p)->Release() -#define IDirect3DResource8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DResource8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DResource8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DResource8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DResource8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DResource8_GetPriority(p) (p)->GetPriority() -#define IDirect3DResource8_PreLoad(p) (p)->PreLoad() -#define IDirect3DResource8_GetType(p) (p)->GetType() -#endif - - - - -#undef INTERFACE -#define INTERFACE IDirect3DBaseTexture8 - -DECLARE_INTERFACE_(IDirect3DBaseTexture8, IDirect3DResource8) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DResource8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; - STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; - STDMETHOD_(DWORD, GetLOD)(THIS) PURE; - STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; -}; - -typedef struct IDirect3DBaseTexture8 *LPDIRECT3DBASETEXTURE8, *PDIRECT3DBASETEXTURE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DBaseTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DBaseTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DBaseTexture8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DBaseTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DBaseTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DBaseTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DBaseTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DBaseTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DBaseTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DBaseTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DBaseTexture8_GetType(p) (p)->lpVtbl->GetType(p) -#define IDirect3DBaseTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) -#define IDirect3DBaseTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) -#define IDirect3DBaseTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) -#else -#define IDirect3DBaseTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DBaseTexture8_AddRef(p) (p)->AddRef() -#define IDirect3DBaseTexture8_Release(p) (p)->Release() -#define IDirect3DBaseTexture8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DBaseTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DBaseTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DBaseTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DBaseTexture8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DBaseTexture8_GetPriority(p) (p)->GetPriority() -#define IDirect3DBaseTexture8_PreLoad(p) (p)->PreLoad() -#define IDirect3DBaseTexture8_GetType(p) (p)->GetType() -#define IDirect3DBaseTexture8_SetLOD(p,a) (p)->SetLOD(a) -#define IDirect3DBaseTexture8_GetLOD(p) (p)->GetLOD() -#define IDirect3DBaseTexture8_GetLevelCount(p) (p)->GetLevelCount() -#endif - - - - - -#undef INTERFACE -#define INTERFACE IDirect3DTexture8 - -DECLARE_INTERFACE_(IDirect3DTexture8, IDirect3DBaseTexture8) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DBaseTexture8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; - STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; - STDMETHOD_(DWORD, GetLOD)(THIS) PURE; - STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; - STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DSURFACE_DESC *pDesc) PURE; - STDMETHOD(GetSurfaceLevel)(THIS_ UINT Level,IDirect3DSurface8** ppSurfaceLevel) PURE; - STDMETHOD(LockRect)(THIS_ UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) PURE; - STDMETHOD(UnlockRect)(THIS_ UINT Level) PURE; - STDMETHOD(AddDirtyRect)(THIS_ CONST RECT* pDirtyRect) PURE; -}; - -typedef struct IDirect3DTexture8 *LPDIRECT3DTEXTURE8, *PDIRECT3DTEXTURE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DTexture8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DTexture8_GetType(p) (p)->lpVtbl->GetType(p) -#define IDirect3DTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) -#define IDirect3DTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) -#define IDirect3DTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) -#define IDirect3DTexture8_GetLevelDesc(p,a,b) (p)->lpVtbl->GetLevelDesc(p,a,b) -#define IDirect3DTexture8_GetSurfaceLevel(p,a,b) (p)->lpVtbl->GetSurfaceLevel(p,a,b) -#define IDirect3DTexture8_LockRect(p,a,b,c,d) (p)->lpVtbl->LockRect(p,a,b,c,d) -#define IDirect3DTexture8_UnlockRect(p,a) (p)->lpVtbl->UnlockRect(p,a) -#define IDirect3DTexture8_AddDirtyRect(p,a) (p)->lpVtbl->AddDirtyRect(p,a) -#else -#define IDirect3DTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DTexture8_AddRef(p) (p)->AddRef() -#define IDirect3DTexture8_Release(p) (p)->Release() -#define IDirect3DTexture8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DTexture8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DTexture8_GetPriority(p) (p)->GetPriority() -#define IDirect3DTexture8_PreLoad(p) (p)->PreLoad() -#define IDirect3DTexture8_GetType(p) (p)->GetType() -#define IDirect3DTexture8_SetLOD(p,a) (p)->SetLOD(a) -#define IDirect3DTexture8_GetLOD(p) (p)->GetLOD() -#define IDirect3DTexture8_GetLevelCount(p) (p)->GetLevelCount() -#define IDirect3DTexture8_GetLevelDesc(p,a,b) (p)->GetLevelDesc(a,b) -#define IDirect3DTexture8_GetSurfaceLevel(p,a,b) (p)->GetSurfaceLevel(a,b) -#define IDirect3DTexture8_LockRect(p,a,b,c,d) (p)->LockRect(a,b,c,d) -#define IDirect3DTexture8_UnlockRect(p,a) (p)->UnlockRect(a) -#define IDirect3DTexture8_AddDirtyRect(p,a) (p)->AddDirtyRect(a) -#endif - - - - - -#undef INTERFACE -#define INTERFACE IDirect3DVolumeTexture8 - -DECLARE_INTERFACE_(IDirect3DVolumeTexture8, IDirect3DBaseTexture8) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DBaseTexture8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; - STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; - STDMETHOD_(DWORD, GetLOD)(THIS) PURE; - STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; - STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DVOLUME_DESC *pDesc) PURE; - STDMETHOD(GetVolumeLevel)(THIS_ UINT Level,IDirect3DVolume8** ppVolumeLevel) PURE; - STDMETHOD(LockBox)(THIS_ UINT Level,D3DLOCKED_BOX* pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) PURE; - STDMETHOD(UnlockBox)(THIS_ UINT Level) PURE; - STDMETHOD(AddDirtyBox)(THIS_ CONST D3DBOX* pDirtyBox) PURE; -}; - -typedef struct IDirect3DVolumeTexture8 *LPDIRECT3DVOLUMETEXTURE8, *PDIRECT3DVOLUMETEXTURE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DVolumeTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DVolumeTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DVolumeTexture8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DVolumeTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DVolumeTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DVolumeTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DVolumeTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DVolumeTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DVolumeTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DVolumeTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DVolumeTexture8_GetType(p) (p)->lpVtbl->GetType(p) -#define IDirect3DVolumeTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) -#define IDirect3DVolumeTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) -#define IDirect3DVolumeTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) -#define IDirect3DVolumeTexture8_GetLevelDesc(p,a,b) (p)->lpVtbl->GetLevelDesc(p,a,b) -#define IDirect3DVolumeTexture8_GetVolumeLevel(p,a,b) (p)->lpVtbl->GetVolumeLevel(p,a,b) -#define IDirect3DVolumeTexture8_LockBox(p,a,b,c,d) (p)->lpVtbl->LockBox(p,a,b,c,d) -#define IDirect3DVolumeTexture8_UnlockBox(p,a) (p)->lpVtbl->UnlockBox(p,a) -#define IDirect3DVolumeTexture8_AddDirtyBox(p,a) (p)->lpVtbl->AddDirtyBox(p,a) -#else -#define IDirect3DVolumeTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DVolumeTexture8_AddRef(p) (p)->AddRef() -#define IDirect3DVolumeTexture8_Release(p) (p)->Release() -#define IDirect3DVolumeTexture8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DVolumeTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DVolumeTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DVolumeTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DVolumeTexture8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DVolumeTexture8_GetPriority(p) (p)->GetPriority() -#define IDirect3DVolumeTexture8_PreLoad(p) (p)->PreLoad() -#define IDirect3DVolumeTexture8_GetType(p) (p)->GetType() -#define IDirect3DVolumeTexture8_SetLOD(p,a) (p)->SetLOD(a) -#define IDirect3DVolumeTexture8_GetLOD(p) (p)->GetLOD() -#define IDirect3DVolumeTexture8_GetLevelCount(p) (p)->GetLevelCount() -#define IDirect3DVolumeTexture8_GetLevelDesc(p,a,b) (p)->GetLevelDesc(a,b) -#define IDirect3DVolumeTexture8_GetVolumeLevel(p,a,b) (p)->GetVolumeLevel(a,b) -#define IDirect3DVolumeTexture8_LockBox(p,a,b,c,d) (p)->LockBox(a,b,c,d) -#define IDirect3DVolumeTexture8_UnlockBox(p,a) (p)->UnlockBox(a) -#define IDirect3DVolumeTexture8_AddDirtyBox(p,a) (p)->AddDirtyBox(a) -#endif - - - - - -#undef INTERFACE -#define INTERFACE IDirect3DCubeTexture8 - -DECLARE_INTERFACE_(IDirect3DCubeTexture8, IDirect3DBaseTexture8) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DBaseTexture8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; - STDMETHOD_(DWORD, SetLOD)(THIS_ DWORD LODNew) PURE; - STDMETHOD_(DWORD, GetLOD)(THIS) PURE; - STDMETHOD_(DWORD, GetLevelCount)(THIS) PURE; - STDMETHOD(GetLevelDesc)(THIS_ UINT Level,D3DSURFACE_DESC *pDesc) PURE; - STDMETHOD(GetCubeMapSurface)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level,IDirect3DSurface8** ppCubeMapSurface) PURE; - STDMETHOD(LockRect)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level,D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) PURE; - STDMETHOD(UnlockRect)(THIS_ D3DCUBEMAP_FACES FaceType,UINT Level) PURE; - STDMETHOD(AddDirtyRect)(THIS_ D3DCUBEMAP_FACES FaceType,CONST RECT* pDirtyRect) PURE; -}; - -typedef struct IDirect3DCubeTexture8 *LPDIRECT3DCUBETEXTURE8, *PDIRECT3DCUBETEXTURE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DCubeTexture8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DCubeTexture8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DCubeTexture8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DCubeTexture8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DCubeTexture8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DCubeTexture8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DCubeTexture8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DCubeTexture8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DCubeTexture8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DCubeTexture8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DCubeTexture8_GetType(p) (p)->lpVtbl->GetType(p) -#define IDirect3DCubeTexture8_SetLOD(p,a) (p)->lpVtbl->SetLOD(p,a) -#define IDirect3DCubeTexture8_GetLOD(p) (p)->lpVtbl->GetLOD(p) -#define IDirect3DCubeTexture8_GetLevelCount(p) (p)->lpVtbl->GetLevelCount(p) -#define IDirect3DCubeTexture8_GetLevelDesc(p,a,b) (p)->lpVtbl->GetLevelDesc(p,a,b) -#define IDirect3DCubeTexture8_GetCubeMapSurface(p,a,b,c) (p)->lpVtbl->GetCubeMapSurface(p,a,b,c) -#define IDirect3DCubeTexture8_LockRect(p,a,b,c,d,e) (p)->lpVtbl->LockRect(p,a,b,c,d,e) -#define IDirect3DCubeTexture8_UnlockRect(p,a,b) (p)->lpVtbl->UnlockRect(p,a,b) -#define IDirect3DCubeTexture8_AddDirtyRect(p,a,b) (p)->lpVtbl->AddDirtyRect(p,a,b) -#else -#define IDirect3DCubeTexture8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DCubeTexture8_AddRef(p) (p)->AddRef() -#define IDirect3DCubeTexture8_Release(p) (p)->Release() -#define IDirect3DCubeTexture8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DCubeTexture8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DCubeTexture8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DCubeTexture8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DCubeTexture8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DCubeTexture8_GetPriority(p) (p)->GetPriority() -#define IDirect3DCubeTexture8_PreLoad(p) (p)->PreLoad() -#define IDirect3DCubeTexture8_GetType(p) (p)->GetType() -#define IDirect3DCubeTexture8_SetLOD(p,a) (p)->SetLOD(a) -#define IDirect3DCubeTexture8_GetLOD(p) (p)->GetLOD() -#define IDirect3DCubeTexture8_GetLevelCount(p) (p)->GetLevelCount() -#define IDirect3DCubeTexture8_GetLevelDesc(p,a,b) (p)->GetLevelDesc(a,b) -#define IDirect3DCubeTexture8_GetCubeMapSurface(p,a,b,c) (p)->GetCubeMapSurface(a,b,c) -#define IDirect3DCubeTexture8_LockRect(p,a,b,c,d,e) (p)->LockRect(a,b,c,d,e) -#define IDirect3DCubeTexture8_UnlockRect(p,a,b) (p)->UnlockRect(a,b) -#define IDirect3DCubeTexture8_AddDirtyRect(p,a,b) (p)->AddDirtyRect(a,b) -#endif - - - - -#undef INTERFACE -#define INTERFACE IDirect3DVertexBuffer8 - -DECLARE_INTERFACE_(IDirect3DVertexBuffer8, IDirect3DResource8) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DResource8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; - STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,BYTE** ppbData,DWORD Flags) PURE; - STDMETHOD(Unlock)(THIS) PURE; - STDMETHOD(GetDesc)(THIS_ D3DVERTEXBUFFER_DESC *pDesc) PURE; -}; - -typedef struct IDirect3DVertexBuffer8 *LPDIRECT3DVERTEXBUFFER8, *PDIRECT3DVERTEXBUFFER8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DVertexBuffer8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DVertexBuffer8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DVertexBuffer8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DVertexBuffer8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DVertexBuffer8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DVertexBuffer8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DVertexBuffer8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DVertexBuffer8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DVertexBuffer8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DVertexBuffer8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DVertexBuffer8_GetType(p) (p)->lpVtbl->GetType(p) -#define IDirect3DVertexBuffer8_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) -#define IDirect3DVertexBuffer8_Unlock(p) (p)->lpVtbl->Unlock(p) -#define IDirect3DVertexBuffer8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) -#else -#define IDirect3DVertexBuffer8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DVertexBuffer8_AddRef(p) (p)->AddRef() -#define IDirect3DVertexBuffer8_Release(p) (p)->Release() -#define IDirect3DVertexBuffer8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DVertexBuffer8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DVertexBuffer8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DVertexBuffer8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DVertexBuffer8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DVertexBuffer8_GetPriority(p) (p)->GetPriority() -#define IDirect3DVertexBuffer8_PreLoad(p) (p)->PreLoad() -#define IDirect3DVertexBuffer8_GetType(p) (p)->GetType() -#define IDirect3DVertexBuffer8_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) -#define IDirect3DVertexBuffer8_Unlock(p) (p)->Unlock() -#define IDirect3DVertexBuffer8_GetDesc(p,a) (p)->GetDesc(a) -#endif - - - - -#undef INTERFACE -#define INTERFACE IDirect3DIndexBuffer8 - -DECLARE_INTERFACE_(IDirect3DIndexBuffer8, IDirect3DResource8) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DResource8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD_(DWORD, SetPriority)(THIS_ DWORD PriorityNew) PURE; - STDMETHOD_(DWORD, GetPriority)(THIS) PURE; - STDMETHOD_(void, PreLoad)(THIS) PURE; - STDMETHOD_(D3DRESOURCETYPE, GetType)(THIS) PURE; - STDMETHOD(Lock)(THIS_ UINT OffsetToLock,UINT SizeToLock,BYTE** ppbData,DWORD Flags) PURE; - STDMETHOD(Unlock)(THIS) PURE; - STDMETHOD(GetDesc)(THIS_ D3DINDEXBUFFER_DESC *pDesc) PURE; -}; - -typedef struct IDirect3DIndexBuffer8 *LPDIRECT3DINDEXBUFFER8, *PDIRECT3DINDEXBUFFER8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DIndexBuffer8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DIndexBuffer8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DIndexBuffer8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DIndexBuffer8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DIndexBuffer8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DIndexBuffer8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DIndexBuffer8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DIndexBuffer8_SetPriority(p,a) (p)->lpVtbl->SetPriority(p,a) -#define IDirect3DIndexBuffer8_GetPriority(p) (p)->lpVtbl->GetPriority(p) -#define IDirect3DIndexBuffer8_PreLoad(p) (p)->lpVtbl->PreLoad(p) -#define IDirect3DIndexBuffer8_GetType(p) (p)->lpVtbl->GetType(p) -#define IDirect3DIndexBuffer8_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) -#define IDirect3DIndexBuffer8_Unlock(p) (p)->lpVtbl->Unlock(p) -#define IDirect3DIndexBuffer8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) -#else -#define IDirect3DIndexBuffer8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DIndexBuffer8_AddRef(p) (p)->AddRef() -#define IDirect3DIndexBuffer8_Release(p) (p)->Release() -#define IDirect3DIndexBuffer8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DIndexBuffer8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DIndexBuffer8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DIndexBuffer8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DIndexBuffer8_SetPriority(p,a) (p)->SetPriority(a) -#define IDirect3DIndexBuffer8_GetPriority(p) (p)->GetPriority() -#define IDirect3DIndexBuffer8_PreLoad(p) (p)->PreLoad() -#define IDirect3DIndexBuffer8_GetType(p) (p)->GetType() -#define IDirect3DIndexBuffer8_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) -#define IDirect3DIndexBuffer8_Unlock(p) (p)->Unlock() -#define IDirect3DIndexBuffer8_GetDesc(p,a) (p)->GetDesc(a) -#endif - - - - -#undef INTERFACE -#define INTERFACE IDirect3DSurface8 - -DECLARE_INTERFACE_(IDirect3DSurface8, IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DSurface8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD(GetContainer)(THIS_ REFIID riid,void** ppContainer) PURE; - STDMETHOD(GetDesc)(THIS_ D3DSURFACE_DESC *pDesc) PURE; - STDMETHOD(LockRect)(THIS_ D3DLOCKED_RECT* pLockedRect,CONST RECT* pRect,DWORD Flags) PURE; - STDMETHOD(UnlockRect)(THIS) PURE; -}; - -typedef struct IDirect3DSurface8 *LPDIRECT3DSURFACE8, *PDIRECT3DSURFACE8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DSurface8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DSurface8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DSurface8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DSurface8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DSurface8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DSurface8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DSurface8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DSurface8_GetContainer(p,a,b) (p)->lpVtbl->GetContainer(p,a,b) -#define IDirect3DSurface8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) -#define IDirect3DSurface8_LockRect(p,a,b,c) (p)->lpVtbl->LockRect(p,a,b,c) -#define IDirect3DSurface8_UnlockRect(p) (p)->lpVtbl->UnlockRect(p) -#else -#define IDirect3DSurface8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DSurface8_AddRef(p) (p)->AddRef() -#define IDirect3DSurface8_Release(p) (p)->Release() -#define IDirect3DSurface8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DSurface8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DSurface8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DSurface8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DSurface8_GetContainer(p,a,b) (p)->GetContainer(a,b) -#define IDirect3DSurface8_GetDesc(p,a) (p)->GetDesc(a) -#define IDirect3DSurface8_LockRect(p,a,b,c) (p)->LockRect(a,b,c) -#define IDirect3DSurface8_UnlockRect(p) (p)->UnlockRect() -#endif - - - - -#undef INTERFACE -#define INTERFACE IDirect3DVolume8 - -DECLARE_INTERFACE_(IDirect3DVolume8, IUnknown) -{ - /*** IUnknown methods ***/ - STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - - /*** IDirect3DVolume8 methods ***/ - STDMETHOD(GetDevice)(THIS_ IDirect3DDevice8** ppDevice) PURE; - STDMETHOD(SetPrivateData)(THIS_ REFGUID refguid,CONST void* pData,DWORD SizeOfData,DWORD Flags) PURE; - STDMETHOD(GetPrivateData)(THIS_ REFGUID refguid,void* pData,DWORD* pSizeOfData) PURE; - STDMETHOD(FreePrivateData)(THIS_ REFGUID refguid) PURE; - STDMETHOD(GetContainer)(THIS_ REFIID riid,void** ppContainer) PURE; - STDMETHOD(GetDesc)(THIS_ D3DVOLUME_DESC *pDesc) PURE; - STDMETHOD(LockBox)(THIS_ D3DLOCKED_BOX * pLockedVolume,CONST D3DBOX* pBox,DWORD Flags) PURE; - STDMETHOD(UnlockBox)(THIS) PURE; -}; - -typedef struct IDirect3DVolume8 *LPDIRECT3DVOLUME8, *PDIRECT3DVOLUME8; - -#if !defined(__cplusplus) || defined(CINTERFACE) -#define IDirect3DVolume8_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) -#define IDirect3DVolume8_AddRef(p) (p)->lpVtbl->AddRef(p) -#define IDirect3DVolume8_Release(p) (p)->lpVtbl->Release(p) -#define IDirect3DVolume8_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a) -#define IDirect3DVolume8_SetPrivateData(p,a,b,c,d) (p)->lpVtbl->SetPrivateData(p,a,b,c,d) -#define IDirect3DVolume8_GetPrivateData(p,a,b,c) (p)->lpVtbl->GetPrivateData(p,a,b,c) -#define IDirect3DVolume8_FreePrivateData(p,a) (p)->lpVtbl->FreePrivateData(p,a) -#define IDirect3DVolume8_GetContainer(p,a,b) (p)->lpVtbl->GetContainer(p,a,b) -#define IDirect3DVolume8_GetDesc(p,a) (p)->lpVtbl->GetDesc(p,a) -#define IDirect3DVolume8_LockBox(p,a,b,c) (p)->lpVtbl->LockBox(p,a,b,c) -#define IDirect3DVolume8_UnlockBox(p) (p)->lpVtbl->UnlockBox(p) -#else -#define IDirect3DVolume8_QueryInterface(p,a,b) (p)->QueryInterface(a,b) -#define IDirect3DVolume8_AddRef(p) (p)->AddRef() -#define IDirect3DVolume8_Release(p) (p)->Release() -#define IDirect3DVolume8_GetDevice(p,a) (p)->GetDevice(a) -#define IDirect3DVolume8_SetPrivateData(p,a,b,c,d) (p)->SetPrivateData(a,b,c,d) -#define IDirect3DVolume8_GetPrivateData(p,a,b,c) (p)->GetPrivateData(a,b,c) -#define IDirect3DVolume8_FreePrivateData(p,a) (p)->FreePrivateData(a) -#define IDirect3DVolume8_GetContainer(p,a,b) (p)->GetContainer(a,b) -#define IDirect3DVolume8_GetDesc(p,a) (p)->GetDesc(a) -#define IDirect3DVolume8_LockBox(p,a,b,c) (p)->LockBox(a,b,c) -#define IDirect3DVolume8_UnlockBox(p) (p)->UnlockBox() -#endif - -/**************************************************************************** - * Flags for SetPrivateData method on all D3D8 interfaces - * - * The passed pointer is an IUnknown ptr. The SizeOfData argument to SetPrivateData - * must be set to sizeof(IUnknown*). Direct3D will call AddRef through this - * pointer and Release when the private data is destroyed. The data will be - * destroyed when another SetPrivateData with the same GUID is set, when - * FreePrivateData is called, or when the D3D8 object is freed. - ****************************************************************************/ -#define D3DSPD_IUNKNOWN 0x00000001L - -/**************************************************************************** - * - * Parameter for IDirect3D8 Enum and GetCaps8 functions to get the info for - * the current mode only. - * - ****************************************************************************/ - -#define D3DCURRENT_DISPLAY_MODE 0x00EFFFFFL - -/**************************************************************************** - * - * Flags for IDirect3D8::CreateDevice's BehaviorFlags - * - ****************************************************************************/ - -#define D3DCREATE_FPU_PRESERVE 0x00000002L -#define D3DCREATE_MULTITHREADED 0x00000004L - -#define D3DCREATE_PUREDEVICE 0x00000010L -#define D3DCREATE_SOFTWARE_VERTEXPROCESSING 0x00000020L -#define D3DCREATE_HARDWARE_VERTEXPROCESSING 0x00000040L -#define D3DCREATE_MIXED_VERTEXPROCESSING 0x00000080L - -#define D3DCREATE_DISABLE_DRIVER_MANAGEMENT 0x00000100L - - -/**************************************************************************** - * - * Parameter for IDirect3D8::CreateDevice's iAdapter - * - ****************************************************************************/ - -#define D3DADAPTER_DEFAULT 0 - -/**************************************************************************** - * - * Flags for IDirect3D8::EnumAdapters - * - ****************************************************************************/ - -#define D3DENUM_NO_WHQL_LEVEL 0x00000002L - -/**************************************************************************** - * - * Maximum number of back-buffers supported in DX8 - * - ****************************************************************************/ - -#define D3DPRESENT_BACK_BUFFERS_MAX 3L - -/**************************************************************************** - * - * Flags for IDirect3DDevice8::SetGammaRamp - * - ****************************************************************************/ - -#define D3DSGR_NO_CALIBRATION 0x00000000L -#define D3DSGR_CALIBRATE 0x00000001L - -/**************************************************************************** - * - * Flags for IDirect3DDevice8::SetCursorPosition - * - ****************************************************************************/ - -#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L - -/**************************************************************************** - * - * Flags for DrawPrimitive/DrawIndexedPrimitive - * Also valid for Begin/BeginIndexed - * Also valid for VertexBuffer::CreateVertexBuffer - ****************************************************************************/ - - -/* - * DirectDraw error codes - */ -#define _FACD3D 0x876 -#define MAKE_D3DHRESULT( code ) MAKE_HRESULT( 1, _FACD3D, code ) - -/* - * Direct3D Errors - */ -#define D3D_OK S_OK - -#define D3DERR_WRONGTEXTUREFORMAT MAKE_D3DHRESULT(2072) -#define D3DERR_UNSUPPORTEDCOLOROPERATION MAKE_D3DHRESULT(2073) -#define D3DERR_UNSUPPORTEDCOLORARG MAKE_D3DHRESULT(2074) -#define D3DERR_UNSUPPORTEDALPHAOPERATION MAKE_D3DHRESULT(2075) -#define D3DERR_UNSUPPORTEDALPHAARG MAKE_D3DHRESULT(2076) -#define D3DERR_TOOMANYOPERATIONS MAKE_D3DHRESULT(2077) -#define D3DERR_CONFLICTINGTEXTUREFILTER MAKE_D3DHRESULT(2078) -#define D3DERR_UNSUPPORTEDFACTORVALUE MAKE_D3DHRESULT(2079) -#define D3DERR_CONFLICTINGRENDERSTATE MAKE_D3DHRESULT(2081) -#define D3DERR_UNSUPPORTEDTEXTUREFILTER MAKE_D3DHRESULT(2082) -#define D3DERR_CONFLICTINGTEXTUREPALETTE MAKE_D3DHRESULT(2086) -#define D3DERR_DRIVERINTERNALERROR MAKE_D3DHRESULT(2087) - -#define D3DERR_NOTFOUND MAKE_D3DHRESULT(2150) -#define D3DERR_MOREDATA MAKE_D3DHRESULT(2151) -#define D3DERR_DEVICELOST MAKE_D3DHRESULT(2152) -#define D3DERR_DEVICENOTRESET MAKE_D3DHRESULT(2153) -#define D3DERR_NOTAVAILABLE MAKE_D3DHRESULT(2154) -#define D3DERR_OUTOFVIDEOMEMORY MAKE_D3DHRESULT(380) -#define D3DERR_INVALIDDEVICE MAKE_D3DHRESULT(2155) -#define D3DERR_INVALIDCALL MAKE_D3DHRESULT(2156) -#define D3DERR_DRIVERINVALIDCALL MAKE_D3DHRESULT(2157) - -#ifdef __cplusplus -}; -#endif - -#endif /* (DIRECT3D_VERSION >= 0x0800) */ -#endif /* _D3D_H_ */ - diff --git a/plugins/win-capture/d3d8-api/d3d8caps.h b/plugins/win-capture/d3d8-api/d3d8caps.h deleted file mode 100644 index 6af8e6c434c06b..00000000000000 --- a/plugins/win-capture/d3d8-api/d3d8caps.h +++ /dev/null @@ -1,364 +0,0 @@ -/*==========================================================================; - * - * Copyright (C) Microsoft Corporation. All Rights Reserved. - * - * File: d3d8caps.h - * Content: Direct3D capabilities include file - * - ***************************************************************************/ - -#ifndef _D3D8CAPS_H -#define _D3D8CAPS_H - -#ifndef DIRECT3D_VERSION -#define DIRECT3D_VERSION 0x0800 -#endif //DIRECT3D_VERSION - -// include this file content only if compiling for DX8 interfaces -#if(DIRECT3D_VERSION >= 0x0800) - -#if defined(_X86_) || defined(_IA64_) -#pragma pack(4) -#endif - -typedef struct _D3DCAPS8 -{ - /* Device Info */ - D3DDEVTYPE DeviceType; - UINT AdapterOrdinal; - - /* Caps from DX7 Draw */ - DWORD Caps; - DWORD Caps2; - DWORD Caps3; - DWORD PresentationIntervals; - - /* Cursor Caps */ - DWORD CursorCaps; - - /* 3D Device Caps */ - DWORD DevCaps; - - DWORD PrimitiveMiscCaps; - DWORD RasterCaps; - DWORD ZCmpCaps; - DWORD SrcBlendCaps; - DWORD DestBlendCaps; - DWORD AlphaCmpCaps; - DWORD ShadeCaps; - DWORD TextureCaps; - DWORD TextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DTexture8's - DWORD CubeTextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DCubeTexture8's - DWORD VolumeTextureFilterCaps; // D3DPTFILTERCAPS for IDirect3DVolumeTexture8's - DWORD TextureAddressCaps; // D3DPTADDRESSCAPS for IDirect3DTexture8's - DWORD VolumeTextureAddressCaps; // D3DPTADDRESSCAPS for IDirect3DVolumeTexture8's - - DWORD LineCaps; // D3DLINECAPS - - DWORD MaxTextureWidth, MaxTextureHeight; - DWORD MaxVolumeExtent; - - DWORD MaxTextureRepeat; - DWORD MaxTextureAspectRatio; - DWORD MaxAnisotropy; - float MaxVertexW; - - float GuardBandLeft; - float GuardBandTop; - float GuardBandRight; - float GuardBandBottom; - - float ExtentsAdjust; - DWORD StencilCaps; - - DWORD FVFCaps; - DWORD TextureOpCaps; - DWORD MaxTextureBlendStages; - DWORD MaxSimultaneousTextures; - - DWORD VertexProcessingCaps; - DWORD MaxActiveLights; - DWORD MaxUserClipPlanes; - DWORD MaxVertexBlendMatrices; - DWORD MaxVertexBlendMatrixIndex; - - float MaxPointSize; - - DWORD MaxPrimitiveCount; // max number of primitives per DrawPrimitive call - DWORD MaxVertexIndex; - DWORD MaxStreams; - DWORD MaxStreamStride; // max stride for SetStreamSource - - DWORD VertexShaderVersion; - DWORD MaxVertexShaderConst; // number of vertex shader constant registers - - DWORD PixelShaderVersion; - float MaxPixelShaderValue; // max value of pixel shader arithmetic component - -} D3DCAPS8; - -// -// BIT DEFINES FOR D3DCAPS8 DWORD MEMBERS -// - -// -// Caps -// -#define D3DCAPS_READ_SCANLINE 0x00020000L - -// -// Caps2 -// -#define D3DCAPS2_NO2DDURING3DSCENE 0x00000002L -#define D3DCAPS2_FULLSCREENGAMMA 0x00020000L -#define D3DCAPS2_CANRENDERWINDOWED 0x00080000L -#define D3DCAPS2_CANCALIBRATEGAMMA 0x00100000L -#define D3DCAPS2_RESERVED 0x02000000L -#define D3DCAPS2_CANMANAGERESOURCE 0x10000000L -#define D3DCAPS2_DYNAMICTEXTURES 0x20000000L - -// -// Caps3 -// -#define D3DCAPS3_RESERVED 0x8000001fL - -// Indicates that the device can respect the ALPHABLENDENABLE render state -// when fullscreen while using the FLIP or DISCARD swap effect. -// COPY and COPYVSYNC swap effects work whether or not this flag is set. -#define D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD 0x00000020L - -// -// PresentationIntervals -// -#define D3DPRESENT_INTERVAL_DEFAULT 0x00000000L -#define D3DPRESENT_INTERVAL_ONE 0x00000001L -#define D3DPRESENT_INTERVAL_TWO 0x00000002L -#define D3DPRESENT_INTERVAL_THREE 0x00000004L -#define D3DPRESENT_INTERVAL_FOUR 0x00000008L -#define D3DPRESENT_INTERVAL_IMMEDIATE 0x80000000L - -// -// CursorCaps -// -// Driver supports HW color cursor in at least hi-res modes(height >=400) -#define D3DCURSORCAPS_COLOR 0x00000001L -// Driver supports HW cursor also in low-res modes(height < 400) -#define D3DCURSORCAPS_LOWRES 0x00000002L - -// -// DevCaps -// -#define D3DDEVCAPS_EXECUTESYSTEMMEMORY 0x00000010L /* Device can use execute buffers from system memory */ -#define D3DDEVCAPS_EXECUTEVIDEOMEMORY 0x00000020L /* Device can use execute buffers from video memory */ -#define D3DDEVCAPS_TLVERTEXSYSTEMMEMORY 0x00000040L /* Device can use TL buffers from system memory */ -#define D3DDEVCAPS_TLVERTEXVIDEOMEMORY 0x00000080L /* Device can use TL buffers from video memory */ -#define D3DDEVCAPS_TEXTURESYSTEMMEMORY 0x00000100L /* Device can texture from system memory */ -#define D3DDEVCAPS_TEXTUREVIDEOMEMORY 0x00000200L /* Device can texture from device memory */ -#define D3DDEVCAPS_DRAWPRIMTLVERTEX 0x00000400L /* Device can draw TLVERTEX primitives */ -#define D3DDEVCAPS_CANRENDERAFTERFLIP 0x00000800L /* Device can render without waiting for flip to complete */ -#define D3DDEVCAPS_TEXTURENONLOCALVIDMEM 0x00001000L /* Device can texture from nonlocal video memory */ -#define D3DDEVCAPS_DRAWPRIMITIVES2 0x00002000L /* Device can support DrawPrimitives2 */ -#define D3DDEVCAPS_SEPARATETEXTUREMEMORIES 0x00004000L /* Device is texturing from separate memory pools */ -#define D3DDEVCAPS_DRAWPRIMITIVES2EX 0x00008000L /* Device can support Extended DrawPrimitives2 i.e. DX7 compliant driver*/ -#define D3DDEVCAPS_HWTRANSFORMANDLIGHT 0x00010000L /* Device can support transformation and lighting in hardware and DRAWPRIMITIVES2EX must be also */ -#define D3DDEVCAPS_CANBLTSYSTONONLOCAL 0x00020000L /* Device supports a Tex Blt from system memory to non-local vidmem */ -#define D3DDEVCAPS_HWRASTERIZATION 0x00080000L /* Device has HW acceleration for rasterization */ -#define D3DDEVCAPS_PUREDEVICE 0x00100000L /* Device supports D3DCREATE_PUREDEVICE */ -#define D3DDEVCAPS_QUINTICRTPATCHES 0x00200000L /* Device supports quintic Beziers and BSplines */ -#define D3DDEVCAPS_RTPATCHES 0x00400000L /* Device supports Rect and Tri patches */ -#define D3DDEVCAPS_RTPATCHHANDLEZERO 0x00800000L /* Indicates that RT Patches may be drawn efficiently using handle 0 */ -#define D3DDEVCAPS_NPATCHES 0x01000000L /* Device supports N-Patches */ - -// -// PrimitiveMiscCaps -// -#define D3DPMISCCAPS_MASKZ 0x00000002L -#define D3DPMISCCAPS_LINEPATTERNREP 0x00000004L -#define D3DPMISCCAPS_CULLNONE 0x00000010L -#define D3DPMISCCAPS_CULLCW 0x00000020L -#define D3DPMISCCAPS_CULLCCW 0x00000040L -#define D3DPMISCCAPS_COLORWRITEENABLE 0x00000080L -#define D3DPMISCCAPS_CLIPPLANESCALEDPOINTS 0x00000100L /* Device correctly clips scaled points to clip planes */ -#define D3DPMISCCAPS_CLIPTLVERTS 0x00000200L /* device will clip post-transformed vertex primitives */ -#define D3DPMISCCAPS_TSSARGTEMP 0x00000400L /* device supports D3DTA_TEMP for temporary register */ -#define D3DPMISCCAPS_BLENDOP 0x00000800L /* device supports D3DRS_BLENDOP */ -#define D3DPMISCCAPS_NULLREFERENCE 0x00001000L /* Reference Device that doesnt render */ - -// -// LineCaps -// -#define D3DLINECAPS_TEXTURE 0x00000001L -#define D3DLINECAPS_ZTEST 0x00000002L -#define D3DLINECAPS_BLEND 0x00000004L -#define D3DLINECAPS_ALPHACMP 0x00000008L -#define D3DLINECAPS_FOG 0x00000010L - -// -// RasterCaps -// -#define D3DPRASTERCAPS_DITHER 0x00000001L -#define D3DPRASTERCAPS_PAT 0x00000008L -#define D3DPRASTERCAPS_ZTEST 0x00000010L -#define D3DPRASTERCAPS_FOGVERTEX 0x00000080L -#define D3DPRASTERCAPS_FOGTABLE 0x00000100L -#define D3DPRASTERCAPS_ANTIALIASEDGES 0x00001000L -#define D3DPRASTERCAPS_MIPMAPLODBIAS 0x00002000L -#define D3DPRASTERCAPS_ZBIAS 0x00004000L -#define D3DPRASTERCAPS_ZBUFFERLESSHSR 0x00008000L -#define D3DPRASTERCAPS_FOGRANGE 0x00010000L -#define D3DPRASTERCAPS_ANISOTROPY 0x00020000L -#define D3DPRASTERCAPS_WBUFFER 0x00040000L -#define D3DPRASTERCAPS_WFOG 0x00100000L -#define D3DPRASTERCAPS_ZFOG 0x00200000L -#define D3DPRASTERCAPS_COLORPERSPECTIVE 0x00400000L /* Device iterates colors perspective correct */ -#define D3DPRASTERCAPS_STRETCHBLTMULTISAMPLE 0x00800000L - -// -// ZCmpCaps, AlphaCmpCaps -// -#define D3DPCMPCAPS_NEVER 0x00000001L -#define D3DPCMPCAPS_LESS 0x00000002L -#define D3DPCMPCAPS_EQUAL 0x00000004L -#define D3DPCMPCAPS_LESSEQUAL 0x00000008L -#define D3DPCMPCAPS_GREATER 0x00000010L -#define D3DPCMPCAPS_NOTEQUAL 0x00000020L -#define D3DPCMPCAPS_GREATEREQUAL 0x00000040L -#define D3DPCMPCAPS_ALWAYS 0x00000080L - -// -// SourceBlendCaps, DestBlendCaps -// -#define D3DPBLENDCAPS_ZERO 0x00000001L -#define D3DPBLENDCAPS_ONE 0x00000002L -#define D3DPBLENDCAPS_SRCCOLOR 0x00000004L -#define D3DPBLENDCAPS_INVSRCCOLOR 0x00000008L -#define D3DPBLENDCAPS_SRCALPHA 0x00000010L -#define D3DPBLENDCAPS_INVSRCALPHA 0x00000020L -#define D3DPBLENDCAPS_DESTALPHA 0x00000040L -#define D3DPBLENDCAPS_INVDESTALPHA 0x00000080L -#define D3DPBLENDCAPS_DESTCOLOR 0x00000100L -#define D3DPBLENDCAPS_INVDESTCOLOR 0x00000200L -#define D3DPBLENDCAPS_SRCALPHASAT 0x00000400L -#define D3DPBLENDCAPS_BOTHSRCALPHA 0x00000800L -#define D3DPBLENDCAPS_BOTHINVSRCALPHA 0x00001000L - -// -// ShadeCaps -// -#define D3DPSHADECAPS_COLORGOURAUDRGB 0x00000008L -#define D3DPSHADECAPS_SPECULARGOURAUDRGB 0x00000200L -#define D3DPSHADECAPS_ALPHAGOURAUDBLEND 0x00004000L -#define D3DPSHADECAPS_FOGGOURAUD 0x00080000L - -// -// TextureCaps -// -#define D3DPTEXTURECAPS_PERSPECTIVE 0x00000001L /* Perspective-correct texturing is supported */ -#define D3DPTEXTURECAPS_POW2 0x00000002L /* Power-of-2 texture dimensions are required - applies to non-Cube/Volume textures only. */ -#define D3DPTEXTURECAPS_ALPHA 0x00000004L /* Alpha in texture pixels is supported */ -#define D3DPTEXTURECAPS_SQUAREONLY 0x00000020L /* Only square textures are supported */ -#define D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE 0x00000040L /* Texture indices are not scaled by the texture size prior to interpolation */ -#define D3DPTEXTURECAPS_ALPHAPALETTE 0x00000080L /* Device can draw alpha from texture palettes */ -// Device can use non-POW2 textures if: -// 1) D3DTEXTURE_ADDRESS is set to CLAMP for this texture's stage -// 2) D3DRS_WRAP(N) is zero for this texture's coordinates -// 3) mip mapping is not enabled (use magnification filter only) -#define D3DPTEXTURECAPS_NONPOW2CONDITIONAL 0x00000100L -#define D3DPTEXTURECAPS_PROJECTED 0x00000400L /* Device can do D3DTTFF_PROJECTED */ -#define D3DPTEXTURECAPS_CUBEMAP 0x00000800L /* Device can do cubemap textures */ -#define D3DPTEXTURECAPS_VOLUMEMAP 0x00002000L /* Device can do volume textures */ -#define D3DPTEXTURECAPS_MIPMAP 0x00004000L /* Device can do mipmapped textures */ -#define D3DPTEXTURECAPS_MIPVOLUMEMAP 0x00008000L /* Device can do mipmapped volume textures */ -#define D3DPTEXTURECAPS_MIPCUBEMAP 0x00010000L /* Device can do mipmapped cube maps */ -#define D3DPTEXTURECAPS_CUBEMAP_POW2 0x00020000L /* Device requires that cubemaps be power-of-2 dimension */ -#define D3DPTEXTURECAPS_VOLUMEMAP_POW2 0x00040000L /* Device requires that volume maps be power-of-2 dimension */ - -// -// TextureFilterCaps -// -#define D3DPTFILTERCAPS_MINFPOINT 0x00000100L /* Min Filter */ -#define D3DPTFILTERCAPS_MINFLINEAR 0x00000200L -#define D3DPTFILTERCAPS_MINFANISOTROPIC 0x00000400L -#define D3DPTFILTERCAPS_MIPFPOINT 0x00010000L /* Mip Filter */ -#define D3DPTFILTERCAPS_MIPFLINEAR 0x00020000L -#define D3DPTFILTERCAPS_MAGFPOINT 0x01000000L /* Mag Filter */ -#define D3DPTFILTERCAPS_MAGFLINEAR 0x02000000L -#define D3DPTFILTERCAPS_MAGFANISOTROPIC 0x04000000L -#define D3DPTFILTERCAPS_MAGFAFLATCUBIC 0x08000000L -#define D3DPTFILTERCAPS_MAGFGAUSSIANCUBIC 0x10000000L - -// -// TextureAddressCaps -// -#define D3DPTADDRESSCAPS_WRAP 0x00000001L -#define D3DPTADDRESSCAPS_MIRROR 0x00000002L -#define D3DPTADDRESSCAPS_CLAMP 0x00000004L -#define D3DPTADDRESSCAPS_BORDER 0x00000008L -#define D3DPTADDRESSCAPS_INDEPENDENTUV 0x00000010L -#define D3DPTADDRESSCAPS_MIRRORONCE 0x00000020L - -// -// StencilCaps -// -#define D3DSTENCILCAPS_KEEP 0x00000001L -#define D3DSTENCILCAPS_ZERO 0x00000002L -#define D3DSTENCILCAPS_REPLACE 0x00000004L -#define D3DSTENCILCAPS_INCRSAT 0x00000008L -#define D3DSTENCILCAPS_DECRSAT 0x00000010L -#define D3DSTENCILCAPS_INVERT 0x00000020L -#define D3DSTENCILCAPS_INCR 0x00000040L -#define D3DSTENCILCAPS_DECR 0x00000080L - -// -// TextureOpCaps -// -#define D3DTEXOPCAPS_DISABLE 0x00000001L -#define D3DTEXOPCAPS_SELECTARG1 0x00000002L -#define D3DTEXOPCAPS_SELECTARG2 0x00000004L -#define D3DTEXOPCAPS_MODULATE 0x00000008L -#define D3DTEXOPCAPS_MODULATE2X 0x00000010L -#define D3DTEXOPCAPS_MODULATE4X 0x00000020L -#define D3DTEXOPCAPS_ADD 0x00000040L -#define D3DTEXOPCAPS_ADDSIGNED 0x00000080L -#define D3DTEXOPCAPS_ADDSIGNED2X 0x00000100L -#define D3DTEXOPCAPS_SUBTRACT 0x00000200L -#define D3DTEXOPCAPS_ADDSMOOTH 0x00000400L -#define D3DTEXOPCAPS_BLENDDIFFUSEALPHA 0x00000800L -#define D3DTEXOPCAPS_BLENDTEXTUREALPHA 0x00001000L -#define D3DTEXOPCAPS_BLENDFACTORALPHA 0x00002000L -#define D3DTEXOPCAPS_BLENDTEXTUREALPHAPM 0x00004000L -#define D3DTEXOPCAPS_BLENDCURRENTALPHA 0x00008000L -#define D3DTEXOPCAPS_PREMODULATE 0x00010000L -#define D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR 0x00020000L -#define D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA 0x00040000L -#define D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR 0x00080000L -#define D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA 0x00100000L -#define D3DTEXOPCAPS_BUMPENVMAP 0x00200000L -#define D3DTEXOPCAPS_BUMPENVMAPLUMINANCE 0x00400000L -#define D3DTEXOPCAPS_DOTPRODUCT3 0x00800000L -#define D3DTEXOPCAPS_MULTIPLYADD 0x01000000L -#define D3DTEXOPCAPS_LERP 0x02000000L - -// -// FVFCaps -// -#define D3DFVFCAPS_TEXCOORDCOUNTMASK 0x0000ffffL /* mask for texture coordinate count field */ -#define D3DFVFCAPS_DONOTSTRIPELEMENTS 0x00080000L /* Device prefers that vertex elements not be stripped */ -#define D3DFVFCAPS_PSIZE 0x00100000L /* Device can receive point size */ - -// -// VertexProcessingCaps -// -#define D3DVTXPCAPS_TEXGEN 0x00000001L /* device can do texgen */ -#define D3DVTXPCAPS_MATERIALSOURCE7 0x00000002L /* device can do DX7-level colormaterialsource ops */ -#define D3DVTXPCAPS_DIRECTIONALLIGHTS 0x00000008L /* device can do directional lights */ -#define D3DVTXPCAPS_POSITIONALLIGHTS 0x00000010L /* device can do positional lights (includes point and spot) */ -#define D3DVTXPCAPS_LOCALVIEWER 0x00000020L /* device can do local viewer */ -#define D3DVTXPCAPS_TWEENING 0x00000040L /* device can do vertex tweening */ -#define D3DVTXPCAPS_NO_VSDT_UBYTE4 0x00000080L /* device does not support D3DVSDT_UBYTE4 */ - -#pragma pack() - -#endif /* (DIRECT3D_VERSION >= 0x0800) */ -#endif /* _D3D8CAPS_H_ */ - diff --git a/plugins/win-capture/d3d8-api/d3d8types.h b/plugins/win-capture/d3d8-api/d3d8types.h deleted file mode 100644 index 9774619e5a7d23..00000000000000 --- a/plugins/win-capture/d3d8-api/d3d8types.h +++ /dev/null @@ -1,1690 +0,0 @@ -/*==========================================================================; - * - * Copyright (C) Microsoft Corporation. All Rights Reserved. - * - * File: d3d8types.h - * Content: Direct3D capabilities include file - * - ***************************************************************************/ - -#ifndef _D3D8TYPES_H_ -#define _D3D8TYPES_H_ - -#ifndef DIRECT3D_VERSION -#define DIRECT3D_VERSION 0x0800 -#endif //DIRECT3D_VERSION - -// include this file content only if compiling for DX8 interfaces -#if(DIRECT3D_VERSION >= 0x0800) - -#include - -#if _MSC_VER >= 1200 -#pragma warning(push) -#endif - -#ifdef _MSC_VER -#pragma warning(disable:4201) // anonymous unions warning -#endif - -#if defined(_X86_) || defined(_IA64_) -#pragma pack(4) -#endif - -// D3DCOLOR is equivalent to D3DFMT_A8R8G8B8 -#ifndef D3DCOLOR_DEFINED -typedef DWORD D3DCOLOR; -#define D3DCOLOR_DEFINED -#endif - -// maps unsigned 8 bits/channel to D3DCOLOR -#define D3DCOLOR_ARGB(a,r,g,b) \ - ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) -#define D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b) -#define D3DCOLOR_XRGB(r,g,b) D3DCOLOR_ARGB(0xff,r,g,b) - -// maps floating point channels (0.f to 1.f range) to D3DCOLOR -#define D3DCOLOR_COLORVALUE(r,g,b,a) \ - D3DCOLOR_RGBA((DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f),(DWORD)((a)*255.f)) - - -#ifndef D3DVECTOR_DEFINED -typedef struct _D3DVECTOR { - float x; - float y; - float z; -} D3DVECTOR; -#define D3DVECTOR_DEFINED -#endif - -#ifndef D3DCOLORVALUE_DEFINED -typedef struct _D3DCOLORVALUE { - float r; - float g; - float b; - float a; -} D3DCOLORVALUE; -#define D3DCOLORVALUE_DEFINED -#endif - -#ifndef D3DRECT_DEFINED -typedef struct _D3DRECT { - LONG x1; - LONG y1; - LONG x2; - LONG y2; -} D3DRECT; -#define D3DRECT_DEFINED -#endif - -#ifndef D3DMATRIX_DEFINED -typedef struct _D3DMATRIX { - union { - struct { - float _11, _12, _13, _14; - float _21, _22, _23, _24; - float _31, _32, _33, _34; - float _41, _42, _43, _44; - - }; - float m[4][4]; - }; -} D3DMATRIX; -#define D3DMATRIX_DEFINED -#endif - -typedef struct _D3DVIEWPORT8 { - DWORD X; - DWORD Y; /* Viewport Top left */ - DWORD Width; - DWORD Height; /* Viewport Dimensions */ - float MinZ; /* Min/max of clip Volume */ - float MaxZ; -} D3DVIEWPORT8; - -/* - * Values for clip fields. - */ - -// Max number of user clipping planes, supported in D3D. -#define D3DMAXUSERCLIPPLANES 32 - -// These bits could be ORed together to use with D3DRS_CLIPPLANEENABLE -// -#define D3DCLIPPLANE0 (1 << 0) -#define D3DCLIPPLANE1 (1 << 1) -#define D3DCLIPPLANE2 (1 << 2) -#define D3DCLIPPLANE3 (1 << 3) -#define D3DCLIPPLANE4 (1 << 4) -#define D3DCLIPPLANE5 (1 << 5) - -// The following bits are used in the ClipUnion and ClipIntersection -// members of the D3DCLIPSTATUS8 -// - -#define D3DCS_LEFT 0x00000001L -#define D3DCS_RIGHT 0x00000002L -#define D3DCS_TOP 0x00000004L -#define D3DCS_BOTTOM 0x00000008L -#define D3DCS_FRONT 0x00000010L -#define D3DCS_BACK 0x00000020L -#define D3DCS_PLANE0 0x00000040L -#define D3DCS_PLANE1 0x00000080L -#define D3DCS_PLANE2 0x00000100L -#define D3DCS_PLANE3 0x00000200L -#define D3DCS_PLANE4 0x00000400L -#define D3DCS_PLANE5 0x00000800L - -#define D3DCS_ALL (D3DCS_LEFT | \ - D3DCS_RIGHT | \ - D3DCS_TOP | \ - D3DCS_BOTTOM | \ - D3DCS_FRONT | \ - D3DCS_BACK | \ - D3DCS_PLANE0 | \ - D3DCS_PLANE1 | \ - D3DCS_PLANE2 | \ - D3DCS_PLANE3 | \ - D3DCS_PLANE4 | \ - D3DCS_PLANE5) - -typedef struct _D3DCLIPSTATUS8 { - DWORD ClipUnion; - DWORD ClipIntersection; -} D3DCLIPSTATUS8; - -typedef struct _D3DMATERIAL8 { - D3DCOLORVALUE Diffuse; /* Diffuse color RGBA */ - D3DCOLORVALUE Ambient; /* Ambient color RGB */ - D3DCOLORVALUE Specular; /* Specular 'shininess' */ - D3DCOLORVALUE Emissive; /* Emissive color RGB */ - float Power; /* Sharpness if specular highlight */ -} D3DMATERIAL8; - -typedef enum _D3DLIGHTTYPE { - D3DLIGHT_POINT = 1, - D3DLIGHT_SPOT = 2, - D3DLIGHT_DIRECTIONAL = 3, - D3DLIGHT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DLIGHTTYPE; - -typedef struct _D3DLIGHT8 { - D3DLIGHTTYPE Type; /* Type of light source */ - D3DCOLORVALUE Diffuse; /* Diffuse color of light */ - D3DCOLORVALUE Specular; /* Specular color of light */ - D3DCOLORVALUE Ambient; /* Ambient color of light */ - D3DVECTOR Position; /* Position in world space */ - D3DVECTOR Direction; /* Direction in world space */ - float Range; /* Cutoff range */ - float Falloff; /* Falloff */ - float Attenuation0; /* Constant attenuation */ - float Attenuation1; /* Linear attenuation */ - float Attenuation2; /* Quadratic attenuation */ - float Theta; /* Inner angle of spotlight cone */ - float Phi; /* Outer angle of spotlight cone */ -} D3DLIGHT8; - -/* - * Options for clearing - */ -#define D3DCLEAR_TARGET 0x00000001l /* Clear target surface */ -#define D3DCLEAR_ZBUFFER 0x00000002l /* Clear target z buffer */ -#define D3DCLEAR_STENCIL 0x00000004l /* Clear stencil planes */ - -/* - * The following defines the rendering states - */ - -typedef enum _D3DSHADEMODE { - D3DSHADE_FLAT = 1, - D3DSHADE_GOURAUD = 2, - D3DSHADE_PHONG = 3, - D3DSHADE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DSHADEMODE; - -typedef enum _D3DFILLMODE { - D3DFILL_POINT = 1, - D3DFILL_WIREFRAME = 2, - D3DFILL_SOLID = 3, - D3DFILL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DFILLMODE; - -typedef struct _D3DLINEPATTERN { - WORD wRepeatFactor; - WORD wLinePattern; -} D3DLINEPATTERN; - -typedef enum _D3DBLEND { - D3DBLEND_ZERO = 1, - D3DBLEND_ONE = 2, - D3DBLEND_SRCCOLOR = 3, - D3DBLEND_INVSRCCOLOR = 4, - D3DBLEND_SRCALPHA = 5, - D3DBLEND_INVSRCALPHA = 6, - D3DBLEND_DESTALPHA = 7, - D3DBLEND_INVDESTALPHA = 8, - D3DBLEND_DESTCOLOR = 9, - D3DBLEND_INVDESTCOLOR = 10, - D3DBLEND_SRCALPHASAT = 11, - D3DBLEND_BOTHSRCALPHA = 12, - D3DBLEND_BOTHINVSRCALPHA = 13, - D3DBLEND_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DBLEND; - -typedef enum _D3DBLENDOP { - D3DBLENDOP_ADD = 1, - D3DBLENDOP_SUBTRACT = 2, - D3DBLENDOP_REVSUBTRACT = 3, - D3DBLENDOP_MIN = 4, - D3DBLENDOP_MAX = 5, - D3DBLENDOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DBLENDOP; - -typedef enum _D3DTEXTUREADDRESS { - D3DTADDRESS_WRAP = 1, - D3DTADDRESS_MIRROR = 2, - D3DTADDRESS_CLAMP = 3, - D3DTADDRESS_BORDER = 4, - D3DTADDRESS_MIRRORONCE = 5, - D3DTADDRESS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DTEXTUREADDRESS; - -typedef enum _D3DCULL { - D3DCULL_NONE = 1, - D3DCULL_CW = 2, - D3DCULL_CCW = 3, - D3DCULL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DCULL; - -typedef enum _D3DCMPFUNC { - D3DCMP_NEVER = 1, - D3DCMP_LESS = 2, - D3DCMP_EQUAL = 3, - D3DCMP_LESSEQUAL = 4, - D3DCMP_GREATER = 5, - D3DCMP_NOTEQUAL = 6, - D3DCMP_GREATEREQUAL = 7, - D3DCMP_ALWAYS = 8, - D3DCMP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DCMPFUNC; - -typedef enum _D3DSTENCILOP { - D3DSTENCILOP_KEEP = 1, - D3DSTENCILOP_ZERO = 2, - D3DSTENCILOP_REPLACE = 3, - D3DSTENCILOP_INCRSAT = 4, - D3DSTENCILOP_DECRSAT = 5, - D3DSTENCILOP_INVERT = 6, - D3DSTENCILOP_INCR = 7, - D3DSTENCILOP_DECR = 8, - D3DSTENCILOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DSTENCILOP; - -typedef enum _D3DFOGMODE { - D3DFOG_NONE = 0, - D3DFOG_EXP = 1, - D3DFOG_EXP2 = 2, - D3DFOG_LINEAR = 3, - D3DFOG_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DFOGMODE; - -typedef enum _D3DZBUFFERTYPE { - D3DZB_FALSE = 0, - D3DZB_TRUE = 1, // Z buffering - D3DZB_USEW = 2, // W buffering - D3DZB_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DZBUFFERTYPE; - -// Primitives supported by draw-primitive API -typedef enum _D3DPRIMITIVETYPE { - D3DPT_POINTLIST = 1, - D3DPT_LINELIST = 2, - D3DPT_LINESTRIP = 3, - D3DPT_TRIANGLELIST = 4, - D3DPT_TRIANGLESTRIP = 5, - D3DPT_TRIANGLEFAN = 6, - D3DPT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DPRIMITIVETYPE; - -typedef enum _D3DTRANSFORMSTATETYPE { - D3DTS_VIEW = 2, - D3DTS_PROJECTION = 3, - D3DTS_TEXTURE0 = 16, - D3DTS_TEXTURE1 = 17, - D3DTS_TEXTURE2 = 18, - D3DTS_TEXTURE3 = 19, - D3DTS_TEXTURE4 = 20, - D3DTS_TEXTURE5 = 21, - D3DTS_TEXTURE6 = 22, - D3DTS_TEXTURE7 = 23, - D3DTS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DTRANSFORMSTATETYPE; - -#define D3DTS_WORLDMATRIX(index) (D3DTRANSFORMSTATETYPE)(index + 256) -#define D3DTS_WORLD D3DTS_WORLDMATRIX(0) -#define D3DTS_WORLD1 D3DTS_WORLDMATRIX(1) -#define D3DTS_WORLD2 D3DTS_WORLDMATRIX(2) -#define D3DTS_WORLD3 D3DTS_WORLDMATRIX(3) - -typedef enum _D3DRENDERSTATETYPE { - D3DRS_ZENABLE = 7, /* D3DZBUFFERTYPE (or TRUE/FALSE for legacy) */ - D3DRS_FILLMODE = 8, /* D3DFILLMODE */ - D3DRS_SHADEMODE = 9, /* D3DSHADEMODE */ - D3DRS_LINEPATTERN = 10, /* D3DLINEPATTERN */ - D3DRS_ZWRITEENABLE = 14, /* TRUE to enable z writes */ - D3DRS_ALPHATESTENABLE = 15, /* TRUE to enable alpha tests */ - D3DRS_LASTPIXEL = 16, /* TRUE for last-pixel on lines */ - D3DRS_SRCBLEND = 19, /* D3DBLEND */ - D3DRS_DESTBLEND = 20, /* D3DBLEND */ - D3DRS_CULLMODE = 22, /* D3DCULL */ - D3DRS_ZFUNC = 23, /* D3DCMPFUNC */ - D3DRS_ALPHAREF = 24, /* D3DFIXED */ - D3DRS_ALPHAFUNC = 25, /* D3DCMPFUNC */ - D3DRS_DITHERENABLE = 26, /* TRUE to enable dithering */ - D3DRS_ALPHABLENDENABLE = 27, /* TRUE to enable alpha blending */ - D3DRS_FOGENABLE = 28, /* TRUE to enable fog blending */ - D3DRS_SPECULARENABLE = 29, /* TRUE to enable specular */ - D3DRS_ZVISIBLE = 30, /* TRUE to enable z checking */ - D3DRS_FOGCOLOR = 34, /* D3DCOLOR */ - D3DRS_FOGTABLEMODE = 35, /* D3DFOGMODE */ - D3DRS_FOGSTART = 36, /* Fog start (for both vertex and pixel fog) */ - D3DRS_FOGEND = 37, /* Fog end */ - D3DRS_FOGDENSITY = 38, /* Fog density */ - D3DRS_EDGEANTIALIAS = 40, /* TRUE to enable edge antialiasing */ - D3DRS_ZBIAS = 47, /* LONG Z bias */ - D3DRS_RANGEFOGENABLE = 48, /* Enables range-based fog */ - D3DRS_STENCILENABLE = 52, /* BOOL enable/disable stenciling */ - D3DRS_STENCILFAIL = 53, /* D3DSTENCILOP to do if stencil test fails */ - D3DRS_STENCILZFAIL = 54, /* D3DSTENCILOP to do if stencil test passes and Z test fails */ - D3DRS_STENCILPASS = 55, /* D3DSTENCILOP to do if both stencil and Z tests pass */ - D3DRS_STENCILFUNC = 56, /* D3DCMPFUNC fn. Stencil Test passes if ((ref & mask) stencilfn (stencil & mask)) is true */ - D3DRS_STENCILREF = 57, /* Reference value used in stencil test */ - D3DRS_STENCILMASK = 58, /* Mask value used in stencil test */ - D3DRS_STENCILWRITEMASK = 59, /* Write mask applied to values written to stencil buffer */ - D3DRS_TEXTUREFACTOR = 60, /* D3DCOLOR used for multi-texture blend */ - D3DRS_WRAP0 = 128, /* wrap for 1st texture coord. set */ - D3DRS_WRAP1 = 129, /* wrap for 2nd texture coord. set */ - D3DRS_WRAP2 = 130, /* wrap for 3rd texture coord. set */ - D3DRS_WRAP3 = 131, /* wrap for 4th texture coord. set */ - D3DRS_WRAP4 = 132, /* wrap for 5th texture coord. set */ - D3DRS_WRAP5 = 133, /* wrap for 6th texture coord. set */ - D3DRS_WRAP6 = 134, /* wrap for 7th texture coord. set */ - D3DRS_WRAP7 = 135, /* wrap for 8th texture coord. set */ - D3DRS_CLIPPING = 136, - D3DRS_LIGHTING = 137, - D3DRS_AMBIENT = 139, - D3DRS_FOGVERTEXMODE = 140, - D3DRS_COLORVERTEX = 141, - D3DRS_LOCALVIEWER = 142, - D3DRS_NORMALIZENORMALS = 143, - D3DRS_DIFFUSEMATERIALSOURCE = 145, - D3DRS_SPECULARMATERIALSOURCE = 146, - D3DRS_AMBIENTMATERIALSOURCE = 147, - D3DRS_EMISSIVEMATERIALSOURCE = 148, - D3DRS_VERTEXBLEND = 151, - D3DRS_CLIPPLANEENABLE = 152, - D3DRS_SOFTWAREVERTEXPROCESSING = 153, - D3DRS_POINTSIZE = 154, /* float point size */ - D3DRS_POINTSIZE_MIN = 155, /* float point size min threshold */ - D3DRS_POINTSPRITEENABLE = 156, /* BOOL point texture coord control */ - D3DRS_POINTSCALEENABLE = 157, /* BOOL point size scale enable */ - D3DRS_POINTSCALE_A = 158, /* float point attenuation A value */ - D3DRS_POINTSCALE_B = 159, /* float point attenuation B value */ - D3DRS_POINTSCALE_C = 160, /* float point attenuation C value */ - D3DRS_MULTISAMPLEANTIALIAS = 161, // BOOL - set to do FSAA with multisample buffer - D3DRS_MULTISAMPLEMASK = 162, // DWORD - per-sample enable/disable - D3DRS_PATCHEDGESTYLE = 163, // Sets whether patch edges will use float style tessellation - D3DRS_PATCHSEGMENTS = 164, // Number of segments per edge when drawing patches - D3DRS_DEBUGMONITORTOKEN = 165, // DEBUG ONLY - token to debug monitor - D3DRS_POINTSIZE_MAX = 166, /* float point size max threshold */ - D3DRS_INDEXEDVERTEXBLENDENABLE = 167, - D3DRS_COLORWRITEENABLE = 168, // per-channel write enable - D3DRS_TWEENFACTOR = 170, // float tween factor - D3DRS_BLENDOP = 171, // D3DBLENDOP setting - D3DRS_POSITIONORDER = 172, // NPatch position interpolation order. D3DORDER_LINEAR or D3DORDER_CUBIC (default) - D3DRS_NORMALORDER = 173, // NPatch normal interpolation order. D3DORDER_LINEAR (default) or D3DORDER_QUADRATIC - - D3DRS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DRENDERSTATETYPE; - -// Values for material source -typedef enum _D3DMATERIALCOLORSOURCE -{ - D3DMCS_MATERIAL = 0, // Color from material is used - D3DMCS_COLOR1 = 1, // Diffuse vertex color is used - D3DMCS_COLOR2 = 2, // Specular vertex color is used - D3DMCS_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum -} D3DMATERIALCOLORSOURCE; - -// Bias to apply to the texture coordinate set to apply a wrap to. -#define D3DRENDERSTATE_WRAPBIAS 128UL - -/* Flags to construct the WRAP render states */ -#define D3DWRAP_U 0x00000001L -#define D3DWRAP_V 0x00000002L -#define D3DWRAP_W 0x00000004L - -/* Flags to construct the WRAP render states for 1D thru 4D texture coordinates */ -#define D3DWRAPCOORD_0 0x00000001L // same as D3DWRAP_U -#define D3DWRAPCOORD_1 0x00000002L // same as D3DWRAP_V -#define D3DWRAPCOORD_2 0x00000004L // same as D3DWRAP_W -#define D3DWRAPCOORD_3 0x00000008L - -/* Flags to construct D3DRS_COLORWRITEENABLE */ -#define D3DCOLORWRITEENABLE_RED (1L<<0) -#define D3DCOLORWRITEENABLE_GREEN (1L<<1) -#define D3DCOLORWRITEENABLE_BLUE (1L<<2) -#define D3DCOLORWRITEENABLE_ALPHA (1L<<3) - -/* - * State enumerants for per-stage texture processing. - */ -typedef enum _D3DTEXTURESTAGESTATETYPE -{ - D3DTSS_COLOROP = 1, /* D3DTEXTUREOP - per-stage blending controls for color channels */ - D3DTSS_COLORARG1 = 2, /* D3DTA_* (texture arg) */ - D3DTSS_COLORARG2 = 3, /* D3DTA_* (texture arg) */ - D3DTSS_ALPHAOP = 4, /* D3DTEXTUREOP - per-stage blending controls for alpha channel */ - D3DTSS_ALPHAARG1 = 5, /* D3DTA_* (texture arg) */ - D3DTSS_ALPHAARG2 = 6, /* D3DTA_* (texture arg) */ - D3DTSS_BUMPENVMAT00 = 7, /* float (bump mapping matrix) */ - D3DTSS_BUMPENVMAT01 = 8, /* float (bump mapping matrix) */ - D3DTSS_BUMPENVMAT10 = 9, /* float (bump mapping matrix) */ - D3DTSS_BUMPENVMAT11 = 10, /* float (bump mapping matrix) */ - D3DTSS_TEXCOORDINDEX = 11, /* identifies which set of texture coordinates index this texture */ - D3DTSS_ADDRESSU = 13, /* D3DTEXTUREADDRESS for U coordinate */ - D3DTSS_ADDRESSV = 14, /* D3DTEXTUREADDRESS for V coordinate */ - D3DTSS_BORDERCOLOR = 15, /* D3DCOLOR */ - D3DTSS_MAGFILTER = 16, /* D3DTEXTUREFILTER filter to use for magnification */ - D3DTSS_MINFILTER = 17, /* D3DTEXTUREFILTER filter to use for minification */ - D3DTSS_MIPFILTER = 18, /* D3DTEXTUREFILTER filter to use between mipmaps during minification */ - D3DTSS_MIPMAPLODBIAS = 19, /* float Mipmap LOD bias */ - D3DTSS_MAXMIPLEVEL = 20, /* DWORD 0..(n-1) LOD index of largest map to use (0 == largest) */ - D3DTSS_MAXANISOTROPY = 21, /* DWORD maximum anisotropy */ - D3DTSS_BUMPENVLSCALE = 22, /* float scale for bump map luminance */ - D3DTSS_BUMPENVLOFFSET = 23, /* float offset for bump map luminance */ - D3DTSS_TEXTURETRANSFORMFLAGS = 24, /* D3DTEXTURETRANSFORMFLAGS controls texture transform */ - D3DTSS_ADDRESSW = 25, /* D3DTEXTUREADDRESS for W coordinate */ - D3DTSS_COLORARG0 = 26, /* D3DTA_* third arg for triadic ops */ - D3DTSS_ALPHAARG0 = 27, /* D3DTA_* third arg for triadic ops */ - D3DTSS_RESULTARG = 28, /* D3DTA_* arg for result (CURRENT or TEMP) */ - D3DTSS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ -} D3DTEXTURESTAGESTATETYPE; - -// Values, used with D3DTSS_TEXCOORDINDEX, to specify that the vertex data(position -// and normal in the camera space) should be taken as texture coordinates -// Low 16 bits are used to specify texture coordinate index, to take the WRAP mode from -// -#define D3DTSS_TCI_PASSTHRU 0x00000000 -#define D3DTSS_TCI_CAMERASPACENORMAL 0x00010000 -#define D3DTSS_TCI_CAMERASPACEPOSITION 0x00020000 -#define D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR 0x00030000 - -/* - * Enumerations for COLOROP and ALPHAOP texture blending operations set in - * texture processing stage controls in D3DTSS. - */ -typedef enum _D3DTEXTUREOP -{ - // Control - D3DTOP_DISABLE = 1, // disables stage - D3DTOP_SELECTARG1 = 2, // the default - D3DTOP_SELECTARG2 = 3, - - // Modulate - D3DTOP_MODULATE = 4, // multiply args together - D3DTOP_MODULATE2X = 5, // multiply and 1 bit - D3DTOP_MODULATE4X = 6, // multiply and 2 bits - - // Add - D3DTOP_ADD = 7, // add arguments together - D3DTOP_ADDSIGNED = 8, // add with -0.5 bias - D3DTOP_ADDSIGNED2X = 9, // as above but left 1 bit - D3DTOP_SUBTRACT = 10, // Arg1 - Arg2, with no saturation - D3DTOP_ADDSMOOTH = 11, // add 2 args, subtract product - // Arg1 + Arg2 - Arg1*Arg2 - // = Arg1 + (1-Arg1)*Arg2 - - // Linear alpha blend: Arg1*(Alpha) + Arg2*(1-Alpha) - D3DTOP_BLENDDIFFUSEALPHA = 12, // iterated alpha - D3DTOP_BLENDTEXTUREALPHA = 13, // texture alpha - D3DTOP_BLENDFACTORALPHA = 14, // alpha from D3DRS_TEXTUREFACTOR - - // Linear alpha blend with pre-multiplied arg1 input: Arg1 + Arg2*(1-Alpha) - D3DTOP_BLENDTEXTUREALPHAPM = 15, // texture alpha - D3DTOP_BLENDCURRENTALPHA = 16, // by alpha of current color - - // Specular mapping - D3DTOP_PREMODULATE = 17, // modulate with next texture before use - D3DTOP_MODULATEALPHA_ADDCOLOR = 18, // Arg1.RGB + Arg1.A*Arg2.RGB - // COLOROP only - D3DTOP_MODULATECOLOR_ADDALPHA = 19, // Arg1.RGB*Arg2.RGB + Arg1.A - // COLOROP only - D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20, // (1-Arg1.A)*Arg2.RGB + Arg1.RGB - // COLOROP only - D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21, // (1-Arg1.RGB)*Arg2.RGB + Arg1.A - // COLOROP only - - // Bump mapping - D3DTOP_BUMPENVMAP = 22, // per pixel env map perturbation - D3DTOP_BUMPENVMAPLUMINANCE = 23, // with luminance channel - - // This can do either diffuse or specular bump mapping with correct input. - // Performs the function (Arg1.R*Arg2.R + Arg1.G*Arg2.G + Arg1.B*Arg2.B) - // where each component has been scaled and offset to make it signed. - // The result is replicated into all four (including alpha) channels. - // This is a valid COLOROP only. - D3DTOP_DOTPRODUCT3 = 24, - - // Triadic ops - D3DTOP_MULTIPLYADD = 25, // Arg0 + Arg1*Arg2 - D3DTOP_LERP = 26, // (Arg0)*Arg1 + (1-Arg0)*Arg2 - - D3DTOP_FORCE_DWORD = 0x7fffffff, -} D3DTEXTUREOP; - -/* - * Values for COLORARG0,1,2, ALPHAARG0,1,2, and RESULTARG texture blending - * operations set in texture processing stage controls in D3DRENDERSTATE. - */ -#define D3DTA_SELECTMASK 0x0000000f // mask for arg selector -#define D3DTA_DIFFUSE 0x00000000 // select diffuse color (read only) -#define D3DTA_CURRENT 0x00000001 // select stage destination register (read/write) -#define D3DTA_TEXTURE 0x00000002 // select texture color (read only) -#define D3DTA_TFACTOR 0x00000003 // select D3DRS_TEXTUREFACTOR (read only) -#define D3DTA_SPECULAR 0x00000004 // select specular color (read only) -#define D3DTA_TEMP 0x00000005 // select temporary register color (read/write) -#define D3DTA_COMPLEMENT 0x00000010 // take 1.0 - x (read modifier) -#define D3DTA_ALPHAREPLICATE 0x00000020 // replicate alpha to color components (read modifier) - -// -// Values for D3DTSS_***FILTER texture stage states -// -typedef enum _D3DTEXTUREFILTERTYPE -{ - D3DTEXF_NONE = 0, // filtering disabled (valid for mip filter only) - D3DTEXF_POINT = 1, // nearest - D3DTEXF_LINEAR = 2, // linear interpolation - D3DTEXF_ANISOTROPIC = 3, // anisotropic - D3DTEXF_FLATCUBIC = 4, // cubic - D3DTEXF_GAUSSIANCUBIC = 5, // different cubic kernel - D3DTEXF_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum -} D3DTEXTUREFILTERTYPE; - -/* Bits for Flags in ProcessVertices call */ - -#define D3DPV_DONOTCOPYDATA (1 << 0) - -//------------------------------------------------------------------- - -// Flexible vertex format bits -// -#define D3DFVF_RESERVED0 0x001 -#define D3DFVF_POSITION_MASK 0x00E -#define D3DFVF_XYZ 0x002 -#define D3DFVF_XYZRHW 0x004 -#define D3DFVF_XYZB1 0x006 -#define D3DFVF_XYZB2 0x008 -#define D3DFVF_XYZB3 0x00a -#define D3DFVF_XYZB4 0x00c -#define D3DFVF_XYZB5 0x00e - -#define D3DFVF_NORMAL 0x010 -#define D3DFVF_PSIZE 0x020 -#define D3DFVF_DIFFUSE 0x040 -#define D3DFVF_SPECULAR 0x080 - -#define D3DFVF_TEXCOUNT_MASK 0xf00 -#define D3DFVF_TEXCOUNT_SHIFT 8 -#define D3DFVF_TEX0 0x000 -#define D3DFVF_TEX1 0x100 -#define D3DFVF_TEX2 0x200 -#define D3DFVF_TEX3 0x300 -#define D3DFVF_TEX4 0x400 -#define D3DFVF_TEX5 0x500 -#define D3DFVF_TEX6 0x600 -#define D3DFVF_TEX7 0x700 -#define D3DFVF_TEX8 0x800 - -#define D3DFVF_LASTBETA_UBYTE4 0x1000 - -#define D3DFVF_RESERVED2 0xE000 // 4 reserved bits - -//--------------------------------------------------------------------- -// Vertex Shaders -// - -/* - -Vertex Shader Declaration - -The declaration portion of a vertex shader defines the static external -interface of the shader. The information in the declaration includes: - -- Assignments of vertex shader input registers to data streams. These -assignments bind a specific vertex register to a single component within a -vertex stream. A vertex stream element is identified by a byte offset -within the stream and a type. The type specifies the arithmetic data type -plus the dimensionality (1, 2, 3, or 4 values). Stream data which is -less than 4 values are always expanded out to 4 values with zero or more -0.F values and one 1.F value. - -- Assignment of vertex shader input registers to implicit data from the -primitive tessellator. This controls the loading of vertex data which is -not loaded from a stream, but rather is generated during primitive -tessellation prior to the vertex shader. - -- Loading data into the constant memory at the time a shader is set as the -current shader. Each token specifies values for one or more contiguous 4 -DWORD constant registers. This allows the shader to update an arbitrary -subset of the constant memory, overwriting the device state (which -contains the current values of the constant memory). Note that these -values can be subsequently overwritten (between DrawPrimitive calls) -during the time a shader is bound to a device via the -SetVertexShaderConstant method. - - -Declaration arrays are single-dimensional arrays of DWORDs composed of -multiple tokens each of which is one or more DWORDs. The single-DWORD -token value 0xFFFFFFFF is a special token used to indicate the end of the -declaration array. The single DWORD token value 0x00000000 is a NOP token -with is ignored during the declaration parsing. Note that 0x00000000 is a -valid value for DWORDs following the first DWORD for multiple word tokens. - -[31:29] TokenType - 0x0 - NOP (requires all DWORD bits to be zero) - 0x1 - stream selector - 0x2 - stream data definition (map to vertex input memory) - 0x3 - vertex input memory from tessellator - 0x4 - constant memory from shader - 0x5 - extension - 0x6 - reserved - 0x7 - end-of-array (requires all DWORD bits to be 1) - -NOP Token (single DWORD token) - [31:29] 0x0 - [28:00] 0x0 - -Stream Selector (single DWORD token) - [31:29] 0x1 - [28] indicates whether this is a tessellator stream - [27:04] 0x0 - [03:00] stream selector (0..15) - -Stream Data Definition (single DWORD token) - Vertex Input Register Load - [31:29] 0x2 - [28] 0x0 - [27:20] 0x0 - [19:16] type (dimensionality and data type) - [15:04] 0x0 - [03:00] vertex register address (0..15) - Data Skip (no register load) - [31:29] 0x2 - [28] 0x1 - [27:20] 0x0 - [19:16] count of DWORDS to skip over (0..15) - [15:00] 0x0 - Vertex Input Memory from Tessellator Data (single DWORD token) - [31:29] 0x3 - [28] indicates whether data is normals or u/v - [27:24] 0x0 - [23:20] vertex register address (0..15) - [19:16] type (dimensionality) - [15:04] 0x0 - [03:00] vertex register address (0..15) - -Constant Memory from Shader (multiple DWORD token) - [31:29] 0x4 - [28:25] count of 4*DWORD constants to load (0..15) - [24:07] 0x0 - [06:00] constant memory address (0..95) - -Extension Token (single or multiple DWORD token) - [31:29] 0x5 - [28:24] count of additional DWORDs in token (0..31) - [23:00] extension-specific information - -End-of-array token (single DWORD token) - [31:29] 0x7 - [28:00] 0x1fffffff - -The stream selector token must be immediately followed by a contiguous set of stream data definition tokens. This token sequence fully defines that stream, including the set of elements within the stream, the order in which the elements appear, the type of each element, and the vertex register into which to load an element. -Streams are allowed to include data which is not loaded into a vertex register, thus allowing data which is not used for this shader to exist in the vertex stream. This skipped data is defined only by a count of DWORDs to skip over, since the type information is irrelevant. -The token sequence: -Stream Select: stream=0 -Stream Data Definition (Load): type=FLOAT3; register=3 -Stream Data Definition (Load): type=FLOAT3; register=4 -Stream Data Definition (Skip): count=2 -Stream Data Definition (Load): type=FLOAT2; register=7 - -defines stream zero to consist of 4 elements, 3 of which are loaded into registers and the fourth skipped over. Register 3 is loaded with the first three DWORDs in each vertex interpreted as FLOAT data. Register 4 is loaded with the 4th, 5th, and 6th DWORDs interpreted as FLOAT data. The next two DWORDs (7th and 8th) are skipped over and not loaded into any vertex input register. Register 7 is loaded with the 9th and 10th DWORDS interpreted as FLOAT data. -Placing of tokens other than NOPs between the Stream Selector and Stream Data Definition tokens is disallowed. - -*/ - -typedef enum _D3DVSD_TOKENTYPE -{ - D3DVSD_TOKEN_NOP = 0, // NOP or extension - D3DVSD_TOKEN_STREAM, // stream selector - D3DVSD_TOKEN_STREAMDATA, // stream data definition (map to vertex input memory) - D3DVSD_TOKEN_TESSELLATOR, // vertex input memory from tessellator - D3DVSD_TOKEN_CONSTMEM, // constant memory from shader - D3DVSD_TOKEN_EXT, // extension - D3DVSD_TOKEN_END = 7, // end-of-array (requires all DWORD bits to be 1) - D3DVSD_FORCE_DWORD = 0x7fffffff,// force 32-bit size enum -} D3DVSD_TOKENTYPE; - -#define D3DVSD_TOKENTYPESHIFT 29 -#define D3DVSD_TOKENTYPEMASK (7 << D3DVSD_TOKENTYPESHIFT) - -#define D3DVSD_STREAMNUMBERSHIFT 0 -#define D3DVSD_STREAMNUMBERMASK (0xF << D3DVSD_STREAMNUMBERSHIFT) - -#define D3DVSD_DATALOADTYPESHIFT 28 -#define D3DVSD_DATALOADTYPEMASK (0x1 << D3DVSD_DATALOADTYPESHIFT) - -#define D3DVSD_DATATYPESHIFT 16 -#define D3DVSD_DATATYPEMASK (0xF << D3DVSD_DATATYPESHIFT) - -#define D3DVSD_SKIPCOUNTSHIFT 16 -#define D3DVSD_SKIPCOUNTMASK (0xF << D3DVSD_SKIPCOUNTSHIFT) - -#define D3DVSD_VERTEXREGSHIFT 0 -#define D3DVSD_VERTEXREGMASK (0x1F << D3DVSD_VERTEXREGSHIFT) - -#define D3DVSD_VERTEXREGINSHIFT 20 -#define D3DVSD_VERTEXREGINMASK (0xF << D3DVSD_VERTEXREGINSHIFT) - -#define D3DVSD_CONSTCOUNTSHIFT 25 -#define D3DVSD_CONSTCOUNTMASK (0xF << D3DVSD_CONSTCOUNTSHIFT) - -#define D3DVSD_CONSTADDRESSSHIFT 0 -#define D3DVSD_CONSTADDRESSMASK (0x7F << D3DVSD_CONSTADDRESSSHIFT) - -#define D3DVSD_CONSTRSSHIFT 16 -#define D3DVSD_CONSTRSMASK (0x1FFF << D3DVSD_CONSTRSSHIFT) - -#define D3DVSD_EXTCOUNTSHIFT 24 -#define D3DVSD_EXTCOUNTMASK (0x1F << D3DVSD_EXTCOUNTSHIFT) - -#define D3DVSD_EXTINFOSHIFT 0 -#define D3DVSD_EXTINFOMASK (0xFFFFFF << D3DVSD_EXTINFOSHIFT) - -#define D3DVSD_MAKETOKENTYPE(tokenType) ((tokenType << D3DVSD_TOKENTYPESHIFT) & D3DVSD_TOKENTYPEMASK) - -// macros for generation of CreateVertexShader Declaration token array - -// Set current stream -// _StreamNumber [0..(MaxStreams-1)] stream to get data from -// -#define D3DVSD_STREAM( _StreamNumber ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAM) | (_StreamNumber)) - -// Set tessellator stream -// -#define D3DVSD_STREAMTESSSHIFT 28 -#define D3DVSD_STREAMTESSMASK (1 << D3DVSD_STREAMTESSSHIFT) -#define D3DVSD_STREAM_TESS( ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAM) | (D3DVSD_STREAMTESSMASK)) - -// bind single vertex register to vertex element from vertex stream -// -// _VertexRegister [0..15] address of the vertex register -// _Type [D3DVSDT_*] dimensionality and arithmetic data type - -#define D3DVSD_REG( _VertexRegister, _Type ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAMDATA) | \ - ((_Type) << D3DVSD_DATATYPESHIFT) | (_VertexRegister)) - -// Skip _DWORDCount DWORDs in vertex -// -#define D3DVSD_SKIP( _DWORDCount ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_STREAMDATA) | 0x10000000 | \ - ((_DWORDCount) << D3DVSD_SKIPCOUNTSHIFT)) - -// load data into vertex shader constant memory -// -// _ConstantAddress [0..95] - address of constant array to begin filling data -// _Count [0..15] - number of constant vectors to load (4 DWORDs each) -// followed by 4*_Count DWORDS of data -// -#define D3DVSD_CONST( _ConstantAddress, _Count ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_CONSTMEM) | \ - ((_Count) << D3DVSD_CONSTCOUNTSHIFT) | (_ConstantAddress)) - -// enable tessellator generated normals -// -// _VertexRegisterIn [0..15] address of vertex register whose input stream -// will be used in normal computation -// _VertexRegisterOut [0..15] address of vertex register to output the normal to -// -#define D3DVSD_TESSNORMAL( _VertexRegisterIn, _VertexRegisterOut ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_TESSELLATOR) | \ - ((_VertexRegisterIn) << D3DVSD_VERTEXREGINSHIFT) | \ - ((0x02) << D3DVSD_DATATYPESHIFT) | (_VertexRegisterOut)) - -// enable tessellator generated surface parameters -// -// _VertexRegister [0..15] address of vertex register to output parameters -// -#define D3DVSD_TESSUV( _VertexRegister ) \ - (D3DVSD_MAKETOKENTYPE(D3DVSD_TOKEN_TESSELLATOR) | 0x10000000 | \ - ((0x01) << D3DVSD_DATATYPESHIFT) | (_VertexRegister)) - -// Generates END token -// -#define D3DVSD_END() 0xFFFFFFFF - -// Generates NOP token -#define D3DVSD_NOP() 0x00000000 - -// bit declarations for _Type fields -#define D3DVSDT_FLOAT1 0x00 // 1D float expanded to (value, 0., 0., 1.) -#define D3DVSDT_FLOAT2 0x01 // 2D float expanded to (value, value, 0., 1.) -#define D3DVSDT_FLOAT3 0x02 // 3D float expanded to (value, value, value, 1.) -#define D3DVSDT_FLOAT4 0x03 // 4D float -#define D3DVSDT_D3DCOLOR 0x04 // 4D packed unsigned bytes mapped to 0. to 1. range - // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) -#define D3DVSDT_UBYTE4 0x05 // 4D unsigned byte -#define D3DVSDT_SHORT2 0x06 // 2D signed short expanded to (value, value, 0., 1.) -#define D3DVSDT_SHORT4 0x07 // 4D signed short - -// assignments of vertex input registers for fixed function vertex shader -// -#define D3DVSDE_POSITION 0 -#define D3DVSDE_BLENDWEIGHT 1 -#define D3DVSDE_BLENDINDICES 2 -#define D3DVSDE_NORMAL 3 -#define D3DVSDE_PSIZE 4 -#define D3DVSDE_DIFFUSE 5 -#define D3DVSDE_SPECULAR 6 -#define D3DVSDE_TEXCOORD0 7 -#define D3DVSDE_TEXCOORD1 8 -#define D3DVSDE_TEXCOORD2 9 -#define D3DVSDE_TEXCOORD3 10 -#define D3DVSDE_TEXCOORD4 11 -#define D3DVSDE_TEXCOORD5 12 -#define D3DVSDE_TEXCOORD6 13 -#define D3DVSDE_TEXCOORD7 14 -#define D3DVSDE_POSITION2 15 -#define D3DVSDE_NORMAL2 16 - -// Maximum supported number of texture coordinate sets -#define D3DDP_MAXTEXCOORD 8 - - -// -// Instruction Token Bit Definitions -// -#define D3DSI_OPCODE_MASK 0x0000FFFF - -typedef enum _D3DSHADER_INSTRUCTION_OPCODE_TYPE -{ - D3DSIO_NOP = 0, // PS/VS - D3DSIO_MOV , // PS/VS - D3DSIO_ADD , // PS/VS - D3DSIO_SUB , // PS - D3DSIO_MAD , // PS/VS - D3DSIO_MUL , // PS/VS - D3DSIO_RCP , // VS - D3DSIO_RSQ , // VS - D3DSIO_DP3 , // PS/VS - D3DSIO_DP4 , // PS/VS - D3DSIO_MIN , // VS - D3DSIO_MAX , // VS - D3DSIO_SLT , // VS - D3DSIO_SGE , // VS - D3DSIO_EXP , // VS - D3DSIO_LOG , // VS - D3DSIO_LIT , // VS - D3DSIO_DST , // VS - D3DSIO_LRP , // PS - D3DSIO_FRC , // VS - D3DSIO_M4x4 , // VS - D3DSIO_M4x3 , // VS - D3DSIO_M3x4 , // VS - D3DSIO_M3x3 , // VS - D3DSIO_M3x2 , // VS - - D3DSIO_TEXCOORD = 64, // PS - D3DSIO_TEXKILL , // PS - D3DSIO_TEX , // PS - D3DSIO_TEXBEM , // PS - D3DSIO_TEXBEML , // PS - D3DSIO_TEXREG2AR , // PS - D3DSIO_TEXREG2GB , // PS - D3DSIO_TEXM3x2PAD , // PS - D3DSIO_TEXM3x2TEX , // PS - D3DSIO_TEXM3x3PAD , // PS - D3DSIO_TEXM3x3TEX , // PS - D3DSIO_TEXM3x3DIFF , // PS - D3DSIO_TEXM3x3SPEC , // PS - D3DSIO_TEXM3x3VSPEC , // PS - D3DSIO_EXPP , // VS - D3DSIO_LOGP , // VS - D3DSIO_CND , // PS - D3DSIO_DEF , // PS - D3DSIO_TEXREG2RGB , // PS - D3DSIO_TEXDP3TEX , // PS - D3DSIO_TEXM3x2DEPTH , // PS - D3DSIO_TEXDP3 , // PS - D3DSIO_TEXM3x3 , // PS - D3DSIO_TEXDEPTH , // PS - D3DSIO_CMP , // PS - D3DSIO_BEM , // PS - - D3DSIO_PHASE = 0xFFFD, - D3DSIO_COMMENT = 0xFFFE, - D3DSIO_END = 0xFFFF, - - D3DSIO_FORCE_DWORD = 0x7fffffff, // force 32-bit size enum -} D3DSHADER_INSTRUCTION_OPCODE_TYPE; - -// -// Co-Issue Instruction Modifier - if set then this instruction is to be -// issued in parallel with the previous instruction(s) for which this bit -// is not set. -// -#define D3DSI_COISSUE 0x40000000 - -// -// Parameter Token Bit Definitions -// -#define D3DSP_REGNUM_MASK 0x00001FFF - -// destination parameter write mask -#define D3DSP_WRITEMASK_0 0x00010000 // Component 0 (X;Red) -#define D3DSP_WRITEMASK_1 0x00020000 // Component 1 (Y;Green) -#define D3DSP_WRITEMASK_2 0x00040000 // Component 2 (Z;Blue) -#define D3DSP_WRITEMASK_3 0x00080000 // Component 3 (W;Alpha) -#define D3DSP_WRITEMASK_ALL 0x000F0000 // All Components - -// destination parameter modifiers -#define D3DSP_DSTMOD_SHIFT 20 -#define D3DSP_DSTMOD_MASK 0x00F00000 - -typedef enum _D3DSHADER_PARAM_DSTMOD_TYPE -{ - D3DSPDM_NONE = 0<>8)&0xFF) -#define D3DSHADER_VERSION_MINOR(_Version) (((_Version)>>0)&0xFF) - -// destination/source parameter register type -#define D3DSI_COMMENTSIZE_SHIFT 16 -#define D3DSI_COMMENTSIZE_MASK 0x7FFF0000 -#define D3DSHADER_COMMENT(_DWordSize) \ - ((((_DWordSize)<= 1200 -#pragma warning(pop) -#else -#ifdef _MSC_VER -#pragma warning(default:4201) -#endif -#endif - -#endif /* (DIRECT3D_VERSION >= 0x0800) */ -#endif /* _D3D8TYPES(P)_H_ */ - diff --git a/plugins/win-capture/graphics-hook-info.h b/plugins/win-capture/graphics-hook-info.h deleted file mode 100644 index f449afa98f1d1f..00000000000000 --- a/plugins/win-capture/graphics-hook-info.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "hook-helpers.h" - -#define EVENT_CAPTURE_RESTART L"CaptureHook_Restart" -#define EVENT_CAPTURE_STOP L"CaptureHook_Stop" - -#define EVENT_HOOK_READY L"CaptureHook_HookReady" -#define EVENT_HOOK_EXIT L"CaptureHook_Exit" - -#define EVENT_HOOK_INIT L"CaptureHook_Initialize" - -#define WINDOW_HOOK_KEEPALIVE L"CaptureHook_KeepAlive" - -#define MUTEX_TEXTURE1 L"CaptureHook_TextureMutex1" -#define MUTEX_TEXTURE2 L"CaptureHook_TextureMutex2" - -#define SHMEM_HOOK_INFO L"CaptureHook_HookInfo" -#define SHMEM_TEXTURE L"CaptureHook_Texture" - -#define PIPE_NAME "CaptureHook_Pipe" - -#pragma pack(push, 8) - -struct d3d8_offsets { - uint32_t present; -}; - -struct d3d9_offsets { - uint32_t present; - uint32_t present_ex; - uint32_t present_swap; - uint32_t d3d9_clsoff; - uint32_t is_d3d9ex_clsoff; -}; - -struct d3d12_offsets { - uint32_t execute_command_lists; -}; - -struct dxgi_offsets { - uint32_t present; - uint32_t resize; - - uint32_t present1; -}; - -struct dxgi_offsets2 { - uint32_t release; -}; - -struct ddraw_offsets { - uint32_t surface_create; - uint32_t surface_restore; - uint32_t surface_release; - uint32_t surface_unlock; - uint32_t surface_blt; - uint32_t surface_flip; - uint32_t surface_set_palette; - uint32_t palette_set_entries; -}; - -struct shmem_data { - volatile int last_tex; - uint32_t tex1_offset; - uint32_t tex2_offset; -}; - -struct shtex_data { - uint32_t tex_handle; -}; - -enum capture_type { - CAPTURE_TYPE_MEMORY, - CAPTURE_TYPE_TEXTURE, -}; - -struct graphics_offsets { - struct d3d8_offsets d3d8; - struct d3d9_offsets d3d9; - struct dxgi_offsets dxgi; - struct ddraw_offsets ddraw; - struct dxgi_offsets2 dxgi2; - struct d3d12_offsets d3d12; -}; - -struct hook_info { - /* hook version */ - uint32_t hook_ver_major; - uint32_t hook_ver_minor; - - /* capture info */ - enum capture_type type; - uint32_t window; - uint32_t format; - uint32_t cx; - uint32_t cy; - uint32_t UNUSED_base_cx; - uint32_t UNUSED_base_cy; - uint32_t pitch; - uint32_t map_id; - uint32_t map_size; - bool flip; - - /* additional options */ - uint64_t frame_interval; - bool UNUSED_use_scale; - bool force_shmem; - bool capture_overlay; - bool allow_srgb_alias; - - /* hook addresses */ - struct graphics_offsets offsets; - - uint32_t reserved[126]; -}; -static_assert(sizeof(struct hook_info) == 648, "ABI compatibility"); - -#pragma pack(pop) - -#define GC_MAPPING_FLAGS (FILE_MAP_READ | FILE_MAP_WRITE) - -static inline HANDLE create_hook_info(DWORD id) -{ - HANDLE handle = NULL; - - wchar_t new_name[64]; - const int len = swprintf(new_name, _countof(new_name), - SHMEM_HOOK_INFO L"%lu", id); - if (len > 0) { - handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, - PAGE_READWRITE, 0, - sizeof(struct hook_info), new_name); - } - - return handle; -} diff --git a/plugins/win-capture/graphics-hook-ver.h b/plugins/win-capture/graphics-hook-ver.h deleted file mode 100644 index fb1fca3c66ba83..00000000000000 --- a/plugins/win-capture/graphics-hook-ver.h +++ /dev/null @@ -1,25 +0,0 @@ -/* DO NOT MODIFY THIS FILE WITHOUT CONSULTING LAIN. OTHERWISE, LAIN WILL DO - * EVERYTHING IN HER POWER TO MAKE YOUR LIFE MISERABLE FROM THEREON OUT WITH A - * LEVEL OF DETERMINATION UNLIKE ANYTHING ANYONE HAS EVER SEEN IN THE HISTORY - * OF MANKIND. - * - * YES, THAT MEANS YOU READING THIS RIGHT NOW. - * - * IF YOU HAVE A FORK AND FEEL YOU NEED TO MODIFY THIS, SUBMIT A PULL REQUEST - * AND WAIT UNTIL IT HAS BEEN MERGED AND FULLY RELEASED IN THE CORE PROJECT - * BEFORE USING IT. - * - * THIS IS YOUR ONLY WARNING. */ - -#define HOOK_VER_MAJOR 1 -#define HOOK_VER_MINOR 8 -#define HOOK_VER_PATCH 3 - -#ifndef STRINGIFY -#define STRINGIFY(s) #s -#endif - -#define MAKE_VERSION_NAME(major, minor, patch) \ - STRINGIFY(major) "." STRINGIFY(minor) "." STRINGIFY(patch) ".0" -#define HOOK_VERSION_NAME \ - MAKE_VERSION_NAME(HOOK_VER_MAJOR, HOOK_VER_MINOR, HOOK_VER_PATCH) diff --git a/plugins/win-capture/hook-helpers.h b/plugins/win-capture/hook-helpers.h deleted file mode 100644 index 98b211c8761a90..00000000000000 --- a/plugins/win-capture/hook-helpers.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#if !defined(__cplusplus) && !defined(inline) -#define inline __inline -#endif - -#define GC_EVENT_FLAGS (EVENT_MODIFY_STATE | SYNCHRONIZE) -#define GC_MUTEX_FLAGS (SYNCHRONIZE) - -static inline HANDLE create_event(const wchar_t *name) -{ - return CreateEventW(NULL, false, false, name); -} - -static inline HANDLE open_event(const wchar_t *name) -{ - return OpenEventW(GC_EVENT_FLAGS, false, name); -} - -static inline HANDLE create_mutex(const wchar_t *name) -{ - return CreateMutexW(NULL, false, name); -} - -static inline HANDLE open_mutex(const wchar_t *name) -{ - return OpenMutexW(GC_MUTEX_FLAGS, false, name); -} - -static inline HANDLE create_event_plus_id(const wchar_t *name, DWORD id) -{ - wchar_t new_name[64]; - _snwprintf(new_name, 64, L"%s%lu", name, id); - return create_event(new_name); -} - -static inline HANDLE create_mutex_plus_id(const wchar_t *name, DWORD id) -{ - wchar_t new_name[64]; - _snwprintf(new_name, 64, L"%s%lu", name, id); - return create_mutex(new_name); -} - -static inline bool object_signalled(HANDLE event) -{ - if (!event) - return false; - - return WaitForSingleObject(event, 0) == WAIT_OBJECT_0; -} diff --git a/plugins/win-capture/inject-library.c b/plugins/win-capture/inject-library.c deleted file mode 100644 index 44d6e0f4abeca9..00000000000000 --- a/plugins/win-capture/inject-library.c +++ /dev/null @@ -1,153 +0,0 @@ -#include -#include -#ifdef OBS_LEGACY -#include "../../libobs/util/windows/obfuscate.h" -#else -#include -#endif -#include "inject-library.h" - -typedef HANDLE(WINAPI *create_remote_thread_t)(HANDLE, LPSECURITY_ATTRIBUTES, - SIZE_T, LPTHREAD_START_ROUTINE, - LPVOID, DWORD, LPDWORD); -typedef BOOL(WINAPI *write_process_memory_t)(HANDLE, LPVOID, LPCVOID, SIZE_T, - SIZE_T *); -typedef LPVOID(WINAPI *virtual_alloc_ex_t)(HANDLE, LPVOID, SIZE_T, DWORD, - DWORD); -typedef BOOL(WINAPI *virtual_free_ex_t)(HANDLE, LPVOID, SIZE_T, DWORD); - -int inject_library_obf(HANDLE process, const wchar_t *dll, - const char *create_remote_thread_obf, uint64_t obf1, - const char *write_process_memory_obf, uint64_t obf2, - const char *virtual_alloc_ex_obf, uint64_t obf3, - const char *virtual_free_ex_obf, uint64_t obf4, - const char *load_library_w_obf, uint64_t obf5) -{ - int ret = INJECT_ERROR_UNLIKELY_FAIL; - DWORD last_error = 0; - bool success = false; - size_t written_size; - DWORD thread_id; - HANDLE thread = NULL; - size_t size; - void *mem; - - /* -------------------------------- */ - - HMODULE kernel32 = GetModuleHandleW(L"KERNEL32"); - create_remote_thread_t create_remote_thread; - write_process_memory_t write_process_memory; - virtual_alloc_ex_t virtual_alloc_ex; - virtual_free_ex_t virtual_free_ex; - FARPROC load_library_w; - - create_remote_thread = (create_remote_thread_t)ms_get_obfuscated_func( - kernel32, create_remote_thread_obf, obf1); - write_process_memory = (write_process_memory_t)ms_get_obfuscated_func( - kernel32, write_process_memory_obf, obf2); - virtual_alloc_ex = (virtual_alloc_ex_t)ms_get_obfuscated_func( - kernel32, virtual_alloc_ex_obf, obf3); - virtual_free_ex = (virtual_free_ex_t)ms_get_obfuscated_func( - kernel32, virtual_free_ex_obf, obf4); - load_library_w = (FARPROC)ms_get_obfuscated_func( - kernel32, load_library_w_obf, obf5); - - /* -------------------------------- */ - - size = (wcslen(dll) + 1) * sizeof(wchar_t); - mem = virtual_alloc_ex(process, NULL, size, MEM_RESERVE | MEM_COMMIT, - PAGE_READWRITE); - if (!mem) { - goto fail; - } - - success = write_process_memory(process, mem, dll, size, &written_size); - if (!success) { - goto fail; - } - - thread = create_remote_thread(process, NULL, 0, - (LPTHREAD_START_ROUTINE)load_library_w, - mem, 0, &thread_id); - if (!thread) { - goto fail; - } - - if (WaitForSingleObject(thread, 4000) == WAIT_OBJECT_0) { - DWORD code; - GetExitCodeThread(thread, &code); - ret = (code != 0) ? 0 : INJECT_ERROR_INJECT_FAILED; - - SetLastError(0); - } - -fail: - if (ret == INJECT_ERROR_UNLIKELY_FAIL) { - last_error = GetLastError(); - } - if (thread) { - CloseHandle(thread); - } - if (mem) { - virtual_free_ex(process, mem, 0, MEM_RELEASE); - } - if (last_error != 0) { - SetLastError(last_error); - } - - return ret; -} - -/* ------------------------------------------------------------------------- */ - -typedef HHOOK(WINAPI *set_windows_hook_ex_t)(int, HOOKPROC, HINSTANCE, DWORD); - -#define RETRY_INTERVAL_MS 500 -#define TOTAL_RETRY_TIME_MS 4000 -#define RETRY_COUNT (TOTAL_RETRY_TIME_MS / RETRY_INTERVAL_MS) - -int inject_library_safe_obf(DWORD thread_id, const wchar_t *dll, - const char *set_windows_hook_ex_obf, uint64_t obf1) -{ - HMODULE user32 = GetModuleHandleW(L"USER32"); - set_windows_hook_ex_t set_windows_hook_ex; - HMODULE lib = LoadLibraryW(dll); - HOOKPROC proc; - HHOOK hook; - size_t i; - - if (!lib || !user32) { - return INJECT_ERROR_UNLIKELY_FAIL; - } - -#ifdef _WIN64 - proc = (HOOKPROC)GetProcAddress(lib, "dummy_debug_proc"); -#else - proc = (HOOKPROC)GetProcAddress(lib, "_dummy_debug_proc@12"); -#endif - - if (!proc) { - return INJECT_ERROR_UNLIKELY_FAIL; - } - - set_windows_hook_ex = (set_windows_hook_ex_t)ms_get_obfuscated_func( - user32, set_windows_hook_ex_obf, obf1); - - hook = set_windows_hook_ex(WH_GETMESSAGE, proc, lib, thread_id); - if (!hook) { - return GetLastError(); - } - - /* SetWindowsHookEx does not inject the library in to the target - * process unless the event associated with it has occurred, so - * repeatedly send the hook message to start the hook at small - * intervals to signal to SetWindowsHookEx to process the message and - * therefore inject the library in to the target process. Repeating - * this is mostly just a precaution. */ - - for (i = 0; i < RETRY_COUNT; i++) { - Sleep(RETRY_INTERVAL_MS); - PostThreadMessage(thread_id, WM_USER + 432, 0, (LPARAM)hook); - } - return 0; -} diff --git a/plugins/win-capture/inject-library.h b/plugins/win-capture/inject-library.h deleted file mode 100644 index f65a129bdd4cf4..00000000000000 --- a/plugins/win-capture/inject-library.h +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include - -#define INJECT_ERROR_INJECT_FAILED -1 -#define INJECT_ERROR_INVALID_PARAMS -2 -#define INJECT_ERROR_OPEN_PROCESS_FAIL -3 -#define INJECT_ERROR_UNLIKELY_FAIL -4 - -extern int inject_library_obf(HANDLE process, const wchar_t *dll, - const char *create_remote_thread_obf, - uint64_t obf1, - const char *write_process_memory_obf, - uint64_t obf2, const char *virtual_alloc_ex_obf, - uint64_t obf3, const char *virtual_free_ex_obf, - uint64_t obf4, const char *load_library_w_obf, - uint64_t obf5); - -extern int inject_library_safe_obf(DWORD thread_id, const wchar_t *dll, - const char *set_windows_hook_ex_obf, - uint64_t obf1); From f82598d10e523e3b19d2f937467e2a9b6a4d4e50 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 2 Aug 2024 17:21:53 +0200 Subject: [PATCH 0421/1073] CI: Add clang-analyze analytics for macOS builds --- .github/scripts/.build.zsh | 32 ++++++++--- .github/workflows/analyze-project.yaml | 74 ++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/.github/scripts/.build.zsh b/.github/scripts/.build.zsh index 1f9d690330adc6..4746caa1938f1e 100755 --- a/.github/scripts/.build.zsh +++ b/.github/scripts/.build.zsh @@ -63,6 +63,7 @@ build() { local config='RelWithDebInfo' local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel) local -i codesign=0 + local -i analyze=0 local -a args while (( # )) { @@ -76,6 +77,7 @@ build() { } case ${1} { --) shift; args+=($@); break ;; + -a|--analyze) analyze=1; shift ;; -t|--target) if (( ! ${_valid_targets[(Ie)${2}]} )) { log_error "Invalid value %B${2}%b for option %B${1}%b" @@ -176,16 +178,32 @@ build() { -exportPath ${project_root}/build_macos ) + local -a analyze_args=( + CLANG_ANALYZER_OUTPUT=sarif + CLANG_ANALYZER_OUTPUT_DIR=${project_root}/analytics + -project obs-studio.xcodeproj + -target obs-studio + -destination "generic/platform=macOS,name=Any Mac" + -configuration ${config} + -parallelizeTargets + -hideShellScriptEnvironment + analyze + ) + pushd build_macos - if [[ ${GITHUB_EVENT_NAME} == push && ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]] { - run_xcodebuild ${archive_args} - run_xcodebuild ${export_args} + if (( analyze )) { + run_xcodebuild ${analyze_args} } else { - run_xcodebuild ${build_args} + if [[ ${GITHUB_EVENT_NAME} == push && ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]] { + run_xcodebuild ${archive_args} + run_xcodebuild ${export_args} + } else { + run_xcodebuild ${build_args} - rm -rf OBS.app - mkdir OBS.app - ditto UI/${config}/OBS.app OBS.app + rm -rf OBS.app + mkdir OBS.app + ditto UI/${config}/OBS.app OBS.app + } } popd ;; diff --git a/.github/workflows/analyze-project.yaml b/.github/workflows/analyze-project.yaml index 3bc5d3a8b48005..45b08ee604881f 100644 --- a/.github/workflows/analyze-project.yaml +++ b/.github/workflows/analyze-project.yaml @@ -38,3 +38,77 @@ jobs: pvsKey: ${{ secrets.PVS_KEY }} target: x64 config: Debug + + macos: + name: macOS 🍏 (clang-analyze) + runs-on: macos-14 + defaults: + run: + shell: zsh --no-rcs --errexit --pipefail {0} + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Set Up Code Signing 🔑 + uses: ./.github/actions/setup-macos-codesigning + id: codesign + with: + codesignIdentity: ${{ secrets.MACOS_SIGNING_IDENTITY }} + codesignCertificate: ${{ secrets.MACOS_SIGNING_CERT }} + certificatePassword: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }} + keychainPassword: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }} + provisioningProfile: ${{ secrets.MACOS_SIGNING_PROVISIONING_PROFILE }} + notarizationUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }} + notarizationPassword: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }} + + - name: Build OBS Studio 🧱 + env: + TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }} + TWITCH_HASH: ${{ secrets.TWITCH_HASH }} + RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }} + RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }} + YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }} + YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }} + YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }} + YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }} + CODESIGN_IDENT: ${{ steps.codesign.outputs.codesignIdent }} + CODESIGN_TEAM: ${{ steps.codesign.outputs.codesignTeam }} + PROVISIONING_PROFILE: ${{ steps.codesign.outputs.provisioningProfileUUID }} + run: | + : Run macOS Build + + local -a build_args=( + --config Debug + --target macos-arm64 + --codesign + --analyze + ) + if (( ${+RUNNER_DEBUG} )) build_args+=(--debug) + + git fetch origin --no-tags --no-recurse-submodules -q + .github/scripts/build-macos ${build_args} + - name: Compile Analytics Data 📊 + run: | + : Compile Analytics Data 📊 + + local analytics_root='${{ github.workspace }}/analytics' + + local -a analytics_files=(${analytics_root}/StaticAnalyzer/obs-studio/**/*.plist) + + for file (${analytics_files}) { + mv ${file} ${analytics_root}/${${file:t}//plist/sarif} + } + + pushd ${analytics_root} + + npx @microsoft/sarif-multitool merge *.sarif + + popd + + - name: Upload SARIF report files 📦 + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: "${{ github.workspace }}/analytics/merged.sarif" + category: 'clang-analyze (macOS Apple Silicon)' From 8758ece291c8c88563078038edcaaa9325c3a5df Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:57:09 +0200 Subject: [PATCH 0422/1073] UI: Remove now unused GetMonitorName for Qt < 6.4 --- UI/platform-windows.cpp | 64 ---------------------------------------- UI/platform.hpp | 3 -- UI/window-basic-main.cpp | 8 +---- 3 files changed, 1 insertion(+), 74 deletions(-) diff --git a/UI/platform-windows.cpp b/UI/platform-windows.cpp index 229045a64330cc..a5a03c575595d0 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -356,70 +356,6 @@ static BOOL CALLBACK GetMonitorCallback(HMONITOR monitor, HDC, LPRECT, return true; } -#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) -#define GENERIC_MONITOR_NAME QStringLiteral("Generic PnP Monitor") - -QString GetMonitorName(const QString &id) -{ - MonitorData data = {}; - data.id = (const wchar_t *)id.utf16(); - data.info.cbSize = sizeof(data.info); - - EnumDisplayMonitors(nullptr, nullptr, GetMonitorCallback, - (LPARAM)&data); - if (!data.found) { - return GENERIC_MONITOR_NAME; - } - - UINT32 numPath, numMode; - if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath, - &numMode) != ERROR_SUCCESS) { - return GENERIC_MONITOR_NAME; - } - - std::vector paths(numPath); - std::vector modes(numMode); - - if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPath, paths.data(), - &numMode, modes.data(), - nullptr) != ERROR_SUCCESS) { - return GENERIC_MONITOR_NAME; - } - - DISPLAYCONFIG_TARGET_DEVICE_NAME target; - bool found = false; - - paths.resize(numPath); - for (size_t i = 0; i < numPath; ++i) { - const DISPLAYCONFIG_PATH_INFO &path = paths[i]; - - DISPLAYCONFIG_SOURCE_DEVICE_NAME s; - s.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; - s.header.size = sizeof(s); - s.header.adapterId = path.sourceInfo.adapterId; - s.header.id = path.sourceInfo.id; - - if (DisplayConfigGetDeviceInfo(&s.header) == ERROR_SUCCESS && - wcscmp(data.info.szDevice, s.viewGdiDeviceName) == 0) { - target.header.type = - DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; - target.header.size = sizeof(target); - target.header.adapterId = path.sourceInfo.adapterId; - target.header.id = path.targetInfo.id; - found = DisplayConfigGetDeviceInfo(&target.header) == - ERROR_SUCCESS; - break; - } - } - - if (!found) { - return GENERIC_MONITOR_NAME; - } - - return QString::fromWCharArray(target.monitorFriendlyDeviceName); -} -#endif - /* Based on https://www.winehq.org/pipermail/wine-devel/2008-September/069387.html */ typedef const char *(CDECL *WINEGETVERSION)(void); bool IsRunningOnWine() diff --git a/UI/platform.hpp b/UI/platform.hpp index 9e9125ad7ad35c..73e15d4af236fd 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -76,9 +76,6 @@ class RunOnceMutex { RunOnceMutex &operator=(RunOnceMutex &&rom); }; -#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) -QString GetMonitorName(const QString &id); -#endif bool IsRunningOnWine(); #endif diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index f131f6f18caeb8..4d22e31ef66164 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -5606,13 +5606,7 @@ QList OBSBasic::GetProjectorMenuMonitorsFormatted() QRect screenGeometry = screen->geometry(); qreal ratio = screen->devicePixelRatio(); QString name = ""; -#if defined(_WIN32) && QT_VERSION < QT_VERSION_CHECK(6, 4, 0) - QTextStream fullname(&name); - fullname << GetMonitorName(screen->name()); - fullname << " ("; - fullname << (i + 1); - fullname << ")"; -#elif defined(__APPLE__) || defined(_WIN32) +#if defined(__APPLE__) || defined(_WIN32) name = screen->name(); #else name = screen->model().simplified(); From db5b226bb93e7478e76e3e92a21f9f29dfbb89cc Mon Sep 17 00:00:00 2001 From: derrod Date: Mon, 19 Aug 2024 13:21:21 +0200 Subject: [PATCH 0423/1073] obs-nvenc: Only show UHQ tune on supported GPUs --- plugins/obs-nvenc/nvenc-properties.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/obs-nvenc/nvenc-properties.c b/plugins/obs-nvenc/nvenc-properties.c index ff67cdba13cba9..486b80a2b15d70 100644 --- a/plugins/obs-nvenc/nvenc-properties.c +++ b/plugins/obs-nvenc/nvenc-properties.c @@ -175,7 +175,10 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec) #define add_tune(val) \ obs_property_list_add_string(p, obs_module_text("Tuning." val), val) #ifdef NVENC_12_2_OR_LATER - if (codec == CODEC_HEVC) + /* The UHQ tune is only supported on Turing or later. + * It uses the temporal filtering feature, so we can use its + * availability as an indicator that we are on a supported GPU. */ + if (codec == CODEC_HEVC && caps->temporal_filter) add_tune("uhq"); #endif add_tune("hq"); From 9e547d847956b9c8d3be951f886d9364dc24af9b Mon Sep 17 00:00:00 2001 From: tytan652 Date: Wed, 21 Aug 2024 09:11:49 +0200 Subject: [PATCH 0424/1073] build-aux: Update Flatpak modules * Update libvpl to version 2.12.0 * Update vpl-gpu-rt to version 24.2.5 * 24.3 requires VVC from libva which is not the case with the actual runtime in use * Update nv-codec to version 12.2.72.0 --- build-aux/modules/20-nv-codec.json | 4 ++-- build-aux/modules/50-libvpl.json | 4 ++-- build-aux/modules/50-vpl-gpu-rt.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build-aux/modules/20-nv-codec.json b/build-aux/modules/20-nv-codec.json index 36feef21640465..ed2f68ddc40d78 100644 --- a/build-aux/modules/20-nv-codec.json +++ b/build-aux/modules/20-nv-codec.json @@ -11,8 +11,8 @@ { "type": "git", "url": "https://github.com/FFmpeg/nv-codec-headers.git", - "tag": "n12.1.14.0", - "commit": "1889e62e2d35ff7aa9baca2bceb14f053785e6f1" + "tag": "n12.2.72.0", + "commit": "c69278340ab1d5559c7d7bf0edf615dc33ddbba7" } ] } diff --git a/build-aux/modules/50-libvpl.json b/build-aux/modules/50-libvpl.json index 7a054e92d01c6d..f9b0be16e31341 100644 --- a/build-aux/modules/50-libvpl.json +++ b/build-aux/modules/50-libvpl.json @@ -18,8 +18,8 @@ { "type": "git", "url": "https://github.com/intel/libvpl.git", - "commit": "11a9bbda5b22ac1c544da59b4007bb57f737b487", - "tag": "v2.11.0" + "commit": "0c13c410095764799afea0cf645bd896378579b8", + "tag": "v2.12.0" } ] } diff --git a/build-aux/modules/50-vpl-gpu-rt.json b/build-aux/modules/50-vpl-gpu-rt.json index 86b88794d28790..2dd0bfaccdb75c 100644 --- a/build-aux/modules/50-vpl-gpu-rt.json +++ b/build-aux/modules/50-vpl-gpu-rt.json @@ -15,8 +15,8 @@ { "type": "git", "url": "https://github.com/intel/vpl-gpu-rt.git", - "commit": "d74cb6391eaad4a2db9cbec6a8a335b6f1a555c4", - "tag": "intel-onevpl-24.2.2" + "commit": "e0b981d1bf8ca4f9d346089d530cc149c09e10df", + "tag": "intel-onevpl-24.2.5" }, { "type": "shell", From ce3d739f3aad011337fcb3de373d1e1058a3838c Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 22 Aug 2024 11:47:16 +0200 Subject: [PATCH 0425/1073] UI: Only use preset2 in simple mode for legacy/FFmpeg NVENC --- UI/window-basic-main-outputs.cpp | 14 +++++++++----- UI/window-basic-settings.cpp | 4 +++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index b1edff50b9c695..aad5fcfb85dfce 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -819,6 +819,7 @@ void SimpleOutput::Update() "x264Settings"); const char *encoder = config_get_string(main->Config(), "SimpleOutput", "StreamEncoder"); + const char *encoder_id = obs_encoder_get_id(videoStreaming); const char *presetType; const char *preset; @@ -855,11 +856,14 @@ void SimpleOutput::Update() } preset = config_get_string(main->Config(), "SimpleOutput", presetType); - obs_data_set_string(videoSettings, - (strcmp(presetType, "NVENCPreset2") == 0) - ? "preset2" - : "preset", - preset); + + /* Only use preset2 for legacy/FFmpeg NVENC Encoder. */ + if (strncmp(encoder_id, "ffmpeg_", 7) == 0 && + strcmp(presetType, "NVENCPreset2") == 0) { + obs_data_set_string(videoSettings, "preset2", preset); + } else { + obs_data_set_string(videoSettings, "preset", preset); + } obs_data_set_string(videoSettings, "rate_control", "CBR"); obs_data_set_int(videoSettings, "bitrate", videoBitrate); diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 16d92395efde09..e385342d722508 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -5484,9 +5484,11 @@ void OBSBasicSettings::SimpleStreamingEncoderChanged() const char *name = get_simple_output_encoder(QT_TO_UTF8(encoder)); + const bool isFFmpegEncoder = strncmp(name, "ffmpeg_", 7) == 0; obs_properties_t *props = obs_get_encoder_properties(name); - obs_property_t *p = obs_properties_get(props, "preset2"); + obs_property_t *p = obs_properties_get( + props, isFFmpegEncoder ? "preset2" : "preset"); size_t num = obs_property_list_item_count(p); for (size_t i = 0; i < num; i++) { const char *name = obs_property_list_item_name(p, i); From 2f1379aa64c5cb7be54eac806555f1a7c1e42683 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Wed, 21 Aug 2024 17:56:41 +0200 Subject: [PATCH 0426/1073] cmake: Default to modern CMake build system for Windows --- CMakeLists.txt | 2 +- CMakePresets.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e029723a9d8b..fb62167ebe0bea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.22...3.25) -if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Darwin)" OR OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) +if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Windows|Darwin)" OR OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) diff --git a/CMakePresets.json b/CMakePresets.json index 1349681c13758e..a213549a0093ff 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -102,7 +102,6 @@ "binaryDir": "${sourceDir}/build_x64", "generator": "Visual Studio 17 2022", "cacheVariables": { - "OBS_CMAKE_VERSION": {"type": "STRING", "value": "3.0.0"}, "GPU_PRIORITY_VAL": {"type": "STRING", "value": "$penv{GPU_PRIORITY_VAL}"}, "VIRTUALCAM_GUID": {"type": "STRING", "value": "A3FCE0F5-3493-419F-958A-ABA1250EC20B"}, "ENABLE_BROWSER": true, From b825399d482cd5d8b1bc204979785bdb4599c021 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Wed, 21 Aug 2024 18:02:04 +0200 Subject: [PATCH 0427/1073] libobs-d3d11: Remove CMake legacy code path --- libobs-d3d11/CMakeLists.txt | 2 -- libobs-d3d11/cmake/legacy.cmake | 50 --------------------------------- 2 files changed, 52 deletions(-) delete mode 100644 libobs-d3d11/cmake/legacy.cmake diff --git a/libobs-d3d11/CMakeLists.txt b/libobs-d3d11/CMakeLists.txt index 6882b4be1da0c5..89045322ef43db 100644 --- a/libobs-d3d11/CMakeLists.txt +++ b/libobs-d3d11/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - add_library(libobs-d3d11 MODULE) add_library(OBS::libobs-d3d11 ALIAS libobs-d3d11) diff --git a/libobs-d3d11/cmake/legacy.cmake b/libobs-d3d11/cmake/legacy.cmake deleted file mode 100644 index 2917628c17cf45..00000000000000 --- a/libobs-d3d11/cmake/legacy.cmake +++ /dev/null @@ -1,50 +0,0 @@ -project(libobs-d3d11) - -add_library(libobs-d3d11 MODULE) -add_library(OBS::libobs-d3d11 ALIAS libobs-d3d11) - -target_sources( - libobs-d3d11 - PRIVATE d3d11-indexbuffer.cpp - d3d11-samplerstate.cpp - d3d11-shader.cpp - d3d11-shaderprocessor.cpp - d3d11-shaderprocessor.hpp - d3d11-stagesurf.cpp - d3d11-subsystem.cpp - d3d11-subsystem.hpp - d3d11-texture2d.cpp - d3d11-texture3d.cpp - d3d11-vertexbuffer.cpp - d3d11-duplicator.cpp - d3d11-rebuild.cpp - d3d11-zstencilbuffer.cpp) - -set(MODULE_DESCRIPTION "OBS Library D3D11 wrapper") -configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in libobs-d3d11.rc) - -target_include_directories(libobs-d3d11 PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -target_sources(libobs-d3d11 PRIVATE libobs-d3d11.rc) - -target_compile_features(libobs-d3d11 PRIVATE cxx_std_17) - -target_compile_definitions(libobs-d3d11 PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS) - -if(NOT DEFINED GPU_PRIORITY_VAL - OR "x${GPU_PRIORITY_VAL}x" STREQUAL "xx" - OR "${GPU_PRIORITY_VAL}" STREQUAL "0") - target_compile_definitions(libobs-d3d11 PRIVATE GPU_PRIORITY_VAL=0) -else() - target_compile_definitions(libobs-d3d11 PRIVATE USE_GPU_PRIORITY=TRUE GPU_PRIORITY_VAL=${GPU_PRIORITY_VAL}) -endif() - -target_link_libraries(libobs-d3d11 PRIVATE OBS::libobs d3d9 d3d11 d3dcompiler dxgi shcore) - -set_target_properties( - libobs-d3d11 - PROPERTIES OUTPUT_NAME libobs-d3d11 - FOLDER "core" - PREFIX "") - -setup_binary_target(libobs-d3d11) From 46b5f7e6d079f06f2a0ca426d89b429c5013e097 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 9 Aug 2024 13:37:54 +0200 Subject: [PATCH 0428/1073] plugins: Remove CMake legacy code paths for Windows modules --- plugins/obs-text/CMakeLists.txt | 2 - plugins/obs-text/cmake/legacy.cmake | 19 -- plugins/win-capture/CMakeLists.txt | 2 - plugins/win-capture/cmake/32bit-build.cmake | 3 - plugins/win-capture/cmake/legacy.cmake | 75 ------ .../get-graphics-offsets/CMakeLists.txt | 2 - .../get-graphics-offsets/cmake/legacy.cmake | 33 --- .../win-capture/graphics-hook/CMakeLists.txt | 2 - .../graphics-hook/cmake/legacy.cmake | 62 ----- .../win-capture/inject-helper/CMakeLists.txt | 2 - .../inject-helper/cmake/legacy.cmake | 21 -- plugins/win-dshow/CMakeLists.txt | 2 - plugins/win-dshow/cmake/legacy.cmake | 240 ------------------ .../virtualcam-module/CMakeLists.txt | 2 - .../virtualcam-module/cmake/legacy.cmake | 60 ----- plugins/win-wasapi/CMakeLists.txt | 2 - plugins/win-wasapi/cmake/legacy.cmake | 19 -- 17 files changed, 548 deletions(-) delete mode 100644 plugins/obs-text/cmake/legacy.cmake delete mode 100644 plugins/win-capture/cmake/32bit-build.cmake delete mode 100644 plugins/win-capture/cmake/legacy.cmake delete mode 100644 plugins/win-capture/get-graphics-offsets/cmake/legacy.cmake delete mode 100644 plugins/win-capture/graphics-hook/cmake/legacy.cmake delete mode 100644 plugins/win-capture/inject-helper/cmake/legacy.cmake delete mode 100644 plugins/win-dshow/cmake/legacy.cmake delete mode 100644 plugins/win-dshow/virtualcam-module/cmake/legacy.cmake delete mode 100644 plugins/win-wasapi/cmake/legacy.cmake diff --git a/plugins/obs-text/CMakeLists.txt b/plugins/obs-text/CMakeLists.txt index c6b654f83b6430..118157862732e1 100644 --- a/plugins/obs-text/CMakeLists.txt +++ b/plugins/obs-text/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - add_library(obs-text MODULE) add_library(OBS::text ALIAS obs-text) diff --git a/plugins/obs-text/cmake/legacy.cmake b/plugins/obs-text/cmake/legacy.cmake deleted file mode 100644 index 0bca288ba16524..00000000000000 --- a/plugins/obs-text/cmake/legacy.cmake +++ /dev/null @@ -1,19 +0,0 @@ -project(obs-text) - -add_library(obs-text MODULE) -add_library(OBS::text ALIAS obs-text) - -target_link_libraries(obs-text PRIVATE OBS::libobs) - -set_target_properties(obs-text PROPERTIES FOLDER "plugins") - -set(MODULE_DESCRIPTION "OBS GDI+ text module") -configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-text.rc) - -target_sources(obs-text PRIVATE gdiplus/obs-text.cpp obs-text.rc) - -target_link_libraries(obs-text PRIVATE gdiplus) - -target_compile_definitions(obs-text PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS) - -setup_plugin_target(obs-text) diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index 0c4a9d670ddc80..3e855943e141ac 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") endif() diff --git a/plugins/win-capture/cmake/32bit-build.cmake b/plugins/win-capture/cmake/32bit-build.cmake deleted file mode 100644 index 6fa9c74da4da77..00000000000000 --- a/plugins/win-capture/cmake/32bit-build.cmake +++ /dev/null @@ -1,3 +0,0 @@ -add_subdirectory(graphics-hook) -add_subdirectory(get-graphics-offsets) -add_subdirectory(inject-helper) diff --git a/plugins/win-capture/cmake/legacy.cmake b/plugins/win-capture/cmake/legacy.cmake deleted file mode 100644 index 7c529a678a2103..00000000000000 --- a/plugins/win-capture/cmake/legacy.cmake +++ /dev/null @@ -1,75 +0,0 @@ -project(win-capture) - -option(ENABLE_COMPAT_UPDATES "Checks for capture compatibility data updates" ON) - -set(COMPAT_URL - "https://obsproject.com/obs2_update/win-capture" - CACHE STRING "Default services package URL") - -mark_as_advanced(COMPAT_URL) - -add_library(win-capture MODULE) -add_library(OBS::capture ALIAS win-capture) - -find_package(Jansson 2.5 REQUIRED) - -if(NOT TARGET OBS::ipc-util) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") -endif() - -if(NOT TARGET OBS::file-updater) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") -endif() - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/compat-config.h.in ${CMAKE_BINARY_DIR}/config/compat-config.h) - -target_sources( - win-capture - PRIVATE plugin-main.c - app-helpers.c - app-helpers.h - audio-helpers.c - audio-helpers.h - cursor-capture.c - cursor-capture.h - dc-capture.c - dc-capture.h - duplicator-monitor-capture.c - game-capture.c - game-capture-file-init.c - graphics-hook-info.h - graphics-hook-ver.h - hook-helpers.h - inject-library.c - inject-library.h - load-graphics-offsets.c - monitor-capture.c - nt-stuff.c - nt-stuff.h - window-capture.c - compat-helpers.c - compat-helpers.h - compat-format-ver.h - ../../libobs/util/windows/obfuscate.c - ../../libobs/util/windows/obfuscate.h - ${CMAKE_BINARY_DIR}/config/compat-config.h) - -target_link_libraries(win-capture PRIVATE OBS::libobs OBS::ipc-util OBS::file-updater Jansson::Jansson) - -set_target_properties(win-capture PROPERTIES FOLDER "plugins/win-capture") - -if(MSVC) - target_link_libraries(win-capture PRIVATE OBS::w32-pthreads) - target_link_options(win-capture PRIVATE "LINKER:/IGNORE:4098") -endif() - -target_compile_definitions(win-capture PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS - OBS_VERSION="${OBS_VERSION_CANONICAL}" OBS_LEGACY) - -set_property(GLOBAL APPEND PROPERTY OBS_MODULE_LIST "win-capture") - -setup_plugin_target(win-capture) - -add_subdirectory(graphics-hook) -add_subdirectory(get-graphics-offsets) -add_subdirectory(inject-helper) diff --git a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt index cdb6c3393a1b04..f48f02803322bf 100644 --- a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt +++ b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - if(NOT TARGET OBS::d3d8-api) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-d3d8-api" obs-d3d8-api) endif() diff --git a/plugins/win-capture/get-graphics-offsets/cmake/legacy.cmake b/plugins/win-capture/get-graphics-offsets/cmake/legacy.cmake deleted file mode 100644 index e5b97e674add8c..00000000000000 --- a/plugins/win-capture/get-graphics-offsets/cmake/legacy.cmake +++ /dev/null @@ -1,33 +0,0 @@ -project(get-graphics-offsets) - -add_executable(get-graphics-offsets) - -target_sources( - get-graphics-offsets - PRIVATE get-graphics-offsets.c - get-graphics-offsets.h - dxgi-offsets.cpp - d3d8-offsets.cpp - d3d9-offsets.cpp - ../graphics-hook-info.h - ../hook-helpers.h) - -target_include_directories(get-graphics-offsets PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. - ${CMAKE_CURRENT_SOURCE_DIR}/../d3d8-api) - -target_link_libraries(get-graphics-offsets d3d9.lib dxgi.lib d3d11.lib) - -target_compile_definitions(get-graphics-offsets PRIVATE OBS_LEGACY) - -if(MSVC) - target_compile_options(get-graphics-offsets PRIVATE "$,/MTd,/MT>") -endif() - -set_target_properties(get-graphics-offsets PROPERTIES FOLDER "plugins/win-capture") -set_target_properties(get-graphics-offsets - PROPERTIES OUTPUT_NAME "get-graphics-offsets$,64,32>") - -set(OBS_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-plugins/win-capture/") -setup_plugin_target(get-graphics-offsets) - -add_dependencies(win-capture get-graphics-offsets) diff --git a/plugins/win-capture/graphics-hook/CMakeLists.txt b/plugins/win-capture/graphics-hook/CMakeLists.txt index 3f2ce5e613140f..63f0fcc6145a65 100644 --- a/plugins/win-capture/graphics-hook/CMakeLists.txt +++ b/plugins/win-capture/graphics-hook/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - find_package(Detours REQUIRED) find_package(Vulkan REQUIRED) diff --git a/plugins/win-capture/graphics-hook/cmake/legacy.cmake b/plugins/win-capture/graphics-hook/cmake/legacy.cmake deleted file mode 100644 index e23a364efb3eb6..00000000000000 --- a/plugins/win-capture/graphics-hook/cmake/legacy.cmake +++ /dev/null @@ -1,62 +0,0 @@ -project(graphics-hook) - -find_package(Detours REQUIRED) -find_package(Vulkan REQUIRED) - -add_library(graphics-hook MODULE) -add_library(OBS::graphics-hook ALIAS graphics-hook) - -if(NOT TARGET OBS::ipc-util) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/ipc-util" "${CMAKE_BINARY_DIR}/shared/ipc-util") -endif() - -target_sources( - graphics-hook - PRIVATE graphics-hook.c - graphics-hook.h - gl-capture.c - gl-decs.h - d3d8-capture.cpp - d3d9-capture.cpp - d3d9-patches.hpp - dxgi-capture.cpp - d3d10-capture.cpp - d3d11-capture.cpp - d3d12-capture.cpp - ../../../libobs/util/windows/obfuscate.c - ../../../libobs/util/windows/obfuscate.h - ../graphics-hook-ver.h - ../graphics-hook-info.h - ../hook-helpers.h - graphics-hook.rc) - -target_include_directories(graphics-hook PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_SOURCE_DIR}/libobs - ${CMAKE_CURRENT_SOURCE_DIR}/../d3d8-api) - -target_link_libraries(graphics-hook PRIVATE OBS::ipc-util Detours::Detours dxguid) -target_link_options(graphics-hook PRIVATE "LINKER:/IGNORE:4099") - -if(MSVC) - target_compile_options(graphics-hook PRIVATE "$,/MTd,/MT>") -endif() - -set_target_properties(graphics-hook PROPERTIES FOLDER "plugins/win-capture" - OUTPUT_NAME "graphics-hook$,64,32>") - -target_compile_definitions(graphics-hook PRIVATE COMPILE_D3D12_HOOK OBS_LEGACY) - -if(TARGET Vulkan::Vulkan) - target_sources(graphics-hook PRIVATE vulkan-capture.c vulkan-capture.h) - - target_link_libraries(graphics-hook PRIVATE Vulkan::Vulkan) - - target_compile_definitions(graphics-hook PRIVATE COMPILE_VULKAN_HOOK) - - add_target_resource(graphics-hook "${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan64.json" "obs-plugins/win-capture/") - add_target_resource(graphics-hook "${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan32.json" "obs-plugins/win-capture/") -endif() - -set(OBS_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-plugins/win-capture/") -setup_plugin_target(graphics-hook) - -add_dependencies(win-capture graphics-hook) diff --git a/plugins/win-capture/inject-helper/CMakeLists.txt b/plugins/win-capture/inject-helper/CMakeLists.txt index 49c52011581345..b73f672ef429d7 100644 --- a/plugins/win-capture/inject-helper/CMakeLists.txt +++ b/plugins/win-capture/inject-helper/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") endif() diff --git a/plugins/win-capture/inject-helper/cmake/legacy.cmake b/plugins/win-capture/inject-helper/cmake/legacy.cmake deleted file mode 100644 index a4a45971afb6e1..00000000000000 --- a/plugins/win-capture/inject-helper/cmake/legacy.cmake +++ /dev/null @@ -1,21 +0,0 @@ -project(inject-helper) - -add_executable(inject-helper) - -target_sources(inject-helper PRIVATE inject-helper.c ../inject-library.c ../inject-library.h - ../../../libobs/util/windows/obfuscate.c ../../../libobs/util/windows/obfuscate.h) - -target_include_directories(inject-helper PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_SOURCE_DIR}/libobs) - -target_compile_definitions(inject-helper PRIVATE OBS_LEGACY) -if(MSVC) - target_compile_options(inject-helper PRIVATE "$,/MTd,/MT>") -endif() - -set_target_properties(inject-helper PROPERTIES FOLDER "plugins/win-capture" - OUTPUT_NAME "inject-helper$,64,32>") - -set(OBS_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-plugins/win-capture/") -setup_plugin_target(inject-helper) - -add_dependencies(win-capture inject-helper) diff --git a/plugins/win-dshow/CMakeLists.txt b/plugins/win-dshow/CMakeLists.txt index f89429f0c01e2d..f98c39c6630def 100644 --- a/plugins/win-dshow/CMakeLists.txt +++ b/plugins/win-dshow/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - find_package(FFmpeg REQUIRED avcodec avutil) add_library(win-dshow MODULE) diff --git a/plugins/win-dshow/cmake/legacy.cmake b/plugins/win-dshow/cmake/legacy.cmake deleted file mode 100644 index 86566a91036eb6..00000000000000 --- a/plugins/win-dshow/cmake/legacy.cmake +++ /dev/null @@ -1,240 +0,0 @@ -if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture/dshowcapture.hpp") - obs_status(FATAL_ERROR "libdshowcapture submodule not found! Please fetch submodules. win-dshow plugin disabled.") - return() -endif() - -option(ENABLE_VIRTUALCAM "Enable building with Virtual Camera (Windows)" ON) - -if(NOT ENABLE_VIRTUALCAM) - obs_status(DISABLED "Windows Virtual Camera") -endif() - -if(ENABLE_VIRTUALCAM AND NOT VIRTUALCAM_GUID) - set(VIRTUALCAM_GUID "" CACHE STRING "Virtual Camera GUID" FORCE) - mark_as_advanced(VIRTUALCAM_GUID) -endif() - -project(win-dshow) - -find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil) - -add_library(win-dshow MODULE) -add_library(OBS::dshow ALIAS win-dshow) - -target_sources( - win-dshow - PRIVATE encode-dstr.hpp win-dshow.cpp win-dshow-encoder.cpp dshow-plugin.cpp ffmpeg-decode.c ffmpeg-decode.h -) - -add_library(libdshowcapture-external INTERFACE) -add_library(libdshowcapture INTERFACE) -add_library(OBS::libdshowcapture-external ALIAS libdshowcapture-external) -add_library(OBS::libdshowcapture ALIAS libdshowcapture) - -target_sources( - libdshowcapture-external - INTERFACE - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.cpp -) - -target_sources( - libdshowcapture - INTERFACE - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshowcapture.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/capture-filter.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/capture-filter.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/output-filter.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/output-filter.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshowcapture.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshowencode.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/device.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/device.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/device-vendor.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/encoder.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/encoder.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-base.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-base.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-demux.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-demux.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-device-defs.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-enum.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-enum.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-formats.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-formats.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-media-type.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-encoded-device.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/dshow-media-type.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/log.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/log.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/source/external/IVideoCaptureFilter.h -) - -target_include_directories( - libdshowcapture - INTERFACE - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library -) - -target_compile_definitions(libdshowcapture-external INTERFACE _UP_WINDOWS=1) -target_compile_definitions(libdshowcapture INTERFACE _UP_WINDOWS=1) -target_compile_options(libdshowcapture-external INTERFACE /wd4018) - -set(MODULE_DESCRIPTION "OBS DirectShow module") - -configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in win-dshow.rc) - -target_sources(win-dshow PRIVATE win-dshow.rc) - -target_compile_definitions( - win-dshow - PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS OBS_LEGACY -) - -set(VIRTUALCAM_AVAILABLE OFF) -if(ENABLE_VIRTUALCAM) - if(VIRTUALCAM_GUID STREQUAL "") - obs_status(WARNING "Windows Virtual Camera - GUID not set. Specify as 'VIRTUALCAM_GUID' to enable.") - else() - set(INVALID_GUID ON) - - string(REPLACE "-" ";" GUID_VALS ${VIRTUALCAM_GUID}) - - list(LENGTH GUID_VALS GUID_VAL_COUNT) - if(GUID_VAL_COUNT EQUAL 5) - string(REPLACE ";" "0" GUID_HEX ${GUID_VALS}) - string(REGEX MATCH "[0-9a-fA-F]+" GUID_ACTUAL_HEX ${GUID_HEX}) - if(GUID_ACTUAL_HEX STREQUAL GUID_HEX) - list(GET GUID_VALS 0 GUID_VALS_DATA1) - list(GET GUID_VALS 1 GUID_VALS_DATA2) - list(GET GUID_VALS 2 GUID_VALS_DATA3) - list(GET GUID_VALS 3 GUID_VALS_DATA4) - list(GET GUID_VALS 4 GUID_VALS_DATA5) - string(LENGTH ${GUID_VALS_DATA1} GUID_VALS_DATA1_LENGTH) - string(LENGTH ${GUID_VALS_DATA2} GUID_VALS_DATA2_LENGTH) - string(LENGTH ${GUID_VALS_DATA3} GUID_VALS_DATA3_LENGTH) - string(LENGTH ${GUID_VALS_DATA4} GUID_VALS_DATA4_LENGTH) - string(LENGTH ${GUID_VALS_DATA5} GUID_VALS_DATA5_LENGTH) - if( - GUID_VALS_DATA1_LENGTH EQUAL 8 - AND GUID_VALS_DATA2_LENGTH EQUAL 4 - AND GUID_VALS_DATA3_LENGTH EQUAL 4 - AND GUID_VALS_DATA4_LENGTH EQUAL 4 - AND GUID_VALS_DATA5_LENGTH EQUAL 12 - ) - set(GUID_VAL01 ${GUID_VALS_DATA1}) - set(GUID_VAL02 ${GUID_VALS_DATA2}) - set(GUID_VAL03 ${GUID_VALS_DATA3}) - string(SUBSTRING ${GUID_VALS_DATA4} 0 2 GUID_VAL04) - string(SUBSTRING ${GUID_VALS_DATA4} 2 2 GUID_VAL05) - string(SUBSTRING ${GUID_VALS_DATA5} 0 2 GUID_VAL06) - string(SUBSTRING ${GUID_VALS_DATA5} 2 2 GUID_VAL07) - string(SUBSTRING ${GUID_VALS_DATA5} 4 2 GUID_VAL08) - string(SUBSTRING ${GUID_VALS_DATA5} 6 2 GUID_VAL09) - string(SUBSTRING ${GUID_VALS_DATA5} 8 2 GUID_VAL10) - string(SUBSTRING ${GUID_VALS_DATA5} 10 2 GUID_VAL11) - set(VIRTUALCAM_AVAILABLE ON) - set(INVALID_GUID OFF) - endif() - endif() - endif() - endif() - - if(INVALID_GUID) - obs_status(WARNING "Windows Virtual Camera - invalid GUID supplied") - endif() -endif() - -target_link_libraries( - win-dshow - PRIVATE - OBS::libobs - OBS::w32-pthreads - OBS::libdshowcapture-external - OBS::libdshowcapture - setupapi - strmiids - ksuser - winmm - wmcodecdspuuid - FFmpeg::avcodec - FFmpeg::avutil -) - -source_group( - "libdshowcapture-external\\Source Files" - FILES - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/EGAVResult.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/ElgatoUVCDevice.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/capture-device-support/SampleCode/DriverInterface.cpp -) -source_group( - "libdshowcapture\\Source Files" - FILES - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/capture-filter.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/output-filter.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshowcapture.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshowencode.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/device.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/device-vendor.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/encoder.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-base.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-demux.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-enum.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-formats.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-media-type.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-encoded-device.cpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/log.cpp -) -source_group( - "libdshowcapture\\Header Files" - FILES - libdshowcapture/dshowcapture.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/capture-filter.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/output-filter.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/device.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/encoder.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-base.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-demux.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-device-defs.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-enum.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-formats.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/dshow-media-type.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/log.hpp - ${CMAKE_SOURCE_DIR}/deps/libdshowcapture/src/external/IVideoCaptureFilter.h -) - -set_target_properties(win-dshow PROPERTIES FOLDER "plugins/win-dshow") - -setup_plugin_target(win-dshow) - -if(ENABLE_VIRTUALCAM AND VIRTUALCAM_AVAILABLE) - target_sources( - win-dshow - PRIVATE tiny-nv12-scale.c tiny-nv12-scale.h shared-memory-queue.c shared-memory-queue.h virtualcam.c - ) - - target_compile_definitions(win-dshow PRIVATE VIRTUALCAM_AVAILABLE) - - target_include_directories(win-dshow PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config) - - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/virtualcam-module/virtualcam-guid.h.in - ${CMAKE_CURRENT_BINARY_DIR}/config/virtualcam-guid.h - ) - - target_sources(win-dshow PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config/virtualcam-guid.h) - - configure_file(virtualcam-module/virtualcam-install.bat.in "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-install.bat") - - configure_file(virtualcam-module/virtualcam-uninstall.bat.in "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-uninstall.bat") - - add_target_resource(win-dshow "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-install.bat" "obs-plugins/win-dshow/") - add_target_resource(win-dshow "${CMAKE_CURRENT_BINARY_DIR}/virtualcam-uninstall.bat" "obs-plugins/win-dshow/") - - add_subdirectory(virtualcam-module) -endif() diff --git a/plugins/win-dshow/virtualcam-module/CMakeLists.txt b/plugins/win-dshow/virtualcam-module/CMakeLists.txt index 916c27d30c731d..ac8f46e18039b4 100644 --- a/plugins/win-dshow/virtualcam-module/CMakeLists.txt +++ b/plugins/win-dshow/virtualcam-module/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - option(ENABLE_VIRTUALCAM "Enable Windows Virtual Camera" ON) if(NOT ENABLE_VIRTUALCAM) target_disable_feature(obs-virtualcam-module "Virtual Camera Support") diff --git a/plugins/win-dshow/virtualcam-module/cmake/legacy.cmake b/plugins/win-dshow/virtualcam-module/cmake/legacy.cmake deleted file mode 100644 index 1afd54a36e56fb..00000000000000 --- a/plugins/win-dshow/virtualcam-module/cmake/legacy.cmake +++ /dev/null @@ -1,60 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(obs-virtualcam-module) - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_output_suffix "64") -else() - set(_output_suffix "32") -endif() - -add_library(obs-virtualcam-module MODULE) -add_library(OBS::virtualcam-module ALIAS obs-virtualcam-module) - -target_sources( - obs-virtualcam-module - PRIVATE sleepto.c - sleepto.h - placeholder.cpp - virtualcam-module.cpp - virtualcam-filter.cpp - virtualcam-filter.hpp - virtualcam-module.rc - ../shared-memory-queue.c - ../shared-memory-queue.h - ../tiny-nv12-scale.c - ../tiny-nv12-scale.h) - -target_include_directories(obs-virtualcam-module PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) - -set(MODULE_DESCRIPTION "OBS Virtual Camera output module") - -configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in virtualcam-module.rc) - -target_sources(obs-virtualcam-module PRIVATE virtualcam-module.rc) - -target_link_libraries(obs-virtualcam-module PRIVATE OBS::libdshowcapture OBS::libdshowcapture-external setupapi winmm - strmiids gdiplus) - -target_link_options(obs-virtualcam-module PRIVATE "LINKER:/ignore:4104") - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/virtualcam-module.def.in ${CMAKE_CURRENT_BINARY_DIR}/virtualcam-module.def) - -target_sources(obs-virtualcam-module PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/virtualcam-module.def) - -target_include_directories(obs-virtualcam-module PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../config - ${CMAKE_SOURCE_DIR}/libobs) - -target_compile_definitions(obs-virtualcam-module PRIVATE VIRTUALCAM_AVAILABLE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS - _CRT_NONSTDC_NO_WARNINGS OBS_LEGACY) - -if(MSVC) - target_compile_options(obs-virtualcam-module PRIVATE "$,/MTd,/MT>") - add_target_resource(win-dshow "$" "obs-plugins/win-dshow/" OPTIONAL) - -endif() - -set_target_properties(obs-virtualcam-module PROPERTIES FOLDER "plugins/win-dshow") - -set_target_properties(obs-virtualcam-module PROPERTIES OUTPUT_NAME "obs-virtualcam-module${_output_suffix}") - -add_target_resource(win-dshow "$" "obs-plugins/win-dshow/") diff --git a/plugins/win-wasapi/CMakeLists.txt b/plugins/win-wasapi/CMakeLists.txt index 1c8c4feb0ab2c3..7b7298acb6afad 100644 --- a/plugins/win-wasapi/CMakeLists.txt +++ b/plugins/win-wasapi/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - add_library(win-wasapi MODULE) add_library(OBS::wasapi ALIAS win-wasapi) diff --git a/plugins/win-wasapi/cmake/legacy.cmake b/plugins/win-wasapi/cmake/legacy.cmake deleted file mode 100644 index dd2e6821946583..00000000000000 --- a/plugins/win-wasapi/cmake/legacy.cmake +++ /dev/null @@ -1,19 +0,0 @@ -project(win-wasapi) - -add_library(win-wasapi MODULE) -add_library(OBS::wasapi ALIAS win-wasapi) - -target_sources(win-wasapi PRIVATE win-wasapi.cpp wasapi-notify.cpp wasapi-notify.hpp enum-wasapi.cpp enum-wasapi.hpp - plugin-main.cpp) - -set(MODULE_DESCRIPTION "OBS WASAPI module") - -configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in win-wasapi.rc) - -target_sources(win-wasapi PRIVATE win-wasapi.rc) - -target_link_libraries(win-wasapi PRIVATE OBS::libobs Avrt) - -set_target_properties(win-wasapi PROPERTIES FOLDER "plugins") - -setup_plugin_target(win-wasapi) From ab6ecde20fb85fd6375df9e5a421e8008aa62444 Mon Sep 17 00:00:00 2001 From: LightcastMediaCloud Date: Thu, 27 Jun 2024 11:53:36 -0400 Subject: [PATCH 0429/1073] rtmp-services: Update Lightcast.com to Premium CDN Incremented Version Update services.json Updated Lightcast.com to new Premium CDN --- plugins/rtmp-services/data/package.json | 2 +- plugins/rtmp-services/data/services.json | 31 ++++++------------------ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 3c819b05caaf48..04d61060d8b8ec 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -5,7 +5,7 @@ "files": [ { "name": "services.json", - "version": 258 + "version": 259 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 1a63db0a5844cd..c9483655e3c4e7 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -1503,38 +1503,23 @@ "name": "Lightcast.com", "servers": [ { - "name": "North America / East", - "url": "rtmp://us-east.live.lightcast.com/202E1F/default" + "name": "North America 1", + "url": "rtmp://ingest-na1.live.lightcast.com/in" }, { - "name": "North America / West", - "url": "rtmp://us-west.live.lightcast.com/202E1F/default" + "name": "North America 2", + "url": "rtmp://ingest-na2.live.lightcast.com/in" }, { - "name": "Europe / Amsterdam", - "url": "rtmp://europe.live.lightcast.com/202E1F/default" - }, - { - "name": "Europe / Frankfurt", - "url": "rtmp://europe-fra.live.lightcast.com/202E1F/default" - }, - { - "name": "Europe / Stockholm", - "url": "rtmp://europe-sto.live.lightcast.com/202E1F/default" - }, - { - "name": "Asia / Hong Kong", - "url": "rtmp://asia.live.lightcast.com/202E1F/default" - }, - { - "name": "Australia / Sydney", - "url": "rtmp://australia.live.lightcast.com/202E1F/default" + "name": "Europe", + "url": "rtmp://ingest-eu1.live.lightcast.com/in" } ], "recommended": { "keyint": 2, "max video bitrate": 6000, - "max audio bitrate": 160 + "max audio bitrate": 320, + "x264opts": "scenecut=0" }, "supported video codecs": [ "h264" From 710d99ef4d1ba687ed23fa1927e0ebf992c5bcce Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Thu, 8 Aug 2024 21:41:52 +0200 Subject: [PATCH 0430/1073] UI: Improve incremental compile times via explicit file includes When a source file contains an explicit include with a filename following the "moc_.cpp" pattern, then CMake's AUTOMOC generation tool will recognize the matching pair and generate the replacement header file and add the required include directory entries. For all files which do contain Q_OBJECT or similar declarations but do not have an explicit include directive, the global mocs_compilation.cpp file will still be generated (which groups all "missing" generated headers). The larger this global file is, the more expensive incremental compilation will be as this file (and all its contained generated headers) will be re-generated regardless of whether actual changes occurred. --- UI/absolute-slider.cpp | 2 +- UI/adv-audio-control.cpp | 2 +- UI/auth-base.cpp | 2 +- UI/auth-listener.cpp | 2 +- UI/auth-oauth.cpp | 2 +- UI/auth-restream.cpp | 2 +- UI/auth-twitch.cpp | 2 +- UI/auth-youtube.cpp | 2 +- UI/basic-controls.cpp | 2 +- UI/context-bar-controls.cpp | 2 +- UI/focus-list.cpp | 2 +- UI/horizontal-scroll-area.cpp | 2 +- UI/hotkey-edit.cpp | 2 +- UI/lineedit-autoresize.cpp | 2 +- UI/log-viewer.cpp | 2 +- UI/media-controls.cpp | 2 +- UI/menu-button.cpp | 2 +- UI/qt-display.cpp | 2 +- UI/remote-text.cpp | 2 +- UI/scene-tree.cpp | 2 +- UI/source-label.cpp | 2 +- UI/ui-validation.cpp | 2 +- UI/undo-stack-obs.cpp | 2 +- UI/update/mac-update.cpp | 2 +- UI/update/shared-update.cpp | 2 +- UI/url-push-button.cpp | 2 +- UI/visibility-item-widget.cpp | 2 +- UI/volume-control.cpp | 2 +- UI/window-basic-about.cpp | 2 +- UI/window-basic-auto-config.cpp | 2 +- UI/window-basic-interaction.cpp | 2 +- UI/window-basic-preview.cpp | 2 +- UI/window-basic-properties.cpp | 2 +- UI/window-basic-settings.cpp | 2 +- UI/window-basic-source-select.cpp | 2 +- UI/window-basic-stats.cpp | 2 +- UI/window-basic-status-bar.cpp | 2 +- UI/window-basic-vcam-config.cpp | 2 +- UI/window-dock-youtube-app.cpp | 2 +- UI/window-dock.cpp | 2 +- UI/window-extra-browsers.cpp | 2 +- UI/window-importer.cpp | 2 +- UI/window-log-reply.cpp | 2 +- UI/window-missing-files.cpp | 2 +- UI/window-namedialog.cpp | 2 +- UI/window-permissions.cpp | 2 +- UI/window-projector.cpp | 1 + UI/window-remux.cpp | 2 +- UI/window-youtube-actions.cpp | 2 +- UI/youtube-api-wrappers.cpp | 2 +- 50 files changed, 50 insertions(+), 49 deletions(-) diff --git a/UI/absolute-slider.cpp b/UI/absolute-slider.cpp index 7efde7c0f59b0e..c3fad5d34ee2fd 100644 --- a/UI/absolute-slider.cpp +++ b/UI/absolute-slider.cpp @@ -1,4 +1,4 @@ -#include "absolute-slider.hpp" +#include "moc_absolute-slider.cpp" AbsoluteSlider::AbsoluteSlider(QWidget *parent) : SliderIgnoreScroll(parent) { diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 1acdecce172bc0..2131419da3b75f 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -7,7 +7,7 @@ #include #include #include "obs-app.hpp" -#include "adv-audio-control.hpp" +#include "moc_adv-audio-control.cpp" #include "window-basic-main.hpp" #ifndef NSEC_PER_MSEC diff --git a/UI/auth-base.cpp b/UI/auth-base.cpp index b1a4315fd29dae..157164b4889c51 100644 --- a/UI/auth-base.cpp +++ b/UI/auth-base.cpp @@ -1,4 +1,4 @@ -#include "auth-base.hpp" +#include "moc_auth-base.cpp" #include "window-basic-main.hpp" #include diff --git a/UI/auth-listener.cpp b/UI/auth-listener.cpp index b6c7a9e0473004..efd637bf29f1f9 100644 --- a/UI/auth-listener.cpp +++ b/UI/auth-listener.cpp @@ -1,4 +1,4 @@ -#include +#include "moc_auth-listener.cpp" #include #include diff --git a/UI/auth-oauth.cpp b/UI/auth-oauth.cpp index bb2fd714ca8501..fd4e53845fb2bd 100644 --- a/UI/auth-oauth.cpp +++ b/UI/auth-oauth.cpp @@ -1,4 +1,4 @@ -#include "auth-oauth.hpp" +#include "moc_auth-oauth.cpp" #include #include diff --git a/UI/auth-restream.cpp b/UI/auth-restream.cpp index bedc10c7053db5..f091016f33a73b 100644 --- a/UI/auth-restream.cpp +++ b/UI/auth-restream.cpp @@ -1,4 +1,4 @@ -#include "auth-restream.hpp" +#include "moc_auth-restream.cpp" #include #include diff --git a/UI/auth-twitch.cpp b/UI/auth-twitch.cpp index 88e14e0befde39..cc391bc292d29b 100644 --- a/UI/auth-twitch.cpp +++ b/UI/auth-twitch.cpp @@ -1,4 +1,4 @@ -#include "auth-twitch.hpp" +#include "moc_auth-twitch.cpp" #include #include diff --git a/UI/auth-youtube.cpp b/UI/auth-youtube.cpp index d3902da5599e8c..ea9c6c7ce6dc4d 100644 --- a/UI/auth-youtube.cpp +++ b/UI/auth-youtube.cpp @@ -1,4 +1,4 @@ -#include "auth-youtube.hpp" +#include "moc_auth-youtube.cpp" #include #include diff --git a/UI/basic-controls.cpp b/UI/basic-controls.cpp index 0f216a3d59d868..075ae344f78099 100644 --- a/UI/basic-controls.cpp +++ b/UI/basic-controls.cpp @@ -1,4 +1,4 @@ -#include "basic-controls.hpp" +#include "moc_basic-controls.cpp" #include "window-basic-main.hpp" diff --git a/UI/context-bar-controls.cpp b/UI/context-bar-controls.cpp index cb4b2f8fc58812..3bd08d1ac6ee57 100644 --- a/UI/context-bar-controls.cpp +++ b/UI/context-bar-controls.cpp @@ -1,5 +1,5 @@ #include "window-basic-main.hpp" -#include "context-bar-controls.hpp" +#include "moc_context-bar-controls.cpp" #include "obs-app.hpp" #include diff --git a/UI/focus-list.cpp b/UI/focus-list.cpp index 9a6e97a879a984..bfa48f34910b04 100644 --- a/UI/focus-list.cpp +++ b/UI/focus-list.cpp @@ -1,4 +1,4 @@ -#include "focus-list.hpp" +#include "moc_focus-list.cpp" #include FocusList::FocusList(QWidget *parent) : QListWidget(parent) {} diff --git a/UI/horizontal-scroll-area.cpp b/UI/horizontal-scroll-area.cpp index 8f927fc5965edd..82ffc0d28e41fc 100644 --- a/UI/horizontal-scroll-area.cpp +++ b/UI/horizontal-scroll-area.cpp @@ -1,5 +1,5 @@ #include -#include "horizontal-scroll-area.hpp" +#include "moc_horizontal-scroll-area.cpp" void HScrollArea::resizeEvent(QResizeEvent *event) { diff --git a/UI/hotkey-edit.cpp b/UI/hotkey-edit.cpp index ebc40361b36b58..63377b781f0db0 100644 --- a/UI/hotkey-edit.cpp +++ b/UI/hotkey-edit.cpp @@ -16,7 +16,7 @@ ******************************************************************************/ #include "window-basic-settings.hpp" -#include "hotkey-edit.hpp" +#include "moc_hotkey-edit.cpp" #include #include diff --git a/UI/lineedit-autoresize.cpp b/UI/lineedit-autoresize.cpp index 9ada354ad486bd..166cc6309027b6 100644 --- a/UI/lineedit-autoresize.cpp +++ b/UI/lineedit-autoresize.cpp @@ -1,4 +1,4 @@ -#include "lineedit-autoresize.hpp" +#include "moc_lineedit-autoresize.cpp" LineEditAutoResize::LineEditAutoResize() : m_maxLength(32767) { diff --git a/UI/log-viewer.cpp b/UI/log-viewer.cpp index dedc04f492126f..322781da13d793 100644 --- a/UI/log-viewer.cpp +++ b/UI/log-viewer.cpp @@ -10,7 +10,7 @@ #include #include -#include "log-viewer.hpp" +#include "moc_log-viewer.cpp" OBSLogViewer::OBSLogViewer(QWidget *parent) : QDialog(parent), diff --git a/UI/media-controls.cpp b/UI/media-controls.cpp index dadc11d0df2b9b..fddc5cbf73cfa1 100644 --- a/UI/media-controls.cpp +++ b/UI/media-controls.cpp @@ -1,5 +1,5 @@ #include "window-basic-main.hpp" -#include "media-controls.hpp" +#include "moc_media-controls.cpp" #include "obs-app.hpp" #include #include diff --git a/UI/menu-button.cpp b/UI/menu-button.cpp index c2da16e8f4a5a5..8c22efcfa4ddcb 100644 --- a/UI/menu-button.cpp +++ b/UI/menu-button.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "menu-button.hpp" +#include "moc_menu-button.cpp" void MenuButton::keyPressEvent(QKeyEvent *event) { diff --git a/UI/qt-display.cpp b/UI/qt-display.cpp index 136fbc8f32b65f..5941e7b05b90bd 100644 --- a/UI/qt-display.cpp +++ b/UI/qt-display.cpp @@ -1,4 +1,4 @@ -#include "qt-display.hpp" +#include "moc_qt-display.cpp" #include "display-helpers.hpp" #include #include diff --git a/UI/remote-text.cpp b/UI/remote-text.cpp index a5c6dd013d2d0e..d07ec0e51965a0 100644 --- a/UI/remote-text.cpp +++ b/UI/remote-text.cpp @@ -18,7 +18,7 @@ #include #include #include "obs-app.hpp" -#include "remote-text.hpp" +#include "moc_remote-text.cpp" using namespace std; diff --git a/UI/scene-tree.cpp b/UI/scene-tree.cpp index f5386e1e67131b..a684d7ca180fa0 100644 --- a/UI/scene-tree.cpp +++ b/UI/scene-tree.cpp @@ -1,4 +1,4 @@ -#include "scene-tree.hpp" +#include "moc_scene-tree.cpp" #include #include diff --git a/UI/source-label.cpp b/UI/source-label.cpp index fc83a9878cd54a..1abc1fa9adace1 100644 --- a/UI/source-label.cpp +++ b/UI/source-label.cpp @@ -15,7 +15,7 @@ along with this program. If not, see . ******************************************************************************/ -#include "source-label.hpp" +#include "moc_source-label.cpp" void OBSSourceLabel::SourceRenamed(void *data, calldata_t *params) { diff --git a/UI/ui-validation.cpp b/UI/ui-validation.cpp index e45fd3f74ad410..50f1d7d5889749 100644 --- a/UI/ui-validation.cpp +++ b/UI/ui-validation.cpp @@ -1,4 +1,4 @@ -#include "ui-validation.hpp" +#include "moc_ui-validation.cpp" #include #include diff --git a/UI/undo-stack-obs.cpp b/UI/undo-stack-obs.cpp index 979ea17655e9fd..293252a8ba98be 100644 --- a/UI/undo-stack-obs.cpp +++ b/UI/undo-stack-obs.cpp @@ -1,4 +1,4 @@ -#include "undo-stack-obs.hpp" +#include "moc_undo-stack-obs.cpp" #include diff --git a/UI/update/mac-update.cpp b/UI/update/mac-update.cpp index c6b35b0e08277a..7ee0c073174d27 100644 --- a/UI/update/mac-update.cpp +++ b/UI/update/mac-update.cpp @@ -1,6 +1,6 @@ #include "update-helpers.hpp" #include "shared-update.hpp" -#include "mac-update.hpp" +#include "moc_mac-update.cpp" #include "obs-app.hpp" #include diff --git a/UI/update/shared-update.cpp b/UI/update/shared-update.cpp index ef1b0b07511c0a..844f857676ce12 100644 --- a/UI/update/shared-update.cpp +++ b/UI/update/shared-update.cpp @@ -1,4 +1,4 @@ -#include "shared-update.hpp" +#include "moc_shared-update.cpp" #include "crypto-helpers.hpp" #include "update-helpers.hpp" #include "obs-app.hpp" diff --git a/UI/url-push-button.cpp b/UI/url-push-button.cpp index 6a2b59847191c9..32c39620dccb43 100644 --- a/UI/url-push-button.cpp +++ b/UI/url-push-button.cpp @@ -1,4 +1,4 @@ -#include "url-push-button.hpp" +#include "moc_url-push-button.cpp" #include #include diff --git a/UI/visibility-item-widget.cpp b/UI/visibility-item-widget.cpp index c310007ed9936b..f229ce8cc29552 100644 --- a/UI/visibility-item-widget.cpp +++ b/UI/visibility-item-widget.cpp @@ -1,4 +1,4 @@ -#include "visibility-item-widget.hpp" +#include "moc_visibility-item-widget.cpp" #include "obs-app.hpp" #include "source-label.hpp" diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 04dde1bc230b75..db994b3c03285e 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -1,5 +1,5 @@ #include "window-basic-main.hpp" -#include "volume-control.hpp" +#include "moc_volume-control.cpp" #include "obs-app.hpp" #include "mute-checkbox.hpp" #include "absolute-slider.hpp" diff --git a/UI/window-basic-about.cpp b/UI/window-basic-about.cpp index d5bdc3eed178fb..9cd0d1d72d8022 100644 --- a/UI/window-basic-about.cpp +++ b/UI/window-basic-about.cpp @@ -1,4 +1,4 @@ -#include "window-basic-about.hpp" +#include "moc_window-basic-about.cpp" #include "window-basic-main.hpp" #include "remote-text.hpp" #include diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 555304daed7300..01c356702ca8e3 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -6,7 +6,7 @@ #include -#include "window-basic-auto-config.hpp" +#include "moc_window-basic-auto-config.cpp" #include "window-basic-main.hpp" #include "obs-app.hpp" #include "url-push-button.hpp" diff --git a/UI/window-basic-interaction.cpp b/UI/window-basic-interaction.cpp index 2507d7cc41fba3..716cbf24176fe0 100644 --- a/UI/window-basic-interaction.cpp +++ b/UI/window-basic-interaction.cpp @@ -16,7 +16,7 @@ ******************************************************************************/ #include "obs-app.hpp" -#include "window-basic-interaction.hpp" +#include "moc_window-basic-interaction.cpp" #include "window-basic-main.hpp" #include "display-helpers.hpp" diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 7d6ced49b97de8..9778169ad10eef 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "window-basic-preview.hpp" +#include "moc_window-basic-preview.cpp" #include "window-basic-main.hpp" #include "obs-app.hpp" #include "platform.hpp" diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index 722812265b170e..47703418a2c8c1 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -16,7 +16,7 @@ ******************************************************************************/ #include "obs-app.hpp" -#include "window-basic-properties.hpp" +#include "moc_window-basic-properties.cpp" #include "window-basic-main.hpp" #include "display-helpers.hpp" diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index e385342d722508..b85a53ad608c49 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -44,7 +44,7 @@ #include "platform.hpp" #include "properties-view.hpp" #include "window-basic-main.hpp" -#include "window-basic-settings.hpp" +#include "moc_window-basic-settings.cpp" #include "window-basic-main-outputs.hpp" #include "window-projector.hpp" diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index b494e5d282ef37..77ebdc7f0df2f6 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -18,7 +18,7 @@ #include #include #include "window-basic-main.hpp" -#include "window-basic-source-select.hpp" +#include "moc_window-basic-source-select.cpp" #include "obs-app.hpp" struct AddSourceData { diff --git a/UI/window-basic-stats.cpp b/UI/window-basic-stats.cpp index 95ea092ff2ad0a..ad391f891d9005 100644 --- a/UI/window-basic-stats.cpp +++ b/UI/window-basic-stats.cpp @@ -1,6 +1,6 @@ #include "obs-frontend-api/obs-frontend-api.h" -#include "window-basic-stats.hpp" +#include "moc_window-basic-stats.cpp" #include "window-basic-main.hpp" #include "platform.hpp" #include "obs-app.hpp" diff --git a/UI/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp index 9ec29190aa1f6a..04d1237d4626c1 100644 --- a/UI/window-basic-status-bar.cpp +++ b/UI/window-basic-status-bar.cpp @@ -2,7 +2,7 @@ #include #include "obs-app.hpp" #include "window-basic-main.hpp" -#include "window-basic-status-bar.hpp" +#include "moc_window-basic-status-bar.cpp" #include "window-basic-main-outputs.hpp" #include "qt-wrappers.hpp" #include "platform.hpp" diff --git a/UI/window-basic-vcam-config.cpp b/UI/window-basic-vcam-config.cpp index e8249967d282d0..86d7d4f5816501 100644 --- a/UI/window-basic-vcam-config.cpp +++ b/UI/window-basic-vcam-config.cpp @@ -1,4 +1,4 @@ -#include "window-basic-vcam-config.hpp" +#include "moc_window-basic-vcam-config.cpp" #include "window-basic-main.hpp" #include diff --git a/UI/window-dock-youtube-app.cpp b/UI/window-dock-youtube-app.cpp index 79835bbc6b686d..498a68a057597d 100644 --- a/UI/window-dock-youtube-app.cpp +++ b/UI/window-dock-youtube-app.cpp @@ -2,7 +2,7 @@ #include "window-basic-main.hpp" #include "youtube-api-wrappers.hpp" -#include "window-dock-youtube-app.hpp" +#include "moc_window-dock-youtube-app.cpp" #include "ui-config.h" #include "qt-wrappers.hpp" diff --git a/UI/window-dock.cpp b/UI/window-dock.cpp index a057add5b17ced..7e2aeff89e9e39 100644 --- a/UI/window-dock.cpp +++ b/UI/window-dock.cpp @@ -1,4 +1,4 @@ -#include "window-dock.hpp" +#include "moc_window-dock.cpp" #include "obs-app.hpp" #include "window-basic-main.hpp" diff --git a/UI/window-extra-browsers.cpp b/UI/window-extra-browsers.cpp index ecfe0766fe895f..e9b7265b62f278 100644 --- a/UI/window-extra-browsers.cpp +++ b/UI/window-extra-browsers.cpp @@ -1,4 +1,4 @@ -#include "window-extra-browsers.hpp" +#include "moc_window-extra-browsers.cpp" #include "window-dock-browser.hpp" #include "window-basic-main.hpp" diff --git a/UI/window-importer.cpp b/UI/window-importer.cpp index c5aa05c9f6e838..61508a2acd5dc6 100644 --- a/UI/window-importer.cpp +++ b/UI/window-importer.cpp @@ -15,7 +15,7 @@ along with this program. If not, see . ******************************************************************************/ -#include "window-importer.hpp" +#include "moc_window-importer.cpp" #include "obs-app.hpp" diff --git a/UI/window-log-reply.cpp b/UI/window-log-reply.cpp index 0972bb2287b128..0f71305863f962 100644 --- a/UI/window-log-reply.cpp +++ b/UI/window-log-reply.cpp @@ -19,7 +19,7 @@ #include #include #include -#include "window-log-reply.hpp" +#include "moc_window-log-reply.cpp" #include "obs-app.hpp" OBSLogReply::OBSLogReply(QWidget *parent, const QString &url, const bool crash) diff --git a/UI/window-missing-files.cpp b/UI/window-missing-files.cpp index 01eb2977eb2460..0fbaf9497c8ced 100644 --- a/UI/window-missing-files.cpp +++ b/UI/window-missing-files.cpp @@ -15,7 +15,7 @@ along with this program. If not, see . ******************************************************************************/ -#include "window-missing-files.hpp" +#include "moc_window-missing-files.cpp" #include "window-basic-main.hpp" #include "obs-app.hpp" diff --git a/UI/window-namedialog.cpp b/UI/window-namedialog.cpp index 86e54aefeed7f6..c2fd69e667613a 100644 --- a/UI/window-namedialog.cpp +++ b/UI/window-namedialog.cpp @@ -15,7 +15,7 @@ along with this program. If not, see . ******************************************************************************/ -#include "window-namedialog.hpp" +#include "moc_window-namedialog.cpp" #include "obs-app.hpp" #include diff --git a/UI/window-permissions.cpp b/UI/window-permissions.cpp index 8fb8766ea0a08d..eabea1b15a1861 100644 --- a/UI/window-permissions.cpp +++ b/UI/window-permissions.cpp @@ -16,7 +16,7 @@ ******************************************************************************/ #include -#include "window-permissions.hpp" +#include "moc_window-permissions.cpp" #include "obs-app.hpp" OBSPermissions::OBSPermissions(QWidget *parent, MacPermissionStatus capture, diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index d5322f54790829..cf0d57b972de3c 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -4,6 +4,7 @@ #include #include #include +#include "moc_window-projector.cpp" #include "obs-app.hpp" #include "window-basic-main.hpp" #include "display-helpers.hpp" diff --git a/UI/window-remux.cpp b/UI/window-remux.cpp index 410ab83d9090d0..5872c1ad2a75c5 100644 --- a/UI/window-remux.cpp +++ b/UI/window-remux.cpp @@ -15,7 +15,7 @@ along with this program. If not, see . ******************************************************************************/ -#include "window-remux.hpp" +#include "moc_window-remux.cpp" #include "obs-app.hpp" diff --git a/UI/window-youtube-actions.cpp b/UI/window-youtube-actions.cpp index a76ce498926fde..e2d1926dfbaaaa 100644 --- a/UI/window-youtube-actions.cpp +++ b/UI/window-youtube-actions.cpp @@ -1,5 +1,5 @@ #include "window-basic-main.hpp" -#include "window-youtube-actions.hpp" +#include "moc_window-youtube-actions.cpp" #include "obs-app.hpp" #include "youtube-api-wrappers.hpp" diff --git a/UI/youtube-api-wrappers.cpp b/UI/youtube-api-wrappers.cpp index 83e6a48996b265..346dabf32ae8fa 100644 --- a/UI/youtube-api-wrappers.cpp +++ b/UI/youtube-api-wrappers.cpp @@ -1,4 +1,4 @@ -#include "youtube-api-wrappers.hpp" +#include "moc_youtube-api-wrappers.cpp" #include #include From 5eb04cdf9dc29a8ee9238da3a2e46dbfacacf9ec Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Thu, 8 Aug 2024 21:42:30 +0200 Subject: [PATCH 0431/1073] shared: Improve incremental compile times via explicit file includes When a source file contains an explicit include with a filename following the "moc_.cpp" pattern, then CMake's AUTOMOC generation tool will recognize the matching pair and generate the replacement header file and add the required include directory entries. For all files which do contain Q_OBJECT or similar declarations but do not have an explicit include directive, the global mocs_compilation.cpp file will still be generated (which groups all "missing" generated headers). The larger this global file is, the more expensive incremental compilation will be as this file (and all its contained generated headers) will be re-generated regardless of whether actual changes occurred. --- shared/properties-view/double-slider.cpp | 2 +- shared/properties-view/properties-view.cpp | 2 +- shared/properties-view/spinbox-ignorewheel.cpp | 2 +- shared/qt/plain-text-edit/plain-text-edit.cpp | 2 +- shared/qt/slider-ignorewheel/slider-ignorewheel.cpp | 2 +- shared/qt/vertical-scroll-area/vertical-scroll-area.cpp | 2 +- shared/qt/wrappers/qt-wrappers.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shared/properties-view/double-slider.cpp b/shared/properties-view/double-slider.cpp index 993dfb95aa60bd..8996de12c09632 100644 --- a/shared/properties-view/double-slider.cpp +++ b/shared/properties-view/double-slider.cpp @@ -1,4 +1,4 @@ -#include "double-slider.hpp" +#include "moc_double-slider.cpp" #include diff --git a/shared/properties-view/properties-view.cpp b/shared/properties-view/properties-view.cpp index c71cbaab801db2..3f991aaafca235 100644 --- a/shared/properties-view/properties-view.cpp +++ b/shared/properties-view/properties-view.cpp @@ -27,7 +27,7 @@ #include #include "double-slider.hpp" #include "spinbox-ignorewheel.hpp" -#include "properties-view.hpp" +#include "moc_properties-view.cpp" #include "properties-view.moc.hpp" #include diff --git a/shared/properties-view/spinbox-ignorewheel.cpp b/shared/properties-view/spinbox-ignorewheel.cpp index 0452b93377ce52..aa599d836bbaba 100644 --- a/shared/properties-view/spinbox-ignorewheel.cpp +++ b/shared/properties-view/spinbox-ignorewheel.cpp @@ -1,4 +1,4 @@ -#include "spinbox-ignorewheel.hpp" +#include "moc_spinbox-ignorewheel.cpp" SpinBoxIgnoreScroll::SpinBoxIgnoreScroll(QWidget *parent) : QSpinBox(parent) { diff --git a/shared/qt/plain-text-edit/plain-text-edit.cpp b/shared/qt/plain-text-edit/plain-text-edit.cpp index 89e9ba8a16b8cf..b2a600e30c173a 100644 --- a/shared/qt/plain-text-edit/plain-text-edit.cpp +++ b/shared/qt/plain-text-edit/plain-text-edit.cpp @@ -1,4 +1,4 @@ -#include +#include "moc_plain-text-edit.cpp" #include OBSPlainTextEdit::OBSPlainTextEdit(QWidget *parent, bool monospace) diff --git a/shared/qt/slider-ignorewheel/slider-ignorewheel.cpp b/shared/qt/slider-ignorewheel/slider-ignorewheel.cpp index 69f595405657b0..36d602a3fd7f12 100644 --- a/shared/qt/slider-ignorewheel/slider-ignorewheel.cpp +++ b/shared/qt/slider-ignorewheel/slider-ignorewheel.cpp @@ -1,4 +1,4 @@ -#include "slider-ignorewheel.hpp" +#include "moc_slider-ignorewheel.cpp" SliderIgnoreScroll::SliderIgnoreScroll(QWidget *parent) : QSlider(parent) { diff --git a/shared/qt/vertical-scroll-area/vertical-scroll-area.cpp b/shared/qt/vertical-scroll-area/vertical-scroll-area.cpp index 684c9c1be78d55..ccfdc8f44ed921 100644 --- a/shared/qt/vertical-scroll-area/vertical-scroll-area.cpp +++ b/shared/qt/vertical-scroll-area/vertical-scroll-area.cpp @@ -1,5 +1,5 @@ #include -#include "vertical-scroll-area.hpp" +#include "moc_vertical-scroll-area.cpp" void VScrollArea::resizeEvent(QResizeEvent *event) { diff --git a/shared/qt/wrappers/qt-wrappers.cpp b/shared/qt/wrappers/qt-wrappers.cpp index 7e016b0d784d7c..f7cc449a1d5a7c 100644 --- a/shared/qt/wrappers/qt-wrappers.cpp +++ b/shared/qt/wrappers/qt-wrappers.cpp @@ -15,7 +15,7 @@ along with this program. If not, see . ******************************************************************************/ -#include "qt-wrappers.hpp" +#include "moc_qt-wrappers.cpp" #include #include From 9870cfe77b4bbb5d5e8c487d7b1ae79397621415 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 22 Aug 2024 16:51:02 -0400 Subject: [PATCH 0432/1073] rtmp-services: Update package.json version --- plugins/rtmp-services/data/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 04d61060d8b8ec..6adb9e3dda946b 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,7 +1,7 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 258, + "version": 259, "files": [ { "name": "services.json", From f96cf354e6c290b80318dd44f911b56201d0efb4 Mon Sep 17 00:00:00 2001 From: Len Date: Tue, 6 Aug 2024 10:57:12 +0900 Subject: [PATCH 0433/1073] rtmp-services: Remove Hakuna Live RTMP service --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 23 ----------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 6adb9e3dda946b..a179122bf1fb0d 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 259, + "version": 260, "files": [ { "name": "services.json", - "version": 259 + "version": 260 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index c9483655e3c4e7..49792bebf65e20 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2752,29 +2752,6 @@ "max audio bitrate": 320 } }, - { - "name": "Hakuna Live - RTMP", - "servers": [ - { - "name": "Republic of Korea / Japan", - "url": "rtmps://media-proxy.prod.an1.media.hpcnt.com/hakuna" - } - ], - "protocol": "RTMP", - "supported video codecs": [ - "h264" - ], - "recommended": { - "keyint": 2, - "supported resolutions": [ - "1920x1080", - "1280x720" - ], - "max video bitrate": 6000, - "max audio bitrate": 320, - "bframes": 0 - } - }, { "name": "sheeta", "common": false, From b0e1eced04cf674580f32e3a5452e004b3034da8 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 03:09:36 +0200 Subject: [PATCH 0434/1073] UI: Remove legacy nlohmann workaround (Ubuntu 22.04) --- UI/models/multitrack-video.hpp | 41 ---------------------------------- UI/update/models/whatsnew.hpp | 23 ------------------- 2 files changed, 64 deletions(-) diff --git a/UI/models/multitrack-video.hpp b/UI/models/multitrack-video.hpp index 30b2218da08284..535da657951951 100644 --- a/UI/models/multitrack-video.hpp +++ b/UI/models/multitrack-video.hpp @@ -23,47 +23,6 @@ #include -/* From whatsnew.hpp */ -#ifndef NLOHMANN_DEFINE_TYPE_INTRUSIVE -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json &nlohmann_json_j, \ - const Type &nlohmann_json_t) \ - { \ - NLOHMANN_JSON_EXPAND( \ - NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \ - } \ - friend void from_json(const nlohmann::json &nlohmann_json_j, \ - Type &nlohmann_json_t) \ - { \ - NLOHMANN_JSON_EXPAND( \ - NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) \ - } -#endif - -#ifndef NLOHMANN_JSON_FROM_WITH_DEFAULT -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) \ - nlohmann_json_t.v1 = \ - nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); -#endif - -#ifndef NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json &nlohmann_json_j, \ - const Type &nlohmann_json_t) \ - { \ - NLOHMANN_JSON_EXPAND( \ - NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \ - } \ - friend void from_json(const nlohmann::json &nlohmann_json_j, \ - Type &nlohmann_json_t) \ - { \ - Type nlohmann_json_default_obj; \ - NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE( \ - NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) \ - } - -#endif - /* * Support for (de-)serialising std::optional * From https://github.com/nlohmann/json/issues/1749#issuecomment-1731266676 diff --git a/UI/update/models/whatsnew.hpp b/UI/update/models/whatsnew.hpp index 59722ebf457035..399bfa78477f29 100644 --- a/UI/update/models/whatsnew.hpp +++ b/UI/update/models/whatsnew.hpp @@ -21,29 +21,6 @@ #include -/* Ubuntu 22.04 be damned. */ -#ifndef NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) \ - nlohmann_json_t.v1 = \ - nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json &nlohmann_json_j, \ - const Type &nlohmann_json_t) \ - { \ - NLOHMANN_JSON_EXPAND( \ - NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) \ - } \ - friend void from_json(const nlohmann::json &nlohmann_json_j, \ - Type &nlohmann_json_t) \ - { \ - Type nlohmann_json_default_obj; \ - NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE( \ - NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) \ - } - -#endif - /* * Support for (de-)serialising std::optional * Adapted from https://github.com/nlohmann/json/issues/1749#issuecomment-1555093802 From 834c15e717cc921177f96c9c0fe3df291d465b90 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 03:10:29 +0200 Subject: [PATCH 0435/1073] UI: Remove legacy Qt workarounds (Ubuntu 22.04) --- UI/obs-app.cpp | 14 -------------- UI/window-basic-main.cpp | 9 +-------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 0db4a9293b92cd..aea7b0270dc054 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1995,20 +1995,6 @@ static int run_program(fstream &logFile, int argc, char *argv[]) const char *platform_theme = getenv("QT_QPA_PLATFORMTHEME"); if (platform_theme && strcmp(platform_theme, "qt5ct") == 0) unsetenv("QT_QPA_PLATFORMTHEME"); - -#if defined(ENABLE_WAYLAND) && defined(USE_XDG) && \ - QT_VERSION < QT_VERSION_CHECK(6, 3, 0) - /* NOTE: Qt doesn't use the Wayland platform on GNOME, so we have to - * force it using the QT_QPA_PLATFORM env var. It's still possible to - * use other QPA platforms using this env var, or the -platform command - * line option. Remove after Qt 6.3 is everywhere. */ - - const char *desktop = getenv("XDG_CURRENT_DESKTOP"); - const char *session_type = getenv("XDG_SESSION_TYPE"); - if (session_type && desktop && strstr(desktop, "GNOME") != nullptr && - strcmp(session_type, "wayland") == 0) - setenv("QT_QPA_PLATFORM", "wayland", false); -#endif #endif /* NOTE: This disables an optimisation in Qt that attempts to determine if diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 4d22e31ef66164..623c4021bad2e3 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3787,14 +3787,7 @@ void OBSBasic::HideAudioControl() if (!SourceMixerHidden(source)) { SetSourceMixerHidden(source, true); - - /* Due to a bug with QT 6.2.4, the version that's in the Ubuntu - * 22.04 ppa, hiding the audio mixer causes a crash, so defer to - * the next event loop to hide it. Doesn't seem to be a problem - * with newer versions of QT. */ - QMetaObject::invokeMethod(this, "DeactivateAudioSource", - Qt::QueuedConnection, - Q_ARG(OBSSource, OBSSource(source))); + DeactivateAudioSource(source); } } From 154f0b549d3a482ff0df964c5de81493df4473f1 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 03:10:49 +0200 Subject: [PATCH 0436/1073] CI: Update all workflows to Ubuntu 24.04 --- .github/workflows/build-project.yaml | 6 +++--- .github/workflows/check-format.yaml | 8 ++++---- .github/workflows/dispatch.yaml | 8 ++++---- .github/workflows/pr-pull.yaml | 6 +++--- .github/workflows/publish.yaml | 4 ++-- .github/workflows/push.yaml | 18 ++++++------------ .github/workflows/scheduled.yaml | 4 ++-- 7 files changed, 24 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build-project.yaml b/.github/workflows/build-project.yaml index 2ccb337e0d91f6..b01d28f22531a2 100644 --- a/.github/workflows/build-project.yaml +++ b/.github/workflows/build-project.yaml @@ -4,7 +4,7 @@ on: jobs: check-event: name: Event Data 🔎 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 defaults: run: shell: bash @@ -172,7 +172,7 @@ jobs: name: Ubuntu 🐧 strategy: matrix: - os: [ubuntu-22.04, ubuntu-24.04] + os: [ubuntu-24.04] runs-on: ${{ matrix.os }} needs: check-event defaults: @@ -241,7 +241,7 @@ jobs: flatpak-build: name: Flatpak 📦 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: check-event defaults: run: diff --git a/.github/workflows/check-format.yaml b/.github/workflows/check-format.yaml index 4ace7677e7c060..14da00f932cf1a 100644 --- a/.github/workflows/check-format.yaml +++ b/.github/workflows/check-format.yaml @@ -3,7 +3,7 @@ on: workflow_call: jobs: clang-format: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -27,7 +27,7 @@ jobs: failCondition: error gersemi: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -39,7 +39,7 @@ jobs: failCondition: error flatpak-validator: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -51,7 +51,7 @@ jobs: failCondition: error qt-xml-validator: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/dispatch.yaml b/.github/workflows/dispatch.yaml index cf6bc78d08ebf1..f2552dadf56f60 100644 --- a/.github/workflows/dispatch.yaml +++ b/.github/workflows/dispatch.yaml @@ -59,7 +59,7 @@ jobs: download-language-files: name: Download Language Files 🌐 if: github.repository_owner == 'obsproject' && inputs.job == 'translations' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }} steps: @@ -91,7 +91,7 @@ jobs: update-documentation: name: Update Documentation 📖 if: github.repository_owner == 'obsproject' && inputs.job == 'documentation' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/generate-docs @@ -99,7 +99,7 @@ jobs: update-documentation-cloudflare: name: Update Documentation for Cloudflare ☁️ if: github.repository_owner == 'obsproject' && inputs.job == 'documentation' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/generate-docs @@ -109,7 +109,7 @@ jobs: deploy-documentation: name: Deploy Documentation to Cloudflare ☁️ if: github.repository_owner == 'obsproject' && inputs.job == 'documentation' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: update-documentation-cloudflare defaults: run: diff --git a/.github/workflows/pr-pull.yaml b/.github/workflows/pr-pull.yaml index 854d24cdfdb8db..c44ad9cf890c9f 100644 --- a/.github/workflows/pr-pull.yaml +++ b/.github/workflows/pr-pull.yaml @@ -29,7 +29,7 @@ jobs: compatibility-validation: name: Validate Compatibility 🕵️ if: github.base_ref == 'master' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: checks: write steps: @@ -51,7 +51,7 @@ jobs: services-validation: name: Validate Services 🕵️ if: github.base_ref == 'master' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: checks: write steps: @@ -77,7 +77,7 @@ jobs: update-documentation: name: Update Documentation 📖 if: github.repository_owner == 'obsproject' && github.base_ref == 'master' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 6efe0abb9a011b..42f91d39361c54 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -16,7 +16,7 @@ jobs: check-tag: name: Check Release Tag if: github.repository_owner == 'obsproject' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: validTag: ${{ steps.check.outputs.validTag }} flatpakMatrix: ${{ steps.check.outputs.flatpakMatrix }} @@ -47,7 +47,7 @@ jobs: name: Flathub 📦 needs: check-tag if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 defaults: run: shell: bash diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 9652cec71fa683..1a7c04213dc433 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -29,7 +29,7 @@ jobs: compatibility-validation: name: Validate Compatibility 🕵️ if: github.ref_name == 'master' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: checks: write steps: @@ -53,7 +53,7 @@ jobs: services-validation: name: Validate Services 🕵️ if: github.ref_name == 'master' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: checks: write steps: @@ -79,7 +79,7 @@ jobs: update-documentation: name: Update Documentation 📖 if: github.repository_owner == 'obsproject' && (github.ref_name == 'master' || github.ref_type == 'tag') - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -101,7 +101,7 @@ jobs: deploy-documentation: name: Deploy Documentation to Cloudflare ☁️ if: github.repository_owner == 'obsproject' && github.ref_type == 'tag' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: update-documentation defaults: run: @@ -195,7 +195,7 @@ jobs: path: ${{ github.workspace }}/output merge-appcasts: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: create-appcast steps: - name: Merge Appcasts @@ -219,7 +219,7 @@ jobs: create-release: name: Create Release 🛫 if: github.ref_type == 'tag' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [build-project, sign-windows-build] defaults: run: @@ -263,8 +263,6 @@ jobs: macos_arm64_dsym_artifact_name="obs-studio-macos-arm64-${commit_hash}-dSYMs" macos_intel_artifact_name="obs-studio-macos-x86_64-${commit_hash}" macos_intel_dsym_artifact_name="obs-studio-macos-x86_64-${commit_hash}-dSYMs" - ubuntu_2204_x86_64_artifact_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}" - ubuntu_2204_x86_64_debug_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}-dbgsym" ubuntu_2404_x86_64_artifact_name="obs-studio-ubuntu-24.04-x86_64-${commit_hash}" ubuntu_2404_x86_64_debug_name="obs-studio-ubuntu-24.04-x86_64-${commit_hash}-dbgsym" ubuntu_2404_sources_name="obs-studio-ubuntu-24.04-sources-${commit_hash}" @@ -281,10 +279,6 @@ jobs: "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel.dmg mv -v "${macos_intel_dsym_artifact_name}/"obs-studio-*-macos-intel-dSYMs.tar.xz \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel-dSYMs.tar.xz - mv -v "${ubuntu_2204_x86_64_artifact_name}/"obs-studio-*-x86_64-ubuntu-gnu.deb \ - "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-22.04-x86_64.deb - mv -v "${ubuntu_2204_x86_64_debug_name}/"obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb \ - "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-22.04-x86_64-dbsym.ddeb mv -v "${ubuntu_2404_x86_64_artifact_name}/"obs-studio-*-x86_64-ubuntu-gnu.deb \ "${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64.deb mv -v "${ubuntu_2404_x86_64_debug_name}/"obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb \ diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml index 68ecac274a7664..d961fa92861e95 100644 --- a/.github/workflows/scheduled.yaml +++ b/.github/workflows/scheduled.yaml @@ -35,7 +35,7 @@ jobs: cache-cleanup: name: Cache Cleanup 🧹 - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: actions: write steps: @@ -99,7 +99,7 @@ jobs: upload-language-files: name: Upload Language Files 🌐 if: github.repository_owner == 'obsproject' && github.ref_name == 'master' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: From 419c1e4bc16fa93eebb152f7f6a51ff02bd9e78f Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 03:21:13 +0200 Subject: [PATCH 0437/1073] CI: Update Ubuntu setup/build scripts with 24.04 baseline - Removes check for 22.10 as 24.04 is now the minimum - Moves librist and libsrt to default dependencies - Adds nv-codec-headers for native NVENC --- .github/scripts/.build.zsh | 1 - .github/scripts/utils.zsh/check_ubuntu | 7 ------- .github/scripts/utils.zsh/setup_ubuntu | 5 ++--- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/scripts/.build.zsh b/.github/scripts/.build.zsh index 4746caa1938f1e..91c93d8db8bbd4 100755 --- a/.github/scripts/.build.zsh +++ b/.github/scripts/.build.zsh @@ -216,7 +216,6 @@ build() { -DCEF_ROOT_DIR:PATH="${project_root}/.deps/cef_binary_${CEF_VERSION}_${target//ubuntu-/linux_}" ) - if (( ! UBUNTU_2210_OR_LATER )) cmake_args+=(-DENABLE_NEW_MPEGTS_OUTPUT:BOOL=OFF) if [[ ${target##*-} == aarch64 ]] cmake-args+=(-DENABLE_QSV11:BOOL=OFF) cmake_build_args+=(build_${target%%-*} --config ${config} --parallel) diff --git a/.github/scripts/utils.zsh/check_ubuntu b/.github/scripts/utils.zsh/check_ubuntu index bb6509cf7b88f2..ddb9ee5de71aed 100644 --- a/.github/scripts/utils.zsh/check_ubuntu +++ b/.github/scripts/utils.zsh/check_ubuntu @@ -3,8 +3,6 @@ autoload -Uz log_debug log_group log_group 'Check Ubuntu build requirements' log_debug 'Checking Ubuntu distribution name and version...' -# Check for Ubuntu version 22.10 or later, which have srt and librist available via apt-get -typeset -g -i UBUNTU_2210_OR_LATER=0 if [[ -f /etc/os-release ]] { local dist_name local dist_version @@ -15,11 +13,6 @@ if [[ -f /etc/os-release ]] { log_group return 2 } - - autoload -Uz is-at-least - if is-at-least 22.10 ${dist_version}; then - typeset -g -i UBUNTU_2210_OR_LATER=1 - fi } else { log_error "Unable to determine local Linux distribution, but Ubuntu is required. Aborting" log_group diff --git a/.github/scripts/utils.zsh/setup_ubuntu b/.github/scripts/utils.zsh/setup_ubuntu index 12ce107b412866..0809db17e6d2f0 100644 --- a/.github/scripts/utils.zsh/setup_ubuntu +++ b/.github/scripts/utils.zsh/setup_ubuntu @@ -101,12 +101,11 @@ sudo apt-get install -y --no-install-recommends \ libasound2-dev libfdk-aac-dev libfontconfig-dev libfreetype6-dev libjack-jackd2-dev \ libpulse-dev libsndio-dev libspeexdsp-dev libudev-dev libv4l-dev libva-dev libvlc-dev \ libpci-dev libdrm-dev \ - nlohmann-json3-dev libwebsocketpp-dev libasio-dev libqrcodegencpp-dev + nlohmann-json3-dev libwebsocketpp-dev libasio-dev libqrcodegencpp-dev \ + libffmpeg-nvenc-dev librist-dev libsrt-openssl-dev if [[ ${target##*-} == x86_64 ]] sudo apt-get install -y --no-install-recommends libvpl-dev libvpl2 -if (( UBUNTU_2210_OR_LATER )) sudo apt-get install -y --no-install-recommends librist-dev libsrt-openssl-dev - local -a _qt_packages=() if (( QT_VERSION == 6 )) { From 2613b6a6f455a84b5c7033e914715268e0654f5b Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 03:11:01 +0200 Subject: [PATCH 0438/1073] cmake: Do not disable native NVENC in Ubuntu preset --- CMakePresets.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index a213549a0093ff..441e66f89f0054 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -73,8 +73,6 @@ "CMAKE_INSTALL_LIBDIR": "lib/CMAKE_SYSTEM_PROCESSOR-linux-gnu", "OBS_CMAKE_VERSION": {"type": "STRING", "value": "3.0.0"}, "ENABLE_AJA": false, - "ENABLE_NVENC": false, - "ENABLE_FFMPEG_NVENC": true, "ENABLE_VLC": true, "ENABLE_WAYLAND": true, "ENABLE_WEBRTC": false From f07004c3b73027e44d6495e8ccdb0026d81c899e Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:07:26 +0200 Subject: [PATCH 0439/1073] libobs: Remove compatibility with FFmpeg < 6.1 --- libobs/CMakeLists.txt | 8 +--- libobs/media-io/media-remux.c | 56 +-------------------------- libobs/media-io/video-scaler-ffmpeg.c | 4 -- libobs/obs-ffmpeg-compat.h | 5 --- 4 files changed, 2 insertions(+), 71 deletions(-) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 4226e7fe88e295..5142078e9815b9 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -6,13 +6,7 @@ include(cmake/obs-version.cmake) find_package(Threads REQUIRED) -if(OS_WINDOWS OR OS_MACOS) - set(ffmpeg_version 6) -else() - set(ffmpeg_version 4.4) -endif() - -find_package(FFmpeg ${ffmpeg_version} REQUIRED avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec) +find_package(FFmpeg 6.1 REQUIRED avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec) find_package(ZLIB REQUIRED) find_package(Uthash REQUIRED) diff --git a/libobs/media-io/media-remux.c b/libobs/media-io/media-remux.c index 1ab843e75715b1..06bd88c0d04ab2 100644 --- a/libobs/media-io/media-remux.c +++ b/libobs/media-io/media-remux.c @@ -22,16 +22,10 @@ #include "../util/platform.h" #include -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(59, 20, 100) #include -#endif #include #include -#ifndef FF_API_BUFFER_SIZE_T -#define FF_API_BUFFER_SIZE_T (LIBAVUTIL_VERSION_MAJOR < 57) -#endif - struct media_remux_job { int64_t in_size; AVFormatContext *ifmt_ctx, *ofmt_ctx; @@ -91,43 +85,6 @@ static inline bool init_output(media_remux_job_t job, const char *out_filename) return false; } -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) -#if FF_API_BUFFER_SIZE_T - int content_size; -#else - size_t content_size; -#endif - const uint8_t *const content_src = av_stream_get_side_data( - in_stream, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - &content_size); - if (content_src) { - uint8_t *const content_dst = av_stream_new_side_data( - out_stream, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - content_size); - if (content_dst) - memcpy(content_dst, content_src, content_size); - } - -#if FF_API_BUFFER_SIZE_T - int mastering_size; -#else - size_t mastering_size; -#endif - const uint8_t *const mastering_src = av_stream_get_side_data( - in_stream, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - &mastering_size); - if (mastering_src) { - uint8_t *const mastering_dst = av_stream_new_side_data( - out_stream, - AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - mastering_size); - if (mastering_dst) { - memcpy(mastering_dst, mastering_src, - mastering_size); - } - } -#endif - ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar); @@ -154,17 +111,7 @@ static inline bool init_output(media_remux_job_t job, const char *out_filename) } if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - out_stream->codecpar->channel_layout = - av_get_default_channel_layout( - in_stream->codecpar->channels); - /* The avutil default channel layout for 5 channels is - * 5.0, which OBS does not support. Manually set 5 - * channels to 4.1. */ - if (in_stream->codecpar->channels == 5) - out_stream->codecpar->channel_layout = - av_get_channel_layout("4.1"); -#else + av_channel_layout_default( &out_stream->codecpar->ch_layout, in_stream->codecpar->ch_layout.nb_channels); @@ -175,7 +122,6 @@ static inline bool init_output(media_remux_job_t job, const char *out_filename) out_stream->codecpar->ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_4POINT1; -#endif } } diff --git a/libobs/media-io/video-scaler-ffmpeg.c b/libobs/media-io/video-scaler-ffmpeg.c index 343b3d7e2511ba..f88f17db4cf386 100644 --- a/libobs/media-io/video-scaler-ffmpeg.c +++ b/libobs/media-io/video-scaler-ffmpeg.c @@ -68,20 +68,16 @@ get_ffmpeg_video_format(enum video_format format) return AV_PIX_FMT_YUVA422P; case VIDEO_FORMAT_YUVA: return AV_PIX_FMT_YUVA444P; -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 31, 100) case VIDEO_FORMAT_YA2L: return AV_PIX_FMT_YUVA444P12LE; -#endif case VIDEO_FORMAT_I010: return AV_PIX_FMT_YUV420P10LE; case VIDEO_FORMAT_P010: return AV_PIX_FMT_P010LE; -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100) case VIDEO_FORMAT_P216: return AV_PIX_FMT_P216LE; case VIDEO_FORMAT_P416: return AV_PIX_FMT_P416LE; -#endif case VIDEO_FORMAT_NONE: case VIDEO_FORMAT_AYUV: default: diff --git a/libobs/obs-ffmpeg-compat.h b/libobs/obs-ffmpeg-compat.h index fdd1b08e9ffc10..2259680440b720 100644 --- a/libobs/obs-ffmpeg-compat.h +++ b/libobs/obs-ffmpeg-compat.h @@ -12,9 +12,4 @@ (LIBAVCODEC_VERSION_MICRO >= 100 && \ LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e))) -#if LIBAVCODEC_VERSION_MAJOR < 60 -#define CODEC_CAP_TRUNC AV_CODEC_CAP_TRUNCATED -#define CODEC_FLAG_TRUNC AV_CODEC_FLAG_TRUNCATED -#endif - #define INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE From 3ebe071c7bd5a29ec6ea2f5820616963155c2bb9 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:07:33 +0200 Subject: [PATCH 0440/1073] UI: Remove compatibility with FFmpeg < 6.1 --- UI/ffmpeg-utils.cpp | 18 ++---------------- UI/window-basic-main.cpp | 15 --------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/UI/ffmpeg-utils.cpp b/UI/ffmpeg-utils.cpp index b8f36cd00ce9f1..a55e2e9a8b8db2 100644 --- a/UI/ffmpeg-utils.cpp +++ b/UI/ffmpeg-utils.cpp @@ -100,12 +100,8 @@ bool FFCodecAndFormatCompatible(const char *codec, const char *format) if (!codec || !format) return false; -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - AVOutputFormat *output_format; -#else - const AVOutputFormat *output_format; -#endif - output_format = av_guess_format(format, nullptr, nullptr); + const AVOutputFormat *output_format = + av_guess_format(format, nullptr, nullptr); if (!output_format) return false; @@ -114,13 +110,8 @@ bool FFCodecAndFormatCompatible(const char *codec, const char *format) if (!codec_desc) return false; -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 0, 100) - return avformat_query_codec(output_format, codec_desc->id, - FF_COMPLIANCE_EXPERIMENTAL) == 1; -#else return avformat_query_codec(output_format, codec_desc->id, FF_COMPLIANCE_NORMAL) == 1; -#endif } static const unordered_set builtin_codecs = { @@ -163,12 +154,9 @@ static const unordered_map> codec_compat = { "opus", "alac", "flac", -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100) - // PCM in MP4 is only supported in FFmpeg > 6.0 "pcm_s16le", "pcm_s24le", "pcm_f32le", -#endif }}, {"fragmented_mp4", { @@ -179,11 +167,9 @@ static const unordered_map> codec_compat = { "opus", "alac", "flac", -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(60, 5, 100) "pcm_s16le", "pcm_s24le", "pcm_f32le", -#endif }}, // Not part of FFmpeg, see obs-outputs module {"hybrid_mp4", diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 623c4021bad2e3..64964f0b8b5fe5 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -7698,26 +7698,11 @@ void OBSBasic::AutoRemux(QString input, bool no_show) const char *format = config_get_string( config, isSimpleMode ? "SimpleOutput" : "AdvOut", "RecFormat2"); -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 5, 100) - const obs_encoder_t *audioEncoder = - obs_output_get_audio_encoder(outputHandler->fileOutput, 0); - const char *aCodecName = obs_encoder_get_codec(audioEncoder); - bool audio_is_pcm = strncmp(aCodecName, "pcm", 3) == 0; - - /* FFmpeg <= 6.0 cannot remux AV1+PCM into any supported format. */ - if (audio_is_pcm && strcmp(vCodecName, "av1") == 0) - return; -#endif - /* Retain original container for fMP4/fMOV */ if (strncmp(format, "fragmented", 10) == 0) { output += "remuxed." + suffix; } else if (strcmp(vCodecName, "prores") == 0) { output += "mov"; -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 5, 100) - } else if (audio_is_pcm) { - output += "mov"; -#endif } else { output += "mp4"; } From baa1e1dc358529d7a2b0bd6d7d79b08ecad0e285 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:11:12 +0200 Subject: [PATCH 0441/1073] obs-ffmpeg: Remove compatibility with FFmpeg < 6.1 --- plugins/obs-ffmpeg/cmake/dependencies.cmake | 8 +- plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c | 48 ----------- .../obs-ffmpeg/obs-ffmpeg-audio-encoders.c | 59 ------------- plugins/obs-ffmpeg/obs-ffmpeg-av1.c | 17 ---- plugins/obs-ffmpeg/obs-ffmpeg-compat.h | 5 -- plugins/obs-ffmpeg/obs-ffmpeg-formats.h | 4 - plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c | 37 +------- plugins/obs-ffmpeg/obs-ffmpeg-output.c | 84 +------------------ 8 files changed, 6 insertions(+), 256 deletions(-) diff --git a/plugins/obs-ffmpeg/cmake/dependencies.cmake b/plugins/obs-ffmpeg/cmake/dependencies.cmake index f85ac9d3012d31..39641fadf2562a 100644 --- a/plugins/obs-ffmpeg/cmake/dependencies.cmake +++ b/plugins/obs-ffmpeg/cmake/dependencies.cmake @@ -1,12 +1,6 @@ -if(OS_WINDOWS OR OS_MACOS) - set(ffmpeg_version 6) -else() - set(ffmpeg_version 4.4) -endif() - find_package( FFmpeg - ${ffmpeg_version} + 6.1 REQUIRED avcodec avfilter avdevice avutil swscale avformat swresample ) diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index b4e9203bf99828..28632621f57893 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -482,13 +482,6 @@ static void create_video_stream(struct ffmpeg_mux *ffm) (AVRational){ffm->params.fps_den, ffm->params.fps_num}; ffm->video_stream->time_base = context->time_base; -#if LIBAVFORMAT_VERSION_MAJOR < 59 - // codec->time_base may still be used if LIBAVFORMAT_VERSION_MAJOR < 59 - PRAGMA_WARN_PUSH - PRAGMA_WARN_DEPRECATION - ffm->video_stream->codec->time_base = context->time_base; - PRAGMA_WARN_POP -#endif ffm->video_stream->avg_frame_rate = av_inv_q(context->time_base); if (ffm->output->oformat->flags & AVFMT_GLOBALHEADER) @@ -503,17 +496,11 @@ static void create_video_stream(struct ffmpeg_mux *ffm) av_content_light_metadata_alloc(&content_size); content->MaxCLL = max_luminance; content->MaxFALL = max_luminance; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - av_stream_add_side_data(ffm->video_stream, - AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - (uint8_t *)content, content_size); -#else av_packet_side_data_add( &ffm->video_stream->codecpar->coded_side_data, &ffm->video_stream->codecpar->nb_coded_side_data, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, (uint8_t *)content, content_size, 0); -#endif AVMasteringDisplayMetadata *const mastering = av_mastering_display_metadata_alloc(); @@ -529,18 +516,11 @@ static void create_video_stream(struct ffmpeg_mux *ffm) mastering->max_luminance = av_make_q(max_luminance, 1); mastering->has_primaries = 1; mastering->has_luminance = 1; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - av_stream_add_side_data(ffm->video_stream, - AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - (uint8_t *)mastering, - sizeof(*mastering)); -#else av_packet_side_data_add( &ffm->video_stream->codecpar->coded_side_data, &ffm->video_stream->codecpar->nb_coded_side_data, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, (uint8_t *)mastering, sizeof(*mastering), 0); -#endif } ffm->video_ctx = context; @@ -586,9 +566,6 @@ static void create_audio_stream(struct ffmpeg_mux *ffm, int idx) context->bit_rate = (int64_t)ffm->audio[idx].abitrate * 1000; channels = ffm->audio[idx].channels; -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 24, 100) - context->channels = channels; -#endif context->sample_rate = ffm->audio[idx].sample_rate; if (!(codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) context->frame_size = ffm->audio[idx].frame_size; @@ -596,17 +573,10 @@ static void create_audio_stream(struct ffmpeg_mux *ffm, int idx) context->time_base = stream->time_base; context->extradata = extradata; context->extradata_size = ffm->audio_header[idx].size; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - context->channel_layout = av_get_default_channel_layout(channels); - //avutil default channel layout for 5 channels is 5.0 ; fix for 4.1 - if (channels == 5) - context->channel_layout = av_get_channel_layout("4.1"); -#else av_channel_layout_default(&context->ch_layout, channels); //avutil default channel layout for 5 channels is 5.0 ; fix for 4.1 if (channels == 5) context->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1; -#endif if (ffm->output->oformat->flags & AVFMT_GLOBALHEADER) context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; @@ -937,11 +907,7 @@ static int ffmpeg_mux_write_av_buffer(void *opaque, uint8_t *buf, int buf_size) static inline int open_output_file(struct ffmpeg_mux *ffm) { -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - AVOutputFormat *format = ffm->output->oformat; -#else const AVOutputFormat *format = ffm->output->oformat; -#endif int ret; if ((format->flags & AVFMT_NOFILE) == 0) { @@ -1033,11 +999,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) static int ffmpeg_mux_init_context(struct ffmpeg_mux *ffm) { -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - AVOutputFormat *output_format; -#else const AVOutputFormat *output_format; -#endif int ret; bool is_http = false; is_http = (strncmp(ffm->params.file, HTTP_PROTO, @@ -1074,16 +1036,6 @@ static int ffmpeg_mux_init_context(struct ffmpeg_mux *ffm) return FFM_ERROR; } -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - ffm->output->oformat->video_codec = AV_CODEC_ID_NONE; - ffm->output->oformat->audio_codec = AV_CODEC_ID_NONE; -#endif - -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 0, 100) - /* Allow FLAC/OPUS in MP4 */ - ffm->output->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; -#endif - if (!init_streams(ffm)) { free_avformat(ffm); return FFM_ERROR; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c index 49ee82dae85421..9b0c7fd8114c1e 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c @@ -56,33 +56,6 @@ struct enc_encoder { int frame_size_bytes; }; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) -static inline uint64_t convert_speaker_layout(enum speaker_layout layout) -{ - switch (layout) { - case SPEAKERS_UNKNOWN: - return 0; - case SPEAKERS_MONO: - return AV_CH_LAYOUT_MONO; - case SPEAKERS_STEREO: - return AV_CH_LAYOUT_STEREO; - case SPEAKERS_2POINT1: - return AV_CH_LAYOUT_SURROUND; - case SPEAKERS_4POINT0: - return AV_CH_LAYOUT_4POINT0; - case SPEAKERS_4POINT1: - return AV_CH_LAYOUT_4POINT1; - case SPEAKERS_5POINT1: - return AV_CH_LAYOUT_5POINT1_BACK; - case SPEAKERS_7POINT1: - return AV_CH_LAYOUT_7POINT1; - } - - /* shouldn't get here */ - return 0; -} -#endif - static const char *aac_getname(void *unused) { UNUSED_PARAMETER(unused); @@ -164,17 +137,8 @@ static bool initialize_codec(struct enc_encoder *enc) return false; } enc->aframe->format = enc->context->sample_fmt; -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 24, 100) - enc->aframe->channels = enc->context->channels; - channels = enc->context->channels; -#else channels = enc->context->ch_layout.nb_channels; -#endif -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - enc->aframe->channel_layout = enc->context->channel_layout; -#else enc->aframe->ch_layout = enc->context->ch_layout; -#endif enc->aframe->sample_rate = enc->context->sample_rate; enc->frame_size = enc->context->frame_size; @@ -262,13 +226,6 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder, const struct audio_output_info *aoi; aoi = audio_output_get_info(audio); -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 24, 100) - enc->context->channels = (int)audio_output_get_channels(audio); -#endif - -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - enc->context->channel_layout = convert_speaker_layout(aoi->speakers); -#else av_channel_layout_default(&enc->context->ch_layout, (int)audio_output_get_channels(audio)); /* The avutil default channel layout for 5 channels is 5.0, which OBS @@ -286,7 +243,6 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder, astrcmpi(enc->type, "alac") == 0) enc->context->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK; -#endif enc->context->sample_rate = audio_output_get_sample_rate(audio); @@ -330,18 +286,11 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder, enc->context->sample_rate = closest; } -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - info("bitrate: %" PRId64 ", channels: %d, channel_layout: %x\n", - (int64_t)enc->context->bit_rate / 1000, - (int)enc->context->channels, - (unsigned int)enc->context->channel_layout); -#else char buf[256]; av_channel_layout_describe(&enc->context->ch_layout, buf, 256); info("bitrate: %" PRId64 ", channels: %d, channel_layout: %s\n", (int64_t)enc->context->bit_rate / 1000, (int)enc->context->ch_layout.nb_channels, buf); -#endif init_sizes(enc, audio); /* enable experimental FFmpeg encoder if the only one available */ @@ -409,12 +358,8 @@ static bool do_encode(struct enc_encoder *enc, struct encoder_packet *packet, enc->aframe->pts = av_rescale_q( enc->total_samples, (AVRational){1, enc->context->sample_rate}, enc->context->time_base); -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100) enc->aframe->ch_layout = enc->context->ch_layout; channels = enc->context->ch_layout.nb_channels; -#else - channels = enc->context->channels; -#endif ret = avcodec_fill_audio_frame(enc->aframe, channels, enc->context->sample_fmt, enc->samples[0], @@ -497,11 +442,7 @@ static void enc_audio_info(void *data, struct audio_convert_info *info) { struct enc_encoder *enc = data; int channels; -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100) channels = enc->context->ch_layout.nb_channels; -#else - channels = enc->context->channels; -#endif info->format = convert_ffmpeg_sample_format(enc->context->sample_fmt); info->samples_per_sec = (uint32_t)enc->context->sample_rate; if (channels != 7 && channels <= 8) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-av1.c b/plugins/obs-ffmpeg/obs-ffmpeg-av1.c index 11c7d90abc487f..4cf5f9b43a25e3 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-av1.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-av1.c @@ -88,11 +88,7 @@ static bool av1_update(struct av1_encoder *enc, obs_data_t *settings) if (enc->type == AV1_ENCODER_TYPE_SVT) { av_opt_set_int(enc->ffve.context->priv_data, "preset", preset, 0); -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 37, 100) av_dict_set_int(&svtav1_opts, "rc", 1, 0); -#else - av_opt_set(enc->ffve.context->priv_data, "rc", "vbr", 0); -#endif } else if (enc->type == AV1_ENCODER_TYPE_AOM) { av_opt_set_int(enc->ffve.context->priv_data, "cpu-used", preset, 0); @@ -114,16 +110,9 @@ static bool av1_update(struct av1_encoder *enc, obs_data_t *settings) av_opt_set_int(enc->ffve.context->priv_data, "crf", cqp, 0); if (enc->type == AV1_ENCODER_TYPE_SVT) { -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 37, 100) av_dict_set_int(&svtav1_opts, "rc", 0, 0); av_opt_set_int(enc->ffve.context->priv_data, "qp", cqp, 0); -#else - av_opt_set(enc->ffve.context->priv_data, "rc", "cqp", - 0); - av_opt_set_int(enc->ffve.context->priv_data, "qp", cqp, - 0); -#endif } } else if (astrcmpi(rc, "vbr") != 0) { /* CBR by default */ @@ -132,16 +121,10 @@ static bool av1_update(struct av1_encoder *enc, obs_data_t *settings) cqp = 0; if (enc->type == AV1_ENCODER_TYPE_SVT) { -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 37, 100) av_dict_set_int(&svtav1_opts, "rc", 2, 0); av_dict_set_int(&svtav1_opts, "pred-struct", 1, 0); av_dict_set_int(&svtav1_opts, "bias-pct", 0, 0); av_dict_set_int(&svtav1_opts, "tbr", rate, 0); -#else - enc->ffve.context->rc_max_rate = rate; - av_opt_set(enc->ffve.context->priv_data, "rc", "cvbr", - 0); -#endif } else { enc->ffve.context->rc_max_rate = rate; } diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-compat.h b/plugins/obs-ffmpeg/obs-ffmpeg-compat.h index 0d019d1a227a75..ee37583eaa70a7 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-compat.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-compat.h @@ -11,8 +11,3 @@ LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c)) || \ (LIBAVCODEC_VERSION_MICRO >= 100 && \ LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e))) - -#if LIBAVCODEC_VERSION_MAJOR < 60 -#define CODEC_CAP_TRUNC AV_CODEC_CAP_TRUNCATED -#define CODEC_FLAG_TRUNC AV_CODEC_FLAG_TRUNCATED -#endif diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-formats.h b/plugins/obs-ffmpeg/obs-ffmpeg-formats.h index 977b3c6d2bee35..b6500e0fdf855b 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-formats.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-formats.h @@ -48,20 +48,16 @@ obs_to_ffmpeg_video_format(enum video_format format) return AV_PIX_FMT_YUVA422P; case VIDEO_FORMAT_YUVA: return AV_PIX_FMT_YUVA444P; -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 31, 100) case VIDEO_FORMAT_YA2L: return AV_PIX_FMT_YUVA444P12LE; -#endif case VIDEO_FORMAT_I010: return AV_PIX_FMT_YUV420P10LE; case VIDEO_FORMAT_P010: return AV_PIX_FMT_P010LE; -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(57, 17, 100) case VIDEO_FORMAT_P216: return AV_PIX_FMT_P216LE; case VIDEO_FORMAT_P416: return AV_PIX_FMT_P416LE; -#endif case VIDEO_FORMAT_NONE: case VIDEO_FORMAT_AYUV: default: diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c b/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c index 5139acc83e3d50..e2892d3a7868a6 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mpegts.c @@ -175,9 +175,6 @@ static bool create_video_stream(struct ffmpeg_output *stream, context->thread_count = 0; data->video->time_base = context->time_base; -#if LIBAVFORMAT_VERSION_MAJOR < 59 - data->video->codec->time_base = context->time_base; -#endif data->video->avg_frame_rate = av_inv_q(context->time_base); data->video_ctx = context; @@ -198,17 +195,11 @@ static bool create_video_stream(struct ffmpeg_output *stream, av_content_light_metadata_alloc(&content_size); content->MaxCLL = hdr_nominal_peak_level; content->MaxFALL = hdr_nominal_peak_level; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - av_stream_add_side_data(data->video, - AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - (uint8_t *)content, content_size); -#else av_packet_side_data_add( &data->video->codecpar->coded_side_data, &data->video->codecpar->nb_coded_side_data, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, (uint8_t *)content, content_size, 0); -#endif AVMasteringDisplayMetadata *const mastering = av_mastering_display_metadata_alloc(); @@ -224,18 +215,11 @@ static bool create_video_stream(struct ffmpeg_output *stream, mastering->max_luminance = av_make_q(hdr_nominal_peak_level, 1); mastering->has_primaries = 1; mastering->has_luminance = 1; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - av_stream_add_side_data(data->video, - AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - (uint8_t *)mastering, - sizeof(*mastering)); -#else av_packet_side_data_add( &data->video->codecpar->coded_side_data, &data->video->codecpar->nb_coded_side_data, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, (uint8_t *)mastering, sizeof(*mastering), 0); -#endif } return true; @@ -270,23 +254,11 @@ static bool create_audio_stream(struct ffmpeg_output *stream, context->bit_rate = (int64_t)data->config.audio_bitrates[idx] * 1000; context->time_base = (AVRational){1, aoi.samples_per_sec}; channels = get_audio_channels(aoi.speakers); -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 24, 100) - context->channels = get_audio_channels(aoi.speakers); -#endif context->sample_rate = aoi.samples_per_sec; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - context->channel_layout = - av_get_default_channel_layout(context->channels); - - //avutil default channel layout for 5 channels is 5.0 ; fix for 4.1 - if (aoi.speakers == SPEAKERS_4POINT1) - context->channel_layout = av_get_channel_layout("4.1"); -#else av_channel_layout_default(&context->ch_layout, channels); if (aoi.speakers == SPEAKERS_4POINT1) context->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1; -#endif context->sample_fmt = AV_SAMPLE_FMT_S16; context->frame_size = data->config.frame_size; @@ -662,13 +634,8 @@ bool ffmpeg_mpegts_data_init(struct ffmpeg_output *stream, avformat_network_init(); -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - AVOutputFormat *output_format; -#else - const AVOutputFormat *output_format; -#endif - - output_format = av_guess_format("mpegts", NULL, "video/M2PT"); + const AVOutputFormat *output_format = + av_guess_format("mpegts", NULL, "video/M2PT"); if (output_format == NULL) { ffmpeg_mpegts_log_error(LOG_WARNING, data, diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 440d58ef79e309..5b42566bbbf194 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -248,17 +248,11 @@ static bool create_video_stream(struct ffmpeg_data *data) av_content_light_metadata_alloc(&content_size); content->MaxCLL = hdr_nominal_peak_level; content->MaxFALL = hdr_nominal_peak_level; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - av_stream_add_side_data(data->video, - AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - (uint8_t *)content, content_size); -#else av_packet_side_data_add( &data->video->codecpar->coded_side_data, &data->video->codecpar->nb_coded_side_data, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, (uint8_t *)content, content_size, 0); -#endif AVMasteringDisplayMetadata *const mastering = av_mastering_display_metadata_alloc(); @@ -274,18 +268,11 @@ static bool create_video_stream(struct ffmpeg_data *data) mastering->max_luminance = av_make_q(hdr_nominal_peak_level, 1); mastering->has_primaries = 1; mastering->has_luminance = 1; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - av_stream_add_side_data(data->video, - AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - (uint8_t *)mastering, - sizeof(*mastering)); -#else av_packet_side_data_add( &data->video->codecpar->coded_side_data, &data->video->codecpar->nb_coded_side_data, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, (uint8_t *)mastering, sizeof(*mastering), 0); -#endif } if (context->pix_fmt != data->config.format || @@ -319,14 +306,8 @@ static bool open_audio_codec(struct ffmpeg_data *data, int idx) } data->aframe[idx]->format = context->sample_fmt; -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 24, 100) - data->aframe[idx]->channels = context->channels; - data->aframe[idx]->channel_layout = context->channel_layout; - channels = context->channels; -#else data->aframe[idx]->ch_layout = context->ch_layout; channels = context->ch_layout.nb_channels; -#endif data->aframe[idx]->sample_rate = context->sample_rate; context->strict_std_compliance = -2; @@ -375,23 +356,13 @@ static bool create_audio_stream(struct ffmpeg_data *data, int idx) context = avcodec_alloc_context3(data->acodec); context->bit_rate = (int64_t)data->config.audio_bitrate * 1000; context->time_base = (AVRational){1, aoi.samples_per_sec}; -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 24, 100) - context->channels = get_audio_channels(aoi.speakers); -#endif channels = get_audio_channels(aoi.speakers); context->sample_rate = aoi.samples_per_sec; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - context->channel_layout = av_get_default_channel_layout(channels); - - //avutil default channel layout for 5 channels is 5.0 ; fix for 4.1 - if (aoi.speakers == SPEAKERS_4POINT1) - context->channel_layout = av_get_channel_layout("4.1"); -#else av_channel_layout_default(&context->ch_layout, channels); if (aoi.speakers == SPEAKERS_4POINT1) context->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1; -#endif + context->sample_fmt = data->acodec->sample_fmts ? data->acodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; @@ -565,34 +536,6 @@ static inline const char *safe_str(const char *s) return s; } -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) -static enum AVCodecID get_codec_id(const char *name, int id) -{ - const AVCodec *codec; - - if (id != 0) - return (enum AVCodecID)id; - - if (!name || !*name) - return AV_CODEC_ID_NONE; - - codec = avcodec_find_encoder_by_name(name); - if (!codec) - return AV_CODEC_ID_NONE; - - return codec->id; -} - -static void set_encoder_ids(struct ffmpeg_data *data) -{ - data->output->oformat->video_codec = get_codec_id( - data->config.video_encoder, data->config.video_encoder_id); - - data->output->oformat->audio_codec = get_codec_id( - data->config.audio_encoder, data->config.audio_encoder_id); -} -#endif - bool ffmpeg_data_init(struct ffmpeg_data *data, struct ffmpeg_cfg *config) { bool is_rtmp = false; @@ -608,13 +551,7 @@ bool ffmpeg_data_init(struct ffmpeg_data *data, struct ffmpeg_cfg *config) is_rtmp = (astrcmpi_n(config->url, "rtmp://", 7) == 0); -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - AVOutputFormat *output_format; -#else - const AVOutputFormat *output_format; -#endif - - output_format = av_guess_format( + const AVOutputFormat *output_format = av_guess_format( is_rtmp ? "flv" : data->config.format_name, data->config.url, is_rtmp ? NULL : data->config.format_mime_type); @@ -640,20 +577,10 @@ bool ffmpeg_data_init(struct ffmpeg_data *data, struct ffmpeg_cfg *config) goto fail; } -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - if (is_rtmp) { - data->output->oformat->video_codec = AV_CODEC_ID_H264; - data->output->oformat->audio_codec = AV_CODEC_ID_AAC; - } else { - if (data->config.format_name) - set_encoder_ids(data); - } -#else if (is_rtmp) { data->config.audio_encoder_id = AV_CODEC_ID_AAC; data->config.video_encoder_id = AV_CODEC_ID_H264; } -#endif if (!init_streams(data)) goto fail; @@ -856,12 +783,7 @@ static void encode_audio(struct ffmpeg_output *output, int idx, AVPacket *packet = NULL; int ret, got_packet; - int channels; -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 24, 100) - channels = context->ch_layout.nb_channels; -#else - channels = context->channels; -#endif + int channels = context->ch_layout.nb_channels; size_t total_size = data->frame_size * block_size * channels; data->aframe[idx]->nb_samples = data->frame_size; From 489f050fafa19ab0d8c47e6e8abca45f4fda9784 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:18:37 +0200 Subject: [PATCH 0442/1073] win-dshow: Remove compatibility with FFmpeg < 6.1 --- plugins/win-dshow/ffmpeg-decode.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/plugins/win-dshow/ffmpeg-decode.c b/plugins/win-dshow/ffmpeg-decode.c index e5ba5ea4fa9ebc..9cd611866d9f66 100644 --- a/plugins/win-dshow/ffmpeg-decode.c +++ b/plugins/win-dshow/ffmpeg-decode.c @@ -92,11 +92,6 @@ int ffmpeg_decode_init(struct ffmpeg_decode *decode, enum AVCodecID id, return ret; } -#if LIBAVCODEC_VERSION_MAJOR < 60 - if (decode->codec->capabilities & CODEC_CAP_TRUNC) - decode->decoder->flags |= CODEC_FLAG_TRUNC; -#endif - return 0; } @@ -265,13 +260,8 @@ bool ffmpeg_decode_audio(struct ffmpeg_decode *decode, uint8_t *data, audio->samples_per_sec = decode->frame->sample_rate; audio->format = convert_sample_format(decode->frame->format); -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) - audio->speakers = - convert_speaker_layout((uint8_t)decode->decoder->channels); -#else audio->speakers = convert_speaker_layout( (uint8_t)decode->decoder->ch_layout.nb_channels); -#endif audio->frames = decode->frame->nb_samples; From d9503d4c279c127d945d68d7683a166e55575a1e Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:21:39 +0200 Subject: [PATCH 0443/1073] shared/media-playback: Remove compatibility with FFmpeg < 6.1 --- .../media-playback/closest-format.h | 4 ---- shared/media-playback/media-playback/decode.c | 15 +-------------- shared/media-playback/media-playback/decode.h | 5 ----- shared/media-playback/media-playback/media.c | 19 +------------------ 4 files changed, 2 insertions(+), 41 deletions(-) diff --git a/shared/media-playback/media-playback/closest-format.h b/shared/media-playback/media-playback/closest-format.h index a2cc69ba67b596..b73669938492c1 100644 --- a/shared/media-playback/media-playback/closest-format.h +++ b/shared/media-playback/media-playback/closest-format.h @@ -95,15 +95,11 @@ static enum AVPixelFormat closest_format(enum AVPixelFormat fmt) case AV_PIX_FMT_YUVA444P10LE: case AV_PIX_FMT_YUVA444P16BE: case AV_PIX_FMT_YUVA444P16LE: -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 31, 100) case AV_PIX_FMT_YUVA444P12BE: -#endif return AV_PIX_FMT_YUVA444P; -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 31, 100) case AV_PIX_FMT_YUVA444P12LE: return AV_PIX_FMT_YUVA444P12LE; -#endif case AV_PIX_FMT_P010LE: return AV_PIX_FMT_P010LE; diff --git a/shared/media-playback/media-playback/decode.c b/shared/media-playback/media-playback/decode.c index 40be7842667fc3..1d64f906098964 100644 --- a/shared/media-playback/media-playback/decode.c +++ b/shared/media-playback/media-playback/decode.c @@ -114,14 +114,9 @@ static uint16_t get_max_luminance(const AVStream *stream) { uint32_t max_luminance = 0; -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102) - for (int i = 0; i < stream->nb_side_data; i++) { - const AVPacketSideData *const sd = &stream->side_data[i]; -#else for (int i = 0; i < stream->codecpar->nb_coded_side_data; i++) { const AVPacketSideData *const sd = &stream->codecpar->coded_side_data[i]; -#endif switch (sd->type) { case AV_PKT_DATA_MASTERING_DISPLAY_METADATA: { const AVMasteringDisplayMetadata *mastering = @@ -217,11 +212,6 @@ bool mp_decode_init(mp_media_t *m, enum AVMediaType type, bool hw) d->in_frame = d->sw_frame; } -#if LIBAVCODEC_VERSION_MAJOR < 60 - if (d->codec->capabilities & CODEC_CAP_TRUNC) - d->decoder->flags |= CODEC_FLAG_TRUNC; -#endif - d->orig_pkt = av_packet_alloc(); d->pkt = av_packet_alloc(); @@ -428,11 +418,8 @@ bool mp_decode_next(struct mp_decode *d) av_rescale_q(d->in_frame->best_effort_timestamp, d->stream->time_base, (AVRational){1, 1000000000}); -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 30, 100) + int64_t duration = d->in_frame->duration; -#else - int64_t duration = d->in_frame->pkt_duration; -#endif if (!duration) duration = get_estimated_duration(d, last_pts); else diff --git a/shared/media-playback/media-playback/decode.h b/shared/media-playback/media-playback/decode.h index bea9571bf79188..97830c336ddcb2 100644 --- a/shared/media-playback/media-playback/decode.h +++ b/shared/media-playback/media-playback/decode.h @@ -36,11 +36,6 @@ extern "C" { #pragma warning(pop) #endif -#if LIBAVCODEC_VERSION_MAJOR < 60 -#define CODEC_CAP_TRUNC AV_CODEC_CAP_TRUNCATED -#define CODEC_FLAG_TRUNC AV_CODEC_FLAG_TRUNCATED -#endif - struct mp_media; struct mp_decode { diff --git a/shared/media-playback/media-playback/media.c b/shared/media-playback/media-playback/media.c index 79f76e9af8a806..e03f843b2aeaee 100644 --- a/shared/media-playback/media-playback/media.c +++ b/shared/media-playback/media-playback/media.c @@ -62,10 +62,8 @@ static inline enum video_format convert_pixel_format(int f) return VIDEO_FORMAT_I42A; case AV_PIX_FMT_YUVA444P: return VIDEO_FORMAT_YUVA; -#if LIBAVUTIL_BUILD >= AV_VERSION_INT(56, 31, 100) case AV_PIX_FMT_YUVA444P12LE: return VIDEO_FORMAT_YA2L; -#endif case AV_PIX_FMT_BGR0: return VIDEO_FORMAT_BGRX; case AV_PIX_FMT_P010LE: @@ -362,17 +360,11 @@ void mp_media_next_audio(mp_media_t *m) struct mp_decode *d = &m->a; struct obs_source_audio audio = {0}; AVFrame *f = d->frame; - int channels; + int channels = f->ch_layout.nb_channels; if (!mp_media_can_play_frame(m, d)) return; -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 19, 100) - channels = f->channels; -#else - channels = f->ch_layout.nb_channels; -#endif - d->frame_ready = false; if (!m->a_cb) return; @@ -503,12 +495,7 @@ void mp_media_next_video(mp_media_t *m, bool preload) } if (!m->is_local_file && !d->got_first_keyframe) { - -#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(58, 29, 100) - if (!f->key_frame) -#else if (!(f->flags & AV_FRAME_FLAG_KEY)) -#endif return; d->got_first_keyframe = true; @@ -694,11 +681,7 @@ static int interrupt_callback(void *data) static bool init_avformat(mp_media_t *m) { -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(59, 0, 100) - AVInputFormat *format = NULL; -#else const AVInputFormat *format = NULL; -#endif if (m->format_name && *m->format_name) { format = av_find_input_format(m->format_name); From 92b5643081b4fd166013e62dd563cb197a3eb9a6 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 5 Jul 2024 19:15:12 -0700 Subject: [PATCH 0444/1073] libobs: Remove broken rescale modification logic This logic would previously have written any changed scale resolution set by the encoder in the `.get_video_info` callback back to the encoder, however this functionality was "broken" by 20d8779d30fd5cfd7f2ebd6b3eb7777c3b99b19f. In reality, this would have never worked with texture encoders or with GPU rescaling enabled, and probably would have had odd side effects for CPU rescaling, too. It's best just to remove this functionality. --- libobs/obs-encoder.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 77698ae44a5c09..c0f53d061ea65b 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -186,9 +186,6 @@ static inline void get_video_info(struct obs_encoder *encoder, if (encoder->info.get_video_info) encoder->info.get_video_info(encoder->context.data, info); - - if (info->width != voi->width || info->height != voi->height) - obs_encoder_set_scaled_size(encoder, info->width, info->height); } static inline bool gpu_encode_available(const struct obs_encoder *encoder) From 16f0bb68aedfdcd952f237cb1bac966a9fb50fb2 Mon Sep 17 00:00:00 2001 From: tt2468 Date: Fri, 5 Jul 2024 18:52:23 -0700 Subject: [PATCH 0445/1073] libobs: Add `OBS_ENCODER_CAP_SCALING` Adds a new encoder cap which tells libobs that rather than scaling video frames in software, the encoder is capable of scaling them via its own (presumably more efficient) means. An encoder may implement this cap by comparing the VOI of its assigned `video_t` and the results of `obs_encoder_get_width/height()`. If the width/height values differ, then the encoder is being asked by libobs to self-scale, and the resolution in VOI will be the raw frame size, with the `...get_width/height()` being the intended output resolution of the encoder. It is important to note that GPU rescaling mode will take priority over self-scaling. If GPU rescaling is enabled, the encoder will never be asked to self-scale. This is useful for discrete hardware encoders, where they might have fixed-function video scaling logic that is highly efficient and fast. Additionally, this feature allows a hardware device which is encoding a full ABR ladder of tracks to be smart and only copy a video frame from GPU -> Host -> Device once for the entire ladder, rather than once for every track. --- docs/sphinx/reference-encoders.rst | 3 ++- libobs/obs-encoder.c | 10 ++++++++++ libobs/obs-encoder.h | 1 + libobs/obs-module.c | 8 ++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/reference-encoders.rst b/docs/sphinx/reference-encoders.rst index b35a2f30c3d751..5c267382e979d4 100644 --- a/docs/sphinx/reference-encoders.rst +++ b/docs/sphinx/reference-encoders.rst @@ -164,8 +164,9 @@ Encoder Definition Structure (obs_encoder_info) - **OBS_ENCODER_CAP_DEPRECATED** - Encoder is deprecated - **OBS_ENCODER_CAP_ROI** - Encoder supports region of interest feature + - **OBS_ENCODER_CAP_SCALING** - Encoder implements its own scaling logic, + desiring to receive unscaled frames - .. versionadded:: 30.1 Encoder Packet Structure (encoder_packet) ----------------------------------------- diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index c0f53d061ea65b..e8916c6cffae5f 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -186,6 +186,16 @@ static inline void get_video_info(struct obs_encoder *encoder, if (encoder->info.get_video_info) encoder->info.get_video_info(encoder->context.data, info); + + /** + * Prevent video output from performing an actual scale. If GPU scaling is + * enabled, the voi will contain the scaled size. Therefore, GPU scaling + * takes priority over self-scaling functionality. + */ + if ((encoder->info.caps & OBS_ENCODER_CAP_SCALING) != 0) { + info->width = voi->width; + info->height = voi->height; + } } static inline bool gpu_encode_available(const struct obs_encoder *encoder) diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h index 26d35a8728aa2a..5c7859b8e4c0b8 100644 --- a/libobs/obs-encoder.h +++ b/libobs/obs-encoder.h @@ -37,6 +37,7 @@ typedef struct obs_encoder obs_encoder_t; #define OBS_ENCODER_CAP_DYN_BITRATE (1 << 2) #define OBS_ENCODER_CAP_INTERNAL (1 << 3) #define OBS_ENCODER_CAP_ROI (1 << 4) +#define OBS_ENCODER_CAP_SCALING (1 << 5) /** Specifies the encoder type */ enum obs_encoder_type { diff --git a/libobs/obs-module.c b/libobs/obs-module.c index e798479553eadf..02c12bf00023b4 100644 --- a/libobs/obs-module.c +++ b/libobs/obs-module.c @@ -934,6 +934,14 @@ void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size) goto error; } + if (((info->caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0 && + info->caps & OBS_ENCODER_CAP_SCALING) != 0) { + encoder_warn( + "Texture encoders cannot self-scale. Encoder id '%s' not registered.", + info->id); + goto error; + } + #define CHECK_REQUIRED_VAL_(info, val, func) \ CHECK_REQUIRED_VAL(struct obs_encoder_info, info, val, func) CHECK_REQUIRED_VAL_(info, get_name, obs_register_encoder); From f9bf7e6c000391d19de650acb16f1afed0e6b2a3 Mon Sep 17 00:00:00 2001 From: pkv Date: Sat, 26 Aug 2023 21:40:09 +0200 Subject: [PATCH 0446/1073] obs-filters: Add NVIDIA Blur Filter & Background Blur This adds a Background Blur filter which blurs the background for a foreground speaker identified by NVIDIA AI Greenscreen FX. Secondly, this adds a Blur filter based on NVIDIA Video Effects, leveraging the Background Blur Filter where the mask used just sets the whole frame as background. Signed-off-by: pkv --- plugins/nv-filters/CMakeLists.txt | 2 +- plugins/nv-filters/data/locale/en-US.ini | 3 + plugins/nv-filters/data/rtx_blur.effect | 158 +++ plugins/nv-filters/nv-filters.c | 7 +- ...creen-filter.c => nvidia-videofx-filter.c} | 1135 +++++++++++------ 5 files changed, 916 insertions(+), 389 deletions(-) create mode 100644 plugins/nv-filters/data/rtx_blur.effect rename plugins/nv-filters/{nvidia-greenscreen-filter.c => nvidia-videofx-filter.c} (51%) diff --git a/plugins/nv-filters/CMakeLists.txt b/plugins/nv-filters/CMakeLists.txt index 21b9968ab2ae16..85ac94635cd55e 100644 --- a/plugins/nv-filters/CMakeLists.txt +++ b/plugins/nv-filters/CMakeLists.txt @@ -15,7 +15,7 @@ if(OS_WINDOWS) if(ENABLE_NVVFX) target_enable_feature(nv-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED) - target_sources(nv-filters PRIVATE nvidia-greenscreen-filter.c) + target_sources(nv-filters PRIVATE nvidia-videofx-filter.c) else() target_disable_feature(nv-filters "NVIDIA Video FX support") endif() diff --git a/plugins/nv-filters/data/locale/en-US.ini b/plugins/nv-filters/data/locale/en-US.ini index e1da7f429e06ab..de7412f79174bb 100644 --- a/plugins/nv-filters/data/locale/en-US.ini +++ b/plugins/nv-filters/data/locale/en-US.ini @@ -14,3 +14,6 @@ Nvvfx.Method.Greenscreen.Threshold="Threshold" Nvvfx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Video SDK is outdated." Nvvfx.Method.Greenscreen.Processing="Mask refresh frequency in frames" Nvvfx.Method.Greenscreen.Processing.Hint="This alleviates GPU load by generating a mask every N frames only (2 on default)." +Nvvfx.Method.BlurFilter="NVIDIA Blur Filter" +Nvvfx.Method.BackgroundBlurFilter="NVIDIA Background Blur Filter" +Nvvfx.Method.Blur.Strength="Blur Intensity" diff --git a/plugins/nv-filters/data/rtx_blur.effect b/plugins/nv-filters/data/rtx_blur.effect new file mode 100644 index 00000000000000..60ee7c081ddf38 --- /dev/null +++ b/plugins/nv-filters/data/rtx_blur.effect @@ -0,0 +1,158 @@ +#include "color.effect" + +uniform float4x4 ViewProj; +uniform texture2d image; +uniform float multiplier; + +uniform texture2d blurred; + +sampler_state texSampler { + Filter = Linear; + AddressU = Clamp; + AddressV = Clamp; +}; + +struct VertData { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +struct VertInOut { + float2 uv : TEXCOORD0; + float4 pos : POSITION; +}; + +struct FragData { + float2 uv : TEXCOORD0; +}; + +struct FragPos { + float4 pos : POSITION; +}; + +VertInOut VSDefault(VertData v_in) +{ + VertInOut v_out; + v_out.uv = v_in.uv; + v_out.pos = mul(float4(v_in.pos.xyz, 1.), ViewProj); + return v_out; +} + +FragPos VSConvertUnorm(uint id : VERTEXID) +{ + float idHigh = float(id >> 1); + float idLow = float(id & uint(1)); + + float x = idHigh * 4.0 - 1.0; + float y = idLow * 4.0 - 1.0; + + FragPos vert_out; + vert_out.pos = float4(x, y, 0.0, 1.0); + return vert_out; +} + +float4 Mask(FragData f_in) +{ + float4 rgba = image.Sample(texSampler, f_in.uv); + rgba.rgb = max(float3(0.0, 0.0, 0.0), blurred.Sample(texSampler, f_in.uv).rgb / rgba.a); + return rgba; +} + +float4 PSMask(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + return rgba; +} + +float4 PSMaskMultiply(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb *= multiplier; + return rgba; +} + +float4 PSMaskTonemap(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + return rgba; +} + +float4 PSMaskMultiplyTonemap(FragData f_in) : TARGET +{ + float4 rgba = Mask(f_in); + rgba.rgb *= multiplier; + rgba.rgb = rec709_to_rec2020(rgba.rgb); + rgba.rgb = reinhard(rgba.rgb); + rgba.rgb = rec2020_to_rec709(rgba.rgb); + return rgba; +} + +float4 PSDefault(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + return rgba; +} + +float4 PSConvertMultiply(FragPos f_in) : TARGET +{ + float4 rgba = image.Load(int3(f_in.pos.xy, 0)); + rgba.rgb *= multiplier; + return rgba; +} + +technique Draw +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMask(f_in); + } +} + +technique DrawMultiply +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskMultiply(f_in); + } +} + +technique DrawTonemap +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskTonemap(f_in); + } +} + +technique DrawMultiplyTonemap +{ + pass + { + vertex_shader = VSDefault(v_in); + pixel_shader = PSMaskMultiplyTonemap(f_in); + } +} + +technique ConvertUnorm +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSDefault(f_in); + } +} + +technique ConvertUnormMultiply +{ + pass + { + vertex_shader = VSConvertUnorm(id); + pixel_shader = PSConvertMultiply(f_in); + } +} diff --git a/plugins/nv-filters/nv-filters.c b/plugins/nv-filters/nv-filters.c index a9f590357bab13..0a5fb44512daa6 100644 --- a/plugins/nv-filters/nv-filters.c +++ b/plugins/nv-filters/nv-filters.c @@ -14,6 +14,8 @@ extern void unload_nvidia_afx(void); #endif #ifdef LIBNVVFX_ENABLED extern struct obs_source_info nvidia_greenscreen_filter_info; +extern struct obs_source_info nvidia_blur_filter_info; +extern struct obs_source_info nvidia_background_blur_filter_info; extern bool load_nvidia_vfx(void); extern void unload_nvidia_vfx(void); #endif @@ -29,8 +31,11 @@ bool obs_module_load(void) obs_enter_graphics(); const bool direct3d = gs_get_device_type() == GS_DEVICE_DIRECT3D_11; obs_leave_graphics(); - if (direct3d && load_nvidia_vfx()) + if (direct3d && load_nvidia_vfx()) { obs_register_source(&nvidia_greenscreen_filter_info); + obs_register_source(&nvidia_blur_filter_info); + obs_register_source(&nvidia_background_blur_filter_info); + } #endif return true; } diff --git a/plugins/nv-filters/nvidia-greenscreen-filter.c b/plugins/nv-filters/nvidia-videofx-filter.c similarity index 51% rename from plugins/nv-filters/nvidia-greenscreen-filter.c rename to plugins/nv-filters/nvidia-videofx-filter.c index 646c3ad5bcb1d4..ad6cd646f18e1b 100644 --- a/plugins/nv-filters/nvidia-greenscreen-filter.c +++ b/plugins/nv-filters/nvidia-videofx-filter.c @@ -6,9 +6,8 @@ #include "nvvfx-load.h" /* -------------------------------------------------------- */ -#define do_log(level, format, ...) \ - blog(level, \ - "[NVIDIA AI Greenscreen (Background removal): '%s'] " format, \ +#define do_log(level, format, ...) \ + blog(level, "[NVIDIA Video Effect: '%s'] " format, \ obs_source_get_name(filter->context), ##__VA_ARGS__) #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) @@ -38,9 +37,18 @@ #define TEXT_PROCESSING MT_("Nvvfx.Method.Greenscreen.Processing") #define TEXT_PROCESSING_HINT MT_("Nvvfx.Method.Greenscreen.Processing.Hint") +/* Blur & background blur FX */ +#define S_STRENGTH "intensity" +#define S_STRENGTH_DEFAULT 0.5 +#define TEXT_MODE_BLUR_STRENGTH MT_("Nvvfx.Method.Blur.Strength") + +enum nvvfx_filter_id { S_FX_AIGS, S_FX_BLUR, S_FX_BG_BLUR }; + bool nvvfx_loaded = false; bool nvvfx_new_sdk = false; -struct nv_greenscreen_data { + +/* clang-format off */ +struct nvvfx_data { obs_source_t *context; bool images_allocated; bool initial_render; @@ -52,13 +60,13 @@ struct nv_greenscreen_data { /* RTX SDK vars */ NvVFX_Handle handle; - CUstream stream; // CUDA stream - int mode; // 0 = quality, 1 = performance - NvCVImage *src_img; // src img in obs format (RGBA ?) on GPU - NvCVImage *BGR_src_img; // src img in BGR on GPU - NvCVImage *A_dst_img; // mask img on GPU - NvCVImage *dst_img; // mask texture - NvCVImage *stage; // planar stage img used for transfer to texture + CUstream stream; // CUDA stream + int mode; // 0 = quality, 1 = performance + NvCVImage *src_img; // src img in obs format (RGBA ?) on GPU + NvCVImage *BGR_src_img; // src img in BGR on GPU + NvCVImage *A_dst_img; // mask img on GPU + NvCVImage *dst_img; // Greenscreen: alpha mask texture; blur: texture initialized from d3d11 (RGBA, chunky, u8) + NvCVImage *stage; // used for transfer to texture unsigned int version; NvVFX_StateObjectHandle stateObjectHandle; @@ -66,9 +74,9 @@ struct nv_greenscreen_data { gs_effect_t *effect; gs_texrender_t *render; gs_texrender_t *render_unorm; - gs_texture_t *alpha_texture; - uint32_t width; // width of texture - uint32_t height; // height of texture + gs_texture_t *alpha_texture; // either alpha or blur + uint32_t width; // width of texture + uint32_t height; // height of texture enum gs_color_space space; gs_eparam_t *mask_param; gs_eparam_t *image_param; @@ -79,10 +87,23 @@ struct nv_greenscreen_data { /* Every nth frame is processed through the FX (where n = * processing_interval) so that the same mask is used for n consecutive * frames. This is to alleviate the GPU load. Default: 1 (process every frame). + * Only used for AIGS. */ int processing_interval; int processing_counter; + + /* blur specific */ + enum nvvfx_filter_id filter_id; + NvVFX_Handle handle_blur; + CUstream stream_blur; // cuda stream + float strength; // from 0 to 1, default = 0.5 + NvCVImage *blur_BGR_dst_img; // dst img of blur FX (BGR, chunky, u8) + NvCVImage *RGBA_dst_img; // tmp img used to transfer to texture + NvCVImage *blur_dst_img; // dst img initialized from d3d11 texture (RGBA, chunky, u8) + gs_texture_t *blur_texture; + gs_eparam_t *blur_param; }; +/* clang-format on */ static const char *nv_greenscreen_filter_name(void *unused) { @@ -90,26 +111,56 @@ static const char *nv_greenscreen_filter_name(void *unused) return obs_module_text("Nvvfx.Method.Greenscreen"); } -static void nv_greenscreen_filter_update(void *data, obs_data_t *settings) +static const char *nv_blur_filter_name(void *unused) +{ + UNUSED_PARAMETER(unused); + return obs_module_text("Nvvfx.Method.BlurFilter"); +} + +static const char *nv_background_blur_filter_name(void *unused) { - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; + UNUSED_PARAMETER(unused); + return obs_module_text("Nvvfx.Method.BackgroundBlurFilter"); +} + +static void nvvfx_filter_update(void *data, obs_data_t *settings) +{ + struct nvvfx_data *filter = (struct nvvfx_data *)data; NvCV_Status vfxErr; - int mode = (int)obs_data_get_int(settings, S_MODE); - if (filter->mode != mode) { - filter->mode = mode; - vfxErr = NvVFX_SetU32(filter->handle, NVVFX_MODE, mode); - vfxErr = NvVFX_Load(filter->handle); - if (NVCV_SUCCESS != vfxErr) - error("Error loading AI Greenscreen FX %i", vfxErr); - } + enum nvvfx_fx_id id = filter->filter_id; + filter->threshold = (float)obs_data_get_double(settings, S_THRESHOLDFX); filter->processing_interval = (int)obs_data_get_int(settings, S_PROCESSING); + float strength = (float)obs_data_get_double(settings, S_STRENGTH); + if (id == S_FX_AIGS || id == S_FX_BG_BLUR) { + int mode = id == S_FX_BG_BLUR + ? (int)S_MODE_QUALITY + : (int)obs_data_get_int(settings, S_MODE); + if (filter->mode != mode) { + filter->mode = mode; + vfxErr = NvVFX_SetU32(filter->handle, NVVFX_MODE, mode); + vfxErr = NvVFX_Load(filter->handle); + if (NVCV_SUCCESS != vfxErr) + error("Error loading AI Greenscreen FX %i", + vfxErr); + } + } + if (id == S_FX_BLUR || id == S_FX_BG_BLUR) { + if (filter->strength != strength) { + filter->strength = strength; + vfxErr = NvVFX_SetF32(filter->handle_blur, + NVVFX_STRENGTH, filter->strength); + } + vfxErr = NvVFX_Load(filter->handle_blur); + if (NVCV_SUCCESS != vfxErr) + error("Error loading blur FX %i", vfxErr); + } } -static void nv_greenscreen_filter_actual_destroy(void *data) +static void nvvfx_filter_actual_destroy(void *data) { - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; + struct nvvfx_data *filter = (struct nvvfx_data *)data; if (!nvvfx_loaded) { bfree(filter); return; @@ -119,7 +170,11 @@ static void nv_greenscreen_filter_actual_destroy(void *data) if (filter->images_allocated) { obs_enter_graphics(); - gs_texture_destroy(filter->alpha_texture); + if (filter->filter_id == S_FX_AIGS) + gs_texture_destroy(filter->alpha_texture); + else + gs_texture_destroy(filter->blur_texture); + gs_texrender_destroy(filter->render); gs_texrender_destroy(filter->render_unorm); obs_leave_graphics(); @@ -128,10 +183,17 @@ static void nv_greenscreen_filter_actual_destroy(void *data) NvCVImage_Destroy(filter->A_dst_img); NvCVImage_Destroy(filter->dst_img); NvCVImage_Destroy(filter->stage); + if (filter->filter_id != S_FX_AIGS) { + NvCVImage_Destroy(filter->blur_BGR_dst_img); + NvCVImage_Destroy(filter->RGBA_dst_img); + NvCVImage_Destroy(filter->blur_dst_img); + } } - if (filter->stream) { + if (filter->stream) NvVFX_CudaStreamDestroy(filter->stream); - } + if (filter->stream_blur) + NvVFX_CudaStreamDestroy(filter->stream_blur); + if (filter->handle) { if (filter->stateObjectHandle) { NvVFX_DeallocateState(filter->handle, @@ -139,6 +201,9 @@ static void nv_greenscreen_filter_actual_destroy(void *data) } NvVFX_DestroyEffect(filter->handle); } + if (filter->handle_blur) { + NvVFX_DestroyEffect(filter->handle_blur); + } if (filter->effect) { obs_enter_graphics(); @@ -149,22 +214,207 @@ static void nv_greenscreen_filter_actual_destroy(void *data) bfree(filter); } -static void nv_greenscreen_filter_destroy(void *data) +static void nvvfx_filter_destroy(void *data) +{ + obs_queue_task(OBS_TASK_GRAPHICS, nvvfx_filter_actual_destroy, data, + false); +} + +static void *log_nverror_destroy(struct nvvfx_data *filter, NvCV_Status vfxErr) { - obs_queue_task(OBS_TASK_GRAPHICS, nv_greenscreen_filter_actual_destroy, - data, false); + const char *errString = NvCV_GetErrorStringFromCode(vfxErr); + error("Error creating NVIDIA Video FX; error %i: %s", vfxErr, + errString); + nvvfx_filter_destroy(filter); + return NULL; } -static void nv_greenscreen_filter_reset(void *data, calldata_t *calldata) +//---------------------------------------------------------------------------// +// filter creation // + +static bool nvvfx_filter_create_internal(struct nvvfx_data *filter) { - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; + NvCV_Status vfxErr; + enum nvvfx_fx_id id = filter->filter_id; + /* 1. Create FX */ + switch (id) { + case S_FX_AIGS: + vfxErr = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, + &filter->handle); + break; + case S_FX_BLUR: + vfxErr = NvVFX_CreateEffect(NVVFX_FX_BGBLUR, + &filter->handle_blur); + break; + case S_FX_BG_BLUR: + vfxErr = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, + &filter->handle); + if (NVCV_SUCCESS != vfxErr) + log_nverror_destroy(filter, vfxErr); + vfxErr = NvVFX_CreateEffect(NVVFX_FX_BGBLUR, + &filter->handle_blur); + break; + default: + return false; + } + if (NVCV_SUCCESS != vfxErr) + log_nverror_destroy(filter, vfxErr); + /* debug */ +#ifdef _DEBUG + const char *info; + vfxErr = NvVFX_GetString(filter->handle_blur, NVVFX_INFO, &info); + blog(LOG_DEBUG, "blur fx settings /n/%s", info); +#endif + /* 2. Set models path & initialize CudaStream */ + if (id == S_FX_AIGS || id == S_FX_BG_BLUR) { + char buffer[MAX_PATH]; + char modelDir[MAX_PATH]; + nvvfx_get_sdk_path(buffer, MAX_PATH); + size_t max_len = sizeof(buffer) / sizeof(char); + snprintf(modelDir, max_len, "%s\\models", buffer); + vfxErr = NvVFX_SetString(filter->handle, NVVFX_MODEL_DIRECTORY, + modelDir); + vfxErr = NvVFX_CudaStreamCreate(&filter->stream); + if (NVCV_SUCCESS != vfxErr) + log_nverror_destroy(filter, vfxErr); + vfxErr = NvVFX_SetCudaStream(filter->handle, NVVFX_CUDA_STREAM, + filter->stream); + if (NVCV_SUCCESS != vfxErr) + log_nverror_destroy(filter, vfxErr); + } + if (id == S_FX_BLUR || id == S_FX_BG_BLUR) { + vfxErr = NvVFX_CudaStreamCreate(&filter->stream_blur); + if (NVCV_SUCCESS != vfxErr) + log_nverror_destroy(filter, vfxErr); + vfxErr = NvVFX_SetCudaStream(filter->handle_blur, + NVVFX_CUDA_STREAM, + filter->stream_blur); + if (NVCV_SUCCESS != vfxErr) + log_nverror_destroy(filter, vfxErr); + } + return true; +} + +static void *nvvfx_filter_create(obs_data_t *settings, obs_source_t *context, + enum nvvfx_fx_id id) +{ + struct nvvfx_data *filter = + (struct nvvfx_data *)bzalloc(sizeof(*filter)); + if (!nvvfx_loaded) { + nvvfx_filter_destroy(filter); + return NULL; + } + + NvCV_Status vfxErr; + filter->context = context; + filter->mode = -1; // should be 0 or 1; -1 triggers an update + filter->images_allocated = false; + filter->processed_frame = true; // start processing when false + filter->width = 0; + filter->height = 0; + filter->initial_render = false; + os_atomic_set_bool(&filter->processing_stop, false); + filter->handler = NULL; + filter->processing_interval = 1; + filter->processing_counter = 0; + // set nvvfx_fx_id + filter->filter_id = id; + // blur specific + filter->strength = -1; + +#ifdef _DEBUG + /* sanity check of sdk version */ + if (NvVFX_GetVersion(&filter->version) == NVCV_SUCCESS) { + uint8_t major = (filter->version >> 24) & 0xff; + uint8_t minor = (filter->version >> 16) & 0x00ff; + uint8_t build = (filter->version >> 8) & 0x0000ff; + uint8_t revision = (filter->version >> 0) & 0x000000ff; + nvvfx_new_sdk = filter->version >= MIN_VFX_SDK_VERSION && + nvvfx_new_sdk; + } +#endif + /* 1. Create FX */ + /* 2. Set models path & initialize CudaStream */ + if (!nvvfx_filter_create_internal(filter)) + return NULL; + + /* 3. Load effect. */ + char *effect_path = obs_module_file( + id != S_FX_AIGS ? "rtx_blur.effect" : "rtx_greenscreen.effect"); + obs_enter_graphics(); + filter->effect = gs_effect_create_from_file(effect_path, NULL); + bfree(effect_path); + if (filter->effect) { + if (id == S_FX_AIGS) { + filter->mask_param = gs_effect_get_param_by_name( + filter->effect, "mask"); + filter->threshold_param = gs_effect_get_param_by_name( + filter->effect, "threshold"); + } else { + filter->blur_param = gs_effect_get_param_by_name( + filter->effect, "blurred"); + } + filter->image_param = + gs_effect_get_param_by_name(filter->effect, "image"); + + filter->multiplier_param = gs_effect_get_param_by_name( + filter->effect, "multiplier"); + } + obs_leave_graphics(); + + /* 4. Allocate state for the AIGS & background blur */ + if (nvvfx_new_sdk && id != S_FX_BLUR) { + vfxErr = NvVFX_AllocateState(filter->handle, + &filter->stateObjectHandle); + if (NVCV_SUCCESS != vfxErr) + return log_nverror_destroy(filter, vfxErr); + vfxErr = NvVFX_SetStateObjectHandleArray( + filter->handle, NVVFX_STATE, + &filter->stateObjectHandle); + if (NVCV_SUCCESS != vfxErr) + return log_nverror_destroy(filter, vfxErr); + } + + if (!filter->effect) { + nvvfx_filter_destroy(filter); + return NULL; + } + + nvvfx_filter_update(filter, settings); + + return filter; +} + +static void *nv_greenscreen_filter_create(obs_data_t *settings, + obs_source_t *context) +{ + return nvvfx_filter_create(settings, context, S_FX_AIGS); +} + +static void *nv_blur_filter_create(obs_data_t *settings, obs_source_t *context) +{ + return nvvfx_filter_create(settings, context, S_FX_BLUR); +} + +static void *nv_background_blur_filter_create(obs_data_t *settings, + obs_source_t *context) +{ + return nvvfx_filter_create(settings, context, S_FX_BG_BLUR); +} + +static void nvvfx_filter_reset(void *data, calldata_t *calldata) +{ + struct nvvfx_data *filter = (struct nvvfx_data *)data; NvCV_Status vfxErr; os_atomic_set_bool(&filter->processing_stop, true); - // first destroy + // [A] first destroy if (filter->stream) { NvVFX_CudaStreamDestroy(filter->stream); } + if (filter->stream_blur) { + NvVFX_CudaStreamDestroy(filter->stream_blur); + } if (filter->handle) { if (filter->stateObjectHandle) { NvVFX_DeallocateState(filter->handle, @@ -172,50 +422,44 @@ static void nv_greenscreen_filter_reset(void *data, calldata_t *calldata) } NvVFX_DestroyEffect(filter->handle); } - // recreate - /* 1. Create FX */ - vfxErr = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, &filter->handle); - if (NVCV_SUCCESS != vfxErr) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error recreating AI Greenscreen FX; error %i: %s", - vfxErr, errString); - nv_greenscreen_filter_destroy(filter); + if (filter->handle_blur) { + NvVFX_DestroyEffect(filter->handle_blur); } - + // [B] recreate + /* 1. Create FX */ /* 2. Set models path & initialize CudaStream */ - char buffer[MAX_PATH]; - char modelDir[MAX_PATH]; - nvvfx_get_sdk_path(buffer, MAX_PATH); - size_t max_len = sizeof(buffer) / sizeof(char); - snprintf(modelDir, max_len, "%s\\models", buffer); - vfxErr = NvVFX_SetString(filter->handle, NVVFX_MODEL_DIRECTORY, - modelDir); - vfxErr = NvVFX_CudaStreamCreate(&filter->stream); - if (NVCV_SUCCESS != vfxErr) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error creating CUDA Stream; error %i: %s", vfxErr, - errString); - nv_greenscreen_filter_destroy(filter); - } - vfxErr = NvVFX_SetCudaStream(filter->handle, NVVFX_CUDA_STREAM, - filter->stream); - if (NVCV_SUCCESS != vfxErr) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error setting CUDA Stream %i", vfxErr); - nv_greenscreen_filter_destroy(filter); - } + if (!nvvfx_filter_create_internal(filter)) + return; /* 3. load FX */ - vfxErr = NvVFX_SetU32(filter->handle, NVVFX_MODE, filter->mode); - vfxErr = NvVFX_Load(filter->handle); - if (NVCV_SUCCESS != vfxErr) - error("Error loading AI Greenscreen FX %i", vfxErr); + if (filter->filter_id != S_FX_BLUR) { + vfxErr = NvVFX_SetU32(filter->handle, NVVFX_MODE, filter->mode); + if (NVCV_SUCCESS != vfxErr) + error("Error loading NVIDIA Video FX %i", vfxErr); + vfxErr = NvVFX_Load(filter->handle); + if (NVCV_SUCCESS != vfxErr) + error("Error loading NVIDIA Video FX %i", vfxErr); + vfxErr = NvVFX_ResetState(filter->handle, + filter->stateObjectHandle); + } + if (filter->filter_id != S_FX_AIGS) { + vfxErr = NvVFX_SetF32(filter->handle_blur, NVVFX_STRENGTH, + filter->strength); + if (NVCV_SUCCESS != vfxErr) + error("Error loading NVIDIA Video FX %i", vfxErr); + vfxErr = NvVFX_Load(filter->handle_blur); + if (NVCV_SUCCESS != vfxErr) + error("Error loading blur FX %i", vfxErr); + } filter->images_allocated = false; os_atomic_set_bool(&filter->processing_stop, false); } -static void init_images_greenscreen(struct nv_greenscreen_data *filter) +//---------------------------------------------------------------------------// +// intialization of images // + +static bool create_alpha_texture(struct nvvfx_data *filter) { NvCV_Status vfxErr; uint32_t width = filter->width; @@ -229,114 +473,199 @@ static void init_images_greenscreen(struct nv_greenscreen_data *filter) gs_texture_create(width, height, GS_A8, 1, NULL, 0); if (filter->alpha_texture == NULL) { error("Alpha texture couldn't be created"); - goto fail; + return false; } struct ID3D11Texture2D *d11texture = (struct ID3D11Texture2D *)gs_texture_get_obj( filter->alpha_texture); /* 2. Create NvCVImage which will hold final alpha texture. */ - if (!filter->dst_img && - (NvCVImage_Create(width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, - NVCV_GPU, 1, &filter->dst_img) != NVCV_SUCCESS)) { - goto fail; + if (!filter->dst_img) + NvCVImage_Create(width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, + NVCV_GPU, 1, &filter->dst_img); + vfxErr = NvCVImage_InitFromD3D11Texture(filter->dst_img, d11texture); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = NvCV_GetErrorStringFromCode(vfxErr); + error("Error passing dst ID3D11Texture to img; error %i: %s", + vfxErr, errString); + return false; } - vfxErr = NvCVImage_InitFromD3D11Texture(filter->dst_img, d11texture); + return true; +} + +static bool create_blur_texture(struct nvvfx_data *filter) +{ + NvCV_Status vfxErr; + uint32_t width = filter->width; + uint32_t height = filter->height; + + /* 1. Create blur texture */ + if (filter->blur_texture) { + gs_texture_destroy(filter->blur_texture); + } + filter->blur_texture = + gs_texture_create(width, height, GS_RGBA_UNORM, 1, NULL, 0); + if (filter->blur_texture == NULL) { + error("Blur texture couldn't be created"); + return false; + } + struct ID3D11Texture2D *d11texture = + (struct ID3D11Texture2D *)gs_texture_get_obj( + filter->blur_texture); + + /* 2. Create NvCVImage which will hold final blur texture */ + if (!filter->blur_dst_img) + NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, + NVCV_GPU, 1, &filter->blur_dst_img); + vfxErr = NvCVImage_InitFromD3D11Texture(filter->blur_dst_img, + d11texture); if (vfxErr != NVCV_SUCCESS) { const char *errString = NvCV_GetErrorStringFromCode(vfxErr); error("Error passing dst ID3D11Texture to img; error %i: %s", vfxErr, errString); - goto fail; + return false; } - /* 3. create texrenders */ + return true; +} + +static bool create_texrenders(struct nvvfx_data *filter) +{ if (filter->render) gs_texrender_destroy(filter->render); filter->render = gs_texrender_create( gs_get_format_from_space(filter->space), GS_ZS_NONE); if (!filter->render) { error("Failed to create render texrenderer"); - goto fail; + return false; } if (filter->render_unorm) gs_texrender_destroy(filter->render_unorm); filter->render_unorm = gs_texrender_create(GS_BGRA_UNORM, GS_ZS_NONE); if (!filter->render_unorm) { error("Failed to create render_unorm texrenderer"); - goto fail; + return false; } - /* 4. Create and allocate BGR NvCVImage (fx src). */ - if (filter->BGR_src_img) { - if (NvCVImage_Realloc(filter->BGR_src_img, width, height, - NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, - 1) != NVCV_SUCCESS) { - goto fail; - } + return true; +} + +static bool init_blur_images(struct nvvfx_data *filter) +{ + uint32_t width = filter->width; + uint32_t height = filter->height; + + /* 1. Create and allocate Blur BGR NvCVimage (blur FX dst) */ + NvCVImage_Create(width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, + NVCV_GPU, 1, &filter->blur_BGR_dst_img); + NvCVImage_Alloc(filter->blur_BGR_dst_img, width, height, NVCV_BGR, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); + + /* 2. Create dst NvCVImage */ + if (filter->RGBA_dst_img) { + NvCVImage_Realloc(filter->RGBA_dst_img, width, height, + NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); } else { - if (NvCVImage_Create(width, height, NVCV_BGR, NVCV_U8, - NVCV_CHUNKY, NVCV_GPU, 1, - &filter->BGR_src_img) != NVCV_SUCCESS) { + NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, NVCV_CHUNKY, + NVCV_GPU, 1, &filter->RGBA_dst_img); + NvCVImage_Alloc(filter->RGBA_dst_img, width, height, NVCV_RGBA, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); + } + + /* 3. Set input & output images for nv blur FX */ + NvVFX_SetImage(filter->handle_blur, NVVFX_INPUT_IMAGE, + filter->BGR_src_img); + NvVFX_SetImage(filter->handle_blur, NVVFX_INPUT_IMAGE_1, + filter->A_dst_img); + NvVFX_SetImage(filter->handle_blur, NVVFX_OUTPUT_IMAGE, + filter->blur_BGR_dst_img); + + if (NvVFX_Load(filter->handle_blur) != NVCV_SUCCESS) { + error("Error loading blur FX"); + return false; + } + + return true; +} + +static void init_images(struct nvvfx_data *filter) +{ + NvCV_Status vfxErr; + uint32_t width = filter->width; + uint32_t height = filter->height; + + /* 1. Create alpha texture & associated NvCVImage */ + if (filter->filter_id == S_FX_BG_BLUR || + filter->filter_id == S_FX_AIGS) { + if (!create_alpha_texture(filter)) goto fail; - } - if (NvCVImage_Alloc(filter->BGR_src_img, width, height, - NVCV_BGR, NVCV_U8, NVCV_CHUNKY, NVCV_GPU, - 1) != NVCV_SUCCESS) { + } + /* 2. Create blur texture & associated NvCVImage */ + if (filter->filter_id == S_FX_BG_BLUR || + filter->filter_id == S_FX_BLUR) { + if (!create_blur_texture(filter)) goto fail; - } } + /* 3. Create texrenders */ + if (!create_texrenders(filter)) + goto fail; - /* 5. Create and allocate Alpha NvCVimage (fx dst). */ - if (filter->A_dst_img) { - if (NvCVImage_Realloc(filter->A_dst_img, width, height, NVCV_A, - NVCV_U8, NVCV_CHUNKY, NVCV_GPU, - 1) != NVCV_SUCCESS) { - goto fail; - } + /* 4. Create and allocate BGR NvCVImage (alpha mask FX src) */ + if (filter->BGR_src_img) { + NvCVImage_Realloc(filter->BGR_src_img, width, height, NVCV_BGR, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); } else { - if (NvCVImage_Create(width, height, NVCV_A, NVCV_U8, - NVCV_CHUNKY, NVCV_GPU, 1, - &filter->A_dst_img) != NVCV_SUCCESS) { - goto fail; - } - if (NvCVImage_Alloc(filter->A_dst_img, width, height, NVCV_A, - NVCV_U8, NVCV_CHUNKY, NVCV_GPU, - 1) != NVCV_SUCCESS) { - goto fail; - } + NvCVImage_Create(width, height, NVCV_BGR, NVCV_U8, NVCV_CHUNKY, + NVCV_GPU, 1, &filter->BGR_src_img); + NvCVImage_Alloc(filter->BGR_src_img, width, height, NVCV_BGR, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); } - /* 6. Create stage NvCVImage which will be used as buffer for transfer */ - if (filter->stage) { - if (NvCVImage_Realloc(filter->stage, width, height, NVCV_RGBA, - NVCV_U8, NVCV_PLANAR, NVCV_GPU, - 1) != NVCV_SUCCESS) { - goto fail; - } + /* 5. Create and allocate Alpha NvCVimage (mask fx dst). */ + if (filter->A_dst_img) { + NvCVImage_Realloc(filter->A_dst_img, width, height, NVCV_A, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); } else { - if (NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, - NVCV_PLANAR, NVCV_GPU, 1, - &filter->stage) != NVCV_SUCCESS) { - goto fail; - } - if (NvCVImage_Alloc(filter->stage, width, height, NVCV_RGBA, - NVCV_U8, NVCV_PLANAR, NVCV_GPU, - 1) != NVCV_SUCCESS) { - goto fail; - } + NvCVImage_Create(width, height, NVCV_A, NVCV_U8, NVCV_CHUNKY, + NVCV_GPU, 1, &filter->A_dst_img); + NvCVImage_Alloc(filter->A_dst_img, width, height, NVCV_A, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); } - /* 7. Set input & output images for nv FX. */ - if (NvVFX_SetImage(filter->handle, NVVFX_INPUT_IMAGE, - filter->BGR_src_img) != NVCV_SUCCESS) { + /* 6. Create stage NvCVImage which will be used as buffer for transfer */ + vfxErr = NvCVImage_Create(width, height, NVCV_RGBA, NVCV_U8, + NVCV_CHUNKY, NVCV_GPU, 1, &filter->stage); + vfxErr = NvCVImage_Alloc(filter->stage, width, height, NVCV_RGBA, + NVCV_U8, NVCV_CHUNKY, NVCV_GPU, 1); + if (vfxErr != NVCV_SUCCESS) { goto fail; } - if (NvVFX_SetImage(filter->handle, NVVFX_OUTPUT_IMAGE, - filter->A_dst_img) != NVCV_SUCCESS) { - goto fail; + + /* 7. Init blur images */ + if (filter->filter_id == S_FX_BLUR || + filter->filter_id == S_FX_BG_BLUR) { + if (!init_blur_images(filter)) + goto fail; } + /* 8. Pass settings for AIGS (AI Greenscreen) FX */ + if (filter->filter_id == S_FX_BG_BLUR || + filter->filter_id == S_FX_AIGS) { + NvVFX_SetImage(filter->handle, NVVFX_INPUT_IMAGE, + filter->BGR_src_img); + NvVFX_SetImage(filter->handle, NVVFX_OUTPUT_IMAGE, + filter->A_dst_img); + if (filter->width) + NvVFX_SetU32(filter->handle, NVVFX_MAX_INPUT_WIDTH, + filter->width); + if (filter->height) + NvVFX_SetU32(filter->handle, NVVFX_MAX_INPUT_HEIGHT, + filter->height); + vfxErr = NvVFX_Load(filter->handle); + if (NVCV_SUCCESS != vfxErr) + error("Error loading AI Greenscreen FX %i", vfxErr); + } filter->images_allocated = true; return; fail: @@ -345,11 +674,28 @@ static void init_images_greenscreen(struct nv_greenscreen_data *filter) return; } -static bool process_texture_greenscreen(struct nv_greenscreen_data *filter) +//---------------------------------------------------------------------------// +// video processing functions // + +static bool process_texture(struct nvvfx_data *filter) { + enum nvvfx_fx_id id = filter->filter_id; + CUstream process_stream; + /* 1. Map src img holding texture. */ + switch (id) { + case S_FX_AIGS: + case S_FX_BG_BLUR: + process_stream = filter->stream; + break; + case S_FX_BLUR: + process_stream = filter->stream_blur; + break; + default: + process_stream = NULL; + } NvCV_Status vfxErr = - NvCVImage_MapResource(filter->src_img, filter->stream); + NvCVImage_MapResource(filter->src_img, process_stream); if (vfxErr != NVCV_SUCCESS) { const char *errString = NvCV_GetErrorStringFromCode(vfxErr); error("Error mapping resource for source texture; error %i : %s", @@ -359,14 +705,14 @@ static bool process_texture_greenscreen(struct nv_greenscreen_data *filter) /* 2. Convert to BGR. */ vfxErr = NvCVImage_Transfer(filter->src_img, filter->BGR_src_img, 1.0f, - filter->stream, filter->stage); + filter->stream_blur, filter->stage); if (vfxErr != NVCV_SUCCESS) { const char *errString = NvCV_GetErrorStringFromCode(vfxErr); error("Error converting src to BGR img; error %i: %s", vfxErr, errString); goto fail; } - vfxErr = NvCVImage_UnmapResource(filter->src_img, filter->stream); + vfxErr = NvCVImage_UnmapResource(filter->src_img, process_stream); if (vfxErr != NVCV_SUCCESS) { const char *errString = NvCV_GetErrorStringFromCode(vfxErr); error("Error unmapping resource for src texture; error %i: %s", @@ -374,214 +720,123 @@ static bool process_texture_greenscreen(struct nv_greenscreen_data *filter) goto fail; } - /* 3. run RTX fx */ - vfxErr = NvVFX_Run(filter->handle, 1); - if (vfxErr != NVCV_SUCCESS) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error running the FX; error %i: %s", vfxErr, errString); - if (vfxErr == NVCV_ERR_CUDA) - nv_greenscreen_filter_reset(filter, NULL); - } - - /* 4. Map dst texture before transfer from dst img provided by FX */ - vfxErr = NvCVImage_MapResource(filter->dst_img, filter->stream); - if (vfxErr != NVCV_SUCCESS) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error mapping resource for dst texture; error %i: %s", - vfxErr, errString); - goto fail; - } - - vfxErr = NvCVImage_Transfer(filter->A_dst_img, filter->dst_img, 1.0f, - filter->stream, filter->stage); - if (vfxErr != NVCV_SUCCESS) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error transferring mask to alpha texture; error %i: %s ", - vfxErr, errString); - goto fail; - } - - vfxErr = NvCVImage_UnmapResource(filter->dst_img, filter->stream); - if (vfxErr != NVCV_SUCCESS) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error unmapping resource for dst texture; error %i: %s", - vfxErr, errString); - goto fail; - } - - return true; -fail: - os_atomic_set_bool(&filter->processing_stop, true); - return false; -} - -static void *nv_greenscreen_filter_create(obs_data_t *settings, - obs_source_t *context) -{ - struct nv_greenscreen_data *filter = - (struct nv_greenscreen_data *)bzalloc(sizeof(*filter)); - if (!nvvfx_loaded) { - nv_greenscreen_filter_destroy(filter); - return NULL; - } - - NvCV_Status vfxErr; - filter->context = context; - filter->mode = -1; // should be 0 or 1; -1 triggers an update - filter->images_allocated = false; - filter->processed_frame = true; // start processing when false - filter->width = 0; - filter->height = 0; - filter->initial_render = false; - os_atomic_set_bool(&filter->processing_stop, false); - filter->handler = NULL; - filter->processing_interval = 1; - filter->processing_counter = 0; - - /* 1. Create FX */ - vfxErr = NvVFX_CreateEffect(NVVFX_FX_GREEN_SCREEN, &filter->handle); - if (NVCV_SUCCESS != vfxErr) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error creating AI Greenscreen FX; error %i: %s", vfxErr, - errString); - nv_greenscreen_filter_destroy(filter); - return NULL; - } - - /* 2. Set models path & initialize CudaStream */ - char buffer[MAX_PATH]; - char modelDir[MAX_PATH]; - nvvfx_get_sdk_path(buffer, MAX_PATH); - size_t max_len = sizeof(buffer) / sizeof(char); - snprintf(modelDir, max_len, "%s\\models", buffer); - vfxErr = NvVFX_SetString(filter->handle, NVVFX_MODEL_DIRECTORY, - modelDir); - vfxErr = NvVFX_CudaStreamCreate(&filter->stream); - if (NVCV_SUCCESS != vfxErr) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error creating CUDA Stream; error %i: %s", vfxErr, - errString); - nv_greenscreen_filter_destroy(filter); - return NULL; - } - vfxErr = NvVFX_SetCudaStream(filter->handle, NVVFX_CUDA_STREAM, - filter->stream); - if (NVCV_SUCCESS != vfxErr) { - const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error setting CUDA Stream %i", vfxErr); - nv_greenscreen_filter_destroy(filter); - return NULL; - } - /* check sdk version */ - if (NvVFX_GetVersion(&filter->version) == NVCV_SUCCESS) { - uint8_t major = (filter->version >> 24) & 0xff; - uint8_t minor = (filter->version >> 16) & 0x00ff; - uint8_t build = (filter->version >> 8) & 0x0000ff; - uint8_t revision = (filter->version >> 0) & 0x000000ff; - // sanity check - nvvfx_new_sdk = filter->version >= MIN_VFX_SDK_VERSION && - nvvfx_new_sdk; - } - - /* 3. Load alpha mask effect. */ - char *effect_path = obs_module_file("rtx_greenscreen.effect"); - - obs_enter_graphics(); - filter->effect = gs_effect_create_from_file(effect_path, NULL); - bfree(effect_path); - if (filter->effect) { - filter->mask_param = - gs_effect_get_param_by_name(filter->effect, "mask"); - filter->image_param = - gs_effect_get_param_by_name(filter->effect, "image"); - filter->threshold_param = gs_effect_get_param_by_name( - filter->effect, "threshold"); - filter->multiplier_param = gs_effect_get_param_by_name( - filter->effect, "multiplier"); + /* 3. Run AIGS (AI Greenscreen) fx */ + if (id != S_FX_BLUR) { + vfxErr = NvVFX_Run(filter->handle, 1); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = + NvCV_GetErrorStringFromCode(vfxErr); + error("Error running the AIGS FX; error %i: %s", vfxErr, + errString); + if (vfxErr == NVCV_ERR_CUDA) + nvvfx_filter_reset(filter, NULL); + } } - obs_leave_graphics(); - /* 4. Allocate state for the effect */ - if (nvvfx_new_sdk) { - vfxErr = NvVFX_AllocateState(filter->handle, - &filter->stateObjectHandle); - if (NVCV_SUCCESS != vfxErr) { + if (id != S_FX_AIGS) { + /* 4. BLUR FX */ + /* 4a. Run BLUR FX except for AIGS */ + vfxErr = NvVFX_Run(filter->handle_blur, 1); + if (vfxErr != NVCV_SUCCESS) { const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error allocating FX state %i", vfxErr); - nv_greenscreen_filter_destroy(filter); - return NULL; + error("Error running the BLUR FX; error %i: %s", vfxErr, + errString); + if (vfxErr == NVCV_ERR_CUDA) + nvvfx_filter_reset(filter, NULL); } - vfxErr = NvVFX_SetStateObjectHandleArray( - filter->handle, NVVFX_STATE, - &filter->stateObjectHandle); - if (NVCV_SUCCESS != vfxErr) { + /* 4b. Transfer blur result to an intermediate dst [RGBA, chunky, u8] */ + vfxErr = NvCVImage_Transfer(filter->blur_BGR_dst_img, + filter->RGBA_dst_img, 1.0f, + filter->stream_blur, filter->stage); + if (vfxErr != NVCV_SUCCESS) { + error("Error transferring blurred to intermediate img [RGBA, chunky, u8], error %i, ", + vfxErr); + goto fail; + } + /* 5. Map blur dst texture before transfer from dst img provided by FX */ + vfxErr = NvCVImage_MapResource(filter->blur_dst_img, + filter->stream_blur); + if (vfxErr != NVCV_SUCCESS) { const char *errString = NvCV_GetErrorStringFromCode(vfxErr); - error("Error setting FX state %i", vfxErr); - nv_greenscreen_filter_destroy(filter); - return NULL; + error("Error mapping resource for dst texture; error %i: %s", + vfxErr, errString); + goto fail; } - } - - if (!filter->effect) { - nv_greenscreen_filter_destroy(filter); - return NULL; - } - /*---------------------------------------- */ + vfxErr = NvCVImage_Transfer(filter->RGBA_dst_img, + filter->blur_dst_img, 1.0f, + filter->stream_blur, filter->stage); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = + NvCV_GetErrorStringFromCode(vfxErr); + error("Error transferring mask to alpha texture; error %i: %s ", + vfxErr, errString); + goto fail; + } - nv_greenscreen_filter_update(filter, settings); + vfxErr = NvCVImage_UnmapResource(filter->blur_dst_img, + filter->stream_blur); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = + NvCV_GetErrorStringFromCode(vfxErr); + error("Error unmapping resource for dst texture; error %i: %s", + vfxErr, errString); + goto fail; + } + } else { + /* 4. Map dst texture before transfer from dst img provided by AIGS FX */ + vfxErr = NvCVImage_MapResource(filter->dst_img, filter->stream); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = + NvCV_GetErrorStringFromCode(vfxErr); + error("Error mapping resource for dst texture; error %i: %s", + vfxErr, errString); + goto fail; + } - return filter; -} + vfxErr = NvCVImage_Transfer(filter->A_dst_img, filter->dst_img, + 1.0f, filter->stream, + filter->stage); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = + NvCV_GetErrorStringFromCode(vfxErr); + error("Error transferring mask to alpha texture; error %i: %s ", + vfxErr, errString); + goto fail; + } -static obs_properties_t *nv_greenscreen_filter_properties(void *data) -{ - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; - obs_properties_t *props = obs_properties_create(); - obs_property_t *mode = obs_properties_add_list(props, S_MODE, TEXT_MODE, - OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(mode, TEXT_MODE_QUALITY, S_MODE_QUALITY); - obs_property_list_add_int(mode, TEXT_MODE_PERF, S_MODE_PERF); - obs_property_t *threshold = obs_properties_add_float_slider( - props, S_THRESHOLDFX, TEXT_MODE_THRESHOLD, 0, 1, 0.05); - obs_property_t *partial = obs_properties_add_int_slider( - props, S_PROCESSING, TEXT_PROCESSING, 1, 4, 1); - obs_property_set_long_description(partial, TEXT_PROCESSING_HINT); - unsigned int version = get_lib_version(); - if (version && version < MIN_VFX_SDK_VERSION) { - obs_property_t *warning = obs_properties_add_text( - props, "deprecation", NULL, OBS_TEXT_INFO); - obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING); - obs_property_set_long_description(warning, TEXT_DEPRECATION); + vfxErr = NvCVImage_UnmapResource(filter->dst_img, + filter->stream); + if (vfxErr != NVCV_SUCCESS) { + const char *errString = + NvCV_GetErrorStringFromCode(vfxErr); + error("Error unmapping resource for dst texture; error %i: %s", + vfxErr, errString); + goto fail; + } } - return props; -} - -static void nv_greenscreen_filter_defaults(obs_data_t *settings) -{ - obs_data_set_default_int(settings, S_MODE, S_MODE_QUALITY); - obs_data_set_default_double(settings, S_THRESHOLDFX, - S_THRESHOLDFX_DEFAULT); - obs_data_set_default_int(settings, S_PROCESSING, 1); + return true; +fail: + os_atomic_set_bool(&filter->processing_stop, true); + return false; } static struct obs_source_frame * -nv_greenscreen_filter_video(void *data, struct obs_source_frame *frame) +nvvfx_filter_video(void *data, struct obs_source_frame *frame) { - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; + struct nvvfx_data *filter = (struct nvvfx_data *)data; filter->got_new_frame = true; return frame; } -static void nv_greenscreen_filter_tick(void *data, float t) +static void nvvfx_filter_tick(void *data, float t) { UNUSED_PARAMETER(t); - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; + struct nvvfx_data *filter = (struct nvvfx_data *)data; if (filter->processing_stop) { return; @@ -603,11 +858,14 @@ static void nv_greenscreen_filter_tick(void *data, float t) } /* minimum size supported by SDK is (512,288) */ - filter->target_valid = cx >= 512 && cy >= 288; - if (!filter->target_valid) { - error("Size must be larger than (512,288)"); - return; + if (filter->filter_id != S_FX_BLUR) { + filter->target_valid = cx >= 512 && cy >= 288; + if (!filter->target_valid) { + error("Size must be larger than (512,288)"); + return; + } } + if (cx != filter->width && cy != filter->height) { filter->images_allocated = false; filter->width = cx; @@ -615,7 +873,7 @@ static void nv_greenscreen_filter_tick(void *data, float t) } if (!filter->images_allocated) { obs_enter_graphics(); - init_images_greenscreen(filter); + init_images(filter); obs_leave_graphics(); filter->initial_render = false; } @@ -666,9 +924,8 @@ get_tech_name_and_multiplier(enum gs_color_space current_space, return tech_name; } -static void draw_greenscreen(struct nv_greenscreen_data *filter) +static void draw_greenscreen_blur(struct nvvfx_data *filter, bool has_blur) { - /* Render alpha mask */ const enum gs_color_space source_space = filter->space; float multiplier; const char *technique = get_tech_name_and_multiplier( @@ -678,12 +935,19 @@ static void draw_greenscreen(struct nv_greenscreen_data *filter) if (obs_source_process_filter_begin_with_color_space( filter->context, format, source_space, OBS_ALLOW_DIRECT_RENDERING)) { - gs_effect_set_texture(filter->mask_param, - filter->alpha_texture); + if (has_blur) { + gs_effect_set_texture_srgb(filter->blur_param, + filter->blur_texture); + } else { + gs_effect_set_texture(filter->mask_param, + filter->alpha_texture); + gs_effect_set_float(filter->threshold_param, + filter->threshold); + } gs_effect_set_texture_srgb( filter->image_param, gs_texrender_get_texture(filter->render)); - gs_effect_set_float(filter->threshold_param, filter->threshold); + gs_effect_set_float(filter->multiplier_param, multiplier); gs_blend_state_push(); @@ -696,12 +960,12 @@ static void draw_greenscreen(struct nv_greenscreen_data *filter) } } -static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) +static void nvvfx_filter_render(void *data, gs_effect_t *effect, bool has_blur) { NvCV_Status vfxErr; - struct nv_greenscreen_data *filter = (struct nv_greenscreen_data *)data; + struct nvvfx_data *filter = (struct nvvfx_data *)data; - if (filter->processing_stop) { + if (filter->processing_stop || (has_blur && filter->strength == 0)) { obs_source_skip_video_filter(filter->context); return; } @@ -717,14 +981,14 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) /* Render processed image from earlier in the frame */ if (filter->processed_frame) { - draw_greenscreen(filter); + draw_greenscreen_blur(filter, has_blur); return; } if (parent && !filter->handler) { filter->handler = obs_source_get_signal_handler(parent); signal_handler_connect(filter->handler, "update", - nv_greenscreen_filter_reset, filter); + nvvfx_filter_reset, filter); } /* 1. Render to retrieve texture. */ @@ -750,7 +1014,7 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) if (filter->space != source_space) { filter->space = source_space; - init_images_greenscreen(filter); + init_images(filter); filter->initial_render = false; } @@ -789,16 +1053,27 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) const char *tech_name = "ConvertUnorm"; float multiplier = 1.f; - switch (source_space) { - case GS_CS_709_EXTENDED: - tech_name = "ConvertUnormTonemap"; - break; - case GS_CS_709_SCRGB: - tech_name = "ConvertUnormMultiplyTonemap"; - multiplier = - 80.0f / obs_get_video_sdr_white_level(); + if (!has_blur) { + switch (source_space) { + case GS_CS_709_EXTENDED: + tech_name = "ConvertUnormTonemap"; + break; + case GS_CS_709_SCRGB: + tech_name = + "ConvertUnormMultiplyTonemap"; + multiplier = + 80.0f / + obs_get_video_sdr_white_level(); + } + } else { + switch (source_space) { + case GS_CS_709_SCRGB: + tech_name = "ConvertUnormMultiply"; + multiplier = + 80.0f / + obs_get_video_sdr_white_level(); + } } - gs_effect_set_texture_srgb( filter->image_param, gs_texrender_get_texture(render)); @@ -862,19 +1137,23 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) if (filter->initial_render && filter->images_allocated) { bool draw = true; if (!async || filter->got_new_frame) { - if (filter->processing_counter % - filter->processing_interval == - 0) { - draw = process_texture_greenscreen(filter); - filter->processing_counter = 1; + if (!has_blur) { + if (filter->processing_counter % + filter->processing_interval == + 0) { + draw = process_texture(filter); + filter->processing_counter = 1; + } else { + filter->processing_counter++; + } } else { - filter->processing_counter++; + draw = process_texture(filter); } filter->got_new_frame = false; } if (draw) { - draw_greenscreen(filter); + draw_greenscreen_blur(filter, has_blur); filter->processed_frame = true; } } else { @@ -883,6 +1162,82 @@ static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) UNUSED_PARAMETER(effect); } +static void nv_greenscreen_filter_render(void *data, gs_effect_t *effect) +{ + nvvfx_filter_render(data, effect, false); +} + +static void nv_blur_filter_render(void *data, gs_effect_t *effect) +{ + nvvfx_filter_render(data, effect, true); +} + +static enum gs_color_space +nvvfx_filter_get_color_space(void *data, size_t count, + const enum gs_color_space *preferred_spaces) +{ + const enum gs_color_space potential_spaces[] = { + GS_CS_SRGB, + GS_CS_SRGB_16F, + GS_CS_709_EXTENDED, + }; + + struct nvvfx_data *const filter = data; + const enum gs_color_space source_space = obs_source_get_color_space( + obs_filter_get_target(filter->context), + OBS_COUNTOF(potential_spaces), potential_spaces); + + enum gs_color_space space = source_space; + for (size_t i = 0; i < count; ++i) { + space = preferred_spaces[i]; + if (space == source_space) + break; + } + + return space; +} + +static obs_properties_t *nvvfx_filter_properties(void *data) +{ + struct nvvfx_data *filter = (struct nvvfx_data *)data; + obs_properties_t *props = obs_properties_create(); + if (filter->filter_id != S_FX_AIGS) { + obs_property_t *strength = obs_properties_add_float_slider( + props, S_STRENGTH, TEXT_MODE_BLUR_STRENGTH, 0, 1, 0.05); + } else { + obs_property_t *mode = obs_properties_add_list( + props, S_MODE, TEXT_MODE, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(mode, TEXT_MODE_QUALITY, + S_MODE_QUALITY); + obs_property_list_add_int(mode, TEXT_MODE_PERF, S_MODE_PERF); + obs_property_t *threshold = obs_properties_add_float_slider( + props, S_THRESHOLDFX, TEXT_MODE_THRESHOLD, 0, 1, 0.05); + obs_property_t *partial = obs_properties_add_int_slider( + props, S_PROCESSING, TEXT_PROCESSING, 1, 4, 1); + obs_property_set_long_description(partial, + TEXT_PROCESSING_HINT); + } + unsigned int version = get_lib_version(); + if (version && version < MIN_VFX_SDK_VERSION) { + obs_property_t *warning = obs_properties_add_text( + props, "deprecation", NULL, OBS_TEXT_INFO); + obs_property_text_set_info_type(warning, OBS_TEXT_INFO_WARNING); + obs_property_set_long_description(warning, TEXT_DEPRECATION); + } + + return props; +} + +static void nvvfx_filter_defaults(obs_data_t *settings) +{ + obs_data_set_default_int(settings, S_MODE, S_MODE_QUALITY); + obs_data_set_default_double(settings, S_THRESHOLDFX, + S_THRESHOLDFX_DEFAULT); + obs_data_set_default_int(settings, S_PROCESSING, 1); + obs_data_set_default_double(settings, S_STRENGTH, S_STRENGTH_DEFAULT); +} + bool load_nvidia_vfx(void) { bool old_sdk_loaded = false; @@ -1031,36 +1386,10 @@ bool load_nvidia_vfx(void) return false; } -#ifdef LIBNVVFX_ENABLED void unload_nvidia_vfx(void) { release_nv_vfx(); } -#endif - -static enum gs_color_space nv_greenscreen_filter_get_color_space( - void *data, size_t count, const enum gs_color_space *preferred_spaces) -{ - const enum gs_color_space potential_spaces[] = { - GS_CS_SRGB, - GS_CS_SRGB_16F, - GS_CS_709_EXTENDED, - }; - - struct nv_greenscreen_data *const filter = data; - const enum gs_color_space source_space = obs_source_get_color_space( - obs_filter_get_target(filter->context), - OBS_COUNTOF(potential_spaces), potential_spaces); - - enum gs_color_space space = source_space; - for (size_t i = 0; i < count; ++i) { - space = preferred_spaces[i]; - if (space == source_space) - break; - } - - return space; -} struct obs_source_info nvidia_greenscreen_filter_info = { .id = "nv_greenscreen_filter", @@ -1068,12 +1397,44 @@ struct obs_source_info nvidia_greenscreen_filter_info = { .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB, .get_name = nv_greenscreen_filter_name, .create = nv_greenscreen_filter_create, - .destroy = nv_greenscreen_filter_destroy, - .get_defaults = nv_greenscreen_filter_defaults, - .get_properties = nv_greenscreen_filter_properties, - .update = nv_greenscreen_filter_update, - .filter_video = nv_greenscreen_filter_video, + .destroy = nvvfx_filter_destroy, + .get_defaults = nvvfx_filter_defaults, + .get_properties = nvvfx_filter_properties, + .update = nvvfx_filter_update, + .filter_video = nvvfx_filter_video, .video_render = nv_greenscreen_filter_render, - .video_tick = nv_greenscreen_filter_tick, - .video_get_color_space = nv_greenscreen_filter_get_color_space, + .video_tick = nvvfx_filter_tick, + .video_get_color_space = nvvfx_filter_get_color_space, +}; + +struct obs_source_info nvidia_blur_filter_info = { + .id = "nv_blur_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB, + .get_name = nv_blur_filter_name, + .create = nv_blur_filter_create, + .destroy = nvvfx_filter_destroy, + .get_defaults = nvvfx_filter_defaults, + .get_properties = nvvfx_filter_properties, + .update = nvvfx_filter_update, + .filter_video = nvvfx_filter_video, + .video_render = nv_blur_filter_render, + .video_tick = nvvfx_filter_tick, + .video_get_color_space = nvvfx_filter_get_color_space, +}; + +struct obs_source_info nvidia_background_blur_filter_info = { + .id = "nv_background_blur_filter", + .type = OBS_SOURCE_TYPE_FILTER, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB, + .get_name = nv_background_blur_filter_name, + .create = nv_background_blur_filter_create, + .destroy = nvvfx_filter_destroy, + .get_defaults = nvvfx_filter_defaults, + .get_properties = nvvfx_filter_properties, + .update = nvvfx_filter_update, + .filter_video = nvvfx_filter_video, + .video_render = nv_blur_filter_render, + .video_tick = nvvfx_filter_tick, + .video_get_color_space = nvvfx_filter_get_color_space, }; From 14f44dfe06288c5148e0c07e0c86fe7c6853c6ca Mon Sep 17 00:00:00 2001 From: Michael Gillett <51103663+migillett@users.noreply.github.com> Date: Fri, 9 Aug 2024 11:24:42 -0400 Subject: [PATCH 0447/1073] rtmp-services: Add NFHS Network --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index a179122bf1fb0d..406d84707bfea7 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 260, + "version": 261, "files": [ { "name": "services.json", - "version": 260 + "version": 261 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 49792bebf65e20..b32abc2be72bc9 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2942,6 +2942,28 @@ "keyint": 1, "bframes": 0 } + }, + { + "name": "NFHS Network", + "more_info_link": "https://support.nfhsnetwork.com/hc/en-us", + "stream_key_link": "https://console.nfhsnetwork.com/nfhs-events/", + "servers": [ + { + "name": "Manual Broadcasts", + "url": "rtmp://video.nfhsnetwork.com/manual" + } + ], + "recommended": { + "supported resolutions": [ + "1920x1080", + "1280x720", + "640x360" + ], + "max fps": 60 + }, + "supported video codecs": [ + "h264" + ] } ] } From 200abd3af00ca6e40d6aaa3fef93985c096f3515 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Wed, 21 Aug 2024 09:54:28 +0200 Subject: [PATCH 0448/1073] UI: Force UpdateEditMenu on UI Thread --- UI/window-basic-main.cpp | 10 +++++++--- UI/window-basic-main.hpp | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 64964f0b8b5fe5..91bac659650bec 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1993,7 +1993,7 @@ void OBSBasic::InitOBSCallbacks() { ProfileScope("OBSBasic::InitOBSCallbacks"); - signalHandlers.reserve(signalHandlers.size() + 7); + signalHandlers.reserve(signalHandlers.size() + 9); signalHandlers.emplace_back(obs_get_signal_handler(), "source_create", OBSBasic::SourceCreated, this); signalHandlers.emplace_back(obs_get_signal_handler(), "source_remove", @@ -2014,13 +2014,17 @@ void OBSBasic::InitOBSCallbacks() signalHandlers.emplace_back( obs_get_signal_handler(), "source_filter_add", [](void *data, calldata_t *) { - static_cast(data)->UpdateEditMenu(); + QMetaObject::invokeMethod(static_cast(data), + "UpdateEditMenu", + Qt::QueuedConnection); }, this); signalHandlers.emplace_back( obs_get_signal_handler(), "source_filter_remove", [](void *data, calldata_t *) { - static_cast(data)->UpdateEditMenu(); + QMetaObject::invokeMethod(static_cast(data), + "UpdateEditMenu", + Qt::QueuedConnection); }, this); } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index aaca4023784015..5f6fea05239873 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -749,6 +749,8 @@ public slots: void PauseRecording(); void UnpauseRecording(); + void UpdateEditMenu(); + private slots: void on_actionMainUndo_triggered(); @@ -1045,8 +1047,6 @@ private slots: obs_data_array_t *undo_array, obs_data_array_t *redo_array); - void UpdateEditMenu(); - void SetDisplayAffinity(QWindow *window); QColor GetSelectionColor() const; From ec5f499cb305e88104136bbcb4bd3d523e16faec Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 18 Aug 2024 02:21:15 +0200 Subject: [PATCH 0449/1073] UI: Inline macOS 13 check --- UI/platform-osx.mm | 9 --------- UI/platform.hpp | 1 - UI/window-basic-main.cpp | 6 +++++- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/UI/platform-osx.mm b/UI/platform-osx.mm index 07f1d77b8ceccf..75f4adfba71c40 100644 --- a/UI/platform-osx.mm +++ b/UI/platform-osx.mm @@ -133,15 +133,6 @@ void SetAlwaysOnTop(QWidget *window, bool enable) window->show(); } -bool shouldCreateDefaultAudioSource(void) -{ - if (@available(macOS 13, *)) { - return false; - } else { - return true; - } -} - bool SetDisplayAffinitySupported(void) { // Not implemented yet diff --git a/UI/platform.hpp b/UI/platform.hpp index 73e15d4af236fd..ecea323c8f17f3 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -101,7 +101,6 @@ void InstallNSApplicationSubclass(); void InstallNSThreadLocks(); void disableColorSpaceConversion(QWidget *window); void SetMacOSDarkMode(bool dark); -bool shouldCreateDefaultAudioSource(); MacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, bool prompt_for_permission); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 91bac659650bec..0d0b0df805ddc0 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1023,7 +1023,11 @@ void OBSBasic::CreateFirstRunSources() bool hasInputAudio = HasAudioDevices(App()->InputAudioSource()); #ifdef __APPLE__ - hasDesktopAudio = hasDesktopAudio && shouldCreateDefaultAudioSource(); + /* On macOS 13 and above, the SCK based audio capture provides a + * better alternative to the device-based audio capture. */ + if (__builtin_available(macOS 13.0, *)) { + hasDesktopAudio = false; + } #endif if (hasDesktopAudio) From 15038232c5744ccb7a1cdc21798b29ee6dd83b1c Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Wed, 21 Aug 2024 15:47:47 +0900 Subject: [PATCH 0450/1073] obs-ffmpeg: Remove unexisting class name declaration The class `Logger` was declared but it is never defined nor used. --- plugins/obs-ffmpeg/obs-ffmpeg-url.h | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-url.h b/plugins/obs-ffmpeg/obs-ffmpeg-url.h index 8bf88e96537d97..32fc1f5ef3a29f 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-url.h +++ b/plugins/obs-ffmpeg/obs-ffmpeg-url.h @@ -137,6 +137,5 @@ enum type { debug = 7 //issue w/ libobs so LOG_DEBUG is removed }; } -class Logger; } // namespace srt_logging #endif From 60a45d3aa3b5900216f0e069e5af901d93013307 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Wed, 21 Aug 2024 21:12:13 +0900 Subject: [PATCH 0451/1073] UI: Use std::clamp instead of macro --- UI/volume-control.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index db994b3c03285e..0111858ab1671f 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -15,7 +15,6 @@ using namespace std; -#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) #define FADER_PRECISION 4096.0 // Size of the audio indicator in pixels @@ -1004,8 +1003,9 @@ VolumeMeter::calculateBallisticsForChannel(int channelNr, uint64_t ts, // 20 dB / 1.7 seconds for Medium Profile (Type I PPM) // 24 dB / 2.8 seconds for Slow Profile (Type II PPM) float decay = float(peakDecayRate * timeSinceLastRedraw); - displayPeak[channelNr] = CLAMP(displayPeak[channelNr] - decay, - currentPeak[channelNr], 0); + displayPeak[channelNr] = + std::clamp(displayPeak[channelNr] - decay, + currentPeak[channelNr], 0.f); } if (currentPeak[channelNr] >= displayPeakHold[channelNr] || @@ -1060,8 +1060,8 @@ VolumeMeter::calculateBallisticsForChannel(int channelNr, uint64_t ts, (timeSinceLastRedraw / magnitudeIntegrationTime) * 0.99); displayMagnitude[channelNr] = - CLAMP(displayMagnitude[channelNr] + attack, - (float)minimumLevel, 0); + std::clamp(displayMagnitude[channelNr] + attack, + (float)minimumLevel, 0.f); } } From 92d5b45a5047cd0244df6078879cd091afd398d8 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Tue, 13 Aug 2024 17:09:11 +0200 Subject: [PATCH 0452/1073] mac-capture: Remove disp_finished event This event is only used within destroy_[audio]_screen_stream, and does not appear to be necessary there. stopCaptureWithCompletionHandler holds a reference to the SCStream object by itself (and the other objects being held aren't used afterwards anyways), so there should be no harm in releasing everything immediately without blocking. --- plugins/mac-capture/mac-sck-audio-capture.m | 4 ---- plugins/mac-capture/mac-sck-common.h | 1 - plugins/mac-capture/mac-sck-video-capture.m | 8 -------- 3 files changed, 13 deletions(-) diff --git a/plugins/mac-capture/mac-sck-audio-capture.m b/plugins/mac-capture/mac-sck-audio-capture.m index 595e46b7261b22..0b7a2662b10586 100644 --- a/plugins/mac-capture/mac-sck-audio-capture.m +++ b/plugins/mac-capture/mac-sck-audio-capture.m @@ -13,9 +13,7 @@ MACCAP_ERR("destroy_audio_screen_stream: Failed to stop stream with error %s\n", [[error localizedFailureReason] cStringUsingEncoding:NSUTF8StringEncoding]); } - os_event_signal(sc->disp_finished); }]; - os_event_wait(sc->disp_finished); } if (sc->stream_properties) { @@ -28,7 +26,6 @@ sc->disp = NULL; } - os_event_destroy(sc->disp_finished); os_event_destroy(sc->stream_start_completed); } @@ -152,7 +149,6 @@ sc->disp = NULL; return !did_add_output; } - os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL); os_event_init(&sc->stream_start_completed, OS_EVENT_TYPE_MANUAL); __block BOOL did_stream_start = false; diff --git a/plugins/mac-capture/mac-sck-common.h b/plugins/mac-capture/mac-sck-common.h index 6bbd0a6e822694..9c9ee0890f60e7 100644 --- a/plugins/mac-capture/mac-sck-common.h +++ b/plugins/mac-capture/mac-sck-common.h @@ -52,7 +52,6 @@ struct API_AVAILABLE(macos(12.5)) screen_capture { SCShareableContent *shareable_content; ScreenCaptureDelegate *capture_delegate; - os_event_t *disp_finished; os_event_t *stream_start_completed; os_sem_t *shareable_content_available; IOSurfaceRef current, prev; diff --git a/plugins/mac-capture/mac-sck-video-capture.m b/plugins/mac-capture/mac-sck-video-capture.m index 2b34851e959f8d..ebafb252fd38f4 100644 --- a/plugins/mac-capture/mac-sck-video-capture.m +++ b/plugins/mac-capture/mac-sck-video-capture.m @@ -9,9 +9,7 @@ MACCAP_ERR("destroy_screen_stream: Failed to stop stream with error %s\n", [[error localizedFailureReason] cStringUsingEncoding:NSUTF8StringEncoding]); } - os_event_signal(sc->disp_finished); }]; - os_event_wait(sc->disp_finished); } if (sc->stream_properties) { @@ -41,7 +39,6 @@ sc->disp = NULL; } - os_event_destroy(sc->disp_finished); os_event_destroy(sc->stream_start_completed); } @@ -110,7 +107,6 @@ MACCAP_ERR("init_screen_stream: Invalid target display ID: %u\n", sc->display); os_sem_post(sc->shareable_content_available); sc->disp = NULL; - os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL); os_event_init(&sc->stream_start_completed, OS_EVENT_TYPE_MANUAL); return true; } @@ -152,7 +148,6 @@ MACCAP_ERR("init_screen_stream: Invalid target window ID: %u\n", sc->window); os_sem_post(sc->shareable_content_available); sc->disp = NULL; - os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL); os_event_init(&sc->stream_start_completed, OS_EVENT_TYPE_MANUAL); return true; } else { @@ -172,7 +167,6 @@ MACCAP_ERR("init_screen_stream: Invalid target display ID: %u\n", sc->display); os_sem_post(sc->shareable_content_available); sc->disp = NULL; - os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL); os_event_init(&sc->stream_start_completed, OS_EVENT_TYPE_MANUAL); return true; } @@ -217,7 +211,6 @@ if (sc->capture_type != ScreenCaptureWindowStream) { sc->disp = NULL; [content_filter release]; - os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL); os_event_init(&sc->stream_start_completed, OS_EVENT_TYPE_MANUAL); return true; } @@ -250,7 +243,6 @@ return !did_add_output; } } - os_event_init(&sc->disp_finished, OS_EVENT_TYPE_MANUAL); os_event_init(&sc->stream_start_completed, OS_EVENT_TYPE_MANUAL); __block BOOL did_stream_start = NO; From 15e9242acb8cbbd5ccdcdc9e80fa1e96a22bd1b8 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 16 Jul 2023 03:02:29 +0200 Subject: [PATCH 0453/1073] UI: Add warning to Custom FFmpeg mode --- UI/data/locale/en-US.ini | 1 + UI/forms/OBSBasicSettings.ui | 73 +++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 04f0e0a6131311..a1a3d446c12301 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -1087,6 +1087,7 @@ Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Custom Output (FFmpeg)" Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Use stream encoder)" Basic.Settings.Output.Adv.Recording.Filename="Filename Formatting" Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Overwrite if file exists" +Basic.Settings.Output.Adv.FFmpeg.CustomModeWarning="Custom Output (FFmpeg) is provided with no safeguards.\nConsider using the \"Standard\" recording type." Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg Output Type" Basic.Settings.Output.Adv.FFmpeg.Type.URL="Output to URL" Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Output to File" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index c58271dd156332..e3a103515c49e1 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -3986,7 +3986,20 @@ 9 - + + + + Basic.Settings.Output.Adv.FFmpeg.CustomModeWarning + + + warning + + + true + + + + @@ -4008,7 +4021,7 @@ - + @@ -4022,7 +4035,7 @@ - + @@ -4044,7 +4057,7 @@ - + @@ -4128,7 +4141,7 @@
- + Basic.Settings.Output.NoSpaceFileName @@ -4138,48 +4151,48 @@ - + Basic.Settings.Output.Adv.FFmpeg.Format - + - + Basic.Settings.Output.Adv.FFmpeg.FormatDesc - + - + Basic.Settings.Output.Adv.FFmpeg.MuxerSettings - + - + Basic.Settings.Output.VideoBitrate - + 0 @@ -4192,14 +4205,14 @@ - + Basic.Settings.Output.Adv.FFmpeg.GOPSize - + 1000000000 @@ -4209,7 +4222,7 @@ - + @@ -4225,7 +4238,7 @@ - + false @@ -4235,41 +4248,41 @@ - + Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat - + Basic.Settings.Output.Adv.FFmpeg.VEncoder - + - + Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings - + - + Basic.Settings.Output.AudioBitrate - + 32 @@ -4285,14 +4298,14 @@ - + Basic.Settings.Output.Adv.AudioTrack - + @@ -4361,24 +4374,24 @@
- + Basic.Settings.Output.Adv.FFmpeg.AEncoder - + - + Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings - +
From 4cdbbacaffbfbe285d863accdee70281e0089134 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Tue, 27 Aug 2024 16:15:11 +0200 Subject: [PATCH 0454/1073] UI: Fix warning color --- UI/data/themes/Yami.obt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/data/themes/Yami.obt b/UI/data/themes/Yami.obt index 7c515f5bda3c61..c76f76a7dff58c 100644 --- a/UI/data/themes/Yami.obt +++ b/UI/data/themes/Yami.obt @@ -1390,7 +1390,7 @@ QLabel#errorLabel { } * [themeID="warning"] { - color: var(--danger); + color: var(--warning); font-weight: bold; } From a50320464a572ebde0653c3369f9cef308c51f93 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 22 Nov 2023 13:45:07 +0100 Subject: [PATCH 0455/1073] libobs: Force sceneitem transform update if scene dimensions change --- libobs/obs-scene.c | 31 +++++++++++++++++++++++-------- libobs/obs-scene.h | 3 +++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 4b44ebbf033010..ca3cd9a1229802 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -925,10 +925,9 @@ static void scene_video_tick(void *data, float seconds) } /* assumes video lock */ -static void -update_transforms_and_prune_sources(obs_scene_t *scene, - obs_scene_item_ptr_array_t *remove_items, - obs_sceneitem_t *group_sceneitem) +static void update_transforms_and_prune_sources( + obs_scene_t *scene, obs_scene_item_ptr_array_t *remove_items, + obs_sceneitem_t *group_sceneitem, bool scene_size_changed) { struct obs_scene_item *item = scene->first_item; bool rebuild_group = @@ -951,12 +950,13 @@ update_transforms_and_prune_sources(obs_scene_t *scene, video_lock(group_scene); update_transforms_and_prune_sources(group_scene, - remove_items, item); + remove_items, item, + scene_size_changed); video_unlock(group_scene); } if (os_atomic_load_bool(&item->update_transform) || - source_size_changed(item)) { + source_size_changed(item) || scene_size_changed) { update_item_transform(item, true); rebuild_group = true; @@ -969,6 +969,19 @@ update_transforms_and_prune_sources(obs_scene_t *scene, resize_group(group_sceneitem); } +static inline bool scene_size_changed(obs_scene_t *scene) +{ + uint32_t width = scene_getwidth(scene); + uint32_t height = scene_getheight(scene); + + if (width == scene->last_width && height == scene->last_height) + return false; + + scene->last_width = width; + scene->last_height = height; + return true; +} + static void scene_video_render(void *data, gs_effect_t *effect) { obs_scene_item_ptr_array_t remove_items; @@ -980,7 +993,9 @@ static void scene_video_render(void *data, gs_effect_t *effect) video_lock(scene); if (!scene->is_group) { - update_transforms_and_prune_sources(scene, &remove_items, NULL); + bool size_changed = scene_size_changed(scene); + update_transforms_and_prune_sources(scene, &remove_items, NULL, + size_changed); } gs_blend_state_push(); @@ -4238,7 +4253,7 @@ void obs_scene_prune_sources(obs_scene_t *scene) da_init(remove_items); video_lock(scene); - update_transforms_and_prune_sources(scene, &remove_items, NULL); + update_transforms_and_prune_sources(scene, &remove_items, NULL, false); video_unlock(scene); for (size_t i = 0; i < remove_items.num; i++) diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index d78e96ad778ec0..97ff2aff7c3a68 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -110,6 +110,9 @@ struct obs_scene { uint32_t cx; uint32_t cy; + uint32_t last_width; + uint32_t last_height; + int64_t id_counter; pthread_mutex_t video_mutex; From 1565ca8eb124475df52aa849e3782a9632b8cde8 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 23 Nov 2023 16:51:00 +0100 Subject: [PATCH 0456/1073] libobs: Change scene items to using relative coordinates --- libobs/obs-scene.c | 346 +++++++++++++++++++++++++++++++++++++++------ libobs/obs-scene.h | 2 + libobs/obs.h | 13 ++ 3 files changed, 315 insertions(+), 46 deletions(-) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index ca3cd9a1229802..06834a26dd12e7 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -24,7 +24,7 @@ const struct obs_source_info group_info; -static void resize_group(obs_sceneitem_t *group); +static void resize_group(obs_sceneitem_t *group, bool scene_resize); static void resize_scene(obs_scene_t *scene); static void signal_parent(obs_scene_t *parent, const char *name, calldata_t *params); @@ -353,6 +353,109 @@ void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy) v->y += (float)(cy / 2); } +static uint32_t scene_getwidth(void *data); +static uint32_t scene_getheight(void *data); + +static inline void get_scene_dimensions(const obs_sceneitem_t *item, float *x, + float *y) +{ + obs_scene_t *parent = item->parent; + if (!parent || parent->is_group) { + *x = (float)obs->video.main_mix->ovi.base_width; + *y = (float)obs->video.main_mix->ovi.base_height; + } else { + *x = (float)scene_getwidth(parent); + *y = (float)scene_getheight(parent); + } +} + +/* Rounds absolute pixel values to next .5. */ +static inline void nudge_abs_values(struct vec2 *dst, const struct vec2 *v) +{ + dst->x = floorf(v->x * 2.0f + 0.5f) / 2.0f; + dst->y = floorf(v->y * 2.0f + 0.5f) / 2.0f; +} + +static inline void pos_from_absolute(struct vec2 *dst, const struct vec2 *v, + const obs_sceneitem_t *item) +{ + float x, y; + get_scene_dimensions(item, &x, &y); + /* Scaled so that height (y) is [-1, 1]. */ + dst->x = (2 * v->x - x) / y; + dst->y = 2 * v->y / y - 1.0f; +} + +static inline void pos_to_absolute(struct vec2 *dst, const struct vec2 *v, + const obs_sceneitem_t *item) +{ + float x, y; + get_scene_dimensions(item, &x, &y); + dst->x = (v->x * y + x) / 2; + dst->y = (v->y * y + y) / 2; + + /* In order for group resizing to behave properly they need all the precision + * they can get, so do not nudge their values. */ + if (!item->is_group && !(item->parent && item->parent->is_group)) + nudge_abs_values(dst, dst); +} + +static inline void size_from_absolute(struct vec2 *dst, const struct vec2 *v, + const obs_sceneitem_t *item) +{ + float x, y; + get_scene_dimensions(item, &x, &y); + /* The height of the canvas is from [-1, 1], so 2.0f * aspect is the + * full width (depending on aspect ratio). */ + dst->x = (2 * v->x) / y; + dst->y = 2 * v->y / y; +} + +static inline void size_to_absolute(struct vec2 *dst, const struct vec2 *v, + const obs_sceneitem_t *item) +{ + float x, y; + get_scene_dimensions(item, &x, &y); + dst->x = (v->x * y) / 2; + dst->y = (v->y * y) / 2; + + if (!item->is_group && !(item->parent && item->parent->is_group)) + nudge_abs_values(dst, dst); +} + +/* Return item's scale value scaled from original to current canvas size. */ +static inline void item_canvas_scale(struct vec2 *dst, + const obs_sceneitem_t *item) +{ + /* Groups will themselves resize so their items do not need to be + * rescaled manually. Nested scenes will use the updated canvas + * resolution, so they also don't need manual adjustment. */ + if (item->is_group || item->is_scene) { + vec2_copy(dst, &item->scale); + return; + } + + float x, y; + get_scene_dimensions(item, &x, &y); + float scale_factor = y / item->scale_ref.y; + vec2_mulf(dst, &item->scale, scale_factor); +} + +/* Return scale value scaled to original canvas size. */ +static inline void item_relative_scale(struct vec2 *dst, const struct vec2 *v, + const obs_sceneitem_t *item) +{ + if (item->is_group || item->is_scene) { + vec2_copy(dst, v); + return; + } + + float x, y; + get_scene_dimensions(item, &x, &y); + float scale_factor = item->scale_ref.y / y; + vec2_mulf(dst, v, scale_factor); +} + static inline bool crop_to_bounds(const struct obs_scene_item *item) { return item->crop_to_bounds && @@ -365,15 +468,17 @@ static void calculate_bounds_data(struct obs_scene_item *item, struct vec2 *origin, struct vec2 *scale, uint32_t *cx, uint32_t *cy) { + struct vec2 bounds; + size_to_absolute(&bounds, &item->bounds, item); float width = (float)(*cx) * fabsf(scale->x); float height = (float)(*cy) * fabsf(scale->y); float item_aspect = width / height; - float bounds_aspect = item->bounds.x / item->bounds.y; + float bounds_aspect = bounds.x / bounds.y; uint32_t bounds_type = item->bounds_type; float width_diff, height_diff; if (item->bounds_type == OBS_BOUNDS_MAX_ONLY) - if (width > item->bounds.x || height > item->bounds.y) + if (width > bounds.x || height > bounds.y) bounds_type = OBS_BOUNDS_SCALE_INNER; if (bounds_type == OBS_BOUNDS_SCALE_INNER || @@ -384,30 +489,29 @@ static void calculate_bounds_data(struct obs_scene_item *item, if (item->bounds_type == OBS_BOUNDS_SCALE_OUTER) use_width = !use_width; - mul = use_width ? item->bounds.x / width - : item->bounds.y / height; + mul = use_width ? bounds.x / width : bounds.y / height; vec2_mulf(scale, scale, mul); } else if (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) { - vec2_mulf(scale, scale, item->bounds.x / width); + vec2_mulf(scale, scale, bounds.x / width); } else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) { - vec2_mulf(scale, scale, item->bounds.y / height); + vec2_mulf(scale, scale, bounds.y / height); } else if (bounds_type == OBS_BOUNDS_STRETCH) { - scale->x = copysignf(item->bounds.x / (float)(*cx), scale->x); - scale->y = copysignf(item->bounds.y / (float)(*cy), scale->y); + scale->x = copysignf(bounds.x / (float)(*cx), scale->x); + scale->y = copysignf(bounds.y / (float)(*cy), scale->y); } width = (float)(*cx) * scale->x; height = (float)(*cy) * scale->y; /* Disregards flip when calculating size diff */ - width_diff = item->bounds.x - fabsf(width); - height_diff = item->bounds.y - fabsf(height); - *cx = (uint32_t)item->bounds.x; - *cy = (uint32_t)item->bounds.y; + width_diff = bounds.x - fabsf(width); + height_diff = bounds.y - fabsf(height); + *cx = (uint32_t)bounds.x; + *cy = (uint32_t)bounds.y; add_alignment(origin, item->bounds_align, (int)-width_diff, (int)-height_diff); @@ -490,6 +594,34 @@ static inline uint32_t calc_cy(const struct obs_scene_item *item, return (crop_cy > height) ? 2 : (height - crop_cy); } +#ifdef _DEBUG +static inline void log_matrix(const struct matrix4 *mat, const char *name) +{ + blog(LOG_DEBUG, + "Matrix \"%s\":\n" + "┏ %9.4f %9.4f %9.4f %9.4f ┓\n" + "┃ %9.4f %9.4f %9.4f %9.4f ┃\n" + "┃ %9.4f %9.4f %9.4f %9.4f ┃\n" + "┗ %9.4f %9.4f %9.4f %9.4f ┛", + name, mat->x.x, mat->x.y, mat->x.z, mat->x.w, mat->y.x, mat->y.y, + mat->y.z, mat->y.w, mat->z.x, mat->z.y, mat->z.z, mat->z.w, + mat->t.x, mat->t.y, mat->t.z, mat->t.w); +} +#endif + +static inline void update_nested_scene_crop(struct obs_scene_item *item, + uint32_t width, uint32_t height) +{ + /* Use last size and new size to calculate factor to adjust crop by. */ + float scale_x = (float)width / (float)item->last_width; + float scale_y = (float)height / (float)item->last_height; + + item->crop.left = (int)((float)item->crop.left * scale_x); + item->crop.right = (int)((float)item->crop.right * scale_x); + item->crop.top = (int)((float)item->crop.top * scale_y); + item->crop.bottom = (int)((float)item->crop.bottom * scale_y); +} + static void update_item_transform(struct obs_scene_item *item, bool update_tex) { uint32_t width; @@ -499,6 +631,7 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) struct vec2 base_origin; struct vec2 origin; struct vec2 scale; + struct vec2 position; struct calldata params; uint8_t stack[128]; @@ -510,9 +643,13 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) width = obs_source_get_width(item->source); height = obs_source_get_height(item->source); + + /* Adjust crop on nested scenes (if any) */ + if (update_tex && item->is_scene) + update_nested_scene_crop(item, width, height); + cx = calc_cx(item, width); cy = calc_cy(item, height); - scale = item->scale; item->last_width = width; item->last_height = height; @@ -522,6 +659,9 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) vec2_zero(&base_origin); vec2_zero(&origin); + item_canvas_scale(&scale, item); + pos_to_absolute(&position, &item->pos, item); + /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { @@ -541,17 +681,23 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot)); matrix4_translate3f(&item->draw_transform, &item->draw_transform, - item->pos.x, item->pos.y, 0.0f); + position.x, position.y, 0.0f); + +#ifdef _DEBUG + blog(LOG_DEBUG, "Transform updated for \"%s\":", + obs_source_get_name(item->source)); + log_matrix(&item->draw_transform, "draw_transform"); +#endif item->output_scale = scale; /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { - vec2_copy(&scale, &item->bounds); + size_to_absolute(&scale, &item->bounds, item); } else { - scale.x = (float)width * item->scale.x; - scale.y = (float)height * item->scale.y; + scale.x *= (float)width; + scale.y *= (float)height; } item->box_scale = scale; @@ -566,7 +712,11 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) matrix4_rotate_aa4f(&item->box_transform, &item->box_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot)); matrix4_translate3f(&item->box_transform, &item->box_transform, - item->pos.x, item->pos.y, 0.0f); + position.x, position.y, 0.0f); + +#ifdef _DEBUG + log_matrix(&item->draw_transform, "box_transform"); +#endif /* ----------------------- */ @@ -966,7 +1116,7 @@ static void update_transforms_and_prune_sources( } if (rebuild_group && group_sceneitem) - resize_group(group_sceneitem); + resize_group(group_sceneitem, scene_size_changed); } static inline bool scene_size_changed(obs_scene_t *scene) @@ -1106,8 +1256,18 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) item->align = (uint32_t)obs_data_get_int(item_data, "align"); visible = obs_data_get_bool(item_data, "visible"); lock = obs_data_get_bool(item_data, "locked"); - obs_data_get_vec2(item_data, "pos", &item->pos); - obs_data_get_vec2(item_data, "scale", &item->scale); + + if (obs_data_has_user_value(item_data, "pos_rel") && + obs_data_has_user_value(item_data, "scale_rel") && + obs_data_has_user_value(item_data, "scale_ref")) { + obs_data_get_vec2(item_data, "pos_rel", &item->pos); + obs_data_get_vec2(item_data, "scale_rel", &item->scale); + obs_data_get_vec2(item_data, "scale_ref", &item->scale_ref); + } else { + obs_data_get_vec2(item_data, "pos", &item->pos); + pos_from_absolute(&item->pos, &item->pos, item); + obs_data_get_vec2(item_data, "scale", &item->scale); + } obs_data_release(item->private_settings); item->private_settings = @@ -1125,6 +1285,13 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) item->crop_to_bounds = obs_data_get_bool(item_data, "bounds_crop"); obs_data_get_vec2(item_data, "bounds", &item->bounds); + if (obs_data_has_user_value(item_data, "bounds_rel")) { + obs_data_get_vec2(item_data, "bounds_rel", &item->bounds); + } else { + obs_data_get_vec2(item_data, "bounds", &item->bounds); + size_from_absolute(&item->bounds, &item->bounds, item); + } + item->crop.left = (uint32_t)obs_data_get_int(item_data, "crop_left"); item->crop.top = (uint32_t)obs_data_get_int(item_data, "crop_top"); item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right"); @@ -1241,18 +1408,29 @@ static void scene_save_item(obs_data_array_t *array, get_ungrouped_transform(backup_group, &pos, &scale, &rot); } + /* For backwards compatibility, also store absolute values. */ + struct vec2 tmp_abs; + obs_data_set_string(item_data, "name", name); obs_data_set_string(item_data, "source_uuid", src_uuid); obs_data_set_bool(item_data, "visible", item->user_visible); obs_data_set_bool(item_data, "locked", item->locked); obs_data_set_double(item_data, "rot", rot); - obs_data_set_vec2(item_data, "pos", &pos); - obs_data_set_vec2(item_data, "scale", &scale); + pos_to_absolute(&tmp_abs, &pos, item); + obs_data_set_vec2(item_data, "pos", &tmp_abs); + obs_data_set_vec2(item_data, "pos_rel", &pos); + item_canvas_scale(&tmp_abs, item); + obs_data_set_vec2(item_data, "scale", &tmp_abs); + obs_data_set_vec2(item_data, "scale_rel", &scale); + obs_data_set_vec2(item_data, "scale_ref", &item->scale_ref); obs_data_set_int(item_data, "align", (int)item->align); obs_data_set_int(item_data, "bounds_type", (int)item->bounds_type); obs_data_set_int(item_data, "bounds_align", (int)item->bounds_align); obs_data_set_bool(item_data, "bounds_crop", item->crop_to_bounds); obs_data_set_vec2(item_data, "bounds", &item->bounds); + size_to_absolute(&tmp_abs, &item->bounds, item); + obs_data_set_vec2(item_data, "bounds", &tmp_abs); + obs_data_set_vec2(item_data, "bounds_rel", &item->bounds); obs_data_set_int(item_data, "crop_left", (int)item->crop.left); obs_data_set_int(item_data, "crop_top", (int)item->crop.top); obs_data_set_int(item_data, "crop_right", (int)item->crop.right); @@ -2339,10 +2517,13 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, item->user_visible = true; item->locked = false; item->is_group = strcmp(source->info.id, group_info.id) == 0; + item->is_scene = strcmp(source->info.id, scene_info.id) == 0; item->private_settings = obs_data_create(); item->toggle_visibility = OBS_INVALID_HOTKEY_PAIR_ID; os_atomic_set_long(&item->active_refs, 1); vec2_set(&item->scale, 1.0f, 1.0f); + vec2_set(&item->scale_ref, (float)scene_getwidth(scene), + (float)scene_getheight(scene)); matrix4_identity(&item->draw_transform); matrix4_identity(&item->box_transform); @@ -2721,6 +2902,15 @@ bool obs_sceneitem_selected(const obs_sceneitem_t *item) } while (false) void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos) +{ + if (item) { + pos_from_absolute(&item->pos, pos, item); + do_update_transform(item); + } +} + +void obs_sceneitem_set_relative_pos(obs_sceneitem_t *item, + const struct vec2 *pos) { if (item) { vec2_copy(&item->pos, pos); @@ -2739,7 +2929,7 @@ void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot) void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale) { if (item) { - vec2_copy(&item->scale, scale); + item_relative_scale(&item->scale, scale, item); do_update_transform(item); } } @@ -2903,12 +3093,28 @@ void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop) void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds) { if (item) { - item->bounds = *bounds; + size_from_absolute(&item->bounds, bounds, item); + do_update_transform(item); + } +} + +void obs_sceneitem_set_relative_bounds(obs_sceneitem_t *item, + const struct vec2 *bounds) +{ + if (item) { + vec2_copy(&item->bounds, bounds); do_update_transform(item); } } void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos) +{ + if (item) + pos_to_absolute(pos, &item->pos, item); +} + +void obs_sceneitem_get_relative_pos(const obs_sceneitem_t *item, + struct vec2 *pos) { if (item) vec2_copy(pos, &item->pos); @@ -2922,7 +3128,7 @@ float obs_sceneitem_get_rot(const obs_sceneitem_t *item) void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale) { if (item) - vec2_copy(scale, &item->scale); + item_canvas_scale(scale, item); } uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item) @@ -2948,19 +3154,27 @@ bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item) void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds) { if (item) - *bounds = item->bounds; + size_to_absolute(bounds, &item->bounds, item); +} + +void obs_sceneitem_get_relative_bounds(const obs_sceneitem_t *item, + struct vec2 *bounds) +{ + if (item) + vec2_copy(bounds, &item->bounds); } void obs_sceneitem_get_info(const obs_sceneitem_t *item, struct obs_transform_info *info) { if (item && info) { - info->pos = item->pos; + pos_to_absolute(&info->pos, &item->pos, item); info->rot = item->rot; - info->scale = item->scale; + item_canvas_scale(&info->scale, item); info->alignment = item->align; info->bounds_type = item->bounds_type; info->bounds_alignment = item->bounds_align; + size_to_absolute(&info->bounds, &item->bounds, item); info->bounds = item->bounds; } } @@ -2969,29 +3183,45 @@ void obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info) { if (item && info) { - info->pos = item->pos; + pos_to_absolute(&info->pos, &item->pos, item); info->rot = item->rot; - info->scale = item->scale; + item_canvas_scale(&info->scale, item); info->alignment = item->align; info->bounds_type = item->bounds_type; info->bounds_alignment = item->bounds_align; + size_to_absolute(&info->bounds, &item->bounds, item); info->bounds = item->bounds; info->crop_to_bounds = item->crop_to_bounds; } } +void obs_sceneitem_get_info3(const obs_sceneitem_t *item, + struct obs_transform_info *info) +{ + if (item && info) { + info->pos = item->pos; + info->rot = item->rot; + item_canvas_scale(&info->scale, item); + info->alignment = item->align; + info->bounds_type = item->bounds_type; + info->bounds_alignment = item->bounds_align; + info->bounds = item->bounds; + } +} + void obs_sceneitem_set_info(obs_sceneitem_t *item, const struct obs_transform_info *info) { if (item && info) { - item->pos = info->pos; + pos_from_absolute(&item->pos, &info->pos, item); item->rot = info->rot; if (isfinite(info->scale.x) && isfinite(info->scale.y)) { - item->scale = info->scale; + item_relative_scale(&item->scale, &info->scale, item); } item->align = info->alignment; item->bounds_type = info->bounds_type; item->bounds_align = info->bounds_alignment; + size_from_absolute(&item->bounds, &info->bounds, item); item->bounds = info->bounds; do_update_transform(item); } @@ -3001,20 +3231,38 @@ void obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info) { if (item && info) { - item->pos = info->pos; + pos_from_absolute(&item->pos, &info->pos, item); item->rot = info->rot; if (isfinite(info->scale.x) && isfinite(info->scale.y)) { - item->scale = info->scale; + item_relative_scale(&item->scale, &info->scale, item); } item->align = info->alignment; item->bounds_type = info->bounds_type; item->bounds_align = info->bounds_alignment; + size_from_absolute(&item->bounds, &info->bounds, item); item->bounds = info->bounds; item->crop_to_bounds = info->crop_to_bounds; do_update_transform(item); } } +void obs_sceneitem_set_info3(obs_sceneitem_t *item, + const struct obs_transform_info *info) +{ + if (item && info) { + item->pos = info->pos; + item->rot = info->rot; + if (isfinite(info->scale.x) && isfinite(info->scale.y)) { + item_relative_scale(&item->scale, &info->scale, item); + } + item->align = info->alignment; + item->bounds_type = info->bounds_type; + item->bounds_align = info->bounds_alignment; + item->bounds = info->bounds; + do_update_transform(item); + } +} + void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform) { @@ -3483,10 +3731,15 @@ static bool resize_scene_base(obs_scene_t *scene, struct vec2 *minv, } item = scene->first_item; - while (item) { - vec2_sub(&item->pos, &item->pos, minv); - update_item_transform(item, false); - item = item->next; + if (item) { + struct vec2 minv_rel; + size_from_absolute(&minv_rel, minv, item); + + while (item) { + vec2_sub(&item->pos, &item->pos, &minv_rel); + update_item_transform(item, false); + item = item->next; + } } vec2_sub(scale, maxv, minv); @@ -3504,7 +3757,7 @@ static void resize_scene(obs_scene_t *scene) } /* assumes group scene and parent scene is locked */ -static void resize_group(obs_sceneitem_t *group) +static void resize_group(obs_sceneitem_t *group, bool scene_resize) { obs_scene_t *scene = group->source->context.data; struct vec2 minv; @@ -3517,7 +3770,7 @@ static void resize_group(obs_sceneitem_t *group) if (!resize_scene_base(scene, &minv, &maxv, &scale)) return; - if (group->bounds_type == OBS_BOUNDS_NONE) { + if (group->bounds_type == OBS_BOUNDS_NONE && !scene_resize) { struct vec2 new_pos; if ((group->align & OBS_ALIGN_LEFT) != 0) @@ -3535,6 +3788,7 @@ static void resize_group(obs_sceneitem_t *group) new_pos.y = (maxv.y - minv.y) * 0.5f + minv.y; transform_val(&new_pos, &group->draw_transform); + pos_from_absolute(&new_pos, &new_pos, group); vec2_copy(&group->pos, &new_pos); } @@ -3602,7 +3856,7 @@ obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name, apply_group_transform(items[idx], item); } items[0]->prev = NULL; - resize_group(item); + resize_group(item, false); full_unlock(sub_scene); full_unlock(scene); @@ -3749,7 +4003,7 @@ void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item) apply_group_transform(item, group); - resize_group(group); + resize_group(group, false); full_unlock(groupscene); full_unlock(scene); @@ -3778,7 +4032,7 @@ void obs_sceneitem_group_remove_item(obs_sceneitem_t *group, detach_sceneitem(item); attach_sceneitem(scene, item, NULL); - resize_group(group); + resize_group(group, false); full_unlock(groupscene); full_unlock(scene); @@ -3961,7 +4215,7 @@ bool obs_scene_reorder_items2(obs_scene_t *scene, sub_prev = sub_item; } - resize_group(info->item); + resize_group(info->item, false); full_unlock(sub_scene); obs_scene_release(sub_scene); } diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 97ff2aff7c3a68..005c828453de0b 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -32,6 +32,7 @@ struct obs_scene_item { volatile bool removed; bool is_group; + bool is_scene; bool update_transform; bool update_group_resize; @@ -52,6 +53,7 @@ struct obs_scene_item { struct vec2 pos; struct vec2 scale; + struct vec2 scale_ref; float rot; uint32_t align; diff --git a/libobs/obs.h b/libobs/obs.h index a8b6d4dc1a7d78..b1105a7f4c315d 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -1855,6 +1855,8 @@ EXPORT bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock); /* Functions for getting/setting specific orientation of a scene item */ EXPORT void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos); +EXPORT void obs_sceneitem_set_relative_pos(obs_sceneitem_t *item, + const struct vec2 *pos); EXPORT void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg); EXPORT void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale); @@ -1871,11 +1873,15 @@ EXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, EXPORT void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop); EXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds); +EXPORT void obs_sceneitem_set_relative_bounds(obs_sceneitem_t *item, + const struct vec2 *bounds); EXPORT int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item); EXPORT void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos); +EXPORT void obs_sceneitem_get_relative_pos(const obs_sceneitem_t *item, + struct vec2 *pos); EXPORT float obs_sceneitem_get_rot(const obs_sceneitem_t *item); EXPORT void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale); @@ -1887,6 +1893,9 @@ EXPORT uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item); EXPORT bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item); EXPORT void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds); +EXPORT void obs_sceneitem_get_relative_bounds(const obs_sceneitem_t *item, + struct vec2 *bounds); + OBS_DEPRECATED EXPORT void obs_sceneitem_get_info(const obs_sceneitem_t *item, struct obs_transform_info *info); @@ -1897,6 +1906,10 @@ EXPORT void obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info); EXPORT void obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info); +EXPORT void obs_sceneitem_get_info3(const obs_sceneitem_t *item, + struct obs_transform_info *info); +EXPORT void obs_sceneitem_set_info3(obs_sceneitem_t *item, + const struct obs_transform_info *info); EXPORT void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform); From ddd586ae0327401ecd045431bf698f6d796dd1db Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 23 Apr 2024 05:04:33 +0200 Subject: [PATCH 0457/1073] libobs: Allow scene items to use legacy absolute mode This is to keep old collections running in relative mode until they can be migrated. --- libobs/obs-scene.c | 259 ++++++++++++++++++++++++++++++++------------- libobs/obs-scene.h | 2 + 2 files changed, 187 insertions(+), 74 deletions(-) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 06834a26dd12e7..678e171cfa5a04 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -178,6 +178,9 @@ static void *scene_create(obs_data_t *settings, struct obs_source *source) goto fail; } + scene->absolute_coordinates = obs_data_get_bool(obs->data.private_data, + "AbsoluteCoordinates"); + UNUSED_PARAMETER(settings); return scene; @@ -469,7 +472,11 @@ static void calculate_bounds_data(struct obs_scene_item *item, uint32_t *cx, uint32_t *cy) { struct vec2 bounds; - size_to_absolute(&bounds, &item->bounds, item); + if (item->absolute_coordinates) + vec2_copy(&bounds, &item->bounds); + else + size_to_absolute(&bounds, &item->bounds, item); + float width = (float)(*cx) * fabsf(scale->x); float height = (float)(*cy) * fabsf(scale->y); float item_aspect = width / height; @@ -659,8 +666,13 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) vec2_zero(&base_origin); vec2_zero(&origin); - item_canvas_scale(&scale, item); - pos_to_absolute(&position, &item->pos, item); + if (!item->absolute_coordinates) { + item_canvas_scale(&scale, item); + pos_to_absolute(&position, &item->pos, item); + } else { + scale = item->scale; + position = item->pos; + } /* ----------------------- */ @@ -694,7 +706,10 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex) /* ----------------------- */ if (item->bounds_type != OBS_BOUNDS_NONE) { - size_to_absolute(&scale, &item->bounds, item); + if (!item->absolute_coordinates) + size_to_absolute(&scale, &item->bounds, item); + else + vec2_copy(&scale, &item->bounds); } else { scale.x *= (float)width; scale.y *= (float)height; @@ -1257,7 +1272,8 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) visible = obs_data_get_bool(item_data, "visible"); lock = obs_data_get_bool(item_data, "locked"); - if (obs_data_has_user_value(item_data, "pos_rel") && + if (!item->absolute_coordinates && + obs_data_has_user_value(item_data, "pos_rel") && obs_data_has_user_value(item_data, "scale_rel") && obs_data_has_user_value(item_data, "scale_ref")) { obs_data_get_vec2(item_data, "pos_rel", &item->pos); @@ -1265,7 +1281,8 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) obs_data_get_vec2(item_data, "scale_ref", &item->scale_ref); } else { obs_data_get_vec2(item_data, "pos", &item->pos); - pos_from_absolute(&item->pos, &item->pos, item); + if (!item->absolute_coordinates) + pos_from_absolute(&item->pos, &item->pos, item); obs_data_get_vec2(item_data, "scale", &item->scale); } @@ -1285,11 +1302,13 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data) item->crop_to_bounds = obs_data_get_bool(item_data, "bounds_crop"); obs_data_get_vec2(item_data, "bounds", &item->bounds); - if (obs_data_has_user_value(item_data, "bounds_rel")) { + if (!item->absolute_coordinates && + obs_data_has_user_value(item_data, "bounds_rel")) { obs_data_get_vec2(item_data, "bounds_rel", &item->bounds); } else { obs_data_get_vec2(item_data, "bounds", &item->bounds); - size_from_absolute(&item->bounds, &item->bounds, item); + if (!item->absolute_coordinates) + size_from_absolute(&item->bounds, &item->bounds, item); } item->crop.left = (uint32_t)obs_data_get_int(item_data, "crop_left"); @@ -1374,6 +1393,9 @@ static void scene_load(void *data, obs_data_t *settings) if (obs_data_has_user_value(settings, "id_counter")) scene->id_counter = obs_data_get_int(settings, "id_counter"); + scene->absolute_coordinates = obs_data_get_bool(obs->data.private_data, + "AbsoluteCoordinates"); + if (!items) return; @@ -1408,29 +1430,16 @@ static void scene_save_item(obs_data_array_t *array, get_ungrouped_transform(backup_group, &pos, &scale, &rot); } - /* For backwards compatibility, also store absolute values. */ - struct vec2 tmp_abs; - obs_data_set_string(item_data, "name", name); obs_data_set_string(item_data, "source_uuid", src_uuid); obs_data_set_bool(item_data, "visible", item->user_visible); obs_data_set_bool(item_data, "locked", item->locked); obs_data_set_double(item_data, "rot", rot); - pos_to_absolute(&tmp_abs, &pos, item); - obs_data_set_vec2(item_data, "pos", &tmp_abs); - obs_data_set_vec2(item_data, "pos_rel", &pos); - item_canvas_scale(&tmp_abs, item); - obs_data_set_vec2(item_data, "scale", &tmp_abs); - obs_data_set_vec2(item_data, "scale_rel", &scale); obs_data_set_vec2(item_data, "scale_ref", &item->scale_ref); obs_data_set_int(item_data, "align", (int)item->align); obs_data_set_int(item_data, "bounds_type", (int)item->bounds_type); obs_data_set_int(item_data, "bounds_align", (int)item->bounds_align); obs_data_set_bool(item_data, "bounds_crop", item->crop_to_bounds); - obs_data_set_vec2(item_data, "bounds", &item->bounds); - size_to_absolute(&tmp_abs, &item->bounds, item); - obs_data_set_vec2(item_data, "bounds", &tmp_abs); - obs_data_set_vec2(item_data, "bounds_rel", &item->bounds); obs_data_set_int(item_data, "crop_left", (int)item->crop.left); obs_data_set_int(item_data, "crop_top", (int)item->crop.top); obs_data_set_int(item_data, "crop_right", (int)item->crop.right); @@ -1438,6 +1447,27 @@ static void scene_save_item(obs_data_array_t *array, obs_data_set_int(item_data, "id", item->id); obs_data_set_bool(item_data, "group_item_backup", !!backup_group); + if (!item->absolute_coordinates) { + /* For backwards compatibility, also store absolute values. */ + struct vec2 tmp_abs; + + pos_to_absolute(&tmp_abs, &pos, item); + obs_data_set_vec2(item_data, "pos", &tmp_abs); + obs_data_set_vec2(item_data, "pos_rel", &pos); + + item_canvas_scale(&tmp_abs, item); + obs_data_set_vec2(item_data, "scale", &tmp_abs); + obs_data_set_vec2(item_data, "scale_rel", &scale); + + size_to_absolute(&tmp_abs, &item->bounds, item); + obs_data_set_vec2(item_data, "bounds", &tmp_abs); + obs_data_set_vec2(item_data, "bounds_rel", &item->bounds); + } else { + obs_data_set_vec2(item_data, "pos", &pos); + obs_data_set_vec2(item_data, "scale", &scale); + obs_data_set_vec2(item_data, "bounds", &item->bounds); + } + if (item->is_group) { obs_scene_t *group_scene = item->source->context.data; obs_sceneitem_t *group_item; @@ -2520,6 +2550,7 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, item->is_scene = strcmp(source->info.id, scene_info.id) == 0; item->private_settings = obs_data_create(); item->toggle_visibility = OBS_INVALID_HOTKEY_PAIR_ID; + item->absolute_coordinates = scene->absolute_coordinates; os_atomic_set_long(&item->active_refs, 1); vec2_set(&item->scale, 1.0f, 1.0f); vec2_set(&item->scale_ref, (float)scene_getwidth(scene), @@ -2904,7 +2935,11 @@ bool obs_sceneitem_selected(const obs_sceneitem_t *item) void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos) { if (item) { - pos_from_absolute(&item->pos, pos, item); + if (!item->absolute_coordinates) + pos_from_absolute(&item->pos, pos, item); + else + vec2_copy(&item->pos, pos); + do_update_transform(item); } } @@ -2913,7 +2948,11 @@ void obs_sceneitem_set_relative_pos(obs_sceneitem_t *item, const struct vec2 *pos) { if (item) { - vec2_copy(&item->pos, pos); + if (!item->absolute_coordinates) + vec2_copy(&item->pos, pos); + else + pos_to_absolute(&item->pos, pos, item); + do_update_transform(item); } } @@ -2929,7 +2968,11 @@ void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot) void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale) { if (item) { - item_relative_scale(&item->scale, scale, item); + if (!item->absolute_coordinates) + item_relative_scale(&item->scale, scale, item); + else + vec2_copy(&item->scale, scale); + do_update_transform(item); } } @@ -3093,7 +3136,11 @@ void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop) void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds) { if (item) { - size_from_absolute(&item->bounds, bounds, item); + if (!item->absolute_coordinates) + size_from_absolute(&item->bounds, bounds, item); + else + vec2_copy(&item->bounds, bounds); + do_update_transform(item); } } @@ -3102,22 +3149,36 @@ void obs_sceneitem_set_relative_bounds(obs_sceneitem_t *item, const struct vec2 *bounds) { if (item) { - vec2_copy(&item->bounds, bounds); + if (!item->absolute_coordinates) + vec2_copy(&item->bounds, bounds); + else + size_to_absolute(&item->bounds, bounds, item); + do_update_transform(item); } } void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos) { - if (item) + if (!item) + return; + + if (!item->absolute_coordinates) pos_to_absolute(pos, &item->pos, item); + else + vec2_copy(pos, &item->pos); } void obs_sceneitem_get_relative_pos(const obs_sceneitem_t *item, struct vec2 *pos) { - if (item) + if (!item) + return; + + if (!item->absolute_coordinates) vec2_copy(pos, &item->pos); + else + pos_from_absolute(pos, &item->pos, item); } float obs_sceneitem_get_rot(const obs_sceneitem_t *item) @@ -3127,8 +3188,13 @@ float obs_sceneitem_get_rot(const obs_sceneitem_t *item) void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale) { - if (item) + if (!item) + return; + + if (!item->absolute_coordinates) item_canvas_scale(scale, item); + else + vec2_copy(scale, &item->scale); } uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item) @@ -3153,44 +3219,58 @@ bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item) void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds) { - if (item) + if (!item) + return; + + if (!item->absolute_coordinates) size_to_absolute(bounds, &item->bounds, item); + else + vec2_copy(bounds, &item->bounds); } void obs_sceneitem_get_relative_bounds(const obs_sceneitem_t *item, struct vec2 *bounds) { - if (item) + if (!item) + return; + + if (!item->absolute_coordinates) vec2_copy(bounds, &item->bounds); + else + size_from_absolute(bounds, &item->bounds, item); } -void obs_sceneitem_get_info(const obs_sceneitem_t *item, - struct obs_transform_info *info) +static inline void scene_item_get_info_internal(const obs_sceneitem_t *item, + struct obs_transform_info *info) { - if (item && info) { + if (!item->absolute_coordinates) { pos_to_absolute(&info->pos, &item->pos, item); - info->rot = item->rot; item_canvas_scale(&info->scale, item); - info->alignment = item->align; - info->bounds_type = item->bounds_type; - info->bounds_alignment = item->bounds_align; size_to_absolute(&info->bounds, &item->bounds, item); + } else { + info->pos = item->pos; + info->scale = item->scale; info->bounds = item->bounds; } + info->rot = item->rot; + info->alignment = item->align; + info->bounds_type = item->bounds_type; + info->bounds_alignment = item->bounds_align; +} + +void obs_sceneitem_get_info(const obs_sceneitem_t *item, + struct obs_transform_info *info) +{ + if (item && info) { + scene_item_get_info_internal(item, info); + } } void obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info) { if (item && info) { - pos_to_absolute(&info->pos, &item->pos, item); - info->rot = item->rot; - item_canvas_scale(&info->scale, item); - info->alignment = item->align; - info->bounds_type = item->bounds_type; - info->bounds_alignment = item->bounds_align; - size_to_absolute(&info->bounds, &item->bounds, item); - info->bounds = item->bounds; + scene_item_get_info_internal(item, info); info->crop_to_bounds = item->crop_to_bounds; } } @@ -3199,30 +3279,52 @@ void obs_sceneitem_get_info3(const obs_sceneitem_t *item, struct obs_transform_info *info) { if (item && info) { - info->pos = item->pos; + if (!item->absolute_coordinates) { + info->pos = item->pos; + item_canvas_scale(&info->scale, item); + info->bounds = item->bounds; + } else { + pos_from_absolute(&info->pos, &item->pos, item); + item_relative_scale(&info->scale, &item->scale, item); + size_from_absolute(&info->bounds, &item->bounds, item); + } info->rot = item->rot; - item_canvas_scale(&info->scale, item); info->alignment = item->align; info->bounds_type = item->bounds_type; info->bounds_alignment = item->bounds_align; - info->bounds = item->bounds; + info->crop_to_bounds = item->crop_to_bounds; } } -void obs_sceneitem_set_info(obs_sceneitem_t *item, - const struct obs_transform_info *info) +static inline void +scene_item_set_info_internal(obs_sceneitem_t *item, + const struct obs_transform_info *info) { - if (item && info) { + if (!item->absolute_coordinates) { pos_from_absolute(&item->pos, &info->pos, item); - item->rot = info->rot; + size_from_absolute(&item->bounds, &info->bounds, item); if (isfinite(info->scale.x) && isfinite(info->scale.y)) { item_relative_scale(&item->scale, &info->scale, item); } - item->align = info->alignment; - item->bounds_type = info->bounds_type; - item->bounds_align = info->bounds_alignment; - size_from_absolute(&item->bounds, &info->bounds, item); + } else { + item->pos = info->pos; item->bounds = info->bounds; + if (isfinite(info->scale.x) && isfinite(info->scale.y)) { + item->scale = info->scale; + } + } + + item->rot = info->rot; + item->align = info->alignment; + item->bounds_type = info->bounds_type; + item->bounds_align = info->bounds_alignment; +} + +void obs_sceneitem_set_info(obs_sceneitem_t *item, + const struct obs_transform_info *info) +{ + if (item && info) { + scene_item_set_info_internal(item, info); do_update_transform(item); } } @@ -3231,16 +3333,7 @@ void obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info) { if (item && info) { - pos_from_absolute(&item->pos, &info->pos, item); - item->rot = info->rot; - if (isfinite(info->scale.x) && isfinite(info->scale.y)) { - item_relative_scale(&item->scale, &info->scale, item); - } - item->align = info->alignment; - item->bounds_type = info->bounds_type; - item->bounds_align = info->bounds_alignment; - size_from_absolute(&item->bounds, &info->bounds, item); - item->bounds = info->bounds; + scene_item_set_info_internal(item, info); item->crop_to_bounds = info->crop_to_bounds; do_update_transform(item); } @@ -3250,15 +3343,28 @@ void obs_sceneitem_set_info3(obs_sceneitem_t *item, const struct obs_transform_info *info) { if (item && info) { - item->pos = info->pos; - item->rot = info->rot; - if (isfinite(info->scale.x) && isfinite(info->scale.y)) { - item_relative_scale(&item->scale, &info->scale, item); + if (!item->absolute_coordinates) { + item->pos = info->pos; + item->bounds = info->bounds; + if (isfinite(info->scale.x) && + isfinite(info->scale.y)) { + item_relative_scale(&item->scale, &info->scale, + item); + } + } else { + pos_to_absolute(&item->pos, &info->pos, item); + size_to_absolute(&item->bounds, &info->bounds, item); + if (isfinite(info->scale.x) && + isfinite(info->scale.y)) { + item_canvas_scale(&item->scale, item); + } } + + item->rot = info->rot; item->align = info->alignment; item->bounds_type = info->bounds_type; item->bounds_align = info->bounds_alignment; - item->bounds = info->bounds; + item->crop_to_bounds = info->crop_to_bounds; do_update_transform(item); } } @@ -3733,7 +3839,10 @@ static bool resize_scene_base(obs_scene_t *scene, struct vec2 *minv, item = scene->first_item; if (item) { struct vec2 minv_rel; - size_from_absolute(&minv_rel, minv, item); + if (!item->absolute_coordinates) + size_from_absolute(&minv_rel, minv, item); + else + vec2_copy(&minv_rel, minv); while (item) { vec2_sub(&item->pos, &item->pos, &minv_rel); @@ -3788,7 +3897,9 @@ static void resize_group(obs_sceneitem_t *group, bool scene_resize) new_pos.y = (maxv.y - minv.y) * 0.5f + minv.y; transform_val(&new_pos, &group->draw_transform); - pos_from_absolute(&new_pos, &new_pos, group); + if (!group->absolute_coordinates) + pos_from_absolute(&new_pos, &new_pos, group); + vec2_copy(&group->pos, &new_pos); } diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 005c828453de0b..6df6fe4547472d 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -51,6 +51,7 @@ struct obs_scene_item { gs_texrender_t *item_render; struct obs_sceneitem_crop crop; + bool absolute_coordinates; struct vec2 pos; struct vec2 scale; struct vec2 scale_ref; @@ -112,6 +113,7 @@ struct obs_scene { uint32_t cx; uint32_t cy; + bool absolute_coordinates; uint32_t last_width; uint32_t last_height; From f61d0bb3c296ff2efbb641ed5cd9b5c11801df14 Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 18 Aug 2024 09:46:41 +0200 Subject: [PATCH 0458/1073] docs: Add new relative positioning scene functions --- docs/sphinx/reference-scenes.rst | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/sphinx/reference-scenes.rst b/docs/sphinx/reference-scenes.rst index ec93482d0c05ba..b3c39ddbefa706 100644 --- a/docs/sphinx/reference-scenes.rst +++ b/docs/sphinx/reference-scenes.rst @@ -369,6 +369,18 @@ Scene Item Functions --------------------- +.. function:: void obs_sceneitem_set_relative_pos(obs_sceneitem_t *item, const struct vec2 *pos) + void obs_sceneitem_get_relative_pos(const obs_sceneitem_t *item, struct vec2 *pos) + + Sets/gets the position of a scene item in relative coordinates. + In this system `(0.0, 0.0)` is the center of the screen, `(0, -1.0)` the bottom and `(0, 1.0)` the top. + The visible range of the horizontal axis depends on aspect ratio, for example, with 16:9 (1.7777...) this is `[-1.777.., -1.777..]`. + Positions are rounded to the nearest half-pixel when converting from relative to absolute pixel values to maintain backwards compaibility. + + .. versionadded:: 31.0 + +--------------------- + .. function:: void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg) float obs_sceneitem_get_rot(const obs_sceneitem_t *item) @@ -461,6 +473,16 @@ Scene Item Functions --------------------- +.. function:: void obs_sceneitem_set_relative_bounds(obs_sceneitem_t *item, const struct vec2 *bounds) + void obs_sceneitem_get_relative_bounds(const obs_sceneitem_t *item, struct vec2 *bounds) + + Sets/gets the bounding box width/height of the scene item in relative sizes. + See :c:func:`obs_sceneitem_get_relative_pos()`/:c:func:`obs_sceneitem_set_relative_pos()` for details on the relative coordinate system. + + .. versionadded:: 31.0 + +--------------------- + .. function:: void obs_sceneitem_set_info(obs_sceneitem_t *item, const struct obs_transform_info *info) void obs_sceneitem_get_info(const obs_sceneitem_t *item, struct obs_transform_info *info) @@ -468,6 +490,26 @@ Scene Item Functions --------------------- +.. function:: void obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info) + void obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info) + + Sets/gets the transform information of the scene item. + This version of the function also sets the `crop_to_bounds` member of `obs_transform_info`. + + .. versionadded:: 30.1 + +--------------------- + +.. function:: void obs_sceneitem_set_info3(obs_sceneitem_t *item, const struct obs_transform_info *info) + void obs_sceneitem_get_info3(const obs_sceneitem_t *item, struct obs_transform_info *info) + + Sets/gets the transform information of the scene item. + This version uses relative coordinates, see :c:func:`obs_sceneitem_get_relative_pos()`/:c:func:`obs_sceneitem_set_relative_pos()` for details. + + .. versionadded:: 31.0 + +--------------------- + .. function:: void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform) Gets the transform matrix of the scene item used for drawing the From 8251005ad3acb54a67b68e2cb0846a783f0aeed7 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 23 Apr 2024 05:05:50 +0200 Subject: [PATCH 0459/1073] UI: Add migration for relative coordinate system --- UI/data/locale/en-US.ini | 10 ++ UI/forms/OBSBasic.ui | 6 + UI/window-basic-main-scene-collections.cpp | 91 ++++++++++++++++ UI/window-basic-main.cpp | 121 ++++++++++++++++++++- UI/window-basic-main.hpp | 10 +- 5 files changed, 232 insertions(+), 6 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index a1a3d446c12301..2f15c5abdd0d92 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -594,6 +594,14 @@ Basic.Main.AddSceneCollection.Text="Please enter the name of the scene collectio # rename scene collection dialog Basic.Main.RenameSceneCollection.Title="Rename Scene Collection" +# remigrate scene collection dialog +Basic.Main.RemigrateSceneCollection.Title="Update Scene Collection Resolution" +Basic.Main.RemigrateSceneCollection.Text="Do you want to update the scene collection resolution of \"%1\" to match the current profile's canvas resolution of %2x%3?" +Basic.Main.RemigrateSceneCollection.CannotMigrate.Active="Cannot update scene collection resolution while outputs are active." +Basic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution="Failed to update scene collection resolution. The original resolution is unknown." +Basic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset="Reset not possible: Changing OBS resolution failed." +Basic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches="Reset not possible: Current resolution already the base resolution of scene collection." + # add profile dialog AddProfile.Title="Add Profile" AddProfile.Text="Please enter the name of the profile" @@ -835,6 +843,8 @@ Basic.MainMenu.Profile.Import="Import Profile" Basic.MainMenu.Profile.Export="Export Profile" Basic.MainMenu.SceneCollection.Import="Import Scene Collection" Basic.MainMenu.SceneCollection.Export="Export Scene Collection" +Basic.MainMenu.SceneCollection.Remigrate="Reset Base Resolution" +Basic.MainMenu.SceneCollection.Migrate="Set Base Resolution" Basic.MainMenu.Profile.Exists="The profile already exists" # basic mode help menu diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index b97fe095021ba4..5c02445d7a2dd6 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -777,6 +777,7 @@ + @@ -1934,6 +1935,11 @@ Basic.MainMenu.File.ShowMissingFiles
+ + + Basic.MainMenu.SceneCollection.Remigrate + + New diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index b3d6d18815ff61..a1ce86cbb8ad0d 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -434,6 +434,97 @@ void OBSBasic::on_actionExportSceneCollection_triggered() } } +void OBSBasic::on_actionRemigrateSceneCollection_triggered() +{ + if (Active()) { + OBSMessageBox::warning( + this, + QTStr("Basic.Main.RemigrateSceneCollection.Title"), + QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.Active")); + return; + } + + OBSDataAutoRelease priv = obs_get_private_data(); + + if (!usingAbsoluteCoordinates && !migrationBaseResolution) { + OBSMessageBox::warning( + this, + QTStr("Basic.Main.RemigrateSceneCollection.Title"), + QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution")); + return; + } + + obs_video_info ovi; + obs_get_video_info(&ovi); + + if (!usingAbsoluteCoordinates && + migrationBaseResolution->first == ovi.base_width && + migrationBaseResolution->second == ovi.base_height) { + OBSMessageBox::warning( + this, + QTStr("Basic.Main.RemigrateSceneCollection.Title"), + QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches")); + return; + } + + QString name = config_get_string(App()->GlobalConfig(), "Basic", + "SceneCollection"); + QString message = QTStr("Basic.Main.RemigrateSceneCollection.Text") + .arg(name) + .arg(ovi.base_width) + .arg(ovi.base_height); + + auto answer = OBSMessageBox::question( + this, QTStr("Basic.Main.RemigrateSceneCollection.Title"), + message); + + if (answer == QMessageBox::No) + return; + + lastOutputResolution = {ovi.base_width, ovi.base_height}; + + if (!usingAbsoluteCoordinates) { + /* Temporarily change resolution to migration resolution */ + ovi.base_width = migrationBaseResolution->first; + ovi.base_height = migrationBaseResolution->second; + + if (obs_reset_video(&ovi) != OBS_VIDEO_SUCCESS) { + OBSMessageBox::critical( + this, + QTStr("Basic.Main.RemigrateSceneCollection.Title"), + QTStr("Basic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset")); + return; + } + } + + char path[512]; + int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); + if (ret <= 0) { + blog(LOG_WARNING, "Failed to get scene collection config path"); + return; + } + + std::string fileName = path; + fileName += config_get_string(App()->GlobalConfig(), "Basic", + "SceneCollectionFile"); + fileName += ".json"; + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + + /* Save and immediately reload to (re-)run migrations. */ + SaveProjectNow(); + /* Reset video if we potentially changed to a temporary resolution */ + if (!usingAbsoluteCoordinates) + ResetVideo(); + + Load(fileName.c_str(), !usingAbsoluteCoordinates); + RefreshSceneCollections(); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); +} + void OBSBasic::ChangeSceneCollection() { QAction *action = reinterpret_cast(sender()); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 0d0b0df805ddc0..5e03d5544c6edf 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -953,7 +953,16 @@ void OBSBasic::Save(const char *file) obs_data_set_obj(saveData, "resolution", res); } - if (!obs_data_save_json_safe(saveData, file, "tmp", "bak")) + obs_data_set_int(saveData, "version", usingAbsoluteCoordinates ? 1 : 2); + + if (migrationBaseResolution && !usingAbsoluteCoordinates) { + OBSDataAutoRelease res = obs_data_create(); + obs_data_set_int(res, "x", migrationBaseResolution->first); + obs_data_set_int(res, "y", migrationBaseResolution->second); + obs_data_set_obj(saveData, "migration_resolution", res); + } + + if (!obs_data_save_json_pretty_safe(saveData, file, "tmp", "bak")) blog(LOG_ERROR, "Could not save scene data to %s", file); } @@ -1038,6 +1047,20 @@ void OBSBasic::CreateFirstRunSources() Str("Basic.AuxDevice1"), 3); } +void OBSBasic::DisableRelativeCoordinates(bool enable) +{ + /* Allow disabling relative positioning to allow loading collections + * that cannot yet be migrated. */ + OBSDataAutoRelease priv = obs_get_private_data(); + obs_data_set_bool(priv, "AbsoluteCoordinates", enable); + usingAbsoluteCoordinates = enable; + + ui->actionRemigrateSceneCollection->setText( + enable ? QTStr("Basic.MainMenu.SceneCollection.Migrate") + : QTStr("Basic.MainMenu.SceneCollection.Remigrate")); + ui->actionRemigrateSceneCollection->setEnabled(enable); +} + void OBSBasic::CreateDefaultScene(bool firstStart) { disableSaving++; @@ -1048,6 +1071,7 @@ void OBSBasic::CreateDefaultScene(bool firstStart) ui->transitionDuration->setValue(300); SetTransition(fadeTransition); + DisableRelativeCoordinates(false); OBSSceneAutoRelease scene = obs_scene_create(Str("Basic.Scene")); if (firstStart) @@ -1188,10 +1212,11 @@ void OBSBasic::LogScenes() blog(LOG_INFO, "------------------------------------------------"); } -void OBSBasic::Load(const char *file) +void OBSBasic::Load(const char *file, bool remigrate) { disableSaving++; lastOutputResolution.reset(); + migrationBaseResolution.reset(); obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak"); if (!data) { @@ -1229,7 +1254,7 @@ void OBSBasic::Load(const char *file) return; } - LoadData(data, file); + LoadData(data, file, remigrate); } static inline void AddMissingFiles(void *data, obs_source_t *source) @@ -1241,7 +1266,27 @@ static inline void AddMissingFiles(void *data, obs_source_t *source) obs_missing_files_destroy(sf); } -void OBSBasic::LoadData(obs_data_t *data, const char *file) +static void ClearRelativePosCb(obs_data_t *data, void *) +{ + const string_view id = obs_data_get_string(data, "id"); + if (id != "scene" && id != "group") + return; + + OBSDataAutoRelease settings = obs_data_get_obj(data, "settings"); + OBSDataArrayAutoRelease items = obs_data_get_array(settings, "items"); + + obs_data_array_enum( + items, + [](obs_data_t *data, void *) { + obs_data_unset_user_value(data, "pos_rel"); + obs_data_unset_user_value(data, "scale_rel"); + obs_data_unset_user_value(data, "scale_ref"); + obs_data_unset_user_value(data, "bounds_rel"); + }, + nullptr); +} + +void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate) { ClearSceneData(); ClearContextBar(); @@ -1322,9 +1367,70 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file) obs_data_array_push_back_array(sources, groups); } + /* Reset relative coordinate data if forcefully remigrating. */ + if (remigrate) { + obs_data_set_int(data, "version", 1); + obs_data_array_enum(sources, ClearRelativePosCb, nullptr); + } + + bool resetVideo = false; + bool disableRelativeCoords = false; + obs_video_info ovi; + + int64_t version = obs_data_get_int(data, "version"); + OBSDataAutoRelease res = obs_data_get_obj(data, "resolution"); + if (res) { + lastOutputResolution = {obs_data_get_int(res, "x"), + obs_data_get_int(res, "y")}; + } + + /* Only migrate legacy collection if resolution is saved. */ + if (version < 2 && lastOutputResolution) { + obs_get_video_info(&ovi); + + uint32_t width = obs_data_get_int(res, "x"); + uint32_t height = obs_data_get_int(res, "y"); + + migrationBaseResolution = {width, height}; + + if (ovi.base_height != height || ovi.base_width != width) { + ovi.base_width = width; + ovi.base_height = height; + + /* Attempt to reset to last known canvas resolution for migration. */ + resetVideo = obs_reset_video(&ovi) == OBS_VIDEO_SUCCESS; + disableRelativeCoords = !resetVideo; + } + + /* If migration is possible, and it wasn't forced, back up the original file. */ + if (!disableRelativeCoords && !remigrate) { + auto path = filesystem::u8path(file); + auto backupPath = path.concat(".v1"); + if (!filesystem::exists(backupPath)) { + if (!obs_data_save_json_pretty_safe( + data, backupPath.u8string().c_str(), + "tmp", NULL)) { + blog(LOG_WARNING, + "Failed to create a backup of existing scene collection data!"); + } + } + } + } else if (version < 2) { + disableRelativeCoords = true; + } else if (OBSDataAutoRelease migration_res = + obs_data_get_obj(data, "migration_resolution")) { + migrationBaseResolution = {obs_data_get_int(migration_res, "x"), + obs_data_get_int(migration_res, + "y")}; + } + + DisableRelativeCoordinates(disableRelativeCoords); + obs_missing_files_t *files = obs_missing_files_create(); obs_load_sources(sources, AddMissingFiles, files); + if (resetVideo) + ResetVideo(); if (transitions) LoadTransitions(transitions, AddMissingFiles, files); if (sceneOrder) @@ -4953,6 +5059,13 @@ int OBSBasic::ResetVideo() OBSBasicStats::InitializeValues(); OBSProjector::UpdateMultiviewProjectors(); + bool canMigrate = + usingAbsoluteCoordinates || + (migrationBaseResolution && + (migrationBaseResolution->first != ovi.base_width || + migrationBaseResolution->second != ovi.base_height)); + ui->actionRemigrateSceneCollection->setEnabled(canMigrate); + emit CanvasResized(ovi.base_width, ovi.base_height); emit OutputResized(ovi.output_width, ovi.output_height); } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 5f6fea05239873..3f213483219201 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -358,6 +358,10 @@ class OBSBasic : public OBSMainWindow { std::atomic currentScene = nullptr; std::optional> lastOutputResolution; + std::optional> migrationBaseResolution; + bool usingAbsoluteCoordinates = false; + + void DisableRelativeCoordinates(bool disable); void OnEvent(enum obs_frontend_event event); @@ -377,8 +381,9 @@ class OBSBasic : public OBSMainWindow { void UploadLog(const char *subdir, const char *file, const bool crash); void Save(const char *file); - void LoadData(obs_data_t *data, const char *file); - void Load(const char *file); + void LoadData(obs_data_t *data, const char *file, + bool remigrate = false); + void Load(const char *file, bool remigrate = false); void InitHotkeys(); void CreateHotkeys(); @@ -1149,6 +1154,7 @@ private slots: void on_actionRemoveSceneCollection_triggered(); void on_actionImportSceneCollection_triggered(); void on_actionExportSceneCollection_triggered(); + void on_actionRemigrateSceneCollection_triggered(); void on_actionNewProfile_triggered(); void on_actionDupProfile_triggered(); From 72428ccd97ae032825fb3b390c0b2b50768655bf Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 23 Aug 2024 21:43:49 +0200 Subject: [PATCH 0460/1073] Remove CMake legacy code path Except submodules and legacy_check macro since the former depend on it. --- CMakeLists.txt | 141 +---- CMakePresets.json | 1 - UI/cmake/legacy.cmake | 562 ------------------ .../aja-output-ui/cmake/legacy.cmake | 93 --- .../decklink-captions/cmake/legacy.cmake | 62 -- .../decklink-output-ui/cmake/legacy.cmake | 66 -- .../frontend-tools/cmake/legacy.cmake | 110 ---- UI/obs-frontend-api/cmake/legacy.cmake | 36 -- UI/xdg-data/CMakeLists.txt | 58 -- UI/xdg-data/com.obsproject.Studio.desktop | 97 --- .../com.obsproject.Studio.metainfo.xml.in | 68 --- UI/xdg-data/icons/obs-logo-128.png | Bin 18671 -> 0 bytes UI/xdg-data/icons/obs-logo-256.png | Bin 45887 -> 0 bytes UI/xdg-data/icons/obs-logo-512.png | Bin 130086 -> 0 bytes UI/xdg-data/icons/obs-logo-scalable.svg | 92 --- build-aux/com.obsproject.Studio.json | 2 - cmake/Modules/CompilerConfig.cmake | 192 ------ cmake/Modules/CopyMSVCBins.cmake | 364 ------------ cmake/Modules/DeprecationHelpers.cmake | 237 -------- cmake/Modules/FindAMF.cmake | 92 --- cmake/Modules/FindAsio.cmake | 94 --- cmake/Modules/FindCEF.cmake | 128 ---- cmake/Modules/FindDetours.cmake | 68 --- cmake/Modules/FindFFmpeg.cmake | 189 ------ cmake/Modules/FindFFnvcodec.cmake | 96 --- cmake/Modules/FindGio.cmake | 52 -- cmake/Modules/FindJack.cmake | 74 --- cmake/Modules/FindJansson.cmake | 80 --- cmake/Modules/FindLibAJANTV2.cmake | 179 ------ cmake/Modules/FindLibUUID.cmake | 32 - cmake/Modules/FindLibVLC.cmake | 56 -- cmake/Modules/FindLibdrm.cmake | 34 -- cmake/Modules/FindLibfdk.cmake | 66 -- cmake/Modules/FindLibpci.cmake | 48 -- cmake/Modules/FindLibrist.cmake | 67 --- cmake/Modules/FindLibrnnoise.cmake | 65 -- cmake/Modules/FindLibspeexdsp.cmake | 66 -- cmake/Modules/FindLibsrt.cmake | 67 --- cmake/Modules/FindLibv4l2.cmake | 41 -- cmake/Modules/FindLibva.cmake | 71 --- cmake/Modules/FindLibx264.cmake | 65 -- cmake/Modules/FindLuajit.cmake | 85 --- cmake/Modules/FindMbedTLS.cmake | 156 ----- cmake/Modules/FindOSS.cmake | 36 -- cmake/Modules/FindPipeWire.cmake | 100 ---- cmake/Modules/FindPythonWindows.cmake | 60 -- cmake/Modules/FindSndio.cmake | 66 -- cmake/Modules/FindSwigWindows.cmake | 19 - cmake/Modules/FindSysinfo.cmake | 34 -- cmake/Modules/FindUdev.cmake | 41 -- cmake/Modules/FindUthash.cmake | 84 --- cmake/Modules/FindVPL.cmake | 147 ----- cmake/Modules/FindWayland.cmake | 125 ---- cmake/Modules/FindWebsocketpp.cmake | 91 --- cmake/Modules/FindX11_XCB.cmake | 45 -- cmake/Modules/FindXCB.cmake | 267 --------- cmake/Modules/FindXkbcommon.cmake | 41 -- cmake/Modules/Findqrcodegencpp.cmake | 174 ------ cmake/Modules/IDLFileHelper.cmake | 74 --- cmake/Modules/ObsDefaults_Linux.cmake | 175 ------ cmake/Modules/ObsDefaults_Windows.cmake | 159 ----- cmake/Modules/ObsDefaults_macOS.cmake | 189 ------ cmake/Modules/ObsHelpers.cmake | 477 --------------- cmake/Modules/ObsHelpers_Linux.cmake | 104 ---- cmake/Modules/ObsHelpers_Windows.cmake | 457 -------------- cmake/Modules/ObsHelpers_macOS.cmake | 457 -------------- cmake/Modules/VersionConfig.cmake | 118 ---- deps/CMakeLists.txt | 7 - deps/glad/CMakeLists.txt | 4 - deps/libcaption/cmake/legacy.cmake | 44 -- deps/w32-pthreads/cmake/legacy.cmake | 22 - libobs-opengl/cmake/legacy.cmake | 71 --- libobs-winrt/cmake/legacy.cmake | 31 - libobs/cmake/legacy.cmake | 508 ---------------- plugins/CMakeLists.txt | 233 +++----- plugins/aja/cmake/legacy.cmake | 74 --- plugins/coreaudio-encoder/cmake/legacy.cmake | 48 -- plugins/decklink/cmake/legacy.cmake | 104 ---- plugins/image-source/cmake/legacy.cmake | 23 - plugins/linux-alsa/cmake/legacy.cmake | 20 - plugins/linux-capture/cmake/legacy.cmake | 37 -- plugins/linux-jack/cmake/legacy.cmake | 20 - plugins/linux-pipewire/cmake/legacy.cmake | 46 -- plugins/linux-pulseaudio/cmake/legacy.cmake | 21 - plugins/linux-v4l2/cmake/legacy.cmake | 31 - plugins/obs-ffmpeg/cmake/legacy.cmake | 125 ---- plugins/obs-filters/cmake/legacy.cmake | 188 ------ plugins/obs-libfdk/cmake/legacy.cmake | 21 - plugins/obs-outputs/cmake/legacy.cmake | 121 ---- plugins/obs-qsv11/cmake/legacy.cmake | 60 -- plugins/obs-transitions/cmake/legacy.cmake | 45 -- plugins/obs-vst/cmake/legacy.cmake | 79 --- plugins/obs-webrtc/cmake/legacy.cmake | 22 - plugins/obs-x264/cmake/legacy.cmake | 34 -- plugins/oss-audio/cmake/legacy.cmake | 27 - plugins/rtmp-services/cmake/legacy.cmake | 55 -- plugins/sndio/cmake/legacy.cmake | 20 - plugins/text-freetype2/cmake/legacy.cmake | 46 -- plugins/vlc-video/cmake/legacy.cmake | 35 -- shared/media-playback/CMakeLists.txt | 5 - shared/obs-scripting/cmake/legacy.cmake | 181 ------ .../obs-scripting/obslua/cmake/legacy.cmake | 63 -- .../obspython/cmake/legacy.cmake | 107 ---- 103 files changed, 83 insertions(+), 10187 deletions(-) delete mode 100644 UI/cmake/legacy.cmake delete mode 100644 UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake delete mode 100644 UI/frontend-plugins/decklink-captions/cmake/legacy.cmake delete mode 100644 UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake delete mode 100644 UI/frontend-plugins/frontend-tools/cmake/legacy.cmake delete mode 100644 UI/obs-frontend-api/cmake/legacy.cmake delete mode 100644 UI/xdg-data/CMakeLists.txt delete mode 100644 UI/xdg-data/com.obsproject.Studio.desktop delete mode 100644 UI/xdg-data/com.obsproject.Studio.metainfo.xml.in delete mode 100644 UI/xdg-data/icons/obs-logo-128.png delete mode 100644 UI/xdg-data/icons/obs-logo-256.png delete mode 100644 UI/xdg-data/icons/obs-logo-512.png delete mode 100644 UI/xdg-data/icons/obs-logo-scalable.svg delete mode 100644 cmake/Modules/CompilerConfig.cmake delete mode 100644 cmake/Modules/CopyMSVCBins.cmake delete mode 100644 cmake/Modules/DeprecationHelpers.cmake delete mode 100644 cmake/Modules/FindAMF.cmake delete mode 100644 cmake/Modules/FindAsio.cmake delete mode 100644 cmake/Modules/FindCEF.cmake delete mode 100644 cmake/Modules/FindDetours.cmake delete mode 100644 cmake/Modules/FindFFmpeg.cmake delete mode 100644 cmake/Modules/FindFFnvcodec.cmake delete mode 100644 cmake/Modules/FindGio.cmake delete mode 100644 cmake/Modules/FindJack.cmake delete mode 100644 cmake/Modules/FindJansson.cmake delete mode 100644 cmake/Modules/FindLibAJANTV2.cmake delete mode 100644 cmake/Modules/FindLibUUID.cmake delete mode 100644 cmake/Modules/FindLibVLC.cmake delete mode 100644 cmake/Modules/FindLibdrm.cmake delete mode 100644 cmake/Modules/FindLibfdk.cmake delete mode 100644 cmake/Modules/FindLibpci.cmake delete mode 100644 cmake/Modules/FindLibrist.cmake delete mode 100644 cmake/Modules/FindLibrnnoise.cmake delete mode 100644 cmake/Modules/FindLibspeexdsp.cmake delete mode 100644 cmake/Modules/FindLibsrt.cmake delete mode 100644 cmake/Modules/FindLibv4l2.cmake delete mode 100644 cmake/Modules/FindLibva.cmake delete mode 100644 cmake/Modules/FindLibx264.cmake delete mode 100644 cmake/Modules/FindLuajit.cmake delete mode 100644 cmake/Modules/FindMbedTLS.cmake delete mode 100644 cmake/Modules/FindOSS.cmake delete mode 100644 cmake/Modules/FindPipeWire.cmake delete mode 100644 cmake/Modules/FindPythonWindows.cmake delete mode 100644 cmake/Modules/FindSndio.cmake delete mode 100644 cmake/Modules/FindSwigWindows.cmake delete mode 100644 cmake/Modules/FindSysinfo.cmake delete mode 100644 cmake/Modules/FindUdev.cmake delete mode 100644 cmake/Modules/FindUthash.cmake delete mode 100644 cmake/Modules/FindVPL.cmake delete mode 100644 cmake/Modules/FindWayland.cmake delete mode 100644 cmake/Modules/FindWebsocketpp.cmake delete mode 100644 cmake/Modules/FindX11_XCB.cmake delete mode 100644 cmake/Modules/FindXCB.cmake delete mode 100644 cmake/Modules/FindXkbcommon.cmake delete mode 100644 cmake/Modules/Findqrcodegencpp.cmake delete mode 100644 cmake/Modules/IDLFileHelper.cmake delete mode 100644 cmake/Modules/ObsDefaults_Linux.cmake delete mode 100644 cmake/Modules/ObsDefaults_Windows.cmake delete mode 100644 cmake/Modules/ObsDefaults_macOS.cmake delete mode 100644 cmake/Modules/ObsHelpers.cmake delete mode 100644 cmake/Modules/ObsHelpers_Linux.cmake delete mode 100644 cmake/Modules/ObsHelpers_Windows.cmake delete mode 100644 cmake/Modules/ObsHelpers_macOS.cmake delete mode 100644 cmake/Modules/VersionConfig.cmake delete mode 100644 deps/CMakeLists.txt delete mode 100644 deps/libcaption/cmake/legacy.cmake delete mode 100644 deps/w32-pthreads/cmake/legacy.cmake delete mode 100644 libobs-opengl/cmake/legacy.cmake delete mode 100644 libobs-winrt/cmake/legacy.cmake delete mode 100644 libobs/cmake/legacy.cmake delete mode 100644 plugins/aja/cmake/legacy.cmake delete mode 100644 plugins/coreaudio-encoder/cmake/legacy.cmake delete mode 100644 plugins/decklink/cmake/legacy.cmake delete mode 100644 plugins/image-source/cmake/legacy.cmake delete mode 100644 plugins/linux-alsa/cmake/legacy.cmake delete mode 100644 plugins/linux-capture/cmake/legacy.cmake delete mode 100644 plugins/linux-jack/cmake/legacy.cmake delete mode 100644 plugins/linux-pipewire/cmake/legacy.cmake delete mode 100644 plugins/linux-pulseaudio/cmake/legacy.cmake delete mode 100644 plugins/linux-v4l2/cmake/legacy.cmake delete mode 100644 plugins/obs-ffmpeg/cmake/legacy.cmake delete mode 100644 plugins/obs-filters/cmake/legacy.cmake delete mode 100644 plugins/obs-libfdk/cmake/legacy.cmake delete mode 100644 plugins/obs-outputs/cmake/legacy.cmake delete mode 100644 plugins/obs-qsv11/cmake/legacy.cmake delete mode 100644 plugins/obs-transitions/cmake/legacy.cmake delete mode 100644 plugins/obs-vst/cmake/legacy.cmake delete mode 100644 plugins/obs-webrtc/cmake/legacy.cmake delete mode 100644 plugins/obs-x264/cmake/legacy.cmake delete mode 100644 plugins/oss-audio/cmake/legacy.cmake delete mode 100644 plugins/rtmp-services/cmake/legacy.cmake delete mode 100644 plugins/sndio/cmake/legacy.cmake delete mode 100644 plugins/text-freetype2/cmake/legacy.cmake delete mode 100644 plugins/vlc-video/cmake/legacy.cmake delete mode 100644 shared/obs-scripting/cmake/legacy.cmake delete mode 100644 shared/obs-scripting/obslua/cmake/legacy.cmake delete mode 100644 shared/obs-scripting/obspython/cmake/legacy.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index fb62167ebe0bea..9819235af760bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,145 +1,34 @@ cmake_minimum_required(VERSION 3.22...3.25) -if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Windows|Darwin)" OR OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) - include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) - project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) - - if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") - include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/architecture.cmake") - if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) - return() - endif() - endif() - - include(compilerconfig) - include(defaults) - include(helpers) - - option(ENABLE_UI "Enable building with UI (requires Qt)" ON) - option(ENABLE_SCRIPTING "Enable scripting support" ON) - option(ENABLE_HEVC "Enable HEVC encoders" ON) +project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) - add_subdirectory(libobs) - if(OS_WINDOWS) - add_subdirectory(libobs-d3d11) - add_subdirectory(libobs-winrt) +if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/architecture.cmake") + if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + return() endif() - add_subdirectory(libobs-opengl) - add_subdirectory(plugins) - - add_subdirectory(test/test-input) - - add_subdirectory(UI) - - message_configuration() - return() endif() -message( - DEPRECATION - "\n" - "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" - "\n" - "You are using the legacy build system to build OBS Studio. " - "The legacy build system is unsupported and will be removed in the near future." - "\n" - "To migrate to the new build system, familiarize yourself with CMake presets " - "(https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) and create " - "a user preset with your customized build settings, inheriting from one of the default presets." - "\n" - "============ LEGACY BUILD SYSTEM IS DEPRECATED ============" -) - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/Modules") -include(VersionConfig) +include(compilerconfig) +include(defaults) +include(helpers) -# Prohibit in-source builds -if("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") - message( - FATAL_ERROR - "OBS: You cannot build in a source directory (or any directory with CMakeLists.txt file). " - "Please make a build subdirectory. Feel free to remove CMakeCache.txt and CMakeFiles." - ) -endif() - -project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) -set_property(GLOBAL PROPERTY USE_FOLDERS ON) - -# Use target folders for MSVC/Xcode/etc. -include(DeprecationHelpers) -include(ObsHelpers) -# Set default compiler flags -include(CompilerConfig) - -# Allow selection of common build types via UI -if(NOT CMAKE_BUILD_TYPE) - set( - CMAKE_BUILD_TYPE - "RelWithDebInfo" - CACHE STRING - "OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]" - FORCE - ) - set_property( - CACHE CMAKE_BUILD_TYPE - PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel - ) -endif() - -# Global project options -option(ENABLE_HEVC "Enable HEVC encoders" ON) -if(ENABLE_HEVC) - add_compile_definitions(ENABLE_HEVC) -endif() -option(BUILD_FOR_DISTRIBUTION "Build for distribution (enables optimizations)" OFF) option(ENABLE_UI "Enable building with UI (requires Qt)" ON) option(ENABLE_SCRIPTING "Enable scripting support" ON) -option(USE_LIBCXX "Use libc++ instead of libstdc++" ${APPLE}) -option(BUILD_TESTS "Build test directory (includes test sources and possibly a platform test executable)" OFF) - -if(OS_WINDOWS) - option( - INSTALLER_RUN - "Build a multiarch installer (needs to run independently after both archs have compiled) (Windows)" - OFF - ) -elseif(OS_POSIX) - option(LINUX_PORTABLE "Build portable version (Linux)" OFF) - option(USE_XDG "Utilize XDG Base Directory Specification (Linux)" ON) - option(ENABLE_PULSEAUDIO "Enable PulseAudio support" ON) - if(OS_LINUX) - option(ENABLE_WAYLAND "Enable building with support for Wayland (Linux)" ON) - option(BUILD_FOR_PPA "Build for PPA distribution" OFF) - endif() -endif() - -setup_obs_project() -mark_as_advanced(BUILD_TESTS USE_LIBCXX) - -if(INSTALLER_RUN) - generate_multiarch_installer() - return() -endif() +option(ENABLE_HEVC "Enable HEVC encoders" ON) -# OBS sources and plugins -add_subdirectory(deps) -add_subdirectory(libobs-opengl) +add_subdirectory(libobs) if(OS_WINDOWS) add_subdirectory(libobs-d3d11) add_subdirectory(libobs-winrt) endif() -add_subdirectory(libobs) +add_subdirectory(libobs-opengl) add_subdirectory(plugins) -# OBS main app -add_subdirectory(UI) +add_subdirectory(test/test-input) -# Tests -if(ENABLE_UNIT_TESTS) - enable_testing() -endif() +add_subdirectory(UI) -if(BUILD_TESTS OR ENABLE_UNIT_TESTS) - add_subdirectory(test) -endif() +message_configuration() diff --git a/CMakePresets.json b/CMakePresets.json index 441e66f89f0054..bce62abc9a83fc 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -71,7 +71,6 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_INSTALL_LIBDIR": "lib/CMAKE_SYSTEM_PROCESSOR-linux-gnu", - "OBS_CMAKE_VERSION": {"type": "STRING", "value": "3.0.0"}, "ENABLE_AJA": false, "ENABLE_VLC": true, "ENABLE_WAYLAND": true, diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake deleted file mode 100644 index d25e900dba7e68..00000000000000 --- a/UI/cmake/legacy.cmake +++ /dev/null @@ -1,562 +0,0 @@ -add_subdirectory(obs-frontend-api) - -option(ENABLE_UI "Enable building with UI (requires Qt)" ON) -if(NOT ENABLE_UI) - obs_status(DISABLED "OBS UI") - return() -endif() - -project(obs) - -# Legacy support -if(TARGET obs-browser - AND NOT TARGET OBS::browser-panels - AND BROWSER_PANEL_SUPPORT_ENABLED) - add_library(obs-browser-panels INTERFACE) - add_library(OBS::browser-panels ALIAS obs-browser-panels) - - target_include_directories(obs-browser-panels INTERFACE ${CMAKE_SOURCE_DIR}/plugins/obs-browser/panel) -endif() - -set(OAUTH_BASE_URL - "https://auth.obsproject.com/" - CACHE STRING "Default OAuth base URL") - -mark_as_advanced(OAUTH_BASE_URL) - -if(NOT DEFINED TWITCH_CLIENTID - OR "${TWITCH_CLIENTID}" STREQUAL "" - OR NOT DEFINED TWITCH_HASH - OR "${TWITCH_HASH}" STREQUAL "" - OR NOT TARGET OBS::browser-panels) - set(TWITCH_ENABLED OFF) - set(TWITCH_CLIENTID "") - set(TWITCH_HASH "0") -else() - set(TWITCH_ENABLED ON) -endif() - -if(NOT DEFINED RESTREAM_CLIENTID - OR "${RESTREAM_CLIENTID}" STREQUAL "" - OR NOT DEFINED RESTREAM_HASH - OR "${RESTREAM_HASH}" STREQUAL "" - OR NOT TARGET OBS::browser-panels) - set(RESTREAM_ENABLED OFF) - set(RESTREAM_CLIENTID "") - set(RESTREAM_HASH "0") -else() - set(RESTREAM_ENABLED ON) -endif() - -if(NOT DEFINED YOUTUBE_CLIENTID - OR "${YOUTUBE_CLIENTID}" STREQUAL "" - OR NOT DEFINED YOUTUBE_SECRET - OR "${YOUTUBE_SECRET}" STREQUAL "" - OR NOT DEFINED YOUTUBE_CLIENTID_HASH - OR "${YOUTUBE_CLIENTID_HASH}" STREQUAL "" - OR NOT DEFINED YOUTUBE_SECRET_HASH - OR "${YOUTUBE_SECRET_HASH}" STREQUAL "") - set(YOUTUBE_SECRET_HASH "0") - set(YOUTUBE_CLIENTID_HASH "0") - set(YOUTUBE_ENABLED OFF) -else() - set(YOUTUBE_ENABLED ON) -endif() - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ui-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/ui-config.h) - -find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) -find_package(CURL REQUIRED) - -add_subdirectory(frontend-plugins) -add_executable(obs) - -if(NOT TARGET OBS::properties-view) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") -endif() - -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-slider-ignorewheel) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel" - "${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel") -endif() - -if(NOT TARGET OBS::qt-vertical-scroll-area) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area" - "${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area") -endif() - -if(NOT TARGET OBS::qt-wrappers) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") -endif() - -find_qt(COMPONENTS Widgets Network Svg Xml COMPONENTS_LINUX Gui DBus) - -target_link_libraries(obs PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network) - -set_target_properties( - obs - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON - AUTOUIC_SEARCH_PATHS "forms;forms/source-toolbar") - -if(OS_WINDOWS) - set_target_properties(obs PROPERTIES AUTORCC_OPTIONS "--format-version;1") -endif() - -target_include_directories(obs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - -target_sources(obs PRIVATE forms/obs.qrc) -target_sources( - obs - PRIVATE forms/AutoConfigFinishPage.ui - forms/AutoConfigStartPage.ui - forms/AutoConfigStartPage.ui - forms/AutoConfigStreamPage.ui - forms/AutoConfigTestPage.ui - forms/AutoConfigVideoPage.ui - forms/ColorSelect.ui - forms/OBSAbout.ui - forms/OBSAdvAudio.ui - forms/OBSBasic.ui - forms/OBSBasicControls.ui - forms/OBSBasicFilters.ui - forms/OBSBasicInteraction.ui - forms/OBSBasicProperties.ui - forms/OBSBasicSettings.ui - forms/OBSBasicSourceSelect.ui - forms/OBSBasicTransform.ui - forms/OBSBasicVCamConfig.ui - forms/OBSExtraBrowsers.ui - forms/OBSImporter.ui - forms/OBSLogReply.ui - forms/OBSLogViewer.ui - forms/OBSMissingFiles.ui - forms/OBSRemux.ui - forms/OBSUpdate.ui - forms/OBSYoutubeActions.ui - forms/StatusBarWidget.ui - forms/source-toolbar/browser-source-toolbar.ui - forms/source-toolbar/color-source-toolbar.ui - forms/source-toolbar/device-select-toolbar.ui - forms/source-toolbar/game-capture-toolbar.ui - forms/source-toolbar/image-source-toolbar.ui - forms/source-toolbar/media-controls.ui - forms/source-toolbar/text-source-toolbar.ui) - -target_sources( - obs - PRIVATE auth-oauth.cpp - auth-oauth.hpp - auth-listener.cpp - auth-listener.hpp - obf.c - obf.h - obs-app-theming.cpp - obs-app-theming.hpp - obs-app.cpp - obs-app.hpp - obs-proxy-style.cpp - obs-proxy-style.hpp - api-interface.cpp - auth-base.cpp - auth-base.hpp - display-helpers.hpp - platform.hpp - qt-display.cpp - qt-display.hpp - ui-validation.cpp - ui-validation.hpp - multiview.cpp - multiview.hpp - ffmpeg-utils.cpp - ffmpeg-utils.hpp - ${CMAKE_SOURCE_DIR}/deps/json11/json11.cpp - ${CMAKE_SOURCE_DIR}/deps/json11/json11.hpp - ${CMAKE_CURRENT_BINARY_DIR}/ui-config.h) - -target_sources( - obs - PRIVATE absolute-slider.cpp - absolute-slider.hpp - adv-audio-control.cpp - adv-audio-control.hpp - audio-encoders.cpp - audio-encoders.hpp - balance-slider.hpp - basic-controls.cpp - basic-controls.hpp - clickable-label.hpp - horizontal-scroll-area.cpp - horizontal-scroll-area.hpp - item-widget-helpers.cpp - item-widget-helpers.hpp - context-bar-controls.cpp - context-bar-controls.hpp - focus-list.cpp - focus-list.hpp - hotkey-edit.cpp - hotkey-edit.hpp - lineedit-autoresize.cpp - lineedit-autoresize.hpp - log-viewer.cpp - log-viewer.hpp - media-controls.cpp - media-controls.hpp - menu-button.cpp - menu-button.hpp - mute-checkbox.hpp - noncheckable-button.hpp - preview-controls.cpp - preview-controls.hpp - remote-text.cpp - remote-text.hpp - scene-tree.cpp - scene-tree.hpp - screenshot-obj.hpp - source-label.cpp - source-label.hpp - source-tree.cpp - source-tree.hpp - url-push-button.cpp - url-push-button.hpp - undo-stack-obs.cpp - undo-stack-obs.hpp - volume-control.cpp - volume-control.hpp - visibility-item-widget.cpp - visibility-item-widget.hpp) - -target_sources( - obs - PRIVATE window-basic-about.cpp - window-basic-about.hpp - window-basic-auto-config.cpp - window-basic-auto-config.hpp - window-basic-auto-config-test.cpp - window-basic-adv-audio.cpp - window-basic-adv-audio.hpp - window-basic-filters.cpp - window-basic-filters.hpp - window-basic-interaction.cpp - window-basic-interaction.hpp - window-basic-main.cpp - window-basic-main.hpp - window-basic-main-browser.cpp - window-basic-main-dropfiles.cpp - window-basic-main-icons.cpp - window-basic-main-outputs.cpp - window-basic-main-outputs.hpp - window-basic-main-profiles.cpp - window-basic-main-scene-collections.cpp - window-basic-main-screenshot.cpp - window-basic-main-transitions.cpp - window-basic-preview.cpp - window-basic-properties.cpp - window-basic-properties.hpp - window-basic-settings.cpp - window-basic-settings.hpp - window-basic-settings-a11y.cpp - window-basic-settings-appearance.cpp - window-basic-settings-stream.cpp - window-basic-source-select.cpp - window-basic-source-select.hpp - window-basic-stats.cpp - window-basic-stats.hpp - window-basic-status-bar.cpp - window-basic-status-bar.hpp - window-basic-transform.cpp - window-basic-transform.hpp - window-basic-preview.hpp - window-basic-vcam.hpp - window-basic-vcam-config.cpp - window-basic-vcam-config.hpp - window-dock.cpp - window-dock.hpp - window-importer.cpp - window-importer.hpp - window-log-reply.hpp - window-main.hpp - window-missing-files.cpp - window-missing-files.hpp - window-namedialog.cpp - window-namedialog.hpp - window-log-reply.cpp - window-projector.cpp - window-projector.hpp - window-remux.cpp - window-remux.hpp) - -target_sources( - obs - PRIVATE # cmake-format: sortable - goliveapi-censoredjson.cpp - goliveapi-censoredjson.hpp - goliveapi-network.cpp - goliveapi-network.hpp - goliveapi-postdata.cpp - goliveapi-postdata.hpp - multitrack-video-error.cpp - multitrack-video-error.hpp - multitrack-video-output.cpp - multitrack-video-output.hpp - system-info.hpp) - -target_sources(obs PRIVATE importers/importers.cpp importers/importers.hpp importers/classic.cpp importers/sl.cpp - importers/studio.cpp importers/xsplit.cpp) - -target_compile_features(obs PRIVATE cxx_std_17) - -target_include_directories(obs PRIVATE ${CMAKE_SOURCE_DIR}/deps/json11) - -target_link_libraries( - obs - PRIVATE CURL::libcurl - FFmpeg::avcodec - FFmpeg::avutil - FFmpeg::avformat - OBS::libobs - OBS::frontend-api - OBS::qt-wrappers - OBS::qt-plain-text-edit - OBS::qt-vertical-scroll-area - OBS::qt-slider-ignorewheel - OBS::properties-view) - -set_target_properties(obs PROPERTIES FOLDER "frontend") - -if(TARGET OBS::browser-panels) - get_target_property(_PANEL_INCLUDE_DIRECTORY OBS::browser-panels INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(obs PRIVATE ${_PANEL_INCLUDE_DIRECTORY}) - - target_compile_definitions(obs PRIVATE BROWSER_AVAILABLE) - - target_sources(obs PRIVATE window-dock-browser.cpp window-dock-browser.hpp window-extra-browsers.cpp - window-extra-browsers.hpp) - - if(TWITCH_ENABLED) - target_compile_definitions(obs PRIVATE TWITCH_ENABLED) - target_sources(obs PRIVATE auth-twitch.cpp auth-twitch.hpp) - endif() - - if(RESTREAM_ENABLED) - target_compile_definitions(obs PRIVATE RESTREAM_ENABLED) - target_sources(obs PRIVATE auth-restream.cpp auth-restream.hpp) - endif() - - if(OS_WINDOWS OR OS_MACOS) - set(ENABLE_WHATSNEW - ON - CACHE INTERNAL "Enable WhatsNew dialog") - elseif(OS_LINUX) - option(ENABLE_WHATSNEW "Enable WhatsNew dialog" ON) - endif() - - if(ENABLE_WHATSNEW) - target_compile_definitions(obs PRIVATE WHATSNEW_ENABLED) - endif() -endif() - -if(YOUTUBE_ENABLED) - target_compile_definitions(obs PRIVATE YOUTUBE_ENABLED) - target_sources( - obs - PRIVATE auth-youtube.cpp - auth-youtube.hpp - window-dock-youtube-app.cpp - window-dock-youtube-app.hpp - window-youtube-actions.cpp - window-youtube-actions.hpp - youtube-api-wrappers.cpp - youtube-api-wrappers.hpp) -endif() - -if(OS_WINDOWS) - set_target_properties(obs PROPERTIES WIN32_EXECUTABLE ON OUTPUT_NAME "obs${_ARCH_SUFFIX}") - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/obs.rc.in ${CMAKE_BINARY_DIR}/obs.rc) - - find_package(Detours REQUIRED) - find_package(nlohmann_json REQUIRED) - - target_sources( - obs - PRIVATE obs.manifest - platform-windows.cpp - win-dll-blocklist.c - update/update-window.cpp - update/update-window.hpp - update/win-update.cpp - update/win-update.hpp - update/shared-update.cpp - update/shared-update.hpp - update/update-helpers.cpp - update/update-helpers.hpp - update/crypto-helpers-mbedtls.cpp - update/crypto-helpers.hpp - update/models/branches.hpp - update/models/whatsnew.hpp - win-update/updater/manifest.hpp - ${CMAKE_BINARY_DIR}/obs.rc) - - target_sources(obs PRIVATE system-info-windows.cpp) - - find_package(MbedTLS) - target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2 Detours::Detours) - - target_compile_features(obs PRIVATE cxx_std_17) - - target_compile_definitions(obs PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS - PSAPI_VERSION=2) - - set_source_files_properties(update/win-update.cpp PROPERTIES COMPILE_DEFINITIONS OBS_COMMIT="${OBS_COMMIT}") - if(MSVC) - target_link_options(obs PRIVATE "LINKER:/IGNORE:4098" "LINKER:/IGNORE:4099") - target_link_libraries(obs PRIVATE OBS::w32-pthreads) - endif() - - if(CMAKE_SIZEOF_VOID_P EQUAL 4) - target_link_options(obs PRIVATE /LARGEADDRESSAWARE) - endif() - - add_subdirectory(win-update/updater) - -elseif(OS_MACOS) - set_target_properties( - obs - PROPERTIES OUTPUT_NAME ${OBS_BUNDLE_NAME} - MACOSX_BUNDLE ON - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/Info.plist.in) - - if(XCODE) - set_target_properties( - obs - PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${MACOSX_BUNDLE_GUI_IDENTIFIER}" - XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon - XCODE_ATTRIBUTE_PRODUCT_NAME "OBS") - - set(APP_ICON_TARGET ${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/Assets.xcassets) - - target_sources(obs PRIVATE ${APP_ICON_TARGET}) - set_source_files_properties(${APP_ICON_TARGET} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - else() - set(APP_ICON_TARGET ${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/AppIcon.iconset) - set(APP_ICON_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/AppIcon.icns) - - add_custom_command(OUTPUT ${APP_ICON_OUTPUT} COMMAND iconutil -c icns "${APP_ICON_TARGET}" -o "${APP_ICON_OUTPUT}") - - set(MACOSX_BUNDLE_ICON_FILE AppIcon.icns) - target_sources(obs PRIVATE ${APP_ICON_OUTPUT} ${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS) - - set_source_files_properties(${APP_ICON_OUTPUT} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - endif() - - find_library(APPKIT Appkit) - find_library(AVFOUNDATION AVFoundation) - find_library(APPLICATIONSERVICES ApplicationServices) - mark_as_advanced(APPKIT AVFOUNDATION APPLICATIONSERVICES) - - target_link_libraries(obs PRIVATE ${APPKIT} ${AVFOUNDATION} ${APPLICATIONSERVICES}) - - target_sources(obs PRIVATE platform-osx.mm) - target_sources(obs PRIVATE forms/OBSPermissions.ui window-permissions.cpp window-permissions.hpp) - - target_sources(obs PRIVATE system-info-macos.mm) - - if(ENABLE_WHATSNEW) - find_library(SECURITY Security) - find_package(nlohmann_json REQUIRED) - mark_as_advanced(SECURITY) - - target_link_libraries(obs PRIVATE ${SECURITY} OBS::blake2 nlohmann_json::nlohmann_json) - - target_sources( - obs - PRIVATE update/crypto-helpers.hpp - update/crypto-helpers-mac.mm - update/shared-update.cpp - update/shared-update.hpp - update/update-helpers.cpp - update/update-helpers.hpp - update/models/whatsnew.hpp) - - if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY) - find_library(SPARKLE Sparkle) - mark_as_advanced(SPARKLE) - - target_sources(obs PRIVATE update/mac-update.cpp update/mac-update.hpp update/sparkle-updater.mm - update/models/branches.hpp) - target_compile_definitions(obs PRIVATE ENABLE_SPARKLE_UPDATER) - target_link_libraries(obs PRIVATE ${SPARKLE}) - # Enable Automatic Reference Counting for Sparkle wrapper - set_source_files_properties(update/sparkle-updater.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) - endif() - endif() - - set_source_files_properties(platform-osx.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) - -elseif(OS_POSIX) - target_sources(obs PRIVATE platform-x11.cpp) - target_link_libraries(obs PRIVATE Qt::GuiPrivate Qt::DBus) - - target_sources(obs PRIVATE system-info-posix.cpp) - - target_compile_definitions(obs PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}" - "$<$:LINUX_PORTABLE>") - if(TARGET obspython) - find_package(Python REQUIRED COMPONENTS Interpreter Development) - target_link_libraries(obs PRIVATE Python::Python) - target_link_options(obs PRIVATE "LINKER:-no-as-needed") - endif() - - if(NOT LINUX_PORTABLE) - add_subdirectory(xdg-data) - endif() - - if(OS_FREEBSD) - target_link_libraries(obs PRIVATE procstat) - target_compile_options(obs PRIVATE -Wno-unqualified-std-cast-call) - endif() - - if(OS_LINUX) - if(USE_XDG) - target_compile_definitions(obs PRIVATE USE_XDG) - endif() - - if(ENABLE_WHATSNEW) - find_package(MbedTLS) - find_package(nlohmann_json REQUIRED) - if(NOT MBEDTLS_FOUND) - obs_status(FATAL_ERROR "mbedTLS not found, but required for WhatsNew support on Linux") - endif() - - target_sources(obs PRIVATE update/crypto-helpers.hpp update/crypto-helpers-mbedtls.cpp update/shared-update.cpp - update/shared-update.hpp update/update-helpers.cpp update/update-helpers.hpp) - target_link_libraries(obs PRIVATE Mbedtls::Mbedtls nlohmann_json::nlohmann_json OBS::blake2) - endif() - endif() -endif() - -get_target_property(_SOURCES obs SOURCES) -set(_UI ${_SOURCES}) -list(FILTER _UI INCLUDE REGEX ".*\\.ui?") - -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" - PREFIX "UI Files" - FILES ${_UI}) -unset(_SOURCES) -unset(_UI) - -get_property(OBS_MODULE_LIST GLOBAL PROPERTY OBS_MODULE_LIST) -list(JOIN OBS_MODULE_LIST "|" SAFE_MODULES) -target_compile_definitions(obs PRIVATE "SAFE_MODULES=\"${SAFE_MODULES}\"") - -define_graphic_modules(obs) -setup_obs_app(obs) -setup_target_resources(obs obs-studio) -add_target_resource(obs ${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS obs-studio/authors) diff --git a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake deleted file mode 100644 index df7e2b2adc41cd..00000000000000 --- a/UI/frontend-plugins/aja-output-ui/cmake/legacy.cmake +++ /dev/null @@ -1,93 +0,0 @@ -project(aja-output-ui) - -if(NOT ENABLE_AJA) - return() -endif() - -find_package(LibAJANTV2 REQUIRED) - -add_library(aja-output-ui MODULE) -add_library(OBS::aja-output-ui ALIAS aja-output-ui) - -if(NOT TARGET OBS::properties-view) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") -endif() - -find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) - -set_target_properties( - aja-output-ui - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON - AUTOUIC_SEARCH_PATHS "forms") - -if(OS_WINDOWS) - set_target_properties(aja-output-ui PROPERTIES AUTORCC_OPTIONS "--format-version;1") -endif() - -target_sources(aja-output-ui PRIVATE forms/output.ui) - -target_sources( - aja-output-ui - PRIVATE AJAOutputUI.h - AJAOutputUI.cpp - aja-ui-main.cpp - aja-ui-main.h - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-card-manager.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-card-manager.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-common.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-common.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-enums.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-presets.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-presets.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-props.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-props.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-routing.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-routing.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-ui-props.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-vpid-data.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-vpid-data.hpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.cpp - ${CMAKE_SOURCE_DIR}/plugins/aja/aja-widget-io.hpp) - -target_link_libraries(aja-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::properties-view Qt::Widgets - AJA::LibAJANTV2) - -if(OS_MACOS) - find_library(IOKIT_FRAMEWORK Iokit) - find_library(COREFOUNDATION_LIBRARY CoreFoundation) - find_library(APPKIT_FRAMEWORK AppKit) - - target_link_libraries(aja-output-ui PRIVATE ${IOKIT} ${COREFOUNDATION} ${APPKIT}) -elseif(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS AJA Output UI") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in aja-output-ui.rc) - target_sources(aja-output-ui PRIVATE aja-output-ui.rc) - - target_compile_options(aja-output-ui PRIVATE /wd4996) - target_link_libraries(aja-output-ui PRIVATE ws2_32.lib setupapi.lib Winmm.lib netapi32.lib Shlwapi.lib) - target_link_options(aja-output-ui PRIVATE "LINKER:/IGNORE:4099") -else() - find_package(X11 REQUIRED) - target_link_libraries(aja-output-ui PRIVATE X11::X11 Qt::GuiPrivate) -endif() - -if(NOT MSVC) - target_compile_options(aja-output-ui PRIVATE -Wno-error=deprecated-declarations) -endif() - -set_target_properties(aja-output-ui PROPERTIES FOLDER "frontend" PREFIX "") - -get_target_property(_SOURCES aja-output-ui SOURCES) -set(_UI ${_SOURCES}) -list(FILTER _UI INCLUDE REGEX ".*\\.ui?") - -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" - PREFIX "UI Files" - FILES ${_UI}) -unset(_SOURCES) -unset(_UI) - -setup_plugin_target(aja-output-ui) diff --git a/UI/frontend-plugins/decklink-captions/cmake/legacy.cmake b/UI/frontend-plugins/decklink-captions/cmake/legacy.cmake deleted file mode 100644 index 50a4d33ccd3841..00000000000000 --- a/UI/frontend-plugins/decklink-captions/cmake/legacy.cmake +++ /dev/null @@ -1,62 +0,0 @@ -project(decklink-captions) - -if(NOT ENABLE_DECKLINK) - return() -endif() - -add_library(decklink-captions MODULE) -add_library(OBS::decklink-captions ALIAS decklink-captions) - -find_qt(COMPONENTS Widgets) - -target_link_libraries(decklink-captions PRIVATE Qt::Widgets) - -set_target_properties( - decklink-captions - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON - AUTOUIC_SEARCH_PATHS "forms") - -if(OS_WINDOWS) - set_target_properties(decklink-captions PROPERTIES AUTORCC_OPTIONS "--format-version;1") -endif() - -target_compile_features(decklink-captions PRIVATE cxx_std_17) - -target_sources(decklink-captions PRIVATE forms/captions.ui) - -target_sources(decklink-captions PRIVATE decklink-captions.cpp decklink-captions.h) - -target_link_libraries(decklink-captions PRIVATE OBS::frontend-api OBS::libobs) - -if(OS_MACOS) - find_library(COCOA Cocoa) - mark_as_advanced(COCOA) - target_link_libraries(decklink-captions PRIVATE ${COCOA}) - -elseif(OS_POSIX) - find_package(X11 REQUIRED) - mark_as_advanced(X11) - target_link_libraries(decklink-captions PRIVATE X11::X11) -elseif(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS DeckLink Captions module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in decklink-captions.rc) - - target_sources(decklink-captions PRIVATE decklink-captions.rc) -endif() - -set_target_properties(decklink-captions PROPERTIES FOLDER "plugins/decklink" PREFIX "") - -get_target_property(_SOURCES decklink-captions SOURCES) -set(_UI ${_SOURCES}) -list(FILTER _UI INCLUDE REGEX ".*\\.ui?") - -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" - PREFIX "UI Files" - FILES ${_UI}) -unset(_SOURCES) -unset(_UI) - -setup_plugin_target(decklink-captions) diff --git a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake b/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake deleted file mode 100644 index 45c9cc2bce165c..00000000000000 --- a/UI/frontend-plugins/decklink-output-ui/cmake/legacy.cmake +++ /dev/null @@ -1,66 +0,0 @@ -project(decklink-output-ui) - -if(NOT ENABLE_DECKLINK) - return() -endif() - -add_library(decklink-output-ui MODULE) -add_library(OBS::decklink-output-ui ALIAS decklink-output-ui) - -if(NOT TARGET OBS::properties-view) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") -endif() - -find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) - -set_target_properties( - decklink-output-ui - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON - AUTOUIC_SEARCH_PATHS "forms") - -if(OS_WINDOWS) - set_target_properties(decklink-output-ui PROPERTIES AUTORCC_OPTIONS "--format-version;1") -endif() - -target_sources(decklink-output-ui PRIVATE forms/output.ui) - -target_sources(decklink-output-ui PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp - decklink-ui-main.h) - -target_link_libraries(decklink-output-ui PRIVATE OBS::libobs OBS::frontend-api OBS::properties-view Qt::Widgets) - -target_compile_features(decklink-output-ui PRIVATE cxx_std_17) - -set_target_properties(decklink-output-ui PROPERTIES FOLDER "frontend" PREFIX "") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Decklink Output UI") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in decklink-output-ui.rc) - - target_sources(decklink-output-ui PRIVATE decklink-output-ui.rc) - -elseif(OS_MACOS) - find_library(COCOA Cocoa) - mark_as_advanced(COCOA) - - target_link_libraries(decklink-output-ui PRIVATE ${COCOA}) - -elseif(OS_POSIX) - find_package(X11 REQUIRED) - target_link_libraries(decklink-output-ui PRIVATE X11::X11 Qt::GuiPrivate) -endif() - -get_target_property(_SOURCES decklink-output-ui SOURCES) -set(_UI ${_SOURCES}) -list(FILTER _UI INCLUDE REGEX ".*\\.ui?") - -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" - PREFIX "UI Files" - FILES ${_UI}) -unset(_SOURCES) -unset(_UI) - -setup_plugin_target(decklink-output-ui) diff --git a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake b/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake deleted file mode 100644 index cbfbaa5e0df3a9..00000000000000 --- a/UI/frontend-plugins/frontend-tools/cmake/legacy.cmake +++ /dev/null @@ -1,110 +0,0 @@ -project(frontend-tools) - -add_library(frontend-tools MODULE) -add_library(OBS::frontend-tools ALIAS frontend-tools) - -if(NOT TARGET OBS::properties-view) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/properties-view" "${CMAKE_BINARY_DIR}/shared/properties-view") -endif() - -if(NOT TARGET OBS::qt-plain-text-edit) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit" "${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit") -endif() - -if(NOT TARGET OBS::qt-wrappers) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/wrappers" "${CMAKE_BINARY_DIR}/shared/qt/wrappers") -endif() - -find_qt(COMPONENTS Widgets COMPONENTS_LINUX Gui) - -set_target_properties( - frontend-tools - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON - AUTOUIC_SEARCH_PATHS "forms") - -if(OS_WINDOWS) - set_target_properties(frontend-tools PROPERTIES AUTORCC_OPTIONS "--format-version;1") -endif() - -target_sources(frontend-tools PRIVATE forms/auto-scene-switcher.ui forms/captions.ui forms/output-timer.ui - forms/scripts.ui) - -target_sources(frontend-tools PRIVATE frontend-tools.c auto-scene-switcher.hpp auto-scene-switcher.cpp output-timer.hpp - tool-helpers.hpp output-timer.cpp) - -target_compile_features(frontend-tools PRIVATE cxx_std_17) - -target_link_libraries(frontend-tools PRIVATE OBS::frontend-api OBS::qt-wrappers OBS::qt-plain-text-edit - OBS::properties-view OBS::libobs Qt::Widgets) - -if(OS_POSIX AND NOT OS_MACOS) - target_link_libraries(frontend-tools PRIVATE Qt::GuiPrivate) -endif() - -add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-scripting" "${CMAKE_BINARY_DIR}/shared/obs-scripting") - -if(ENABLE_SCRIPTING AND TARGET OBS::scripting) - target_compile_definitions(frontend-tools PRIVATE ENABLE_SCRIPTING) - - target_sources(frontend-tools PRIVATE scripts.cpp scripts.hpp) - - target_link_libraries(frontend-tools PRIVATE OBS::scripting) - - if(TARGET obslua) - target_compile_definitions(frontend-tools PRIVATE LUAJIT_FOUND) - endif() - - if(TARGET obspython) - target_compile_definitions(frontend-tools PRIVATE Python_FOUND) - endif() -endif() - -set_target_properties(frontend-tools PROPERTIES FOLDER "frontend" PREFIX "") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Frontend Tools") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in frontend-tools.rc) - - target_sources( - frontend-tools - PRIVATE auto-scene-switcher-win.cpp - frontend-tools.rc - captions.cpp - captions.hpp - captions-handler.cpp - captions-handler.hpp - captions-mssapi.cpp - captions-mssapi.hpp - captions-mssapi-stream.cpp - captions-mssapi-stream.hpp) - -elseif(OS_MACOS) - find_library(COCOA Cocoa) - mark_as_advanced(COCOA) - target_link_libraries(frontend-tools PRIVATE ${COCOA}) - - target_sources(frontend-tools PRIVATE auto-scene-switcher-osx.mm) - set_source_files_properties(auto-scene-switcher-osx.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) - -elseif(OS_POSIX) - find_package(X11 REQUIRED) - - target_link_libraries(frontend-tools PRIVATE X11::X11) - - target_sources(frontend-tools PRIVATE auto-scene-switcher-nix.cpp) -endif() - -get_target_property(_SOURCES frontend-tools SOURCES) -set(_UI ${_SOURCES}) -list(FILTER _UI INCLUDE REGEX ".*\\.ui?") - -source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}/forms" - PREFIX "UI Files" - FILES ${_UI}) -unset(_SOURCES) -unset(_UI) - -setup_plugin_target(frontend-tools) diff --git a/UI/obs-frontend-api/cmake/legacy.cmake b/UI/obs-frontend-api/cmake/legacy.cmake deleted file mode 100644 index d29b46b8554fe7..00000000000000 --- a/UI/obs-frontend-api/cmake/legacy.cmake +++ /dev/null @@ -1,36 +0,0 @@ -if(POLICY CMP0090) - cmake_policy(SET CMP0090 NEW) -endif() - -project(obs-frontend-api) - -add_library(obs-frontend-api SHARED) -add_library(OBS::frontend-api ALIAS obs-frontend-api) - -target_sources(obs-frontend-api PRIVATE obs-frontend-api.h obs-frontend-api.cpp obs-frontend-internal.hpp) - -target_link_libraries(obs-frontend-api PRIVATE OBS::libobs) - -target_compile_features(obs-frontend-api PUBLIC cxx_auto_type cxx_std_17 c_std_11) - -target_include_directories(obs-frontend-api PUBLIC $ - $) - -set_target_properties( - obs-frontend-api - PROPERTIES FOLDER "frontend" - VERSION "${OBS_VERSION_MAJOR}" - SOVERSION "0" - PUBLIC_HEADER obs-frontend-api.h) - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Frontend API") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-frontend-api.rc) - - target_sources(obs-frontend-api PRIVATE obs-frontend-api.rc) -elseif(OS_MACOS) - set_target_properties(obs-frontend-api PROPERTIES SOVERSION "1") -endif() - -setup_binary_target(obs-frontend-api) -export_target(obs-frontend-api) diff --git a/UI/xdg-data/CMakeLists.txt b/UI/xdg-data/CMakeLists.txt deleted file mode 100644 index 2f0eba758c7902..00000000000000 --- a/UI/xdg-data/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -if(NOT DEFINED APPDATA_RELEASE_DATE) - if(EXISTS "${CMAKE_SOURCE_DIR}/.git") - execute_process( - COMMAND git log --tags -1 --pretty=%cd --date=short - OUTPUT_VARIABLE APPDATA_RELEASE_DATE - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - else() - file(TIMESTAMP "${CMAKE_SOURCE_DIR}/CMakeLists.txt" APPDATA_RELEASE_DATE "%Y-%m-%d") - endif() -endif() - -if(NOT DEFINED GIT_HASH) - if(EXISTS "${CMAKE_SOURCE_DIR}/.git") - execute_process( - COMMAND git rev-parse HEAD - OUTPUT_VARIABLE GIT_HASH - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - else() - set(GIT_HASH "master") - endif() -endif() - -configure_file(com.obsproject.Studio.metainfo.xml.in com.obsproject.Studio.metainfo.xml) - -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo -) - -install(FILES com.obsproject.Studio.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) - -install( - FILES icons/obs-logo-128.png - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps - RENAME com.obsproject.Studio.png -) - -install( - FILES icons/obs-logo-256.png - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps - RENAME com.obsproject.Studio.png -) - -install( - FILES icons/obs-logo-512.png - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps - RENAME com.obsproject.Studio.png -) - -install( - FILES icons/obs-logo-scalable.svg - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps - RENAME com.obsproject.Studio.svg -) diff --git a/UI/xdg-data/com.obsproject.Studio.desktop b/UI/xdg-data/com.obsproject.Studio.desktop deleted file mode 100644 index 95b07a89180be2..00000000000000 --- a/UI/xdg-data/com.obsproject.Studio.desktop +++ /dev/null @@ -1,97 +0,0 @@ -[Desktop Entry] -Version=1.0 -Name=OBS Studio -GenericName=Streaming/Recording Software -Comment=Free and Open Source Streaming/Recording Software -Exec=obs -Icon=com.obsproject.Studio -Terminal=false -Type=Application -Categories=AudioVideo;Recorder; -StartupNotify=true -StartupWMClass=obs - -GenericName[an_ES]=Programa de retransmisión/gravación -Comment[an_ES]=Program de retransmisión/gravación libre y de codigo ubierto -GenericName[ar_SA]=برامج البث / التسجيل -Comment[ar_SA]=برنامج بث / تسجيل مجاني ومفتوح المصدر -GenericName[bn_BD]=স্ট্রিমিং/রেকর্ডিং সফটওয়্যার -Comment[bn_BD]=ফ্রি এবং মুক্ত সোর্স স্ট্রিমিং/রেকর্ডিং সফ্টওয়্যার -GenericName[ca_ES]=Programa de retransmissió/enregistrament -Comment[ca_ES]=Programa de retransmissió/enregistrament de codi lliure i gratuït -GenericName[cs_CZ]=Software pro vysílání a nahrávání -Comment[cs_CZ]=Svobodný software pro vysílání a nahrávání -GenericName[da_DK]=Streaming-/optagelsessoftware -Comment[da_DK]=Gratis og open-source streaming-/optagelsessoftware -GenericName[de_DE]=Streaming-/Aufnahme-Software -Comment[de_DE]=Freie und Open-Source-Streaming-/Aufnahme-Software -GenericName[el_GR]=Λογισμικό Ροής/Καταγραφής -Comment[el_GR]=Δωρεαν Λογισμικό Streaming/Kαταγραφή ανοιχτου κωδικα -GenericName[en_GB]=Streaming/Recording Software -Comment[en_GB]=Free and Open Source Streaming/Recording Software -GenericName[es_ES]=Disfusion digital/ Software de grabacion -Comment[es_ES]=Difusion Digital/Software de grabacion Gratis y con Fuentes Abiertas -GenericName[et_EE]=Video voogesituse ja salvestuse tarkvara -Comment[et_EE]=Tasuta ja avatud lähtekoodiga video voogesituse ja salvestuse tarkvara -GenericName[fa_IR]=نرم افزار جریان/ضبط -Comment[fa_IR]=نرم افزار منبع باز و رایگان جریان/ضبط -GenericName[fi_FI]=Striimaus-/tallennusohjelmisto -Comment[fi_FI]=Ilmainen ja avoimen lähdekoodin striimaus-/tallennusohjelmisto -GenericName[fil_PH]=Software para sa Streaming/Recording -Comment[fil_PH]=Libre at Open Source na Streaming/Recording Software -GenericName[fr_FR]=Logiciel de diffusion/enregistrement -Comment[fr_FR]=Logiciel de diffusion/enregistrement gratuit et Open Source -GenericName[gd_GB]=Bathar-bog sruthaidh/clàraidh -Comment[gd_GB]=Bathar-bog sruthaidh/clàraidh saor le bun-tùs fosgailte -GenericName[he_IL]=תוכנה לשידורים חיים והקלטה -Comment[he_IL]=תכנה חינמית בקוד פתוח לשידורים חיים ולהקלטה -GenericName[hi_IN]=स्ट्रीमिंग/रिकॉर्डिंग सॉफ्टवेयर -Comment[hi_IN]=स्वतंत्र एवं खुले स्रोत वाला स्ट्रीमिंग/रिकॉर्डिंग सॉफ्टवेयर -GenericName[hr_HR]=Softver za emitiranje/snimanje -Comment[hr_HR]=Slobodan softver otvorenog koda za emitiranje/snimanje -GenericName[hu_HU]=Közvetítő/rögzítő szoftver -Comment[hu_HU]=Szabad és nyílt forráskódú közvetítő/rögzítő szoftver -GenericName[id_ID]=Perangkat Lunak Streaming/Perekaman -Comment[id_ID]=Perangkat Lunak Streaming/Perekaman Gratis dan Sumber Terbuka -GenericName[it_IT]=Software per dirette e registrazione schermo -Comment[it_IT]=Software Libero e Open Source Streaming/Registrazione -GenericName[ja_JP]=配信/録画ソフトウェア -Comment[ja_JP]=無料のオープンソース配信/録画ソフトウェア -GenericName[ka_GE]=ვიდეოს ეთერში გამშვები/ჩამწერი პროგრამა -Comment[ka_GE]=თავისუფალი და ღია წყაროს მქონე, ვიდეოს ეთერში გამშვები/ჩამწერი პროგრამა -GenericName[kmr_TR]=Nermalava weşandin/tomarkirin-ê -Comment[kmr_TR]=Nermalava weşandin/tomarkirin-ê belaş û çavkaniya azad -GenericName[ko_KR]=방송 및 녹화 프로그램 -Comment[ko_KR]=무료 오픈소스 방송 및 녹화 프로그램 -GenericName[ms_MY]=Perisian Penstriman/Rakaman -Comment[ms_MY]=Perisian Penstriman/Rakaman Bersumber Terbuka dan Bebas -GenericName[nb_NO]=Strømming- og Opptaksprogramvare -Comment[nb_NO]=Gratis Strømming- og Opptaksprogramvare med Åpen Kildekode -GenericName[nl_NL]=Streaming/Opname Software -Comment[nl_NL]=Vrij en Open Source Streaming/Opname Sofware -GenericName[pl_PL]=Oprogramowanie do transmisji strumieniowej/nagrywania -Comment[pl_PL]=Darmowe i otwarte oprogramowanie do transmisji strumieniowej/nagrywania -GenericName[pt_BR]=Software de Streaming/Gravação -Comment[pt_BR]=Software de Streaming/Gravação de Código Aberto e Livre -GenericName[pt_PT]=Programa de transmissão/gravação -Comment[pt_PT]=Programa de transmissão/gravação livre e de código aberto -GenericName[ro_RO]=Program de Streaming/Înregistrare -Comment[ro_RO]=Program de streaming / înregistrare gratuit și open source -GenericName[ru_RU]=Программа для видеостриминга и видеозаписи -Comment[ru_RU]=Свободное и открытое ПО для видеостриминга и видеозаписи -GenericName[sk_SK]=Streamovací/Nahrávací Software -Comment[sk_SK]=Bezplatný a otvorený streamovací/nahrávací software -GenericName[sl_SI]=Pretočna/snemalna programska oprema -Comment[sl_SI]=Brezplačni in odprtokodna programska oprema za pretakanje/snemanje -GenericName[sv_SE]=Programvara för strömning/inspelning -Comment[sv_SE]=Fri programvara för strömning/inspelning med öppen källkod -GenericName[tr_TR]=Yayın/Kayıt Yazılımı -Comment[tr_TR]=Ücretsiz ve Açık Kaynaklı Yayın/Kayıt Yazılımı -GenericName[uk_UA]=Програма для трансляцій/запису -Comment[uk_UA]=Вільне та відкрите програмне забезпечення для трансляцій/запису -GenericName[vi_VN]=Phần mềm ghi hình/phát luồng -Comment[vi_VN]=Phần mềm ghi hình / phát luồng mở và miễn phí -GenericName[zh_CN]=直播/录像软件 -Comment[zh_CN]=自由且开源的用于直播串流以及视频录制的软件 -GenericName[zh_TW]=串流與錄影軟體 -Comment[zh_TW]=免費,開源的串流與錄影軟體 diff --git a/UI/xdg-data/com.obsproject.Studio.metainfo.xml.in b/UI/xdg-data/com.obsproject.Studio.metainfo.xml.in deleted file mode 100644 index a59d7946d846ce..00000000000000 --- a/UI/xdg-data/com.obsproject.Studio.metainfo.xml.in +++ /dev/null @@ -1,68 +0,0 @@ - - - com.obsproject.Studio - com.obsproject.Studio.desktop - CC0-1.0 - GPL-2.0-or-later - OBS Studio - - OBS Project - - Live stream and record videos - -

Free and open source software for video capturing, recording, and live streaming.

-

Features:

-
    -
  • High performance real time video/audio capturing and mixing. Create scenes made up of multiple sources including window captures, images, text, browser windows, webcams, capture cards and more.
  • -
  • Set up an unlimited number of scenes you can switch between seamlessly via custom transitions.
  • -
  • Intuitive audio mixer with per-source filters such as noise gate, noise suppression, and gain. Take full control with VST plugin support.
  • -
  • Powerful and easy to use configuration options. Add new Sources, duplicate existing ones, and adjust their properties effortlessly.
  • -
  • Streamlined Settings panel gives you access to a wide array of configuration options to tweak every aspect of your broadcast or recording.
  • -
  • Modular 'Dock' UI allows you to rearrange the layout exactly as you like. You can even pop out each individual Dock to its own window.
  • -
-

Create Professional Productions:

-
    -
  • Choose from a number of different and customizable transitions for when you switch between your scenes or add your own stinger video files.
  • -
  • Set hotkeys for nearly every sort of action, such as switching between scenes, starting/stopping streams or recordings, muting audio sources, push to talk, and more.
  • -
  • Studio Mode lets you preview your scenes and sources before pushing them live. Adjust your scenes and sources or create new ones and ensure they're perfect before your viewers ever see them.
  • -
  • Get a high level view of your production using the Multiview. Monitor 8 different scenes and easily cue or transition to any of them with merely a single or double click.
  • -
-
- https://obsproject.com - https://github.com/obsproject/obs-studio/issues - https://obsproject.com/contribute - https://crowdin.com/project/obs-studio - - - always - pointing - keyboard - - - - offline-only - touch - - - - - https://obsproject.com/assets/images/OBSDemoApp301Flathub.png - The OBS Studio window showing a card game, the project website, a video, and a purple rectangle - - - - - #284cb8 - - - - - - - https://github.com/obsproject/obs-studio/releases/tag/@OBS_VERSION@ - - - - https://github.com/obsproject/obs-studio/blob/@GIT_HASH@/build-aux/ - -
diff --git a/UI/xdg-data/icons/obs-logo-128.png b/UI/xdg-data/icons/obs-logo-128.png deleted file mode 100644 index f8c688da2c71e3c0ae440ba0ab9d7b24005b5209..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18671 zcmX_o1zc2L)a|8*9uSabh(FRG4H83#(v39I(k)1rpdj5Lf`EXubc=|9Al)6(DIM?l zz3)AKGlULz&N=s-z1LoAZQiJ>D&XT#;y@6DucRoe3BCsXd%`fm=WYq}eh5OLveVZ0 z&{ugTYU%9AW&Xn1!ivks(FOcJ1c^!dxR_fySa~2UtZeL@#2F5n+ZhmcFT@%21XOrb zTx6_l?G#_RS!ume)wX=)U@7u~K~e%o%tsVlz|qRX9O2{m(#c)aN1Wk*t}6<@|M$<_ z42b`EiHCzYgT9J7LdMz63L(HHz{SHLfrAipdtoiADJ%cKzYKm7XR!6~a1rI^_V)JX z^5)}mcC+DrEFvPp&BM#h%gYH~!RhYnEzD%?-Kv_8nRaImTq<~9(K-7h=12K zw{Z6K5NBZE{`V%_|NAarxZMALyx@obKVvH=FikfwA4=uYZLmxsb}};R?wShH2qifg z!N($kf}Ffuk0FR9-ZQ@WDVh{KvR$_4LRtzJH-<$cQ`SGo){^KqOR=0=NCk6aOBfTa z_e0s27|*4oYL9gOnC6w6al)b*qRec{Z9gt&@dcwlt{M1M-#$;_Wz$(wFjlKEjl1=> z1Pd+TeC{Ts`3mEqK<%)KpT0ot)^LNg!F5ZodGGUFQDq3}mx zMY<}38#K#g0u-@0x!8W5jd_RJG(>!aOZA>A|&a=OktTMbw!@y3#Yq1{SnQ3+1h@|SEl%dt>!zWXg`yp z#R{na`?w|XYJTsF0{xon`}zub|2#?4)&)YHp_IQ1*yFSBXRFI982f+H$n-ogvW~uF zkK{R}ztYt>p+>G^W|@-K{opcsDZTTNY@)keGrWP1HnLvJHZ5|4s@QnoysUDl4~=E# zSZA`bp3vB>&2`ZJIMFV#T!hP`Xi2`M0kwXvyQj(R@_Q~2a_Vy>$ znF;tcFt?zo$cPRz4pvY4#9^l3(j)h>A)0|b;0wO2URmkR<`+*_&z>d=16NNS*@x;< z+V550lQCh_!=+z2Ik~y-3}hX|v3hU(nqwmA50EqsklFZ^l`gXRI=4QH;6}QAnEMp> z7JRukWn*QfS5Z-sg`-Dk{!~|!^xGp+RAAl!PEzsA2ExEN?~2@y~$9#>UWjr}GlQ%<}dg3sy4&!A$cs ze(9)0t7!r$ZdiySE55PkxDKb-(Bz3&pfKgTl)AdQJ@HqkwtYJl?Ix!Lt$oSMpxnEZ^IdpH%XdO5u8ch%l*~35lE(iCnNITXa%%KFf$I!B$vw2#cmvLH;S%RyJm|EJI{wX67Ob zD{G_2#y~oSvIJ{knC4GiZS5Pq=IPupN-8SsLhYTLwDRZmIpAKMzT&4Wf0_^s);K2r zhgN*Fb^ZC$CQba3lY`C7>40dvJ5PnVV~(p>ZU+x3|~xiBeUL-oPaUQNV+v#J%K88P{tk zMq4E2{CN&@BtQluj0GqEO*jlQG_(KbPp2P!eP=A|AClbs&UP;iP?l{HDU?|iV9O|G zXS+K!v;i0KjRpn=Lenq5`is0#WR;cfKt&{JvS9UB_Z>)UX+5xaaBw~SqcKKZE+z9C zEh8gycw#0(T{ujiA5#hrL3rIA_H|ubZe6Qq2u40MaH$_VIg$JG&5YdKgz4$&X5_67 z57AM=;C~?uj9^I77d=RkNz=g8^uEh;()fIPrq1Ek?M`1L=(ca`5f+kz%#wo)H=bfe z!sE{{r$}St$xB^_uUTP5BzW{@5RybLbl5kzcUdN~%KN}%ce)j;@cOCi+7BXrAt6TM z*q}{c13~mK^9~l&QFtKXI)Cp3Kt8mHJc>yeN(`oUb=G*t|QUbIarN zP*i@}1y>!!jd!# zKSZMirKF^&Ed<`*VhAnxSh5x-IXXHPO5`3W)MHADLbXvKHnlvGl)P7??sbLG)s>O3 zk1+MeMPfs%vws&?J`zfHeSep^bS~6w?yY!$#$!8-43jpe7Jo&eP4q?nhrFv+O$-zj z%-ED0os5umW5h#ai;j~22p)%5E#w0BBmsQ4LTMfoO#0=z&?jUbH02{kE8>4~D1LkO z*N+U6)mS5sxYQ``jKd7F>Q9Nc8~x-O_Wq(~NMiP&Y3GaI;LcxF2vTOP;O~#lFF?oZ zM$pDt{(V?lb(&o`rO|@sDnU@xjh#795a`ZV?OyHLiSf{E@{2JG>oiu1l@z3NLdu(5 zmrjb1_kt(-%{W9qw;;y^D@TnJ%!4iyNzjw5XZ(g$tEj`erUa#wSzf(=Ze-+mdQ?sF zwpoGhb#1h!J$=}><{uLeKG9)7Agbd*BhB88jX9bry+<0@u^KsfckQBOqji{Jyq)1T zO;%GZYEMSIhhX8|;D*aYk}h9OPn!QsVucdQf?ph?Lq`0R4hvNueI^=QS0(;l-@Y_9 z&K9;E&ZBEFxWvLj%E+K)CMW*~A$4@dS3$sTpyIJ_&8N6NKj_5`3`B9ztq5k(Y{*g_ zFA9b83kjG{c-HV(ZEMeo+^ddFOmwXtA(#H^pSFTLI#JUy0&5nPHqjF>81X_~SkR?N zj!I46^&U9iibvp6VM1^Wl(%?ZoZM{e>~w4#9RBDKNH|k1EiH7lO&6i4d4+}HPoF+r zMQO;)$hb?7k9U0xX?(GM40dbT-`}?A75A3dDJ^a2oOfgm;+ofQSSH7%V1|Z`1$!e_S}LuA2`)Kg%l~!bHpH~ppFnYf*h}F*<)pU@z*z%d-n%MT&V^x zk&jv!#fg&876BTq%Ca(B7SJmMA#76frG53>{U!a!GaIa-RC;Ze#J|*RNzwskpBH zZ7bxPD~i59y`CbhO1xH-5LyeChGW1&p!!*S)TP26?&XZqiq47h=Q`exz($1=AAvBd z_qDdRjFpY;=fJ=K2}oAss*5SWOYL~dN*ZdLCUr%B7j^SEBVkdW^v`VhRa!;BpB z;O2y8&gB-LWx?bvzbPfTbU_3ao4^mA>WxL=bMJ=Bl+2^0P(t&~U&e>Ntx>(Jl=Muv zMq}8IT%$@b*rJu};z;>Rqi_=vf7k`~7IT{?*^MB0 zc?!*YXvLkLdbCwl9buuM%=#u+g82&mT0J_9A>$NXPkHWiIkW6?nwRt)o1P4b6QZ9Y z=jR6}ygwppx#+Us!9#1FCkTE>?CGuC=?0dI%;-4N?qc*~U{_JDf3!``9lO6ouHmBE zbc3bty~5|9$`e21zsawJ%v!jk8On6oVL=rW0pZohyl-iQ$W)88Jh35tWxF~1x{a*Y zXVdM=zIUr290*K~kGrag{~c3uWEU=Zj5Ic;Nli@bzF+4Kq;R?3s}HoSpLHJyGu6=v z4-eFj;>@h?NZkl!Q5Jx7Y~4Ir$zS#^(T5%#mvWFWHT;#iSOEemde9WI^hJ?}aB z?V*X!vWtoeerjr}_s9&6Y6foc=g$`IZf+M;P7NQHCZ0RrN|QXOi&Gg=1y-iG#(l)$%>zV9fmOD_{H@WO0#logP$;{ zR4~TR7TD#x8Y-g(DYu4g^MxtXJX5xr5clRr@2?iVzd6yNB`(42v9YlkV&UcvtTk;9 zI0wi@pZ021=F!+oe`%<=yu5C0W8-_h)ATFjp&W7N$TcE-iNMZmTsT2E(=x8K^74ZS z5HKv(>=^$=Vg+Gpc_!k{v%Q|m>6f3+9`)kVgi75WM3T0p@S{$=F=f9f@I&Vv5++M} zSb0c@yX@-6v;WrB(7OXQ@AdUr>p`Y-+a(t__rap4r)Nr5R@N$Jr)zUibTryaCnu+y zX`5V5HcH)7hWAlj$%41tk%XlZi`Ml;BmrF<%FogyUW*FHhW1!U`TC03$I^!PSo}_R z`LR*w{^0dXDYM>B5`DYgJp$^}QfX;{HSSXIPFLy`S6bZt7Tm?a`_>5>I2)hu>7U;f zx66cqf4~`juy}uax001nN_;Lx!ja--cTRKTv3JMAke)E!6mO4X6 zY92`i{aJ~>qG1lsdXvlp8E+^_PciFCqmikz@^~~Ffw&WmwI2v2SP+HlQ9}}pjFuKk zN`3R-G_UUL>~LaA-NK>bva%N^$Hyzq?YC#?udhxGSd5e9k8#{879l1|1gYPQt}hEsJiCk%-SzSHy?oq% zl18Ur??eOX7d=Zq=I`doOTvNZqZb!j{epiZcCypN^0!l1G+@P5m!~G10FGOCuQ?6RCUR{wxV)S9j z0}n!7gx~hw-w4kb&URE~&2$x!8F6Xzj$qRR{P}0Hf^KAVv{02+QKpk29u~xe|Ck85 zGA^I!zYG1D6D=Tl#ist}=X+RM-GuTEYV8o5!R{`X?wSuZ7y|p`i#rtNG~bln>bm;f z-~scqB?kwGxDxA0D~ROb!^F8cqkxeDrAstSyau&jEuz{~&_7)B{Oil`*}Vz58|yz4 zWF(M}6!QM=w!`gZ*MJ62e6-RX2^(CL#NTFSy5#+ntL&sy5<{m-)Dq--yhGU!V|l|{ zE@t=V*GD(*Lo)sDY-*WQq8@)_sCzy82M0;pE;ll{gy5bO5GdrSAyCn?pJb`p2r}*2 zkc_rFFG{pjt>Js-g=+FRN)2>CihL5}(!-^C+)*Jf1J=1Lac$x=0)m<3y%86QU;e&I zMWj2g&+>*QVC15=5lFrFaQe=kvk7+j(L(z|cM9k80>j|ESjHt|F1-rlX0KaXh=#`u znOcrah13lVm7BZ|XxrWIve9sHaGVlw2c!knpvsHz9^0e(pjF|hnUFLRdGWO!<%f2v zssuhcSoG*2ik$`=E{*de#fWS{PwHWs+=Ig`j_-g&-dpmlDSKRP5CK%b}G2zUlS_@TYP4JuPTGkp)1#A zf9cgcH-{UQkNzb`9Jc`F0~>ekfZ(-XKI*}bau0!t;iN>${+B;f(Y*%Bi~O|;NOWT` zo|>43m{qFa!${fH)zx`y<{Dft0TFuB_WD}14h*1U=WjIxp!1D^SMxk>-}wW!kH<0G zZOEguJr|Me&n3DV0ogimR{aQ+UZrpieFm5q05sfvD# zea!GL3DOQuoT;bkh%ew!e5FewH$_9RyJ#A1MfVyH>5KvP+8Hi)i;Do1k(@lf)eua8 zqOPkeUtL`-3mITwU`#6d=G(hFbC7knr`+MnmR_>*=03YRHMD+oW6VS>1rNRm~@BjhHFz12sEG;t-(_B)5mXn~I7JW(reIbUrL|JUZ}pM;tv= zs+`me%B!qA6onSBXni_+5e#9F^5bV z;?ux1lZ;Ez4Nc$wL@n0sa8lU1)AYZa-h@0DW8`v&ynhzu2E5u|>4^#d^yw2pIWiIg znf(!zCKZY~7#0z%yw<+^(aL^op#(#{45_*N!PbvITAc+wf*yO6RShrOXMzb9QjF8} z!=?3&e1d>dHgmY0!m&V|Iiu#3hdiR+I}u4WqYpcJ9648s+XosAtYc|?FLSHjJ#{@ zC;6BcPy*GDpTD}sVm>j9EfDxhYY=yre72o&rl@PibwV<-*dKpEhrdN=haq4shB4;k z@;Nl${%{mh1bRfiVb~&xR(@v_?}E;*_eFc187nlTd--tQ|736zC1~DBZ3|Nq)eK}< z$c&SPC5*e)RGMbO>g6URN->eWx^~NN!pJi)Z2w_N4Eat zhAGPlXLG?(!@B9w4GovhuZi$5J5R*O83vi; z6K45GPtivQID?6C{hRNe1I|lkKTE4qOJCoI2>MiA&6}Jy7#Ri#UE+$#{zbQb&d^qq z1G{6Z@tXs{l&!C?kJQ>|@bQdue5_sdBT+AkN={B;Ad6xBxH}#q`%ztMa}a(qNfkcF{v*kR z??Xk~)KO7It~*@ z*%Sb4p8QE>*VB#4|AET$j90)D1sp7N=+)otLXIUQ=?UFCxhlsUL6_Z$1jf^xN z3>ZUUr9MY-X{n5o681|$Dp~s9jKyhsP{Zaz_>Uhyko?5S-?3Skxx>$jPWPCx685PU zK5YDVgx|#zC7Tak@U6@5p)`q{ECW$kHxYzYP>yLbS!xXSy@!UL*&|=g%{DF(Ku`!k2*Dkwk$&VW(o{_{w#; zE1V(d6nu;(dqkhneHIHt!(ub5GbmK0Yc23h7m#hD2}VT+^-+`a1cg5ZQVJHT&Jxzu zwQ#Z8O-TRiWc#O)=!c@?d3%#nFU89uakYR{TJ1O0MA8TxRG*w)I=TQ@cz7!jL1ahu z+p;$a?KTFwG|MEm_y{=c-}tQ;UBpvbs?g6lTClb#whrrS5mjqho#yGZV95;2i$9Bo zjErce*O3H`u_VREKc)=Y2_=-^(NRN#LOKzbfXxVN-ft=$UUp$&_ac%D`Ooj^niO`H zR0nJLyb?X2lrcaq^1t50{6|;)lSEY6Ncxon&dO?rx^4;QBG1meN>F6c$U{lUC{6 zn{hb$p6R?&-OnZC=htkH&!WBKM|iu<{WFu%`pZb{2OkMp()5{%eY)ZCBe4!qf!c zEFQy9x%EoX0TMtBtfAwx9_R=q6pwNdPB2SL?|2VyqD3zu_>5zK5i&unj+0^qkLe}C zjyzhDAzT@uNCky@|9lODtgi}`lS&{pO!w1_Km{hBwd%5nj}WWP$b}S zT-?&%eo5KZSEDc2sF_{V}qAQjj^c|d=WS%1oa`0#eq zHz_&gzhR{(WR>Zk9_5x!vLH)fF+I_Q3-^%N5Fh`c94Y57Guk#&_ z@u_uL*L(%$~JLPgtf}3Z#z8-$`dAl%tQ#v9UUuYU;1pP7r9JZ@ePlSl?C*7 z87z5-F8{rHpOO+$d&&=j3nM2dKDab(9F|uz4W79+vNBrkuzqRoWT;2dBoJRFCL+JE zP>wT^qNF7mYmAmf5>fx@vjr(i9$>#iQ4rJwR2)Gc$Xxym(?e#XBO{o71KM*Z7In)0i{E#66rhQt$lj!+)wp^j zY&e5T{`$&x?!MzSs6M!F0wh~Z2!XHwst-y^Ds?Tbn5!ny+S*z$)=*7AId|>+Wnh0o zHQ&5R50scKyDc+)%y?UpBYiwP^Wha|DH9V~vi1h@^F?pTiLy&#imk2fVirzT-{hd) z4`03z0DbPyXi*#+oivP>mlqQS38?)|=;E-OkQJ)$U*9hGSHSiGD*#dyetBTYZJ^#@ z;EI%;$9oLAfD57vAEWF*=PLcBBcJ!Vxp9CgGTUvaA{X>0htdC8Y^Tc!T=wNKzT#SV zb;8NdKi<9W5?rH?T(85@?$GRc{O3f8g?O?KSDdx;c_ry|xZocgD3n8HMKuHE9)g5B|psadu(S~mF4dm z8x&a$Sr!s$KffotUQcrTx0U9+4_@onZS%b-D0-SFXvdugk*CO2az-)gD7=7}sOx9< z_HjEJeNU{rNqFaHSqj78(g58qy3c-f%xp1njy1wy!s|9$k51O-2@K|cS|4gYqv$$+ z6SUGm=Snn~9b+`O{0wJd`GXRa*HVUe003y2Q6tV@ z70)HJ;6W$83$0KL#tgY!`gd(_?dxr{_k*vgDg{+sx6ivzNZLY_FHj{+S}zcNE0j5S%8 z{Q;u*@=&yu{IMJQFVP*2tk=J$foO--UuEmy(0#m~yObSx_aHer5-=}INmTCIJJNDd zsL5h6Zz^HtW;F)OF2ZA5hoG*C&?%MZp0>#j7ZJK62#w5&Mm`0iwbeF zr!SGVzl@?YJ}T7d!61E(l((R`5q^Dnyk6JG86paWNzeKQBPg+1{+ssCA8j zl~eJsW3jVhRLRGsil_Th>^CnEX$89Fp~Z&ynS+?k-NAt0l?!|xHY9P4cd{|mJ>_co zo0IRs$<<(9cja1+Ljg3Z7$z$G@t2{^9XtmL@9cO1nuXs8ynVj3Ol{@A|a8?y~@cb>J&q+ z)Vw3SE=x#^fWQ;ZesJ6nPWR`jZm-X=F5itYQN59czme5`FaS#QslCeC zy=~&dDt)})wj|3gr<6q3#&HTMdaQ`sm-#fq~l2=cwokO0nAp>B$Q`?sHzwMeSopIuOI*eOmH1 z5rAgkR!Eb)SN%5#MupgKwNF3MPfdLKB2XsA{#W$7=hIEL7j=ev){A2($k*y7s|u!q z8KPHXFS_)z5|P%AKlE|eeI^Ww>s@v)T0RlTU2fdWnN$(g$mOl#4MB8lo-AhvwuJ_^ zU90(IFMP{S>RWvBLE7^f8w?$VAQJ6v!+Ngy&;;N=i;Z!<-{1t=QMR7ZOXuM^s4+*F z3nTGQA50FmtE!Ft&V3;&!E+==Sl=D`pL1I*IOET7D$!S)`f=Km1YXxW&&Iajc@kv_ z7TMuu`I5aWQN=`(z=0^8l#;TtEGZF!f`$b!$1gd*N@E)g(W^YvXnl@P&A-zABS!Bu zlYzy1{}BFEcF&oMv3BxlbF*rpg8AMdoUqvV&Ru=rlh(Y4Hz-Mc|!Ox9FqkjLezfWn+_b)*+O&z{~&))bXh_3N#vl$a2L|}8@zdARa z(rY4gDtb=2_tJ=I;$~9mdo#tgtVY=A);H5!gMVbE`BbGj5Sh1`Ni{qADePeo{GGhz z@e_}V2^2t%Xh0y$5pMA^Iz;wwC481NW-Zf}+Lv`PW=hl}=Zb$3%tUF4E)_Jo{S8EU zvx9|JmqsH|_B?(z9X&nEtr2f?E5|on&RUm6EQwoI38Jq@WSF)Tnc0MJXv&H^9_9Ln zocwz7FkVsCF%r%iKDwoS&NdQ&Ai)WT>@G4=JfQbffr@}*Ej=?2H*ks@!Sj79trLtC z-HhyGY}!#w2sJ3B;-6eI#O-&D`qup~BAM4Ix27v|25I5|FtJX@t?;p3igGt2i6TK* zAVB7NJ}F3ztfI}uc%@hj*0XoE6dD*9)UxjjL{m2X5|*xYxBnt`>`dn8=H_sMWEqyn z$iV7rLIrJY3qy}4D)d+x8CS{u^9@jv?xW@|Ly{DJ_TnG#np-9aAl^r+yLngmvf|6ppdqu@suAma^vbNeUQ`6S)UtAN7{L8S z^hUlgyjGS7luGuZ{|SLW1pv4s3euv=XA>$eF196zlnycC`rN|tC>%R?C zpCH3~{6=50*?LC>T`xiOMmp&Nl4*mHh|=l-7U-KrmAaDB8#?F#L8PbYFba^FG@$_? z^JjegUA%&s_Xpt5oUw8?K9+H*7oTWM8@1AvT78F`QOnhdpYg%e+wS-37nKwF*J!08 z%q#C^o&SqqvEuO+Ty}ki97cghZ5U`dq#iSS9Ox*%F<>LmMkY;?r-ZnNwA}1Roc^}0 zwHp7d!wIFGot=%5EnV$Y6v@fS4djUX-7LE_NL}V=W&i=Rd;fh(;1jY=ul-xmpr<38 zp-Td0eEPvmQku%<^@=t2K;kRdvoat93jb7%gAm7Cf#sEi@LUd`?)nFln$~SH1T<(G zII+o1pQtFQfL65bNhacJB+Kw0Ko1{Y$>wE-ls2(qgP5 zN)!7ClSDxstk*PP3Z2s`mHPaosXI)Bp%73LdeWIM$NA5|V<@k@Tm`-3CE&D85pZZNyC=!8 z2Z*wT=}*|u25{r4VV^`jFq8sxR@jJPJVY%MlMgU%XC4R0+E~HJ`LA!?K9Whf*O;bK zt z=igiPV&jwSu%&VL_18FgB<7hxziaXOGt zYxx79h96&_?eW#}P(hV1`iMon_l;2V+S=Lz>QC|WF+;+`!~cxdbLYFQ%INVwNgo(x zuAMVu829`6`dyDE zpoF7ppQG~~T08&I+aZNkIExod+&n%p5rCEFyFFIob3X6=1Pg-JUTj7|3_zWys+&wM ze<~kGQHU85k>%Nm&y+w_9z@Yie17`u8B5liN<7L;ka|l5^TsYYbxW_r|5IW6uu~Si zY=mZnZ~M1e04Wi^LLQi2m^@+s*6iDlcmN+F_V*JK67ZoFr}|kBo=8@RGLsSm8i*!k z7reX;z!4!(=ame&@e$tj8joaF8d5A#cVcPaKu7C7oz%i%7&m=q{lNFCws%h>GSF9? zww3g;n1dU6n{$pH4LSe$fRFjJ^dDMoqW^Isg3VUGVb44#fo6xN$X_3{o_|F(11C^L zC{GZhqsen8Y*h8hyVpDA^~&2+lu0u|lvpJAbP;X0SM+QZJI)cR|GCe!f3ow~csBG+ z`z=34Fbdw4gW)nNiNUnNPbVY5Mo%=t_MSY!NcoC4bMuKshAp5#6OLwA{;5URG9W+# ztQsda|KkiQ&$_xNl0fFK?n``B18i1XO&C*+?vH3B1H>?fjdTs0bg#IGk~=>LmXg|J zi(~(`9r%O!3@4#F=(C-w#oLl$0mGM=sA%M;$YGqfJa`~5&^I zI?5!d_R^GJ`;F9Qi77=g2dLEHN3Z^+P2ID%Vav5Iu~ZURlyG;}yg9}RQeVBQ=j zT2^EbLTlvEEhW_o6xUg=65G#keEE`O!FMxz97+RT;3Pl%BJ_^!@zTr3mftJ6+i#SQ z;u#gZex}OF?|V`6xIo|E(lDwhOw{I;NKe}PV(B7>@HlJu_3H2hU^g2Y%(McL*Fksn6HR~6oxHvX+*7Xe5PdCiD`>z( z_0@>ti6F!8NqcO`WX_8Ri-JLbhj>FaXRVUq(mo6OH?D#0*}KTa@iZx#B88G@54cpb$PBnrFG}(C{ATqNH|F_9WSciKq85P!^!~ zwyE)Si@?jL02%t7{-Fm2*(d6}$z|Sr5GG9+^8`t*?Mfs*Lu{X#;*{SRr4P$_9xfZw1q{b7qy(FBbAO1cD9fT%(D`rVPN z0)mGqS{{xagLuqB_AVdP&*XmJ^?|9T5)U`R3LYEw_@zD(CLzUJ7vZuA0_}B`qj%a) zon|GPu$+#yCS`ftJo|RMpmaS`jG|HOV7;QZxKO3Tb3{t>e0rwTmV$$BED52eSQ3GE z|C?X(1$fR~+$9U62jE5KHBT~(pC7dIzEtH2aSe{y$y z;LOF!YVhBBC@h*3nEBe9kGw~Gi>y@~G*xgi{8eyK6U>}a!#*et75`nQy%;>*!sbjJ zb)k+i+fAjtMvhAQ#vy}C)0l!x)}%Ys_$e*13b zMgl7;$h@595{bBZoOva>1!gJVUwP<|_aT;r2>=oW90;cPt2IWln7-~#$nMC_&N2Vv zxWezTDVD*&Ac{OuZ?powaJ%Wcm46XIeG+?E2g+oHT=&n%pa7W zv3KbOdK2s?eOi>2g3K1OPsTKm$KL1=3l~>FU{5sZ^G~YT$`ci)i-nx)%}vgEQ!_J^ z7;wLvquyL#;V*_7`VU&&}$+jS;tBIHpX zNoi@?lny=F_1XY(9*_pr|*@^a1&2u@-mLzuf}NlyLFee&H@>&;@0quc0@~?L zHWq`aHWy%lPjRQdp++zH{xeWr*zf5zxZk1rs+h|2ePBT0T|Ef<&JGFO&h|CRN6{Ec zrkD@Y;w<;1l3A`4yjfbeawS;+18%McWzy%N%_sI%`ZH8iRLyunmNQ~apFc-Sp3k}a zLRhI>##ax0Grd0#(5(5=%5_J(%(l$_o?+v*93kCPv&bnanf#<8N^Dmcl>-MV*3#Em zr=EB2&Pj6#QYX#k^Fli=Lv;HnGEoD=vBppHb)i;gRZoZ}$D7R`B8&akG*IK5_U=t0 zvosvtbNeCvNQ&tFMXw|+n!32#+7BOq;)~+jQ$*+WxYB{p8V2&;)FQ}>w>CMx8}9>v z)9O*!Aw*uN4v3VF&gjsoI*0n&+&r->%;5{+bt6WfWO}7Z(J|gZQ&p4l#7*CuUnS0+sZX+m1WUg~Y783i3b z18AZgi-6kY(!UKQQ_%6s1KjTPXpsc(G9(L0*8sasKA^QhLxYcR^1E+MfybgP%=-{h z)6nBWZ^ky7VR$4YBnPA6;O_dc)Kmre^l1!}HYCQ~v0vHUWpMbDy~6AWmmA1MA<6ZP zk7!)G@ZQdxBq4bg`0ORdKIf6asQVd+-!ME9qB>|h=K}m8A}XZXI@sc>G$uMa0Dnn6 zARxdG=*Z8~m*;gvnI9q^hy#N2Mz8%!M+^?zHEr=Zx&=B2B7s}I^pR@OgCU7?4qje9 zP;(1N(f_tnR=$Tla4)lB>JG9ruzNFh<^}XvBhgjOmZObKasb>=gHyg?Y9oI97(iOe zFoGi)w|RF)oy8T|vQ}Q~aLJc;wzkm8MaN^?puO;d&v`9)Mb%M|olLwL1`OXzz9vKx z|AqZCxcY3Q+w5bg`+lde`3A6QoWVuMkpUDwb+p=h4Pwy{mqGPfl>u-&qI75}i%3UN z76V_|s&`EwmI>T!S=dvKYcE4)`_+9$(Bo;!;`IMCC4D%^WQ0jVdMrANe}ClJX)+)e za-!~9u54+gM(Kt(%sT;pfPc%bWy|Tx(|q-@Q&h@ps@Xl^ctyHeXRbx-gLrK4rK_N*|^Dgvd;1RV8@AKj@{;-M}G-%C;*RAj7 zR^19H{gzU_ih%1Z_g@rPL6I#`VZsqe!Z`KwZq2p&y#1e>gWY*YWR`hA6z+F36Va_- z3Fi$>o#fd0&V%PxUGwT^-`ow>451IS~UnK5^0m}VDI%jCM+ zVRJ1;QTafy#fx1X+n)p0#4^)%+J4D9^u@gkf-ri(ie2e+H~%D862(Lo5I3+H36v{w zn4yxVSQXwzbeZ351>T2Iqz(pk;F^gDsBWUTWC}TR(LM3nwSrJ z0(y5TMBEBW8ycnrtq0PooLi2*w__4e|0zhq34_zXCITACPB!PUgE!4qIn;_tq+_t#S&8`gwUi)kF)C{dth zfb#DD0~Cq@aEt!R&(Hty#ke_EWLwSP*8?Wt)vU@<$rN6WmO8O+MUJ^;XZJ^A7!6b&@^K-w0I-GhuEu-eqR5HS zDu7g#ClC~Ig-I>qh7NY4`s=lSy(0y&G?6kU_|LSx>Cl?KF!$Y=dW&O2Gp7-3FCW@t zQTNhQzX9!UD}6<|9U_}qjM%wWNMs(1rmwH>?mwgc-e;ZCiC9{3(KgV2goqtIDB{>T z(v+9Sv~zN@?v16{nXk2b@Wgw6uAk0lDR}ebFl}lKXcf{$jI38vn0*= z94G{VDr?AW@*DF%X3$LHh*zc0QK?_Pea4p0!tBLA%7y7E_H641YH?~4gAaM_B16Sd zZtfyYOpb9_zPX%eZUwA;Wl8F)<5IE`mMZI?Q1AuwS@x4(O_UL0F^mGkj>WJCB zPo$)zH=wS0Nf&r?)TYm`D-J9-eI^yDKQPRbUPprZHzFqBH4z(uuGzu9I8bvmJ-F-X z^~**KuT*`Vq0D*AQk@wwsp+K7eV5`G8TM8`Z7p|2?M|~Yx11j+mZSpfe#sugMLN*@ z?z!b$LzU0vc$RO#(q9 zI7xz;?`TCmSR{Zw?Yi2k{|r5}vpW=vFef2Cz7xr-_jW{e$win40P-acYC-22j&Z-_ zX?g3{SV2{4$mfo-3F-)39Amh)9(vBrohmxvywHccI(=bT%g3^eL~}fwm}0k+MdK0G zYqsrWWgK_FJ@V@{7)~naNj03Swt8CHzfKIgP*rEx-af*Ig7|sI?dwK9fBt;2Rg~*= zy_TqUHa<3%gh#=Kf78B{;ZPBO!Hlz{|5`i{ z7(#==;tU#meL4MEkW=k-qbzyTsbf|EV=pRgJm=xat7%v31@pd~xtYHMP=RF7^7 ztXB$<`P6`KP;*IA9CNgT~j0gwFLcNbyt10wP>*a+1j%kgf(C{v?#y5F2;!_D~o4o!z zUQ-WxiI;0fRUd$k+=*=$;R_dGf6Z{htQNzdU}^#^?8xr9C?ZBN5Sl70IcD!cSKMoB z(7yN)isGH2Ks??dhd@6^tlSVs*M>@_dEIn@QgMEoeyxG!apDk4!Ckh8xBwB2o+f4( zP1XYlw0!c8q`1VpkatHAcGH|3bX***qCluap(^Np4DmN)25tVAO`ze26LPV=I9#qe zXuJM0xbBACSL&C|an%X>z`?YEZ|ZDJOU*MiDQOC&p#5~@DK2;O&9TGC=m?}TT17n1 zj=cEO9d|fWT48>FURXKpXn!l|i`$S$0Oj9d-BO5_p|XY8tU=ri&O*SH7qw$!8p-~p z36-Op*`d|`n2M6}a38d3`p-Jni{s~c0*~)9P)Y0kS{Dc&d1bNO=7C}44k-YS_~=Ik zLf|a*2JJ}(PEA{%DDF=r?@xO3^BK4yspb12vFz`m7S`XrZ)~m6h-R zs;LkEJZq~VA*l4UarJ=NlXoHBzRdnjNC4qK2Ijko_Q1dcz?duP)|Yj1w;vy)Jv4IZ zl#T30@i4;&1;6eW{WpYnxAa~C=QIqzS*X35`F#@MjO0loyu!2(A@5zn4)?A=S2 zRpdayDkH=FSFCTPs`kpYx51q1se;DtKH$lXUR?oe5wJuY*DT(h)yMU$9*ux0R{>jg z^5!MEYGrsu>8T|u=sAXKkZ{UG;JF@5*Ysj`)R}dLu-gC#>-;T>h{BCGSo3EF3JL@^ zUc|kB6Y5U8LF8vf1zk!UC+2jFj(! z;F!QOv;6kRW+^|0s|(2YAGBqBi47M3k&Q9-@9zSbsMjZ?4z|uHR6EXoy#k%Dt0kMc zfp=)oGANN0&Vcs4CLFuzdxyn$$y|8r+PCGApw99QbmEB4In_6}v}Ea*Puu~Hc!e{O zS*5A0vJ(FO8xv)4N3j6`I_gxD6?@y=l>_KblGhn03R@S62DXi?7Mx-RJelVq$E50^ z&XZR@%B-?dNWeGjnaL3eHq%^QUINdAFtch_)MLwD1fxtRH}T#DB2OJ-2tokk=Sr$+ zz~!&GM)!wk!T`1)t4bP0rlyABuA_xYCj|xi`XyTf%fyKj z&$#1`J8l{~ZtTR|oLnFQ1Xc3dt=AO_heO!^eJQ^C?mO(-xeMjx2T)g6i?+6)v`w1a zWXv$EG(t!jz zBobjVGBRv=d3o-=d-t*cT+^ptI%(FdYp*-)wA0Sa&(Bwq3jigQCPp!tfdP+9ifZbn;Mn-r+U<7!6{k*$Wq;ef~EMtXWWva+*~pP!F{f&vuu zEI?jfp44QmArqlP7P`8w(xF3#P+VO6)vK?*x@5(Q73%HP zUd|h(l+wL>_qM|K+bIv(40rUlsJ8LWB^8Qp)!2 z+t&fWap1rK0>II~fB#eG&Ye4T%$PA33>Yw=Pj+^;@(6`wIti6_I#bJ|>llKupCA<} z&zU`|O(ttPplMC5si{F}Sy}0(jT=|}{qKKYSyff_J%Gr70Rw0}9uFNncrZc;5va+M ztv?n0tN@@P1^`=FSZKG~?T&r>_A!tcojqg5WuqofzToV^r=EI7&tAO>x_8&DkFNj% zNPq$`D<@5P$B>FhE2Ah+BqC7&RaI3ueE4wX-o1Oj{m*~?^X2m8%eMn)03e=v>Zw9| zdwclMp+nK1o8T4txdH%y)O9F|B9oh&Yt72avR7AETjklwRscB{O}%LFS!bO!deA9@ zPRq~FACQ}yo0gT8rSr)$QO(g6()JIjBnSfP>+4ZhSJ!;x$dS^0`}S>Lvu4fa&p!LC z1VBB2cve;xa&vQIb#---`uh49!!SbfPF+7I`dI@&A-Vm4vD@ujj~+d&4u`{b@Zdo% z7z_fEWpFzjj_wy;cwyh+!-o$lEG+DwlatdsBRwO>?eU~}JZ_uEuyETrQV27z}b16%~rwGmtEV3qabS zL4z{-_3PIyKR-VsGcz;YlGP> z;X|Pi-`w2H2Lb_J5Cl=m!he_0we(*N025Mux5Z*%TrL;ma=96&)4@mz&k}}V7`gKa zN{}c8c@zMlq9}@jAc)auR1Am1VlWsKL!poukH?kz{jtOnKYRNB27pwQYy~U+DiC{U4}j1<<0s6k6WlB86p8v?v|2tbveIP5n zE2X>*rk;#U#h&J>QAUw5H!_U*y|HDHRrW%PfFI&PdEc+9rWtA%%a(#KBNghCcx&MW z*cd)1)0h5r=a`g26@$+_wS_9y2CHRtF6ui?I`z^GzFpS%7V*#syl|)vRCw`BB)`B(JapBRPe^lfA}vMI*enw|$$3($vDX;>wOEYs#2^qmxz40L0# zZ|rN1msAlMI5xgr!*Fef=kFl;^!#v5?$SpJ2LWg2=DsVLPBdvlQN~4@w!(z;=O$3%kXH|6Rh)NE!wQi3 z@|;6ZL}d6WTfn>jo{e7}l0M6O{%(7^CQ-^)@`_s0nYU743lnXGB4ChA!KW1t$EOpG zez!gniU^QlM#2kTpnw1d2yv zo}cU({pYedv6>+hz*+0ErK_W^u1$TH_tSJm?->n&{O<=s{g*IXGNT!fGE>tQKd-AtO)90gNHS#|C zbUEI1tyzH}2)`r#W4%vcpm1&cyHV8FuU}{H-o4up&|*x~G&iS%3XJq;jB}!+B-VEE zp?yNup2F1|s?RgduK4@cQ?~MlsVeqXc6QeV#l?14>!aU9>i2u@>ouJ%Cwo9*rqiC= zH40S!y<<;M$y@uKqr4O)b**h!VKrnZJ%M4x3tc{ap{e=km&C!-?Lj%BB_(C$Zaw>k zy90?2+Sqsf_7*!`#n1l5o~+F_TttV3g^4g?4r-9UE0(4h*%heudybp4R@oFr z$plGDXG(I)Lu5U8S?pmo`gRIzu{!BbShWNN1-W#Ze6Gq~59b#7Y*mi`Xn}BF8hKYb z^gZZwAUGm8?ET&tXVI4^LmK+iQ2LY_{9T)p%%CKj-B(gOx2ooRyyWCC8jk;1M||er z|4FKPNGs;}PiAyYM9c(AR>Q(M6zQt zA$S-+cAKqh6pVabUS8g*vYGkM_qb38&%WvE*ctk*D`w=jfN3O3*8xf_r)t(ix%k_+ zDLkyYP*7$(xnI9>?$^y-MT)Ox1e}`p#t&iByGfIJI3A6@JdwP)JP0}3oOF8O?ta!~ z%qKdya&X42s0Q)p#QCF9sAq%66+g0Z$<%RimB@_ejX5kqWwFI}FO_O?p~Y7W&I+&`qKke}8+) zyX}WfzuR#5TN(Bb^QGJ0(0hd1rLWqzA|fJ;EXyakgkt5*#qMe7WX1Koa~&Rt&gSZk z;p!DrDlj|jGUE?DKR@^7)hYYeQMc3R8J$U3Q=pNDwOzePnECZ!DPQ#%SiP+kg z*i%YFw-(>yNy`htPGz2&FDKxle=qp{Jw3JGLV$t^Wv)Z9Ho)XVaXyKc0r@}SAdiSl zddgM`uA%DkK)Uamy271N&2Qt$cD3Cnhr_BD3Xyc(ro5=lob&+D$Y9buO_z=f?UY9N zjP_%}QkT1Ju~l|6=N|v1+LiEm6hD*Qp;7*E(HqxOc4puk*RN9cAbNV8~X%W=dPZfgER?$IsGy&ezb(KHw2K-zQD9EZF;E%qK?v?$JFZX7Wczc{LwM$RB-F%6jrh@$6e5CHKOkhj(;h z_SbsrFqsmK2L}gtkNh$5iVKU2x4&pfxr3evPcZVSfzSxJUxP7d*a@PMEKoJAU3H7d z+rRP#ySFbCW2{K;M4msXu#(R%@`ujq*1Db+NnRP(3Z_1}mLmBeoW%TnY<5k`K54tY z`s=P38#rz^l5pL3>#1()dfXFM;X>WY_3MrAO+E}o4{s0CY@T4i1~Il6JMg01G8#Vp zll5sz2R)hJZd$a_b!T=u5Qmf>;&Waf`Pj+ed&vM=ty{3kAC)X{1)sD$60no6)D$Jl z2;}tT(4^df0#ObrVjuK*Lf-us0ZJo2o&pSx?3^6T(he$;P((~O>)$=m-)n-7HFW$* z_^gW0_u&xSfet%uW#Qz4m@tGm;xTckJPJ2N&Ct;A_ivK8xVVgpLb8tzRGDg2lIa7_ zU2t{cj~q#y72!;V6Mfa}-9VHH3_MDn zI781ZAr3l1mZYcKsSh188ldJ3D?ugjm;92vu<<$FUAT-8nN5VD!F6;V*#qY^=#0Fh zy5dd`Npp~o3LV)NE8{+zH&@CmDrKU}yO+|tgo>)lC~l;$oU$%8@1?MrswJ4JvRAO( zA{10O+F zPW#ZgjX=bU2m&qQ#qMI16vfTacVERJE3!Q|FJ6>3=&=)_xAVAe-XG6v+`!cU3-Z1` zTOqprHgBgql~k4S3VTnSMz)mCXh(^>;?nH*or@Etrr5$Z6X;6ou~%DvoF^*Yvb?Dp z&nhUuS$jtl!^*KIN*K7QE3DxWUgr-xwx-DTGwA)5e6=CD@XKTBn;^M|9 zC^9+*S`9H5p1mb-*`5xyVZ&>EyNd&N^BXw1sC)p! zhRu!Y+s?>-sUyt_H^`Tk;Rs?^LbcPeD*7{?s_E$XNqV0fH(i~ywO})Z!!WQjOgkgz zMSnq9h?MwvlkDfjg>T^g@BTBW@;F-WJf8EtS%LlBuH7hZ{;GL@3CvBs=_&40UwlYc z%6BkBW*YR5<+`=pKRgC{M!%s(~WIi%JZrUD3 ze(G{vb$VU5Q+l0u1RxC-bm}FEl$4>wnsA1F4Ylw7G%=357y%2y~n8i6Zv12KQ1N zS_L7u!yyF>qKAiS#h}aHI9JWz+=Z;NFGS|N%I()j3LL$?y|=sXrAusH9t`-F$b}Fp zd~1uPj7QOaI&tXSuRJTnZmtAmRS0#5kg#l*Q= zfE@kPl`K3xx*9DkQ?r3n3j{(Z{$uZQH0dJ*9Co-mbSH#RoGklsMC;&Ha8_9v3B;(# z3}r=Glc~nR<^6FHpm%uQ>~-FFT>K0rO-Oa@;kf?%nMUGd+Ub=jUG5EoCEm3WOj=P9 z=k@E?EdU3ffuho`J{r>-hB&nfwclFLHG110pLt2>il^pn&JSVNxTS@#hE7z!CT%=B zr@dVHaAUj{lVcSo#4&6OYhs>XXQX99WJq&;y5giXqZi{Q3Dn6xDq5oOu}bHz<=+17zGng|3Utz7E{(3TFA(gd zAgum*ZeBxb*ymlKv7hl6)b8>&96mok zTS;ed2AD%qPfP0u@4UfAGT}fO(#K#%1yqaJ>3jfD$!SP?mqi&8fidrH$j~K_ivht0 zSm>hyQ2xq3zBsj`BgX5!8l@&z9`ek@cZ>`7_6en;Fy{-ewi<(K5kbw;I><+iy<|!6 zXc!S!7bk5K8a_d!suD0EA)zqvg?Tr6{f`uWrCS>t8;$21M(Ned*B3iC3xIiy=Mw{_d^P99HZoma`f~te?C{-q0yP_`;=}{FH?~F*jl)#--Gct;r zEVo!}aGh%dJcvn|-#;_s)sNmXo@~K%bYt1~(o*7!?K)n&rbQPny;}Sz$#W|99AuE} zf7ROjB2BfFBOCX~6?l425{th;jH24mXYpRnt}ma=`x-6|J2>*v{7ai?zQO$vI$9lu zVVg9oV*qt1`rKTdh(yqd?Yl#bZx=h>#Ns}<|Pab%>&#oc>}=w%1DVx zn@9c4^~HeZ%a`f+WRv3}_}krO_-QLcBh!Jr7T$iZcb>Mzv@1(%?O%6HxFXpDhe7ES-xhb#>p)Vq=g;BlWJes@ z-y@#Mn8RgZyU(6I>rL}Mo>aC2{R7i=*4=V7C&94CcBWPn7c1aN-tdip^sW>Jr0aW= z;`EEyqW^PHs<`Vi1(&wNFgTW|`ql@^%9w$P0c^B+Z^A-EQ-hz*O-vvd*DrYvugHaEZP zn6Os0b90=qUUjAb%K(DDl<%MNs%ezFD%#Doj~j3V0YeLacKkGJsD$z+fZ zxPNfUKTgxBaa=MDz#^z6fUd4~dT{zXns7@`bwFXsFuq#7ptPl248Ph==A@^Y z3Q^nDr>>NXBf~0>Sn2#&!{#wt%7u*eB`N^tCaQJbIQE1H)|(xVeOpLQb1)!S^9&Gj z`~XW8cL#Tt!3D?^jRw!3|0X44MoN6XBr7XKN(~%LRJ@ZU7Fh^PW@@C*^3ui()-58Q_p;?3sRf%0C?N7bE|~w zKl8zEa)HBT{GnzT1G2HSPmNfC%8N zE9#~>UckGJxR963F!YK$vmyzS>9VM5cHMV1GbG0M=3-7&Y^!4UnjRmErTy^aK*O_V z?zVm@(|lo4#n;4{4pelVt1AM6jmO1YYdKF?W2%?Na71tX4`6CuyDcHrS3jh(S!py+ zekU(}{#{-zaCN7ZudR~Q{SF4Z;N1zsE8jf6u{}R|=FrGH;`1@eye&GGsX=O@`iB!x zj-Uc^?=DH`pYGUu>F&4u!x=D(pdBDx;CH?)&3SW{yB;vWM#Ty%+zzwDBqlR zPOM;~-6SIQuHEo>>UU+$_+MXETM!i2Zz`ENiJ8BRep=a8tlJpfykm$5702Fde|ENH zP=9=+V`n%gmZrdyAjgx?2JTtM=|)F35aqfGwoD$r>JJs1?J?tDI|0vn+Ub|r%9YgB zn(6{JgBKs3^z&oiCr#{(r;lK0wo8LLmnBuPC9;JeO2a4psp2QO3vD4%xMZbYRc8wF zp6zgJmjLdyN9Ln_DEFy&Z+cWY6CW;S!==reT??3mKdWj?C50fyf zbTGcvG2u`JT8n*}gGJ zx7uOhAvT>ub1GWWSOe7)!R9NR+;X@ku>1Fbmzly^R+R4f%HjuNucgpMcR}F}hqau9 z`CJ^4%*;j@`6qI>!;cELGo~TxRQwmhZFqeo=CNn&%36hs_F>z*@^*8-Ag0K>5(5Wa zW&-ob5T^kt%9r}im_5q9OiCn(7QKY#e5*##C#)Yw&>U?(6PY3N0lH85+PiZ~On4I# z8Wi*eOy?|sNK!jd*jCqL?{7O1!JYpODWEvn29a`zBl}Vem6doIo0b~r+jye73n!96 zCF;)o*PP(nCw1VD);48Pq4(sc_gaPh)cgBhp5@f8#g)o+RP+?X?~<7z{s-?Xy5{qT zp13u*ksIg?Y|`|lv0&H8MMjM<04<4G2s^ob>oGB~@TGYY0_NYwH|IMn|4Q{7eoxFo z-*kPx(KaA;bIFT~ICg_j2*3APPikFvei1Q9>btF^y7B{_o+@hA6-^843frW-CY&6c zGivmrW|O>RZ@k&|3}hjO;)#8tbkk*Z9BHCDZ^541b~HNCQEB$QsY4CLu(N*}HLap*Rn63c)uu0`qWqr< ze+cVQn!ZTzBt~KZWl>#MH*9S<&vd9Y=zUGJxXBDO&@;=8m*i9bUR{Tt9otPS!O*<>$$7EHM6(ngHE1GiH;P(Bw;p=%v z+Q%y&MJy|-RDcfw34Nk%&g-E6nJN&{9WPJ!d?28O2UeVEFB>R(+AJWM0-q}Z3B`4< zo$}(IYFdgu5W24}cA9Q%cMH!rSQUd=m7>jXZYQLG>cfB+oI%oj8R^Cwnj3m`7=eq? zPgr+%e}=pT0$xUDCalF634|%0#?>k-D=Rc$8;Hcw9KKK0iR};T+-Xn_CW*4*H~92@ z7S^AByu5zvQ`PCZPVyus5C~j&zMf3i4*d)$+#m>dS9YOACj0L0Qi748$eWS;JhrEa zs3g(1uO6ciI%JP(jG3iKw@N9{9z+bTfKOnvfj>M&%JF@H` z-Bp$-=H&iXhFx*IrVqh>=PH?W12beF7N(sBS^!Z3b&ntD03hpa8J4_0u*lir?d>~! znhN_op7@ymweB}Im%)y>Rz?B-f6M@x z7?r#l98xyKJ(ZNiL?P?3Y9J+_>p&Une%^L_9FQUzQ!l@+uI}znqx9yw;knVO%{OdK zBooY+=m?X-1<^=3&LsIcpDVtlrG(}K33)~Txg9xggNHtzL+wB{dyYjRlxdN)OK8z> z7d17*@k~q9LSI^M`XxSPMGu@0AO2r^l@P~$mHQVLE>cSj-^`_v(xUWx@f7$BZ$-Cj zPVC<8%-+djm)*s|bdI#Z=_(5c^_uPs8jot-b?pYTs>GSue^mHAo_~2-AO3<52Ks{f zY6&I1$9BCJ<(nepacK4I)Ldj?Trh_{EF&(fxR~%{)`{2RX|kW1=$F8cc-Dyc<-sdR zO$Td1blPp0;JtiyS15Bn7YiBgLK~6f&mToo^9L74_Z!3gm0GrG^(qseO||^p9$`57 z+HZ2UF$v#|=XE}1iwzd>X{uB?6m8c=YzkLgEKSXJVe?~}DarzIY;}0Z#}q-i z`am8&tKP?6*+5j;~ZW^C;j3n5}+MjOAZRaf*~mRUu6us8aB2rq`gdt=$0* zS|z}1bCkDEh>v~!UdN`DpzfA=5JL`G7Au@=qSpETcFhHS--QKz)?IaT`2-qjROHiU z3(h45S)j_%0k>^;-THe*MFp8y8xw zHo)U6(4a|6Y#Fv-e5Ksy&O|h^+(Wdc$Tr=^!W976yKuDQqNyMkuT=N#I`9y&~cX7A`1q@jZLkKk)r1fN#BVGhX$? zaoB%t<)t9=Q8VZQZdF*_c0JxfW72nIlNHJ*ds*#8p~44g_j`0QoJl`-d|V%V{r2r+ zx5cNsxoRpUSBFOF>*BCa`4;74U+>_l29(aE3YHf4FYIT(sWDD59Qo^6g2-n|!{)X} z2=LCW*%1Gm>L{SixTrf7)5Xdabyo%|r0H;OfAvwkUa7;7>?jG!HpA|&?0cYi-@kt! z+ZMdo_C>FEqKIo6_JXAql2op=GK@W}dN&J?I5hC+>avWhjbYbxcG}&_#%d%AKE7|v zFQkE278v+n^-8Z5g+#eAzc1MjH#Ub1rAE8g+6NZ?>aa|a=7^Sl7F42Jb2p9g2=B%u z<0e~shQ%v{Uw?Ad{12(wVDjDsITYeOJq4ajP0Ukz39G^CTTrr|VCccI7b11Z6y0wT zI=6E(DKl@038fDx07u0Yz_{~!&pyZ9s^h=@13JzQ1Su6<;Ns(66`wE1TS#0Eww=ED z_0qkZfj)3)Z!jc3_oK#%kzu|u7 zK|9@v?EyH_sVV@I@C7Dp{2kTniMHwh7{0rxw+NRrGC)s#3~ef|p`o)m;4VEVZG3f3 zP>QL zom1a~Mh=bKf4?4{OndtteKTr+prG6fXz2|%(8HASc1K867Y+s_6 zNsvO%RbCLrB7+#aN!a7=W=p0sO#QHJ3;T2*g+$nB__F2lBa5D?QNb5jB_iSWdsRV} z?IzkVE{6vz)~cOFHe9YEBF*Q77vf|sWpF>Ix-zbZWIn=>Kda(<`BvZ{kZ;!>*lkYN zkOoHk_Xbz4wMa8I8~L0qX)u3$QU!Rho}}>&hR^XHIvo!JKm}+8d*ZI!|DeEhV5(BG ze0&o0-|C7k&)4Fu1Xi3_qr`CPVGDLzJF{LiLrM@suq6e%)j%K$q%97o@}weZyvayH z0~emF;x^4i0cjP?BSW2IYf<`X(Jl*eY4HQfb;86M9obHrlyW_2yR}FMW?Kf$zp!&f z?;a8gqo@q~wtoHIB=nj;FJxm1CCM5>Hm2TiZ`Ymh0-Gm8(3h*8v|u>kmAkupdX92} zBqrp2aYPR7`ZHeMz@er&BE^_$fnTC+O>TRCN3tG$*ajy*SU^u@r2i?2WBz8hnIvpW zW9Yl(jPRFH!w4Y%eau%)>9LHI`7gEwi0Xm+`;O*3+6P+ScMWUJIDYD@7-?H_2i#j0 zMyAFZ*Wem3EeI>REf_6S=(lRP)acA1+g#lWzIkD}pa%7dwYhheCZqFMmDocc@16DO zxb27LF)C*HbOh2C)N>z!OIji3w5%`geSY|p{H6ZFz>=fX)H!;F5Tfh+Xd~R43Pho6 zN+YiaXMe1}Us-_p(L{{vT77u&X{}P92Rm|cuYHqucXN2)>-ZyUe5ej<@O|cj`aJb| zy>G}@PCBQ}2TyXIp9`xiaRAgJoZQ~?>09$beeLGBxIl8y1pCCXgCU}^WbD`&wmM0P zjk3UNp0_)9ar%|)pV2mVRv>?7OKg8o%=9=8V9XMT3}Wn6zKCk_K` z(iZ&CZJ^`0%l4WJas6u^ERNT(p6ZUc%RjM5 zmWlok2QK%ad<()`F8~RB;cdFy&nzn|qdDGQaV2T z-sX?2w*`AipJ`H`*kUt&d|kW~@0E&p`OdUeS-!OdtB#>5% zvCKQ+Y6{moo&2Zn-BWxNpWg>LJ=*l7%YTSXHLM%BAN0e1Dl&xLjmp{CnK6)zjx6xc z%pIV&9Sukv^x*&-@fkpU1&uQ^KD}%D5&*Wh<_OB{Fg`S}Dh2UIsf;u9L9Kyc}(w#0xZ<#s-QkRt^{1 zwit(3FGScx@IJKkul~vx`N2r5!M(o%C$vivXlgrb2--k%pK>V^{BRgM?@8~>_l*}L z;EhKY&@Gqr92z5l;$kFm(N`YL+Rewl`g2a{C3;ab#q*aKDeT?pa}d_J8Vs&ItFMtTmRx9R}!n_ zCJZ$z)?R)j6P4s;$)%)2z^=wu8^W(sH5yg&Huz79c7hV8w=Kw-m5;RRwf$|BepIL9 zi6*v9PW5kzPfsTruRTwx80C|7C0g-|yD&FSn1;G4hf*|gS zUq-n-uaf`#(UrLRH~hXmPEPnxMhc_E^O3bR-A4K2EcRP#@PcrLa_!w8vukTsMG~6P zSoB1=wFt|MUyJ+hKy)~uaBnuobYYQeI{UeCI{FRt_-O0%A6`z0vq`O6k0y$mRS4Gf z{_xCcZTs8id#GMZ3@lDsvYB-MZQnifI#(S2_2nHV77@Ky+l=d+_rLq!>tpC2dQ_;! zunxHD{s?jrgOL8xocHga)i|xdr8|uxu139~j60eCvK1wGkfWzrkH)eQ_H3Trn8{uq2Tr`~h>z)$e{X>Uh8maqV_mVgR>uirWI z9)_hKuVM5<9Vq-AN8dsrx*02=KHJkk2lJ2I%^adpW#H^sNUQMvcd~zXoodXK7EaDe zoH{|MZv5WR$OuBIz=t!gMpNjwC1!OmPu->@#3YOCd`nj)PJ!Hd4lxFz)X8dMRV62z zKxk-Wy2j~d5ZGZdY802EQ=(sYNh6KN3U%CEJL&*~&fw3U+bMnf^evs5ktyMli!rfW zF#cmcs*0WM;r%aU1!kN(TJPOCp7xphinPWJbt_hf`>(Nejg`>*j__=9m1OcP@dpH+ z>EXd-WLiWj3py?3E|M;FG2rp<&|WWZ4Uy9+YhR3kgY}LF>n?OOMAGX3ihgA9J{$@H z#*0dt`*M|5h(0`kthQPlQ^@dTZLBML4QDI>`&FR&zxIEgn+6LONV%%`-%qnE=H%4(k zIX?#RXD1F^t}M~oQa`x;RUb;Uo!h)tppYVa!N%jRrt_E&b*sZ*fZxas@+n{71~4TK z82TfN!t5-Fa;4pzc1r!*o~gsD^RR8^!2nDKnG-iOXx=4c%HY{Lo&F&9`48V`B5}3UV?4(FU4rQ*B+heN9P3b!sX);P7PvX-`ku_Q0%C* zb|)S$b`4y^`Jvyx{S^nE%9@&Hlk_b`qGXZSxTS@Kg;TfGh26 zWF$V5_w_3-=&^&8&Q>jjp95BEyeK*(QmHg!e|yFG`|sqxCNP;n%uxJV5`ycJMByOf!eXa71k|2BZ@MvQDMOE%5rQu_-lsLSc;b zAa)oL1oX)2H`Akwgzk$GB4uE7WVl>U@W}WXzyYiwcJ_$EM*Rmoxa)lSxP@B#m>zz! zui@uS{~c)pd(?q0VVug2K#tn3(7~NDc{gBr;A-Pf4ju>-J^NN$xGcrg)4`@>o|uFZ zUhZpJIS|0Xk;~BbH^a3&>a~1LY-TNLFJ(QKrb91+{ylwQWOLKu_31TvFUl*sgjOo# zZrt**prp7T8aDIHZe*xGB}7A%vln@aaqIe(nst-x!oM34cQR)cCJ@VIs?DF%7B}HQ zAp66jrsSGLUUDBoca=R=`EQMNZga9+9po>0^asZ8*}>m z#p2?GKEN>*{kB^gs38~rx3a0!B?WUP}C$94JYX&ao2KL_;3iJEdk4a3j=PuVc0sIe%1bNw@H@JPJ^UU=kZNpFwk_bidS z1Ev-AobEd(?v=;@Hyw1mt+da z0gJ$QA7!i4H}mK)PtX3Z;ydX*3w?pSUO+8ikzxoUq$p_Iw4u_k>BWCRB6Dt}09^&n zlNwL!YdmQ#vQv>IY!iqT%P;#`rO_GrLxxrW#qLQ71c8ISB6q#kjhsDvgp0@&nLca* z43Y!}t~1a6*d@CgdTXlkCNZx@mDIpVVifyRlcKfB3OC~Ve|=FdB5=4)%A#Dt=r>n3_3h+f-e+=`fcuUXy!uqIq%aA9FNoAReap3 zlDbdurf(oR>KWAwhUD~ud>aQoHhThV1Z7>cZY%kz*wM`v6d7 zU}!bY&i0>8$u#4ash~50T(Mz4nrSStaB)QLGibSS-d}{>xLw>S)T`1Gd-%t^Gs6P& zaCP?T^j^A`;~{8>pT+*ZXZhp(IAaDvqhvv+uX0*ZfoJ^NZ6j(Tagct2!SN@3{sZrI zz)s`Etit-ZA?EjaY*8ELCLKS2tqBNa0&F~Bn$+ch_#CK3-FN?x^##Py(&A? z+eA~FYO{Qv^`Pv`Z@nk`7G%1h{nOM_=1-JCx;(&A;1~4>qLGmr9nENK5c!}s;WL&?-dTG1NO#V?V60}6Ix4`^X@1(xg$J>+q?%G6a>ub=}0yt0Inr^Y7BeMw2T;c@K*9)n1b z{K4K*{WyLt?)o=<=Y}-yKbv@CCLw*A7F5)hd4#DYq3nCdEy^6W$0LihqVHMg0K_ch zQ2{S7N{gDRC4O z6c9u6h2_7_ZNbh(1}Zfo?ox)q=~@80H=ety=%lLKKvEz#U4T(kq&_~fXn_N>QaG1p zqU${ZF{w`hFrSPl2oHq*C@Hk0knjh(~(&A=ue=K1sZCsPQo_=&y+`7$n zO30WoS~=AJ&wd#%w^?u6#>==ns{QqG{r*}{ob2vdNx2L5tnZGunvwxby3SLWCgSnE zr-=^2(A%zPrmY`*`Te&7Dp-2>p9Z`K=E>?DfPzLCkn`l80!&S5XmAi6C3%S~;}Y`< zRW`2`%AjZsJYR-a3pH}}2G8hW1%FvaAQhq&EeGpd?QfHXNlz)%?6Dn8^%by0yuYgR z?Jq2_9umrKv-K*a&*Hm0>S|hR|8P9s)XA8i7L@v5^~QILc4KUqi)jBqL_$g}eEBe( zIyvS?j`Cd5|}z<74R1(ost)uPQvJ4faNqT0q*XSL}GIvLMd_F26RCf9?*xA z@5;)`_cNYeiBYJ;d~&}zJ6JL8j>$n{;adBa8!ha?E_N}TRpzJ?Q1936$KTi?)> zNRvH^i~sZ&BspK4@St2D3rmPw%Uj-OGy>1P?NTY?Ny|C4lx(bgRY{3?CjvuD%ytew z(KMsYTWFcsHjz${ZuU-4+dDpkO-j%0Ui9(6u4mH~@5ap1)$OVGfDUj5ele^Il zh4Vo|UM(H|v}JlQnW5l7)O!$CKYw6MlaXIQU6T%H>@lA2`pB|yK&{~1kocW5(G7KZ zIZlO;+KI6gEl21|XF5xXOIM@)GQ+o}A$#XnPsTO8Y1UiS=Q&vDGz$JT4ZN0^`p}%* zmS0?kpFR+hl-~an#QIyD>P_k2T$4e;L<~MWXvVllGPc`T6_~e5;%3v$bt_npDhGDk z>O$&&&XN%1tar-N%P-n$(j9XbnBONLsFzXLaILk^xT&>eZ?9%evy6Ii#%@-o!y2QA zYNbbhp^24+g}>krx;maC0t)`~0V1;pB0>@?GZ9M;<*4x1dG^EQL3}laV7 z=j8OWjII|e>+3^co8hdn+@imSfQm1pKfn)&lk7SAA%u6_pnnslQCATjF$H>-SY=!+ z$-S?}nk+MG3KVsS&FvTlTJ7u*A<>4Y*+FeO^=%B06CSH#UNI5pz5GlIlTqo*%*uji zN7Cfz64Ld-2Q0j})#JxRykj5j9{r?h)zNnQaN+lnjc(rw1Dd?Rcn>x9C;vFOin+mY&eL`)@{$UKUp&alX*=IOVT+{_bzlr8l6u-8 zn=;F(QFx3{3Io>z*^VcvebUSlBzz}LEUq=vbcPN=S!&0y@@o*(R zB$`lV7M+oO%F7YJ&V0jcwMEvyLcr=Vc~H+_DlQU*l!MK7vaSIv8xp1;{IPr!@}mk}k@tFj4V z?OoO#4V(?u;e3bkA07}jxHC8;OtOTxaBXwCddYxolC{hi?C|Tep7KQ7fLf8=d@BO) zvG#|0xuW(g!N$EwZP~hGUZ6@brles~ogLeymX+l#om@Py-|6&YjlZp&&1#p77%^(HThARikwhf=|C^D-UIa|Ifx@BHdb1V79VUP8~Jr+u+8 zz;(UbDi_XI{7O;%g&RxLnYX0PWEuIv$}bE^)fx^a{aY?{4@Ra78DdseW=-QH>ppqZpB(O9&dfdlZC3w2S{SB9SfK<>IwG!W=88lO}^3S7Ni}Q z8eiVPp+HS(j}1qF;zWzr*yGMe>UGvpKnL8F0J40`9$mPn=_UnivlS=V^{s#MjsAdy zQiLzHmnuiQ@Abjm=LK-G;=Mk8n022LhntKvSUUV+KZlh*uYt`ege8oJ z8;#8nxi|=Pg^vp1Kh+f?WR;cioVd_5X%TalW`wb7;`JM2ReK0 zpXNR6H_N$K1hCY$+IFWBKDzziKX7?L9^88TE6}%$QtsmTqq83~ICDXNYMd;$QqWo5 zM|P6f%%47ex&+qpk}>;8CD6H8gk1BG{GViyA)o`y=Q(f8pPcpMSwN4?-PuD8zhkl8 zH~XryUnaz*Sqp)%#BGA^c*@t8o%R#gRNRq7LI1OObA6ZgC9pTq=>534CywpjhcWDw zxjQp`JceIOQN*T9I1h3Z@qa6E+r1D%M(q6I?$LFV&2%Kh%=uiP3Ui1yr}l4N9O`6{ zJNZl_C?v%6N$EE64wz*#$PboyPr2C4)RI?LR$AGFGsUXc+WJXDerO}=D8E;4G>W*i zB!0O0@qH@UebkrD?}V1_~zy#kE= zHc&VW2)JtA@HRF+U%ocP2t5I75NE-`!P9a?l5WPJRf{yc?-AudpFk|=r$6Q#)5j;w zBsg`maj!_YqjIV5n>u-&d@fyj#1YSH5GkM{)mD5(9oB4x$x5P#f2WrB*TWq;5bylJ z{yjhN%PgZAiH>Tks!8t{Cce!!MN?L{;ykyH7Gnuc?YSKslM*%WPDnuTtQu_QI{f>n zkO6kyQonuE98M2#@hoOl?)oAC_IG|R_v`I0u8(T}u%Xr})($GttI7}?&7$8WLGb3U zJneBLhAYdMV1-DaMW5tzV=mDaIBr*2ryVT!ch;<=elX_CU$Vi=cCPI0?LE08VmIBa z7|X^@#b*pPy&2^kqNs){JK*!2oSbe@4RDw46vRIYcEpAP$T~p0sX6P02VpO^{mrV( zYRkVIC*HB7mUyiCPuFG{h$lvM+Om|7!K9V<4q+`Ikc-opazH*-Y!INv{>9(WWWhdy z+?dc3PqX4~MtN#%GwoHOv@~SvRJAu#d^HKGK1!b1uO$F`!L_LgQL0U9rX$O9@6Du) z-j8SR`PtMoHG|gI-_ZG-I)G4)HdnB-_4uu6(g{{^Z?|iH($FptAy9-t`1g1A=ejH4 z_1)FoTqU^}-ikF&vbrM1OJU^NO+F8AX+>eO3HD10%ty?vw~ITket%Isc=P5B9MJFI z9WV(I*HTmH=gx^!val^2U1m2^8-XB;{YmjVgBpiPyUOm)*!MXoeJ+9FYTW-pV43Dm zzAe05ONfO#lB;+30%M}Y2SeCyI^XcxgxF(IWYwofoC%#ss{Wj$y}xQ2KXNgSCrak+ zzsfr7g}ivz{<*>QTr837R2h>qk2lDb!}6b%%dgivllPt3d4t3vb9ycC^XPhW-%;B) zn!~e(^8Ylw#JUjWv4>d=K+(NRAdEIRK_v$o4E!1*6y1gNg|;~*PA;9?UiTiX(>R^)$<8Nc3yA~{0xknyMJCRVaPXveUsemT+giF z&ahKt>w(1QVtmZR0e>ivS?~)Q9R5ydWwLff#LQHV!T=w$-ASb zt4kAXaz4O=i`!267NpW&&9uBIDEfW;<|!U2byBz9iT^{Y{9R$pPU5~~@t330KcnB# zA05Tn^cB!?k4=w+EA!TofwgNHE(#1@WR^nX=B3U%ZBWf2CL$@8` z??Nb%X-}_)!!_m#3knH-RD%mXd-Qi59=C#b|Nl5T>!_->E{dN^H&>)X8tFzt;?f~V zNlSOvqY*BRlF})mAl;3$goGeSNC>F3NOygkZ;a<3Z|K0`z31$`)|zwvRzkopg|Z54 z{n|j$rdbysQ{oTY+FU$5d;a&SM;P5H7&&cjbr*6$Ub}q4ZY$k=4IVr1npWOibUE)l zsW7g`kX)ZMontQ*N;FgEzngt9w&Ypx0;n6kzxrcp{ibZRh3RMqu4nZ_KjnF}4AJ;3 zfmqmt{xUS0<-+q%!oyNjK$#51}7Opqo4f_!S6P}LGi8G)pXH)mL`$; zb5Hv6r7 z&GW`_G@Ww0kHerHCP~}mTHU{`Q*PX)oGN4hN2B%_qK|?S!NAdvm~Bel;6D0e1m1jf zjd>tNpiZNRso#C!M<9iyZli%i4J&kIUTJvw(~DLd7b8s+H?{Q1>kb!iT#nG{Z)Zrw zh^FrJ0ar{JJIBF_EMZ~82Q>SWN$v4oSJhNzJ6++a?4Ah!JA?0C67pH<6Av<|hh1(6 zfPyYS0rIOWJyDMkI@TmWL~o;6&~L7|Y!b`vK^6rx)&g5;+`s@iICtEx^6p~OO@goo zX@bRpvOBr;A5~>YplG9qC@dOO?pPq}oW^NlaY$Nh41L!uk6fKkU}N zR7f;?#WCnURO)r_Tr*$E53zgQT+%yOrI)7KGKOK_FRZqqh171x`r(8D-aVjN4t`dA z{HqpR>(wS5a7QP!?*hnM#;z2SoEvh)WJV)NpW~;p<;qWAhi6SGUTh}DYXQv(`oWIC-rRR#z;?JtxO#17Mk-oYL@sN;z zTVM6USrI_!1~G24Q9esv>uq9?=~!mW{z8|lBCU~Lm3>}nHY`sR;o^6DJmvp==HCzp zc2p87LXfhOLMkZG719FwyXFuKCc-R;E4-9y&N?oeT{jligT0wx?Wl?|PTDX8& zme)r}w5);w+pWo_EmgL`G*m971z=8o=y5eOYO{qX8*#GfBFOW`uc?iioXiO^%I@j}Q@=W~AD|=v$1=!Bay|;U z)&xc&rsOZz-TBH#RGov{wxd5-&TCqllS+i}N$1Fi&4=dDa7d+0;EAd%5Ae_q=1ge^ z-3Mu-1|k(237m$oeGVm6c}0 z-+N=eesZ`gyB6u*p8kg#qOd((zoEgJegr2jn3KT7i;!6a;}kYz-QnTkt%JKU;a!R~ zh2w0MtXM982EX&_(68vM+cwk)p^A4HI=bjC3vDE{!N;|2h3XUqGeI-1b*L~^1h6nu zQc<~sv)p3RbG)#vjS{3k-elT;U6MX8P0KgNa+Vn>BOktPZHS|SXB;-C%o=)2!zYLx zWxUe4Gmfw8MSu%#Vr~x0WQee3Eh5sm+Ry1@m6W=bi?8g$gI8SOdFhcQl!&HsZF}%T z6{Gd#SByDBzeNGI_@L_p#b^ue-~lE^3a)Vi8ZkT+xQm?~o%ncj7rKU~W{;4dpc44h z1HG7twu)N?WVMUTGF35lf-kXOG}#Gp#UAX9tEKglXR5XS6b<*W{S?#ICKldj?nYhh zs*Da96en6^Cw;iKGI+AfAP9sEyB_fN(O(ht{9)sbW2{2ZY!>4_7ca$du( zG4@SvFAX|Q?W2w9jvkUrVLQXP-y+|I1A-zebTC9X0$G#&7G017ki9Z^&PB)EwogneXj5pef zfBJbnns*_D_HOXojK0}E%m8X#~$bfEsHhU~$NRN_4| zv!)&Imutux-xK`6VzumU1NJvXo(9G3%Gy+uHXC-TQ#lPWVUG)6v<`u~8Q*tU0wCI8VYC;2ZXYjUv9*MLHwrc7*Q%Qmlf59DG2 z<$^y`)r669%>HrZNQzE9e2O7BGQqZt*0EW(kKid5^ob7qY`O;tzmuME_e_mT#u3nz z!ChZ(L?~gtlwXlo4(I%`;!9AVfn{C9%v#N=Xm4MN(@hO@b#T~cWoH2&M0nadDd;7E z!^n*~%1tD8vQ7&Gt@0}>Ui^T6J`m#-!QV7gUT_7B5u{~2McBr>yzp)A+*Bc&(5Tc( z)xt~K%7Ooa^af$x-)Ag<@61iaMwaIh6yCe~d?PnFhz!#5^J}#o`MG05M--0C{1?XZ7K0r`&2RV8I*a=h?x*4Lmr8d^^Y;fZ&H zV7~E+V#yeT?k#I+*dqJgsQYuD2W!lKE2N~Rp@e5ty|r;Lb==K#k2Fqjv08%Jl`!tG zl27Lnzsmo_F>YuoIx~j^mX=@Z*utxGzvE>;6`_k}jS!wcC^LE{*%M{Pi`DZOG5Q)j ziZ$M2hEDSh*q~Dw0Jt5E=6Zs$v0+gtQ&mBNQ!bJYYXu$FgMe0*(5$VCHs@*|ws~)b z{*!2X4;s+T`9_Xmj$zbnC@SGPuTu7?)dLWDwoe$ zw6nl@{-Wc@{0z~>m|V*I6AHF3(G=_=)UlmaF$+u3o9vMRZMs%!Rxzk_BXg2MlR?J7xUW2ig!~g zP68S4Htzy{dCvRNHNFSPY%$8?&wHho?;cyJ02m9MHbvcP8$XhMcYS}~t&rtUMM<@S$FqKf4#a(*|A2qk#Sv^u<z&be-CP`jD=7Jd|#Y#Ni`fJMntEvjc% zCawuwW2sdD3|6G5MIDFLaj%3qo9aaV_CANDsZRwgUoVtc~u(@V*>f> z7h;tayJTqcyz&%18o)V^rB=)Ge>{c2#Xyi^$izC|Umyj_M%HfiHTl#r>hpcvgfNZ}@%846woaOWrG(-+@SjpP%UDR%}pB zbkl<`O5)W{dR1j*pOe+4UOQG(B^b)SkPKr;XERo} zq-Bv4gkT% zV!pwCbGAz=UgDITI=B}b^yq{hq7O=6XqmEFF}>S(tIDEjU;t8G*Bk@J5(HGjPA@8i zl({{$tF#5+_n%efe+rpF-Vh3Y3{ctvAS}=#XplfS8>f%5Y5`L(s0o8L>d$3RbK z>jeyF0@GiaC!f7l*Cdt)hM>iO@s9J2D_T0r*Q_`C91!m7Rfs4M6{D~9!SFtNqYDtUmPBw*@!3P8Y zE~KW$7YN?6d-o_XM-XHVW1-9Lm!igHm5wx_IdT6Sn)vkjkLvM{H!q*du_vP_iriB= zfd4=&{^_c3N_sl|PogMQHW3m?cD2T1VtBGB9G9_bnT30cI&U3Na$I3&Bob)!yD6^jcA!}xVUmZu1gbCWY&05c1RyMW7{1DpX~ zGN4Z}0^dLX)_-ES-P245-_fJx7(Ue2zKy3At>cGdrPRwxKY$t!z)9f!m$f0~+w!&QW2n{SZyY|BaKQRC*XU` zeq*8`D8#l9JubGxne*SIG8nr`O5Sc-a-N?WeHWe%LrpNF!=fEWrT&Qf_5|_T!eMA` zc^1uNXW>m@^ndKw6hvjiW&eK_4(Dot&y`p1{e#cHl|NgkU9Rvv#sX(vf0<7Ce)Fu! zAea%qjUIgPT0zr9vnt_Qo4G#lw!;6!S0&-6az3vC`b^gw6D=zyax6M(9LC(|zjJ+x zS55P-aqoNCpsR%#Ucg{rBEklt>>}I}oBXjq$GZz;AUXwAb*vjk7nURh@Fm<0Q*o_Z z_Qs-yZ@0+HS~6PHl7sxy;=ng7Ahq@kZ*Ol;?k+S7>%4dYca9v6!+{~2W-?Ie2q^_i zEJ6OJMDmnHLmCMWwPKGb)=zGGTlgx%Q=P5@R~stTA5KR>a>_pK=Az_+q4$6CJE~b( zaCIN89sKnMlwx|Z%559C4Pk28VJFxjp(HwXK?w!Q@)9I$>{f8uWb`B6a zIw345PhCn#afbpOY5@d>ou*!zn-6y0mSECsdx})DRPJk#i zTn5BqPMGUKU!7!?lA2oBSpehd+-E565WpW(9_Gi=b>G=) zpOL<)?YCBuo|f)Dtm6j{!P(DOWI*B{00H{f%O=-GAQQ56rrLx%-gF}Jz>KDV8` z+vxVaPq?7Oj5J`5K^hJ4=0yJ%Vft?WoQtNpaVP|l|F@=(fCKnL&xFBUH90q@zyBLp zXbUu=q5LI>Hk}9=dML4RK_!><=Va*mI_VJw@~+;B?~dX&URR_ju@nsloB^Sszkn9l zv)H=4;?s3S^R;Uw5P-Uv9s=-$23vRN482HtH;FhaS%sY$sF!*w`xEr62( zNN}Oi*P?IH(5OY+n4~4Br=mQUOVI(>$!v5 ztNFKv1CQqs#KZIW@KEr>qYIkbDB3;|D>&b7^P7n`|l z!KnUph{Mj#VI;+>G+p^;OK_WtGB$6N0R;-`k`i&7$d3n_P-g|Z^Vs-XAqf~#3_W`I zaQM05LC#1e9-feJGr#~M^_#W++3=tAna>2pzq$-m)u+X^-L%;g;j+|HN+Y3T<=~J9 zX%I_w@%ZY_OPuCKe_RZF%5Z({WJzZ%=!Yt4X-eH_8>mAzm)eDcyoRemD+@vf1WrFc zp@Z|i8yuJ#?H#1}adE(L01_sEt|aDpC{EaAnrR)a@czCa25d;c6%BnP5s+V8i~+QM z5Y^W`9J6`*{3Ez9)HwDa1d)77N*V*ulz>JG-|IS)3=E|uu_oC@q5WcI&)aASWBVG= z9M#gE3;?cJ((=~mO)ft|v4z#TXS*&aXN`ktfJ6XK7@RhsM~AYrv)#we9l^W>C-Ckj zSX0Ze5YLQMnRwWH?IlZ8WTb!cQ#0CR7+qAKd6i>2Hwk11TsHd}qVL*(LIa0DQ&fI@ z2?M&4wS3xw%=#(A*bF~D9)I1jVR)biFA7TIIT`!Isqd#NEXqV8@rv^H=$&`B0WUAF zM%?=1gjZq()D^|wAvu-bOgz}6WX&GiZXO219>6Ke4>dk)<{V7Ys_FE<0M&eVUIujY#4$^ z7k(hju}>XpahCS7#BSQw>&EkDl+fw_R1=HIE4%Gl$XZ(eQ2O3{8WXG^`}AKMD!_HU z*xP20>SD)Da=AKkZaGeRmb#@H;!1M%kaZ+8+d`#7yR1cYyaPr;LZXA@u1Dt>p3*Y& zYRCg{u(g5F#S$QY{cz%zaz&JR;zLm$$cP zEBwX^JcuIC`9xE*eGU)mwwsK(UKQ;m@>F6lSzBBCf?myXQRjD!`)`F8DY~0cz5He0 zjO1G*e6XA!<<$}_wl(DGgy_q#Ur7Lb$qO?OC%%U#d^%Y-0+0CAM}NQP28=a8eptym zn5D~9Vdr2lQclqlEHQ?V?0uBnFSN9@V9<6XT|R_G%t=9y2H?SvZ`ZP^E~}=q*GvA) zaL$p7AR9^K`-fEnAuDq?!+rHyGOE+(<_HsWnO`_C?jw33=`)AF|pYdmx z0P3S^PsOT8TKc3aRMSoxi*?L3k?*#FCSFC{x-s`jMtYuqollIF0mk4F7Diac`Hv?bkv0!l$BVe}29HEBa@0ZyfVr(<#{koTZ0J9u@Wco>wF0;?~Q~3{HlL$45 zH7U7z*159XcZGh}n!m5RP7z{R`OijD8&SHh*Yar|K~_ZE{L_Qu_Gp@nH6e{E<9oqA zxBa$@$**1M*di}%&^+@w&Nwal@6ghhKYfTzZwL5!qCiM}g@HF=>lEv5z;j8C(I6!B z<$8&jdkuo})UjYIFCbR^+fY(y(0%@$&%2Ea=>6t;XbE20NSSc<-+6;9-DDS`EJ$>j z)}E|<(%Gq7`dOQGiQntOXgk3-Tt>|+kvlHkzyz#jiSpIJ|AG<&LH@JR9Yq@Mwvo5@ zIj4Fff0X^YDNw>{=k+Pwm*zy)H=v>I{tK?Q4H_TzsN{Wd07o4j8F4?@Nae5qi8MbfJ4Rv)z7x|lClPJ2CUkP zzG}6<>9}!APw6H(L(~Bt?ICSuw33`?%Lg#!8q9MOClE0U2J-mAq3={PU#Di$;SxIz zFK{Yptoi-KL@FhHz(;9%MT*!#fft6KO%l|Zr_Yb8=uW8w=#}@o8%EgYy`A}@$uWTT zNe(Xm!1^u!b1__Tr}?I# zD^$wF@&LB1XP!-kU=_3wQG_?e~` z@+U%Ln>5{DkJQB~(S>g01wXnst}<^w(D8RbFUIcpK#PS|>$US_F<{RWVjX!A*Sz}Y zOW%_Yi!HK9loT!FZeWKsaawM|my||`5cSx1iQ>0bG{eLcdWUJ#($)TS==>)Q<%3C+ z7e!H

24W*DH@5GRX#>HRhQXqQ*w?B0bNy*bVqRIGMpOB)Fnh3NUF0zUF$3Uf~|^ zH(6Il9GzUC-5;DydyBPEUWF8fU#IxE`HX1i zWi1_MGn{`mVXI4>{CBH?Gnd)3J7!3roCI9pky}%?W~Vs@(UB_0xgPx~<6KXh+kjH* zLgROL+LWy816*aVeQZY&ti)ObR1}^K&MFg?oWllq|NQKAzW;;hM)*P92`RmJD%w*7 z8Lo%cIDS=i#edM(MX6c;QE`|D$y$cH!fm-LEB9bVi5H+DF7MGeGwS7DSpO~wSXXEP zdC|NNW|et*35@ znfRbzQwg&*6y3xD(3jFE!1~JfoA|2(44kcm$?*1c)%nlgN$Ao%3~`+&u8X(&3O!sR zObb^2wor?R3vY>nZolQ{ml793Gn}rbhhqRSa2S)Ux%>hU$KHN$`Ssh`%2hf_gvf`!H#5-CW@qh|{xCuaA3r`W?-LxStdHQI2lH zO044@f(_A8LER*Z*z_6%1oQ#jbomjgVZPqlCfS92c{H}xr^Z9_{Ux#kz8I<2;j-q} z=Tiy)pN&(-ZD-fLq0iKkd#@Szbk8X8`rt8mKSt~K4EkM+cmGOf6-vBjHNGb}-~U#| z+1Shf{?0uB<0}|X`N=(VF(rVg1hZZ!eWVDc5x*fpgR_mz@?9DsnmzU*_3u(KV~Xc0 z7~5AaHFX3GF^E&rZKC|s@aaEYi8tN!s{+u4am@W0BUek%oB;mGC8B_A5+m2j zU;s=+=%oc+?YZjAcY9)NV~yk5+q1A%f#G6*n4_95_qin?KC@~Hd$KLtD0+IW;D1wk z(IHvsMrBjq$^ZfPXtxn zLxGbT6?)M+@t9+SQG4Uv?N}t{Q%G!0u zZ3Nt*I)7Gs>4)7roDih+!>LLBqD5wl=;W{e*Qv`RQZVSJ{ zGLn73_vNh;@>wQO{SNB~`Ew)F!<0P~63$Z8S`5m?zvRsyPI>9f0;7@}pR@G??Y2?>0aX+nZ^O=^f>*pUm|16uCtFV}3N^>(uX>cLSCf~g`7=KCge|t4G_S1?*fn?4R ztf)Ty7B_m*z%!cOkZAKi%ZFZ72rHlc{jL6Q7}O_G#0=xCOjIU4zk# z3dr9Tv+J4KbZ8|dB|}e1=GK=N7pYMZ9o*lzMX*X(6kv3uF+#*xguC%glau^wD!k6v zjI|BvyFB0S*jFo{CQMXmSX_0uxg%Gxq_zG@lW?TG8HuVvT90fu5X(Y=L+@sPSOzFh zlIJV3(So**UY*Vtxf?h9`Uc`{#l@TL8s@z7?;Bg~nkhllhX%B}2tWDO&gof|7x<m4ep;MbzxV_|AaZvtT`YQUQXVl`QlKPWsvp&JL;Zr0JyjlUBOa=IwAAK z`7W@Q4Ylu6`IZnMhe(Wd`E8@vz22RxeQ&ZvIr=vdej=s924>Wny1F)VH{Uz0_*-*S z`79hA6&T)mgxZD(%XvNwOe<9#mBtxt#AfE)yD;(uJQC08*PFqWQqDrFVAvHGS?quQ zN)n`=6ZZv%t!^+D&RQI9P?n9E9y}D#56`mJ+3n@SC?l9~S?;8{ySeR}13`kfny#;( zq*4rvJm)2s)6v0f)cMNPT2AX{jkzo%#GR_LwwK~VuTBa|@It5s@ExbYL&=}N z_@f)EY}QtUTEX6c!m9LKbW=AucVzS*X}#9nY$2bMBlL!|GdrS>|26KG)@-Lh?dN)| zk05v?17GGkCjc|>u;o2Z>WnoJ0sv_A>^vuI0M@1aUpZ1SqiglAicsMqk8M?dZ#1GA zJxkHrr;ZZ6%M1gavXI|%Uioo@U?`NexVyy`Ebv1$5Jc=wG@$$&3|R7)4|vEc0HN8Q z+EQH%Y~q&w)jMH!Rf~u7i#Q+Yhv+8NQegvmEN{WT-MtYp zV7yG7fP;&MGS^~`n#N@spD*=+aD|lUw#gw_H_m|TsW$+54*NX)lEOfY1ESr`P@>f0 zG$Nc8HyTVo065=-RT&2=wW@OmylpP=@nlt&DQ-)M7#g$U{|>BiBk4S@DlEzP7*Z_u z&q4s88IV~#|Q!Bnd^g?^X|#_IRt%=Pi?y*c!WnHwI%W7MpNYTM`; zk!);)OZWqdVu6IRCJR=#RHOb0yOX5sISoY;c&Q4YK}_!h!{~<#j*eiwT}oYHVIQoV zbZ>S6w*^VVlIL@lWH0E~JzQ}<01xqRvIrM8L8SiK(HPb7aq|AKmrl+giL%Y%gnv8k zLFJ9YR6v29Q;#iQ7Z-Qm2=%C$*XBFCI8`hw_CCcx0CT_wBS+6*60C0f6!GH#j3{)tTs@kw*h2W5^M+Y!{ihwA1DaiKQHPJ0!945U z34VSZ)IEO}r7gK$$cr<&6`_epJ&`12(J9{6H z@Gnw{;C1QD=K;0s8C2x#t<*{_E`%(evZsJ%s_(Wwf)p+ko~ziYZUEe)pr0xwlg;|E zm983bnfWB-iEoQzpyD_8@5M6^3x!oIeuYU#mfw%4@r}N=#+}4bCazii2Q;6?w%T#K zN~z3lSkhgPo=X%V*~SNA-4BnJB~o_**M%inlK^wouP;ehJ|9#Yn?H?VkW5Zz7z(sJ zHx~`OO_i7kfZ#zRoTuHNLx&K6)Qg8SYH<5@3FcxH(1!Pu;wI93#qmdpa_;KRsAL}h zq}ux34{b6#o`;$TG-cj>E;(1TcPLN`RY_r!cqBdpR{yB7SU#vZ;^UUQD)zAOdH`m7Sd{8hHHJH=7-W{2>kSN83vhP5#2hko_jvt@XaG`FU$~A1A z`-=ZO2CzXFSa*5d_G@t?$f59Bkv6!Yv52`-o9)xni<=-2ZjV}Ok=Iy#(~xmu(m=Ja`TIH)>7Coy`BA^lFx_q|pqrg7wWk zcm_0PxXvY0m=M^2V63O9Y5o%Jdk`r?EH};3!9fWuWC2caXI`7HZwhtfx^8cdC16#~ zR+YR9EyzZDvoclBm-R?0j)mSVd2Mq?WX7RD+rps@e>)#1r@bEl_eIs>By%#%<)_$QQ<@P73a z%V#w!gK~prFph0*A8PBDKb8$35xg7Q$`UFPz-ocPB0Sc708w97_Wf4?u5r6(*q5TB z3b70+P31J}9A!63TwVPgukd&OPuntJ?{|9wZi5b61J8q3k2gTMj#u@3O?y$;dA3=k21*NHGFl4wPx1M}!Eya;{Swp29h-t@3t#>c zPEBa;x5|A_$*Q*o*nH>{OF6^Ov7&NV8D@w_2*n& zazkjR!xj$M0#JY@ucPRZGMW8pHnILfrjfuMM>+ew(Q_$|dHjWFTRiyQdXt!+=|Xg0 ztR_XEIc{S}qHBHK1{*pX6JD;U06V_W!hM!#l-N{MRPKeNwaxdsvxIzie4YH1&yTEy zb3~QZTeA*- z3-sLnVg!B}T|ZkF|JpuLqBO4HuN2N>JQoY|Tj-xkN=TNa-*2xjB4&9 zY70Tnws&@nfXpx@1f3TbvtT2xd|>$~5Pf(aSbd>htM3%EJ^0A|1EmM-ih!b!nt&ld zoF`TnkAm2t=3Z?GNQo8A$)5tQx!HLge z5j>ASF;5R51+HTwD+8Rg=0Pz#XLZ{OUe@-$zkb=qRr4vBDDq<1`(TQ~P&A7tn3a21 z=)>@Ro{FR;pdi3n1a;84EC65YWA3*~zNA6_o}$Y-;k3sD(e#lEQ#6 z2q;S`eA1XYs8+%g+ruk*w-T$#SS-TA`s0|-i)DNt1{!~QiReDHA~LbKV2eu~D_c;X zrd<_@wjikEr0?pE;X3B*c{~6W`(T=};$U%lVd~FA+(Dlyr{BK3c&k4=_GjwJe${Gw zsXyT$0C~F}do$obz{h8Rl3xS{F-c5*?_kiIzbG0i*6%jhkfT5gzcb>+l>u~r;3(W& zsIFeUuD4(xq37hgX0!7kA>`E^`$avS`#^1r#KQZeN7Jq%zqYw`;JifoxzWl(rCDb*h0vTRP}h)U{YhXltm2bHu>P-kfVRNUPz}l^W!XN_ zj+fb^JAX91fn`VP6X4qz@1*o&<{y+1Xw9LQ%}Pk3Dru9F2oH675(7l#LZexis-wH@ z!$6m-Cg&}6$y+NvNRl*$0+xTn1wnOL*mv9I)m0#dv{1;9VkwNc8V(~A)o&$uUCU=T zD;O@@FHnIxNL(?x&am$t)O5NxisI0Rt^ckPn*)K2p<&iNAvOc{z-2DYn_}Unj z*0TXngLa#4u)taN$vvsd$k0%ZnEo2!4neH8s7e5&Mh|%doyza+_P;>;l1tb<92uB~ z5~r&aTw?NfxXf%G9ho|sogJ%=ZWc<5I|gG4XRj=vb(>Q|1Tut=R|UhQ-`R_<&9L;@ z(C+fWckv)y@Q1qDH)|rj{LhY^%4B3ED3dL*8v@eD46DDcw_);InZ3NR{k6s5n~msD zZTN&2MIBgE)U8?w?E^Ff7c}9tdAFCujZ7=RTSL*JK)8UYtLJo_bi7>jWLHI5SubdD z!*e!wUH*%sncH+UuVEPClK6P|)Pa`#W>nGz&nWRCSMcNQS8`lB(gJ3Mu1|>`;rYN1 zV~UCpA>c2UiZj+59eKG6fEQSpYDeI%fE($6wO6*<5xYu$#L64_0`0+X zQ^hqatZElLN-*TUJlXr$B1&wyOW_7As9+1?tvDq3RwpVnPAAh(rjoU{s-} z4!ilZ{B?rD-!1P!y*!+@BCNV1lQ{a~UG`p7aw=~ zB}r8yM(nV7@GUUN+N;Y?qn5@V%~I!aVWDG8>35qwkShnLBM|59l!<{$G?7`k7E-$9 z?gli+1DJVl;~e{`MMjy5oLIr-vkDeUQ9{3;vS!$%X@<#0>*(}x!W z|4{HI3M8t5uzV{8)g^fpmTU+>vEQ>6o;LnKQbYg1{)e1;`zaQj^ex11y#z^r!Cqi` zdD+6I#KCT;XewaoXU|X_H)6q>>xaY3{Oqbd6sa>2S6bgt5!3BBT6u>B)*5VTOiQ;` zOKY9lz@ZfO7Y2u0HucWr3uVn9 zNttyZUHwmwNB9*I9^nJ{?mNPv=d!uCzlB}q=lH==H$Uk7^3s3on&%Sh-;6#)j9mQw zokE_tNeEpa?Q_ZQEfBC7{ z!`ZI=Z~iz={=ng#ZpS8|hJ`TuI{kA8vog7hI={G&jn4+3)42c9d@rcj%*qcr&DE^7 zHv(~e@7sIKbX69k={wAMGni5mQ)*h87F2tGDHtU&U46m8V36B!RN%82{dN)X?MFzA zE0jjELOMR91oEf2wS0AYyRPFCo)l;3j)UDK)(X2c~VLdtWzM~vzjsll8{`30}Du5-BuXLx-2nW?1hH#9N zM_yOVFUP&)!~-!BZF{_@e|e9$mZI3>C?p1k`g5@5!4^eHTZLK0zWnkI0kT`j z?{jk*sNH};=sW{DLZ+6l7=Ok;N&$w*w^GlIYf^JxMO9L?TL zDU%=3yR6lv&_s%Pl@nt6IObBle7SgxWy7v87k9FvfLF9eq)#Zz7BRlp2jX=fd|Pww z)Y*!t70-cAILM-wb<7NPR#kCF1>SYpIdDOCOUReNuOQ{QKDtKr#Od5b6z_Wxo!r*W z@$M`cqr#(jRk6x(43R;$!e9lw;6=*bXO^OuBxWKKZ-n7Bk-~R;xGj(7SfVlLg<5u` zWaw0g*g@6V6&!3OVBi)W_6-H@$pH4&#F}&jFeZ;>wb{n6pt$NHDR>VFKcD_J(W`7k zd9#k0=D~a0XdnDUn{-%jAwtw_0)ZzL=qu^`(X*+<;6n3+ayFJzFVsOhM>Iv@*6*pf46tJOR2D-jRS>Xs`<{Yr8 z6HG_9Gbso}+0FtJ!RtAiCA7l`wW_i(%6z-6>TWCT-1|XzYcq`4ZRudj`SkBIT?o?c;%y{2Q9ZvGKId8eBNJOh&}i ziVLwquREL>8555KUXYG0ZQEQ7c*BE4&Z($+ zYmE)W*F&0#U3*oRv(iM>yv2Y409cWJF->1@VA--(WU@w#0xlmImYt|eUi?`^(9wOL zp0@oqyd@Pkj{**w9F-yuiGWik@XcJ=1H5W|O0U-J4+u;yvC!OxOFxR}elUO=tNcPI z#)&xkz^2;672z>?`&BnYBKGpRSy1zJEslgXD_1Yu2ob|7F9DTgZ0U_O6we5(W&K9c1p+-gJ9D;?H%_Heiox0nf18*uJjh&p7=zmm;;FLd+rh>Z z`ec>gz%wpCTV4W;$Y$W=Zo*~S80$kg!Zt#8SIYQ_(MYPV2e{vZ6!H_3q4JPkma0PDo30r zg(+Gn```f?Q_bXGlZ9T&%7zu+7;#-A7GOw}L`ur-dH_nHEsTD}Fqtr*{TkDu!z zy?=e;RA%Mj8LSRIa$NgBXQXr%LNQjVklwi;;RC~DmbWqRqxkytPTcZshFq$>kjvHS?m3`seS`rcYbG^Pm*N&+Hk6oNc`{B@aOENN_8H07p`B=+(&!7 zM;?C=uaQO`j9R%7EW`ldPxfBXo($f1B53b7pY3hazioeHkFvr<+Nr0oA8v zJ~arNlOddnNnRmqK*94Wz7j>sbfV{kOWoc4qs&_!sH05WVB}~$DApDg+zr0Q9=zXF zlxQs~bv{U^Qab8MZP4g2Jqf7F2jy?x;1qOrb{c~hVi$rM;ac43BcO1MLz#OLe0xoi zsT%2wQXa7> zOA#oAtp^Xf1-|+W5I`Shfj)6Qr6DiTjsBny17u6ms~UK-3eN=H8$EhnvyU5J##US` z4F~EC%0-XQ8hz7z9@Ev}7W^7?7bkkn1g`8z*(jPfRt%-o7dj9ONR4Cb6OE2!*V`$k z^qGQ#Q^RXExxDC08b9Id! z{k451@Z=G~;n4HyU|YZ2_4yq>2(>1TSogSlk9@`s)L+(Anmd}_B*CJdA36uD!HAd> z%&>i`(o`H}QGa7Od3kj1wsQtY?7tsM)Us4r4yN@?JJ8JnfSbCM_02B-`A5wF7}5s) z57@29548+F3jVtXQt3)sD#mA+cDe`ydkXH(^X5c@^k{#+S=CTyQt!cI{VkDJl1c8! z>!a9Ff8C#%p#&rC-)L)E4VD_u#d+u+CHw|b;IARZ1O&r_vkx~Xf}#W-@8s-CFyiK^l1wpUyyR&AAYQVB_m!Ae)#T0Bmjt2MEOcwL#Zd z!Vy{fuFdN-R{+YomD~!5$IR>-$}lZ1u82O)*HOzeo=0dncG8g=t@c{wPq9LpCfLdG zeoc<>2&DGx5T-D|_uqF`J;Lq0A*FZz@`OWu@UR725@!~@p1^os?%7o1D{>bz+~#+t z<$e<@U)i43I)V+TKItzjSb8A5R92nmOUgxB83MLnm)VN4!MA4jB-^>U2i&eSz`|BW zf>eAgd3pI}jYA7sv;MJV!W(DL4EQV@)onx}`Q9v9q$mXVJ%|7i#yWho4Ey%~VB~(3 zeTGL~q}RQnD_Cd*GQ|;OXdwDOc0RV%)dL#sSSx;5C)O?@YkI5Pdu9;Ux4sY6hs&6I zeQd@MoC#d&>D~&6CE2!npuhcQi5tU*%&qAlBAKq;-jndnAmux4_3PF@XX*K#8xHbU zo;jHh@a0NXvFWeDRM7!Y()J06ck?3oPi9@_Y3-ySh*K8vsRWb7#jTV}O=JpAov z?PE~=v%~GjS~_E@%2w>U&ecd=cwLX$bb{88}6MUh!?mTX8YDV==mOw&|_F|UPgtSzzY2>vW5ujZ8 zr)ss80t>arlWd{Wn+5svkW-)-u;pOU>FMdEV<5mn^!V8{Zt&Hzy{v$Y|3HwQi61)f z%dARr4LMLdqQaMcES~eWFHjUEb~UOJjLzXWBH5UV|6olSU$E&tGxq5_0b!5ix_5cM zRoxeZQnb!*VBcqjS5~7QVL*MW#Go+MKNLz!EjplBeYrfQ=8UP=gdlSg5Dw<>Ya(A9 z#4Kssv%Wt0O^+3N6)#m%ev7pld~W_NasjMUrs-PVE3d3nL!J8#LoCywAyhw3{Q$Mw zTQDf9$OH#ydFczoA54F={)^Z4{@}Gc50Lv_ty|r2WZWWeD~Std{}<*l3-SRha!~&E zlGEZEpkBO;*|`JalYRxZZSE_U=OU}VU^#CHnXh(dL=XgmG=RmfSmUy!KKvtYdspxu zDN>uPKP=UbVuickJ4F!P%(TsW4dN8iY0UfLu5kqq zi-_w~?E%6E==EV=AP0aAU>+F?ZpZGrrPJ0`ohXX`5vEK1fKl33(I5$e`7eC$b$*k6%BM=r<(*~CrF)&e9yLd zVfr+CE|uK;oMdC2^?W-ZJ1-ZBVzb&DVIM&?cr^53YV$|=W0TZzk8}Ss2N@rB(wKg>B4x$Q%pqL7zOy1I-d(x=@+|I9isSEf4P7ENf@I>mO z@2uy$Wj7(Gm{42;MaWBMXXi3N%en=wi@zOy;Qf9a?)E}j41-Ol)C>LcWG9NFx^t43 z)C_An3&R2myE@Kq?ksL!5$H7tK_nP-|Bd=)yp^ZTE=$b3=yhfc0A>075yMBrJ`@~% zAz;B6@JEAXLvPym+Kf7wQo`I<6Hg_I`6(oe;7TNn=bYx_vsAdm-Lda)ejA^gt1QRa z;;@l}C9DAYG&L@c3L>-b?SjarXeSz_GJ!m<=-P2I^A`YBaKOZj?QdacmqI%V6swCw zqstyy2CV=)-X@F3)EGPKfapCkWYyv5W4tdzbJF4Q1vS2Q{>O zrEgS!0HqZ-D(ek>jz_L=l&^S*kxMS}cf1qX+yuuGs4gC4eLG{YQ(Y_>MIVr z_k3Yk5renKNrM+9w04w}*X?0ob`8~hn87m3s%>MK-fFW^bfqRgHONM&+~XZuZU=To zSE-fKtX0_7$aqY0T8_Rx%?t6^8{noo7nrQ7gN|Jw!f#lZLIN8M$gcDYoJtweCHQJRrauxc<+_(Vuj^hA(eRl=kOCR=9wp2zHF-CYI)U zT6T7p+gM|8H#(UKQlAq;8vfs^?xDNQQxFLzh|)CGOQ3RX!3KYoJQLB(tZ7CN>8>k@ zFBKi!=h?u1WBiFZnVW70Kh%ew(}68j8|9-dH~_Bp7ip_U7yBO(Pxhu2^bru!c_i@sR#hZ*zkxfU6tL&zUEL%`C)h>nb<68?9Aj!WuPkb>i)7zMki zy1!6Z7R991go16`>J$6^6%W`w#cB~?&X%SKk_YfPZ)-KlsN!PsDoP)cs?JLXL1Cft zm{)iQ)4_9DrZP-K(w3HtRkXnpGuJH`-|Vnc7K$i(UC_}`en)KyrD5WQ(V7Roi+{A< z_M~{ukj~9>T+xTf%4h0b=DQFcpf)qS>t53{i3ckrWs?>aZ$hrSmjR(ZebIf8rWy>e zaKIMuS=Bq^nX_3tJyc|;BFF_;19g%Wb?)$T=YLh5cRZE<`^WEd99uc|%nGT@^s%y6 zNJb(nl&!4D%pOHXBzqGg87YZ4D3pYRY&s&@+3WaT=l9>Q$D?}mM|IA9zwh^by|3%_ zdcLZZh~E|@*=PmS+Fx`5*bBK>Za$#x^6lisn-k zalb#aB;x%HsOsu2;z!qqf5Fc}5b{Qz{?hx8`?0#A>P7SLoJ&5xM(@0Wj_P?x_$xoj zIZ8S~rRpzUi;f_1VwIFO75wyrvOAHq+-=#;)w=ZUeX^6j=K*2%(ZbwgQ{pndyrSyb zSoh5)JF}8x{`K=WD%*_o6%^={B|AN3%~B=1vW^>d4@erB5#%vbHT0M5?%#jo{%`g( zcTcXuftq%*jE;T(f24!in9%j%n~#$&bA_e)S*i@(X76jAO4ld!zlx8g^KlP9$#UG3 zr5TP>WAy5BkbJkiJtpBcE>m~0J`{Uf@#@gpYjikd#fXWu@8=ES|5kL~8vHT{PKLo% zN;}H;CI*e9(_9^+(Iu-JmY3(YC5IauH5>w^>MCsCsWy?^VPpK|_k3!q$dPvZe(%jb z$J)D0NoNK((*PZ#m?!J?rG>EdIgO}Sv90+j%DpR@>_s^yP0JNZs|;ch&4C4P_|4}S zI7X9PiVytGB;f6HeIH)YPQ`i20|?|%cW?+B#CE*mv=-@J*&yikir(5tOwI7?lnwsP zJ!g*G+3)y|Y!36&9^s)tLwp{fi(v0+rtakO6$V7oZ$B|3Ifp`ngkuvayU!qf!yWo> ztd}YgD8V2LW`*d6VCmbhW2i&2P|s$*G2TsF{g!WoH4Oh1ImKE^AyBw=K*{SD@O^+) z;&$Yj0qbOb?Ns|<$U&%ph{cc8;jK2pY25<++B)by;n15Wk;YMUpB6V3Du5S2Z~L;~ z9dHDSO75RPI_KBs9}UgR`SD4V6T*S|iZ0WR`|sqeksn4|T>ro)W63cRyY!MNqQsHK z>qkF-)a$qP9rH3LAz-ei02Kk=u-P<0e7bF-%1en7-7PkP?KwAN2G2+}I7K+zDHp*Nu2uFL^QMus1#bW?~tGE1OxJ8z_}13|F?VlzJRp zm4?Y_&!%=(QZZ;GB23n==E!Ze6I0!DEJZKd znK6&NMq~Dv+`r^c(_Q8a1IC77!!1?=ePibjZ1bBh@Jn1 zA)kcvpU->9VjVySE<>Y|eN3z9(%WN1I|Tp-aO=4Icj?B1hHvtyo2}d#o5ugMr9Ykj zLhIy;PpkBBR;7sL-=qb{I7ert%QNl*tKf2i`FXZ)>mIo7sCm18Os>{fUxbQ=|8tBw znivyN3{c772{=d=)YM4COgQb|nf=k!7%sClPl5$pJ)MrTDdVRv#P-^jgl)$r-rwpM zHLjG5J`zhz`AXmacV0;3Y*?8n6PfuisYjsHTt$mT`>1ZR`o}sA6g%#E}O9tl||fNzFvIc*oozvTz51O!}1X7{-AqnXUS6XJ2$E*$;U{DbxlcskDWe@d@((y4rIgJFDdc z+l9!TJ!eDAG}O*X%|1xJYdHYg0U zJO5~e_2Y_>!JnLQ$j4m_RH>{wG>XG<_J$b^tV{7pTA7?-*B~i(E5jfQ`uHV@Xd_;RYnzkI5^Hc@PKQ#%M z`xS~a%*cFXpd>a(fHq*2euyUWwgAblVq_%gXJt^b_ozlju${g3rLfG4moGnpBi#eP zCH-qMkPz-I2f{CU`^;YY?#09jF`ktf-RF|S)D^GH58S@Ae^>0d$9#G%DpDy&rvOx1 zeupugL>Zs(_Kg3Hq-XXSv4vX1O%1}?#$Q9}_KfIIl;?8n*;{3vA@`{w%};u99^rv4 zqx3tR9&BK%XXu3xH>2>@{H~3rX{mU$GPlPFgzCL&{^ufW{TAKtq+XA)Y(y#_d{e4@ zkNKh$_4PrUCy#nV&6y;vws{7Ru4*+;;FRB*?MgyW^y`uLqSDOC^#ogLzsFtH)@FnR z*U5lI*Cs-J3lTdh0B<-cYbL=?W+6M&AD_3Ms{UgR?Z4}|-!5D|MaeB{Z7hZ*OXtPCN))jK_PlY7;uv;U*81Qw zQc2Cjq3eG1!FVh{FHo?QhG|BM9yLX7fN zt(PuY?*`cZ;Mq*y8_{jr|59Q4#^pcAa>KNeYp0ra8FETsCT4$QzcII~I8nc6`m=am z&!ln{+Z^8}Hg^w%@wHNR%otdxW~uQ; z!OK41-6b}?kkC{y@^(LEGcz-7BO`{XmXKp!$dx)8<9*0^jgKPy>&Amtx7Z;p5Et9t z499g2^;qcg*bL4^Vs|m9LVjN0T`blg1>1`#{75oCfi=aGx|mlNS3F<~^W>#_&M0fB zE|?Q0GzTSNe@I@Cc)+G1YtW_>(OQ1tS&}aQP4c84QK$dDP*99)2V=V@3_mzRLN-=S zYyBiZBMWW3tBdud=xnw~afO(LRuq==Vv}qzZ-uhIBwLIXIpyOALy=SS3~ODs44y!2 z4x|rO&RB(DwL>_KytxmpG#2~UBUfiUWU_PxyV|I9;phkNDuX>#lmEZh-z z?-dh+!Dq+Zk2eN@ga$I=zX26#L+ZQxyk380R z$??^<+N*m*g8-caifss6Ryk|CPm$SYWKfQ%2{D3eP|ej<#GxlM9{l9A-KnCay&Irj z$p1q5b?Evi&O|+CwU5b5a?h0@TB6k5b8}6^e5&$spQb@yq&3!0ez!|a2Ko=1Za2cX z&hcv}do^(Gz+`&Xt$A$!cvXH#b^7lb%hleIkT)yJ?XXMlw;QMj^h_aS_ zIkaEnkZhMRcY&=IFhjc8GV4RIx#_L~Ps7^6Vhp3P`zAP~aeQsSe7^w|TBa2ALOj%? zR4+%zXX&R3cgW%TrI3TMsa)%8*Z3&IaV(>js96;om}tG5dCyVp+^(t4M|Z$jP|oI6 z_~@=6@KhsFB;@cw(#h;TiRiDiknXy!X{&bH<&JOuE4MS|nHEpJ+5Nb3isAvdP}zS#mZa%XBPolA_O zt3(nafBn{y$YFZU8g$3Kpw-zdHL3geED*w0qFAi_{)&a{bq~NitPQA1StzF?!cA~M zP#=vWNO~v910-C&_aQ`G7B@7E97BRUw-WxVWCCTBY9S8=sUumaJjF}1*JRQYev-kK zZ`k{te%#7IP%{(|ehk`4bJB>(4}mlJ5hvStA9BUEs*x4Ic@eh1Xn8xJ@T+S_3h6u% z_4p&zNzI-{|a8S>>n1ija?xSXu2cqY5m{lfZXh z+rwbgc!PY4m%n^Eyoh;f-hC_KO^*}#?Z8`<3O{Y8g?3&0_A8gzos5F)f7|={{c2~0 zUXfv&SZv{s@88>wm)%1G4*=)%t`YJ{?4Z9b(na0k)7sg4?hps!6lE?9ri=%=T16vw zrG&cgEm%O)U>u_1XsIx0jR+jv5~5LdZK8fkR+#x3p`VOgR+Z;-?RO>p=O{$rxxk;% z%Jci%D-q2{`-P5Gxn&R(1j9c_{{a8;7e7?#(pSmM?M&G!=R2;H$`kSNU-F+$0YC)Fo#*AK`;b|^5J-J5Y0`C_-c&$8C}e&uN(8Ah zTdQ2vIFN``%FrTGxcm;Q5^wzpKJZ`Js8~KP_*DBAaBK3ceMao1pQWVCUVCv}l~Kd> zxpm{z$^i4I5zMP1Lb;X2+2d1Td*%-PMjU}&ftaDYA>jYMNb&Ku#Q1jUnE2v`8Pp?T zFEb~GWCQV%ycqQyK1K~Xm=lj!!l}M>1Kl=*Eff$a6Z$`_nof7O0lf%haa<$A4r*v8 z^Yg)jpP|dgrBB4L0Ynf=B_cC{Ke_97Z9eiKoHn>cUumS1f71#x*`8u8!8vc<9wm%i zVb!qR#DEk->aSE!lgY=^uL?kF6o8+{rx~XW!!p`BEf5A zZ~oWYG53hx`8&_ndb4G{1{c6#&lKtkT$9_tFXB|D^66>0>=a_tOM>vwqUC>3Ya*Bm zcvUPeF7`M2uC@}(MC!|&p6aDA%>CC?zY;2i)~9H7%nObtUkO1j`7WuOUSe!_Wk~GI zc@Q<3Gj8MI^(#&_6i3sp+r`U>Ql`xIA=HGAoq}8#BXM$W@>cr>rj3=^PHOapg^L&! z+%Ctfc4Cq?;%cqo>2Ta~e6-3h8$) z=Tafc&F5__g^JD4b$8NUkyvFP{wgj?kSi}qA3;$moAfl!%?ave@3KOWJt~g}H@{Oq z!Co!O<`C`a;YD}5iX%Wio`kjH^Jw0>4XK}frfTM7s)>qG9#hFXPgKIZiai^gM=x{T z=U-_$5cRHlu`b_3URHEe$Y1jP>)FAhobamEYXSx_(~ zv6{5_?cnKXXyLQ#?C;zjr0GJf_KqsiXa^_BYKqBWDp8)wyFt4OIj${@oAie>nVwj` zR<985K9)T#fVdIU!&+L_+Nj+z)$OfIQ$CW6U#eVrA={2&tsnP&{E1M?Kuibdle>|oz>VArEMjKc9oRP*ogDk zFJe(*8OZx>5;sKt9CjuUj&Zvk4O9}Lo~ZPLgD6cUe(jP5OA|LYx4GlJURr|J-tV*{ zI_0Nw(>sqDf~V6^NN9vW{xT27fB*@=iXMMolqQItKmRmHk$c8}$0+LJcjiCPB>-kL z5q>jebpYA1{B3kqL&}66Hj43y@p0GSWu8CgNtA}6?L}IRPmC1;83WGj_^>0h{#*;* z?D>qPx!ZTP+D1-3v6F4-?O*r-BWKmB}Ee`|gmqhrhCJ35N3yeeXuojTGqw^ci|e;&I9E$lX038c<1E z7@p!XHaN0Q<6TPq=1ToAIlooWP{hR zk$P*^`TN54*sG>6jaJIOs`_QofaziJmzu{quJ$4CvUQf)E)!%rekMpSQr|s^hfE|g z_ggn@L@5_li)4!%8V?o^|Ew75>tD%)Fh=Lx^8UDH#nSn}ywwlQJ9n%at}$-@yB&4- zCt4dpURjapmM`A&9v26_(@$bcX9(~EL zAEPvASO>hFHP82K0UR6u=7zPJV<5R-P(pKMi1MX(`ESR!mzV?ZE7kv3mFxIyS6f`U znO*r?^Kb>~=bHr|2+Pv?h%bBfqGQ|Itur;xHO%HEC+T9D(zi4dRFsg5T#iXmzfjQH zbn9zy4efS#x((`AHLwxj!WZqPWxFW01JyQ2!N#7H4Q>wE`!-bD<+*A2z=$ zxsJc{R0BjJMumr_z0W@jgIo`?KV_gS!`U%*Fxg=dxKmJHN`CSoiE!stEsWW+&q=lJ zX4$d3n%fVw3$U|2q)#i@ZzV_iHhONE#nF0<>!#-O8xoe z=^x8M0?%|L6ciNvj*n;wj`QDIjzvOe0Ek6T6-DEiXV|&=R(DkdW0V1N9Qv+E$}2iL zIiY|!m^k$aUbZIo1O`~;KFcMp$GH|ijoBgvLJ@hF}$7-1^2)C)Bm_s=^ zWR|a5XgwUF_7i$>Jr=;leA}x^)vFnfgc%Klw`EEn>fMnShxJz_5;?*aR1vbTbm(?| z@#JXBB&|qNWhJ6o&uygk_b^DD1WrDT@ZcmjHzNPnlI&t;f#5(2y+&A_>O4cPs51Oa z)PV7XA)~Tnok?U$+(}mai_NFC$C4aw8eGbA`fgr&c)wyBZUI)24s`J~ZJ05^sNCi# zdh2JMKLaQhsFb-s7~j8Ga7O=3w8Y=i0cV&=8$!0%Z`@+l@vwXF4#2j4`{(}hS3DK3 z8Be7+_x{LeXhfR1gad^%1c${*;XJbk@ar-Wt@#&WnM6UEC)Q$B?-q2$_&yWHO6aFF z;uP_w+@T^DgqQ6F5f9haFLM`FzB%z(oxVv3f`OLPWBzU@*7wvZI2x(=D#^x;ISXb` zKRH{ZX=q@_2G^FRyRbPJ#pSn#I@~PCD};kl=&jfnBNeLiltIpJ$vV%|LA-;9-H4m$ z3KOGOeFafRuL}%>UJ1pHbSt?o1zpm;j7FA@&}^u8W7W2l&6$#C82B_Qm*044HDM{Y+Y|PU?i_0YWmD;%4yMkYyh_S^r=nq9({7~9R zz|5{>{r7!QEDBq^d2fMmPif_S1sg3iPygF_71oJIiM!zEQ;d2^9B4y8qX*Sdz@#5O zo@LU1K8FM`F#l`JmsFiJ=xDrG8uh{b;erlAwxo;Zf?V|Qff2^Tz-Ly-yyf6zfei(th8nhxRIASR za(9gVH|ZgbrA+Gg1B>l;r%`NMT%&bM*X2%Vq|R)=Bc>6iZY_dHL+;10SQlrJ5QfqC zT|8}E`9W=oaUZ-tWFSsz5(Z-*(D_rVJFj8uq0~-Z;tVnpEDfnmU%?==E+m@U`G~7Y zfl;2ZmcnMA+TvT+vF%|PELhF%9K>J0ojF8>X7)2PmZq(JgVr$gARPK<%LJp(=w+)r zUM5#<#nuV5_*HX~XBO7YbVOt8B*&~*hx}2U!OTM_uOzgeQD<*;)d-`P?u6npmU z*`(7+j|PY8<@QS|*GMEIkWfQ<@mlg8qsOEP_)}boV{|lR_Q)jUy8rDZdOemw#J1Kj z;YJaj-)BzN&o5f}x4d~M?d$Ur;EzO6vv|yXWqYvo`-YRddnXQuT>H%El!+?6K_SJU zS|am#%L&z?m$Goo@#KWl&$W(!^Xf*8yw3*=7)gaY3!26gA=_={az$|DTw`WU*)2MC zm9Nj6@Gwqnrx9XP%|SO&?RKezJu>dpX^Y@*$@Gc(fP{VQV> z2F!QEGSpzna7ME6t@+98nWZ+U^YLEF`Su>}n5zY7mgc}6Y4yVd52n4OQ(r$RSDx%7 z5Q4qEy{BoX2&l@bH^2VNP#D`k?K;E^ww{3no&u zCmI3qSD(-H*N&2R!Bp76c*pTanR|!sX~gEPYbXiMh*2QmgO#HYVv^rnXyFwv{R_V?y$8q9~ z5kz<_II5#*WS#oa{lD+UK>ikD15fgYUt;xcMv_$PaoYR+^u5OEdkxUpoDtb5eEIcZZ0tS1?l05l&qs{m$-1i>cHDie95Bs&kB(S- zH7`jg*_%D%k6~kDQwaQNNvBj?}-Mp2?us50y5$ z9QhpNP^{IG)U}Uru%#9XQ4yi4HkqlSuKSd@4GF-#4)_uaL9^Q`05 zj#KzKj)@x7lzk`G)mspN65Bhg^=TI4hHL`BskB^&2d@+d~^+yql3&<79r#RD0-! z17&pQ^S5u`o#{%1KB^^!i<=*-WyGHQr9o z@UdlPO-)TFvs5PJSFL?FJGcLA(g9IZc`(^`ijh?4! zIpJ31<}r3#n6K*}L*K=oMoZD85@#v;7}jqB$1pc(0uhbwnlMyh#- zIygPU%iP|jb}R0+Ij1~WAQXG9`$CZr74+i+0s{I52k*c6@Zo3|)IW(_viFLalqrzq zt$?#;sn1P?Q?D5h>h(yE?MhCzrqMcFFKyH_jlsEadF)>jqd>2E%SD+AYd#-v)|hhK z2D3>Te1VA_Q2?FVLki7RFkp?lNwY?Wxe1n~P=-XV`VHh~0`}~=5EXoTxN3b96Y-Oi zlil;ZdB=Yri#5wMha3}D5e!lWbt9dIVa%u7FTCYknF7?-mj7)(4$mVmF+S6%QN2$Q zm!^Wr_{f)8BKxqtbpsmvs=UgJWi+Ax^6u_xw=3LPX{cy!4z`CV#&F1YD^)yJ-uNg< zbBk5;KWB=$J5xh_mbC1fg&cK-LMeq}?WZU(ePd%B%L@y#!z2DHS++$ot+xg z%3b(>|1olNR;74eRRG$%1lWUUXlT^Q$;d=~L7epF?b}?1ebUK};Y?u;Py-2C`!a&H|)x%>=$JckKAt52*+tV}pR!h)9 zCC5eyHpO+~3*bpi{#=}*^PK-_pM~rGFQ<^F2K=EMw3ql;pJIh#Z_6g&;ghmP-%9?> zpQFVem!OBjqEbYynt=OeWaN7D=1te%*wb6bfGHlMPDT=j8-!)I0?J5lxTpju z?eASsQ3SF=Ekv5u!8+^|n<-^js}OH> zI8U`OD7LY3?D6b^5&e7dZ|D?2MkbP@6cVgqZ2YXHu&@vyWP9Uo`WfB7H=$Ql&qE=a zfaetMUKj56fWJ%AUY(L^@p5s!yxfIhfl5H@?~nM zIVhZ-!oWfp!s(FN(uJ!~na$$FY1#o#w;|tMD@+oa${z!Y0v_{I7m|Y-;!c0h{?rAB z2@~%}G`;IIy&E)F2U55K&jiTmX$vyw2s2CHb@x)yRdec@in+}i4ZSZLGm}BDO(yMR zJMCmf)kkJg^;8WL-Ev>xY6%u;A@y87b~e~HzmHG03WZ_|oHHF*qld`uM-P>=^w*V_ zGxB00v0ut?MM63%Z69leKY3=)gkIm0B|q2C+kFAvmxhc{K%Fi#x4*v8{mrdD_^r|M zW7QC9L;g@D#ST8q`Lm|~kvA^S9mW0ReXf7w+ZQqMhfKy#NtEB)sC;ZM`jw{uj44Z|xDNP}I+z*_os^ue` zMaZ4SW1j)a%GJFGZv(}CNogb@B~oP)ii!2Eg;V2t)YZYcP8M`V7!7o~tk7n?DAj#B k4Ms!t|NqO!i>=2b-4^a&8-J5rLg0tChMxK>Rh#ht0a+)n-v9sr diff --git a/UI/xdg-data/icons/obs-logo-512.png b/UI/xdg-data/icons/obs-logo-512.png deleted file mode 100644 index a536f57a19e4aaebaf144b731832daa645b53d00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 130086 zcmd3N1zS~J)a~KWodN>V64E7|(nvQ*Hz?iRB@K$wp>%h5H%LgAlG5FG@!s#dzv1HM z5e~4=o-5`YbBwVfloX`UQHW3=5D2=Aw73cc0tH_}AxH?|zjOELTL=V(*h)>ySxf#s zpNZWkW+PKOV>4#=Pxj#F5Qu=VySADV1vL{-8xvkrN?{=s0e3!dgHLA8M&$0FKH56*xeHSMcV9m6_4CUtl;r>Y zinEO%rIx%B`8zvDGjc9wE@oCrArx`}M^ke?6>-V``D5^%Af=_VvppXRi<_Gpvl|Dq zoudT{8!s;}3oAPdJ3ABj4JIcKTW2G8CR-<}=Ue>Wdx)DknK)Y6J6qY=l0V^IjKmAk;_QDd&|cA z_AL`TGaCd#7w;0^EC(x!71l1^do3o4iu##OIaS=--_itolCD6)F|eGbu_c(A)Qv#= zBfPGtXw8ZGjzLzbArn$0Ww@b5sb%hZ8b<&eTlL6Tef#oDSBuWVAJa9;^QgN~g@~}e zS4;PS&A0FbTs0H&URqo=yAus!+IKDeM*X^(dd2sRex=Mr9BTFr!BT4S+#$5V`5N-0 zTd&sfxn9PgWFmXHG-e&Amr9-9TRTyXufE-vwZ}`7?>ZDmmXg(y-?K9E;LUJ;Q;dIT z(*8r}>vwdSe^_?i76gZ#YV%%idJ?5%CREOB-*z z;9{6D!P(5|H~pbiefLyfF6o^mY|y%bsXqSoVg-3-F>bM{q?~Ga=+(Pk+z;lFHw~GrzdAo(>Sl$7MdR)+r3-YFP>N#^A7#ODN&fb)DrIz+#yW~EqR$kUtIV96h_j5A zEn+SZiVgo4tI;mhgpe@Q31h3RskvxnmCVIi-39y`_b)g)Guk=y9B*3L_G&#sW&Qu^ z!y8*v=l}ye(skgfTV3%HPix1`OF}}S*Q_x90qax#znU%G(mFCMBfp z@laqOaH9U?NO^rZlXRxqp^U@f;*=1GOal2ZJKayUJLX7AY8f#`{MC{CN1$MEt6bbU2|Gn)BB9sY!(%uL8`*v z4Ldp}q79)C*mrxGl9C8Aii%R;xprrY3FsLasRcdHpIWCq|;^p#I9x;j}VCMFdVlUy}TOU}riC0HgS1xyw?vtIJl&&uE7it-PZgh?P zfRlzrI|Kv-%;&IzxZNqdR5O(5#H*^RoD+@~>sg)y0>hEYZI@?eX2!l@YD;~?33&;g zPr&nhXCg~9sB5z?dVjLsZZ(h;L_RP&IyycuFrXkOH;e%XhltB0P*yQY&5Var%U~-f@V!?4_@Y;< zm`;|dQmdq}y1Kftwzjr18+_E&U3!DZIRN9k&%nUIfAw$v7jo7s5n?=}0K!Kei0RZ1 znWf*ACdfbW@)a}lXcaSlfYGbT%?+xnsj>VTWcn4|oZisz7?}pb%KE!I`dEH}nF8+n z_wUpk9JpXrEv?@NgQZmCCHkoZg(p&P4VD z3JOZ3qmvWryKJR$B|0@cln|6(Q&Xq#q60L+KTZV38J2u@#S*BMWF@q}@|#p@sZ!$< z?*IE&5-m=-y|B1A&h_@~Y#P5?SyM^Lzn;CB;_*)l=Q62`a!e4N><`2W4^*vQS6k-e z8Kdw}7!rQhs0U8v#ZyUfY9K9&D`})LEQI$naq@?=iFE1}S z4h{}ziHV67GXy-U9Pcg-hrxvqwiQTAUc*7wc`)YZ=byJX8mYiHVm@cO!jh67N3B;G zlBWqN!o?Qj8IAQTo;Kw#In1L7Sq(cX($dl>z!V0tb8yJPi4G9J1-R|TO75lD8T@tJ zFpp?Z!=}}gkj;h^h$`8?a1~dMlAw%FnVp@*1&{t>dpuLv^ZIzbn?bWo;6Azi{wIbA zEM9#eGoJUuzQHya5r>@#ikqGY+?vM5??o{2|2=Pqg_($v;m#WYY#|CbfHkOvg)`B} zgwpoG&e3l7ZS@9!L$z+P_4n^z7X6ln@$=nZ{a=%klFThElEEjYoTB0pE)tv=hEEp6 zRNje}yeF&7;PfbGW+~2^^qs~`fk-qouryAbUV;(d=97&L|K!w^I_qs z=Y8M>64c=6=t6bL;<13Dqod=1KMd@ES+ZVVx`0Q5?{Oz$imM{bpJnf}(N??m zhs)tGOfsQ1saVqF?LU8_WaZ@cjc|kWzaP9fg9%YCb`#;Qs0`y%weHPFIjVnT&S)gP z%UVGk(@Ce6sj03$dv9X0xw6pIw9N1J?{}T)U;?aey=^JNM9$as_S>2A(*O}jv+F?u z2!@uMo4fc_vMWih!P~;{`h5^6P??B4%tg7r2_SEto}LbTV>4;k*f2gFVkjR-U_bc) zjdSRIp?QAWiRo)U-{7!W26AdzTxu#mA4ubKc}#=sU&@9{yeN7C6w7PX;NqDtB2<)6 zikh)_oG2y0_G%0b4PD+IOJBSkq|N+zb71GE>w0^>YYj<<1ZTesJ5|kBcny+aO-_!O zEf~g1t&u^#j$oM3arAGHj$`fMUkOb#Q;Ld;w1QrjfBIs{#v^jV{|+bH1S>-D1WpI2 zy35-iZ|Qq`dvQ^2Z@)hcJtD z%ValVn$E}?I=+%l5MNweWB}Wzz4p}o?^b*JDLW73BN6}MA+PXGJ_l?gnnOTDJ$(-NM`1gPlpS%oVMrn1ycF%HlOC{>1lBZWlj{>_gIG4BLQHDk|~$o-@`$$;R62r z{Tq*rOLH7ZZa80MmXeoQX}{hj`k8R~PFB?rO^m`MX7F6-%~z`#J6Y$dvJrQ{N8Z-UX1a);tmSmv|ilDD2k zq%N7?qw2}Mrg&ZIAiX_}pE7S$({$=jP(%OoJykGdD-TXVmIASZY))Dk(|Q%fn|k@?THr&VOoS zW2H{HtUZE*Aj3g_*sZoLfh2yD&hIuwB^A>Hu?KfLIGa{glz+u-yOfrbld}sF-Goxx zoy=;Nl3Mzig~A!`JwB~hx${oPwylJ~1!erw3uGGKx0f(}mj|>d2uVN0l{duV+vtPCDI^;8j^Z-2S?I-hNOc(ackWJ|M0Ed>$ z`MgW5I!EirVTPcBntCoT04y}&JlPgL>A~H!Ygi9ux!RwzQ_#?ug}f7_Pw;g#wx9Md zwV3)8g|9jDf>}OJ=w5=6r{J_tl@*!uvbKTrhxxle-r^uO|DygTlWfXG4A)$i-Zy9i zRZ)F?ea{77_i?YdyzLGI{Y7;Cg0f zX6B3+C?ThtGs-e%ugaTuUPgA!CE6XAb-y<FSS9Ve+&nUAIr zLTtB2zQ6Oktj-*L#q0RzX@4@e-#tjO^!xR)>&&n9Ia%A=@7KLfR5Yp$1QOFl2ejWF z4rBxa;^;wN@$*fg_4|*19Er+*T?>bQUH+JpWi4vF8!2|ahuNGcw^NlE)cMw?%PQZI zMUeV>ARaH}wJd=w0Hrl~dFIPw>HJS^Agy5X@$o6ZuGu#2(yW6-dRnH}{Kt~hdYiQ3 z&eU+n^?Cn`bqJpe5uN{WH>R-n%v{@LZFzE15+f_Zxw7o* zRT^JSFIUYtIyU!ePKK9OX?CMDc5<($H)NIO3f&6R-%LIs(?F{;%EC5Z21i}L#9qFO zQwmU)bl^2gudl_O{s31Y=&*^r6aBj%hldaIgJGs>z4LD!+BEnVWTS&cgn6h%pzV+v zj!kMmF;IM*+m}mb7DN)cpGt1SdoL%KOU!MX1duitg~;3KK1L`S0Um%CI|>ar!Vnb| zl`i=-o_N29i#ano)Dk>Ky(Z_r8mnKnETHbZM1rgNazpJ>ivI*ca2B0YJy>kgK(o+c zkDg8HLuSjS<&3w#aJJ5d{QP8HpqTeYQK=Lzpou%akt&IXT{@>Ei-cx&v7jb@DY9YK zZfWq_4!bC2Ar3J^z?9G3U6yLaAk;9OaQ8q+=O}h?bX_TgEKDo`imVY36B84fNUPXZ z{c*l(?y&W0WfY==h)GI9!s|%=3@m?7Pn$ZUYc0e1AG|U#ltzD!2ZQ?cBgMPW<75Nh z#;w)l$jC_9ugrS4Uxt~R|6PJS%y2%HB)hZaFO+{WA5&G>xpZKeQdc@TYFj#$+;^_? z7M6aWNJ!n$=8q!%%+on%ZcmoqoU?~XLI?vVX&zkCw-8kMEa`ATYPcR|W>J1j!ZTc~ z92D3+BDvt65cu?S`K}=KHKj$)jIedT4fYpoC%&E(*22(iJyd;o>^H@V_?VDNt@lNR zg*W!yq3A5jp8KUouskeCV)mVZ@TQ=NRFRjDp5*F)g9$~2g1qAlFB9SSyTa&xXEOI1 zAboxWe|@)Qmu}y))Kx|jA@A!0&>z6gu(wg&H1mDr|64gnIJ3%k!dp|~)bJ&|R$O9H zk=#v5Nt^1X;j3L)cbX!eL{AT)sZ1!fOn9&H^VOGwmCIJ+=1O;COi8UQ_C889NnGjs z8x2?@0%}euO|U-|36fODnSlE`0|a4;nOL7U+L z6Ua!)!HA<#?~q`4pbGIgc%32y_(|st0!HjqB8fwc>o|~)fozGEHx=XP-SVys(=U{i z-gIO2tLW-Be_E{j#4?u3eWVAm$Y$gp`LxuaTvS$O9ti1%hXqMOMO0;jg^B4eK)0jq zH(RNckR{(c;pnFA46>)QIEr^|8S(MTKfbaZ)+FN%%B(8f)sbrW?#QI5*y>hp(9V8d zq)YOAo#I9AtHk?pVvYW1Ru$SyA{gSmY`iMQcL`?Vx!B=s*Z_TlZc<84jzLE!WpZ*d zVs;jvlbbuYun?JPlquX<5-S2+;uDjT*-tSNCgi~Y)r2R&$>31*i3b_3TRtJebU2Xk z#Ha!)2pb6@4*&f-Rwne_U5enTchabN6iiI$>-K~INem2)iMhJI?OJCAm!qRoJT?ZE zpx~=cL#$2_h!}a64suB#^nHJ+$9c;FTX(da(3#k44?Xva>W2zXH*8ef8;VYMSY17Za+cIr;Yc30TkmBq8~6=;D%+fXjb}`sRY`$y zy?K`!%Yc$MgYCt2CI3wUR~rEgA_OGGF96+m8BOL$l!Fvqg5z(0Hp=Zp{Ptswu$%q(8Z|Y3?$gbu%0;X|l6pDFrx@y*+DXgH?Yg zi}B||Vrgk9;^QMov`0L#vJzv-8EMIhB7>(c8bn9qqTVz)h-vToZs#pVfyJbtx5x)y z`qHjH8WlQ1klrFJBm^up1Z0RxN>$O!L52Qx`nEUC})do!CGYAi^T~wE>kQAk**V63OmE25UU? zce~#cs@sx7zYS>A`(co{mz{C1*2BnT0WHW*5v)=q5x_3$-zf~%RUBGfk7Ckf*!jQt z#8h^($m7dJJ2}W7P~u~ro6;wj-VDsO=jHN4UNeQCo>~sOb?XPd;X&qD%iNP*2@JWMS&B8Cw6c5-JJ7tXR}@*+1Ij2 z+izp6kNFj7hL3idL)a+Fn2J#ZI8_Dd$f*=A6By}#WPLv|Lw)Wiz3{j9$8vLXapR?FO=V_-_HiCo`>=I5%2Q)qUrOSJzlySgiy$Z`W?D5z zU&c95+zkA}I1GV-20+AiDF9uZ>vpuV1L#@=45ILa1f283WnvExkAaZ^e=M~nMA=%| zxNI7{9S+j>s^B=A@>UHVpIgot5?&l8IyySQj{RLF6!aX~ z7#SJShn$Ca-AAR<{Kj(0soPR&j@~ja9oylqPbo2zrI1S~+U*~`NubVd&4rCdN9S?c zLQ_cN35d*n@qEXz4BpSPvl`B>&SD8a8a#9!qOJmn`2MK+)#IF7z_i)6<&_e*bsMrx z7{3w0M$4lhQIHZ9g}FUH2!eq{Fg%Xy+yQJA3My*+_wNygtXXqnJiS#pOPK0dLA`v2 z92arl+P)dpQabatbi4wwx3#tX+l&={%!Eh9Z$Zv-0D!s^^wJ(zhsy~mEc1JTXlM|y zPq4ziT-Pc>XYBlL{wAM2Q>uVs%7vV2l!}7QEf4vUHbDyyfhr- zeXyNCgnLqZzrz(YuH`}n8Zp;D%4%4etGk}9lG`hiYuPK71+P1_22Tp=&yC>eWcd#= z1ay*Mqo3UqE z+S>Xpi$UA{6==Ej(a9YF#7A&FT47Sx&=^9nfjFVE@;q}I{>_b<`#@fBd*%@UG3AOT zvU9VY*=_{5RyP8CeErg|CogC0{9{j)qL*bkRf~0mqjsgKeGcjwhfjoMLh(CUWGQuY zbnFjDv`K{ZBS4Ruweg!Rjn4yxX>>H)xes}@WVxTqOVKoQ<2ZFB!LKlD5#dEx+TpN>t9|XrcLT)^}NOE ze)omWnlCbK+2i#H7qL0IlB}}2dhPvgex~onq!igQ3WR&bo$BfF?&$NUPoH$M$aVF< zb>hE=TpM=}E{!Y8%CHjtF&|5#FVU(LLm*!TEf#M3S^Ckv)YIbwL4ld+4BqO+U(bVS z)neXN_Phmnr`OiEDRl+jLsdWG+L|g9sBuI=^QMr_7t$Suu{n|buAqP?v}r-dxrX8M z?r$7l_Npu18ei)_WyOvDrJGM(C*KS(1E7nKma4A~;Gd-Z*Lnqx64H(ai3EB0>e02BghBugzLCi4hzK^hcV@9vLD^eU2xYvql!wR7@ao;l z)#Eh?_SNC9d_}{7I0~(L+jJy<9Y$!yjp;QM%&NhAYOX6q&?OK^#4lEwNI7=^GIGWD z?%*hjkj>ZuOc!4ki}8k)rqF)D!3e#1W|5VyHq}e*CFoxJHSGrWmZZEvY8^p@xc%e3 zhZ<>l&m-&|pp%W%)H6g(-cbR)DGf<2u}#IzTfyICPLR^9D&O`s3(O*f&3iXzEv}|O z4xu=t0^J1>ventg@*#|$)~&F%f7Ud}YL%z}Ozr0iK?qPWJCjP|^}x~_fL5FV&J0p5FR zRRwf8ZI1$=V~=U|x-@=jdngdlkJ#EWH?z)Ynl4po?MyJ_M(`xTPqg6B;_!~DRiO4M zjGEQ+qx%@h(=?Tl>$UQadBWUBNjCl~saUzb1Xw3>jgB%}f4ACjzlqVv zs;tCLpbOY`w{J0DP|W$eNJ105Q~ZeS@_iv#?+Rz{SUAIVAL--km%pmq--OpnNFf}s z-`_np%aB(HmMpz1X2lvq%57*MG+`kwMuP1Q%asy^3GB?GM8d)l7!TNV@6LHQRGn)1 zVuA#ESyRS@^yQo3PV=~~QOz_E`etlb?gvfg41~`lAfu@Gj7YW%H8Lf|#U|D8ZEIlB zZWFuqlq23C2X>66^J{9gc{jq5H#qN35pR!tKNHc{Us1|J-SXwXtP`?sA{W^VdR;qg zxAIC+mrj!HM9$)4qVoTG0gA9hup^vvnwp3ZF-SUM4)~Q^v(SL78ZKcHnKyn?WGFgA z$b`7yMtI7(K_Gz*v;c7BP>X*3(Sss@ma0djT}#ekZ5k?PR6?WomAK^vRUYQ*MMD#c znvqCe95r?@mZ_PA(ZpJ3!^V(91CRyS4A(?}$Q!Vo)XT)HUrFr{DiL-*H){QtyM@GD zQ#65#89m}Kd5+PRco_<0uH_0KNJx=-?qpv;t^thr>U};bMG48(_1A`N6L}s9f-;?_ z=RQY?t_SpzOr8Ma$9jNYIwBKrKh~VNU$#5cG}?brJE`QN-c#|hgnBqmLMGx*vRQal z@K&C=nwpy7{%q;*Y8&pNA_br;qLZ-Qp3|Gs$_qrCmRY$0eeAjGP*$b@a=oevuDJ5K zciZEdeE9Xh&i3)e)T4G(d?@X(f8Hdl=X+$9;~gNaOGXhz6qjd&53@9*g-A#=srzs9 z2-+(dd>nAirbwj=_+bk(7fhX1r4#d6GItValL9_KBoTmL`v(a3Kz_GFyzvad;Sv}F z5wO6|!hYyHf!CqKUzu=Wew6Ff?hymkqg9xSDwh^7I>2)|V-EKp;p&gkp3xjNj(%d- zv|pq5MSbNGCz_g?CPyo+zpKm`Fi7d%>FK>Hct1LMx2GoDFVyIwnT`xP5e)6GY@X+I zKqLk1FLY+R=t4lKsBZ9Eip`qJc|D_Z=OgL2+Nc{6vik>?jruI?+R`rLpWP3X+`jN3;=%WYsy}+ut94e7tIZ+E@gv z^&5yYp!k-6#KA&IO3(qKggi$HuUYB`wyU?fIZeCE{_H@G#OFRiAkbg}+4cybV`b?` z07?7)vhPFL!spr221T`}(v!A5gXBUqnFI=%9HE`M+S<+Q6O-RfE(~>ci!q!1Zzfk} z%`vv*jG8hou*S~->0xPQ)j1GPHg*n_NeWy42k>!@LYGZfKK!!!U#LdLPHQ4D!%5$l@2spwGX!Zf%#2iC-=?C=bQp0 zn4I9gK1o1^!KJ?a)HQa%?{GPy-Ag@uLQWFVj8d|xl3+BHGU7l&_Y-svlCk&RjJJK1 z=evuDrJ@9r@>J5CjPuE<{(#{Sr=ZSntw(7I`S^H@7VpK^a8w2*v-%bf*} z87Sr^W43K-avhHSjopR)dA?gCJ*k+OkVLTGL_0uJ%tQ&`n%-@lDu*er*;!iBJ@bfu zuG{HuWe|UQ)x5Z9V(!QytPXP&?-)>-#)UrAK7xkB7od#$XFv?l)vs@~nfK*kw^v8P z1Qm0)t(}S8Q*n!iTZr(J5e63*HVo&3IO56|M{+oO);5)1SGFIQuGxkz$wVG*JpEOC z7_@Y8+Y!&&F6aX0mxP~&&m8)>k2*0| zOrE8SWUacBZ9N`_!${BQtMbaQCwUT!1wo%_sHLXAH&O)@mRsuyOpQZi&bQPwc@uZI!wpgx^e_9c~b*7u@Hv0Sb56^KRM1MJ5sUB{p5_)))@DMOuVSi4A?{eLRWN_3nD^XLeprjGW2x!JCTYopcB(n6V;u&uLOY)3=MUa)-Ol;HJ`02X=sFIF1ZN<{yi=+ zF?Sr*t(p1o_~?n-yq)#7y?eREpC(`$K8P6wp8oF9rvdrn66_FEcOoAL|Kczt0+-eh zh{?|b3FtTxB=?vga-`cgL6K<2zlnH_YDM6Yn~l60++?bDX8kQWiI%~}-%O;DzlHVR z@;KW@d;42A(os&K)w+7J{_dBc;@FOTUuHuL+ROdD6W~_#-en?oxAR`1<%mvW@6| zNc(S-imki+jKPI${G(4EhJ2bsS4Hco?BAT+V_%T3O`F|tidI<|DZX#8*G5e; zv!-;Xa6`#t8JjO(5*%g6nTh1#P%B65B~v4_v4vJ!j2ZsQQ>-}5y(pj)jxfi;> z(~ltAn{ETm{+~a8)^t~IX>6Mx-ogcn3S2fzo$N7`Qwj<$e>_#34bAv1Zwq4*@tHMA zKZyrIApcgI&F;I(%;|v>Px49p+*GJK7Tu1ve{p#u(VU*&E6Es=2z#E7IB*+x7zHop zM~EgitUi{K=$#ROS+BdSJ3D&VY(;#QsvqGY>8wW3I@@Jy0`h@i|8a7V%jW~W+`AWGy(EzI!uRVR^}Xq7`HHu7W_t9vE4|nrn zVG<^x;Rnbh(yrfX&zZRI+6RkKsAFNF{X*ou2;n~Gn13xd$Y|Ks)K_T&y4Ug8mmFJ0 zkXoQNqnCVWRB=Kc75049B_Gw7D2X+%?EU^aAaLSGgKi$h4+%3$Jjt>p(g)o8dOx2c-?Ra_p4da-Hn8@ z+SchXdrC?Q$l`BX+HYb1EjD8wd^%{8f4E%mHICd(CNiB_T8esnxZ7O0JM8>1mH!bfSq28jLcdky36qo}$2?T#(Y(BEGBf4uh50a2IXjiH8qO)lwBkfN{x9se z-Y`!CK~vJ41Qx{K_w|GQL6nWdvI_^Wgozxt7{g$}Q5X$dk1`oHCuDglr;q=xmlJ`K z*VNZ@mugjRwCUP5npye!VosGC7&L*+yoGZe1zP@OW)k_wU@TjJS_0fwJZL@V&63fz z!HAduOIs%T4o-93vL5$}(LU=t7pTVj2fQlk;zu8BwSyLfa-+HwaHZ&w_YHlWm^V40vFy+$6 z@@hq@`!E)k)svKYWw^YqfcxL)!)2!roJJkFFFK1aIwY}xkruD>L?9G{IFi@N!el6k z{`rpUNy~`v`qCfNm0B^7544zELc2YOg-GFYjRABxwCIZ_zC!Zv)2=o@0d+d&B_qto zm+Rw5-yrr@T`~8(vh>fkSYgj|%XOgRMhV^SWos{}L;DdXjDdS+H!P%c%!u#ImdfDHeTLGXVB3qRmt2rFBU8Pqr&OFyUdzZfO z-h

BJESPOC@uAmq_vlIK2uak>vw+rlioyN4<|OGjhO+BE21?I(iU?6&7?K4 zQk;Yllw9J^Eoc~*9#JTLyT9_emjL7HRt~1T10c?zVvW*BnS?)e_E@-3W;Rf_rUFyj zGl7?-L`h#?0o0j+BK6|OPXM*FBO?UC$QDW?VYhIb2=h>*Pv=GQh`{vDDb`AO<4qB~ zMxNoC`Q2bd^;o$_Rv@5(TvWCs1=G_HC#RX_$Ct96z5#L1kn-~42ilIqk>X)5uqIDV zPY2ucL;*%~{g7-Wn!{p@l8R(@v`l9I?er1c_=Z1XJ*3k6Xhr#KWABm3yZ*P?Q+vuV zjmcDsOc!=oiGJ-v84{GTs;ZjJY!7cFRTz^h6jO>74=NSj$s}^lzHkfMcBygtByFi9 zw7Rt9z4bF$|Mt22c=!VB5~x5t)q2ma9)}E*43N0}ZpX~3-$Csh0+?>9mm-HNtzKlL zfGnzqIM1(^x8G@O1$f|1;&)HhZ>e+dMf8dfR0V?)^lzyV_o(fR`SM^sY-~&cDKL)j zM0aF0CO1@61k41`o(S2Ukw`tSDV~`s^b7QXks53A`-2vS`m=2~^P0}IL zc%;b)-}ajiY@`>#eyZV10vz@O4ntTjv5qCBNcQYJ_a0+5tK}va%&|1yvt2`0I!xlX zQHQJTnZ5Ad2pw?#a-~x< z=wldsZTRxE)T9s{5pyug!tv^U-d)0 zi+08YAxH@+B~7DGtMJ7lLr~ZVzey;(giK(E6Cs;5bz6%e3@}ax13eHcMnowez0U#_ z%cY#s*tfc{^07=eh*1^ZfCS9kID`{Ws$Ui>jDqw&K)?d001NzASbBS=l5C?0dB-zw zd5(Di7E^%r0Ob1+0U#AK$r|9AyXs zsOlJg>Gm(ad9t0q)+@95_Isa0`zJlf51}~fGk%YfCzaDDufgqkYpf^ZC`>R;JvA)y zy@BP7B34%PfdS+^8J}6Ge=t-2NMbYC4E`ceddx(iRD^(UpRbUv11P?dBT@nP3S%(Q z0qVsXy;*VXmliJY-V3pZ1VjYpMT9|`zvG3a=>K|N{-ck2#jo*f?Yll5W?^aBFKgBx z85(*sJz`teowqEve@@BIxGHNndut{*peW9XA3_&ld1TptsMmZl^_j)!Z{(MP;z9tB zCtP>rwvH6CZty~26+Qt<65Y-F3}fjk(ThU$<)kGX5VI4g*DZt}UV@4d_;FvYT`lk0 zg$rXXPz$*UNi{1V{fx(jX>t$d7n8kj`Oz+xL=rpILbp~W2El;rS)Tb z|Nbr20v-(aY-?1mxS$}8{-;h{POWQRux}(Fi}DerPRaP3ErGNb!ob3^JzJuEMuv*- zeKTmtN~kvaDI@3sr_UK>2fHJ5$LO7L+P&G!s=fW#2sQw}#g%&idwk+&_e7@ebE8i3 zh93{}wA%WC{@Y3GpGm|w3MbKkh4ntVTkUT5+g{w++3}~AS>wR~M>U>qQdf<^_}x+p z%fbtBJ*dq=8{$muS}Jv(N;35>o`uS-(ag_{XIt47 z6%#nr(r9uh#5I1yOqDCyYX5|FK8S!bG&XZ3jeeJDyyL*{j~6d34!XgF4}q}X`PV3c zN11@Y^-sCftztSKSA|hG!b_2L(8oPmtaSu9!3^Lf@$_GGxl+4DURC>K7g9p$0({x2 zWwZwskkH2OyGl zL~}0}u&`3g;DB0p(urtezuJ05L5Nf&TP2q&qVnXWSpq9Vzm$Kpy3p6FaUU(s`YK;X zu1MUrsJo5!Py^7yK0vh!!YF>EL}ErbN^)8oZJC0Gd@?vH-QN(eXZ zwATc)XB(7)OI|ob<14>O`p%9rK-p-%&gYJd=7Y;jHzX%lGCBwFx%R0P9l`#ek!T5qcB>ahmewD|SqXy~hY$6=!o*piQTgmbL;N$z?1TH5{Y?bp0*_g~;a#MAIN zG22+tk|(W4-k5inhwseI^L%>R4W``a6PIj71hkP`x}_J!9-4N@m6l$lEOu=dOz8y% z@MF^^d}sJe%Q2I>r;sqW9p!Ht=D zgdbD0%}x3}4Hca#hAG5bU43T3s5^A|MTGcR!7sSzuaLL$$EpxiXv;BcLc8Sc0fN(O#@hcCj4!$?$R*mpGE+phO>0dk+$n$c4VR}z(lWe~PW z4$Ys+bP>F9TU@EXE3Kgk_eS7;`Q2_O3;?A=rcjgmNKLZ8G7`ivUxP=1 z_`R!egwU)EFvSJ|iCW+}^CEqU^yVfEJdqEeAQb{@GuYT< zYa!bP9T|dNcsHWybC`&yiqw}qxsS^*q1EbCDg0E}&!zCBw%i9DAfGk&*Q6>5)PGF3 z4dtFTcOy{Vky-t@D@)V^rP%5$v*YH!rn}U)ja@tof+LYwMYo0sbMiI&3J83rVOgA{Y#ke*>AY9q>V- zj|(>C=%91sy97e$;~5|-A3xCHq1@=r&WT^&J}so$jOBk?f$gb3cp;%Yd20e>c^4GF zhyB&#+f{ujy-!SV`r*kJ)~9Z$b!X!D5!BK=7=Ww02FN?iPyc)zaM81a24{DgYHDkp znGYW_p*x=kT6GXUoYJxcH5k!&9NGr=nu*Lrr=$5$+`gLadLLbeYH@Z09~Nd6;#+g6 zHvEBAekD(yJLf2Y%YU0f24@J9taH6vDlRiKEGI{Qji>i#eko@GrdH2qxsIixNvt(B z@n|=k?sfZdKq-zL0%TALkipRN<_E{vqQ(4cU1;Dwkb}g~)e(Q+y0>*B#$wA?XO=PxD7=`;OAnq(?R;?E9`5vp)PzT!WwKLQ{cb*o+5Z{94*vBEeW6{> z%h&ss`Nx5Lb34S;t{|Dn?kXRm_f@I=oBZJNe7IMuZz*hy^U-|5TIEWaFUJGLgyeoYNUY+7Rz1&B>c$m(9l9V z5YbKnD~0eJxFQ<>#n5k%i3e$NKNmi<g~GgON+G%t^)rQVZE-+NsmiHsR>{3q7VHec`9`x@U8 zsKEHMbJE~uNYj4Y=PD~V_njca-cXR% z0%FBqo1JAyKcCjM7|ZBfeZ1vvIk}os_G{ChZyYz{5;oBBQ*ml<25O%NCpVV~IEHyW zQ%s;Y){oACgAy^o%U!>u&|HTZO4JKOYw2KS4za z@L?U9cJoHvxP-{RizL*PKwpa}^-bnVy+{^`^*-O5k!y{D3+B*#K>$^g^Do3KTo#r` zLV|-@fsXpYvEBD!C=>9`K7hNEuhxR%MCMJC2Qh)^_OYjaT3Y(h_Txuph1V77vydwM z9h^vn_2;B=X>Z;a%ZoHLi0pT^I-ZX zQ@nbM2LqVX2szzZNxklsP0dXy@uTV6e7LyP&je48%awe8kWlddY9~Wq=~O$Cl0HV+ zo0ts|snSSmLF`%o-TO|n1U`j@(k1Zd(0eePPn*=wHi()TeufB=!}$7xs$LMV9YEm- zc?`(`XKboHxyQAk+4OFj!3J4u_5+8`PWUIk10yUb)TA%!yRhX%*0Uw+dgXazGk%3E zRvYf{fp7)wUq6B9knXO?$bkp`ejX@(N3comj0S(oCHGxe|5o*Z8&d8BeNu1_dQmk8M80j;&Ex%tLqKUpvE!2kiZ ztVA>mr=3~`SbMvw>XX;MBpZlIn&*AL7gW4DGkIzM-T`sObGdZ_VP0<3($HnW)n>U+ z7EbTmrA15MW9^N2aj;)?`%Qsx&eawUpbkEje)?VSMy8{q%WA&{3I5eRqz?@(3_RHv zIX}&7KyyJyGO3=m=scXC2!@DaL4idoO&FYk!i^QY{G9!L za&go$Cs+N(;7E8+mD%vg&~W+2()a4fK{ABI_R-?4@Au7yhjUZ4of=WS$7crjB#HU& zCFSHk3(b-3HG;#sfbZSNHV}%68B;h;G3!7*xtltFrTS5H^vMOa=|kcpq1AY#POgk+ z#Z($a?5@^a|Dr8%rA%brHnaHc;uck%)PvR8TbG9K4~akTY7s1FIc4x1t|yGr3W~&5 z+a8oAauH3nr$QiN8)91CFGR^7KVoSS1j>?o+1xESNFc?*i>^sU5uW@7w)Ovp#x}J> zB$dN(2L-GQE}jHGATtZQ9~&Ay%i|3WCTaKd330+}_$MS}k(&v;6etqb`!~Z?O+^Nr zQ01ugrTJ?2i!VR5E-%?@YU^C-hgO-GD3<$;MM%bTe>T4S{Pc4&c>KV1zrEw8s<>D^N{VS9H6;0eBvYaCVUfx#qd%J!Y} z9+Y0l!(j0UpW~GCd@~E_qK|nh!d2&SzGg-7#D7k93*ouCu+zf0SNoe^)>CeiUd}uy znTldHfEXKt5YYK9uQy|lzH{4=enxKaH-w7pfejBVr@qq#%3a`e%Pz2Jl6_-08xjQ6 zQ#b5>QqV#aS5aUd(;<~i0$;mupcZ53vk~n^_=!bG=MLLc2BF_!WpGt0D^u91{e~`I z;VMNP|ds zi*#?`F1|bN_1`mw!}IRLapFxX~xEdH*8B+nyn>&Vbg z$_MYviD>~UrSNrZ$FJ+Ww4$o#(fc0vy@G_CobSm&Iu*dhIa;XX+UQ$R`pn4c8ftoj zAU*&Xa8!_&j}`%Gv3oF8O>hCp>cjA_{W_^Qv9D7hu_R=PsnBdc#&U@v+9^=sbQno!4HhM%vp~t;VKCcrhQ0!9k<2rDT1Qg}S ziG5Y~y3JN_BVhD;ft!Qp71J*#+sYyyTKxx3dGayr*5=$LXfL6tHJMwN2Zn|^?7ZVn zQN;>i6mx9QZiuB0h)Yf`jy9J6oFFKQPZio=d|tbaMb5*(B<@Fx>d#>{Q`!uYX)@U! z2W1EV3&qk2gdCdu4D=)DLWDfo)A~YSDIFI@UEqiB19h&CFX(q*!%e_Hwn1})Gaaxq ztis>z6a>rq0ue-F=!gzoI8$hy7f7jvHG%v@#D6|NO{B3?nh-B>LvqILVPbd zq@yXqu6Vfj#y@(`jpmzn*k+sdg#TFyzSKO6-)j#FT3n zQ=zr<`Gvhj9Ie+RvvRX_w!Fv020NPZT0SDYfW^_!u*ds48gq0yHqpqw=ZP-h8^H7( z6)>y01c5X=)|Q;s3mFjD68q*~Kd=#wkB*KkKK+_jrp)BF*3<#n?JS(G=ihq`+ht2R zS5M|SMD~;0`giXN2-@@+KAxpeB0=P-LESU^Rc-FZ2~JSNF0;JY!CUyrfpehPVJ;NHGogb~C%{!>xc$O7lu znG|lNg$|eA*4pF6-sAK|3H=Tkj`lGHC)hdh_hUsy`zD+lt8vFCJhY|8_7}oO>}9ti zNnA~w2U^jfWto_gQczxw{(y2J;}b*C!k#c)2^ajIrney})lmraGFLHO3>DSDW!|ig zKYH=D`vto?z^;Y&YQN&t!QUX_LttXQ*?IpCN|d%YpO5xl;Amg)%p)A}u6mtQ&e~dG zp2LKNGVUT`Z=@|Ad4hgv)ODJEoM(U4^DK9`?tH+s>v*KsnKgpq-wLL|mQhz^sB&_+ zvNtCd7Q)fA^n-FlPShmxaV;EUqHfJJ?w|I0oZU|QQo!v0<$+aPoc^p_rByE^Fn=-!}i8=XcLWAqEJjt$lxUJ`UIY{vvmr%*Fom zYaoIQD(KZb4?^y)jw15A#Xpd&s(jEDj{P&Uzr@rdR`R+W3@MSP2Vy9pbsZg#3D5su zTU$d32pSq1E~Kcpn`=_-5A0pCeLH%$bKLQ*)u26Jcu@i= zQ`65IajQc904bjS1f7Z3N;VU^`m@RBeNdnOp!@6Ts@MlRy$M@!wL003P7!LRcF;o? zs4mNQDFA-$!b?jJrR1Nw4|3;eNV#8VJeoW(yF*;z;3mjI;KvJE?#Qsn1p>Pbr1lm| zh}47sVNIC8UAS;D?0t-Fe`PN*jxZ5sniOF&xYtBJ;v;_Y{ za9fL+rT+-3rnz;^T2uRu3g9Ntvi7?6TG zJO2b&Nu=}MG%){ofiCArTiw$@6@|T{ulJn;%bA1W1GlT8K&QUqo$Fkr+<*L?P1CScJ z`{fgaC6Z}-*!*w7xh?aesq^tDKq3T-JBo}y9<-ay#6=0a*i`@Hi-Wpuztlo@{kudx z3XXU)Q$-4^gCiq{hpq~Sf{dGPLjIb(_amof-oX6wX_6?KjCDYPy)E!aw`o8A$NMOj z&3PYZYO4die%4Ww6RHwvB17-dS+@ghWzcj^L*X*%WRwCaE@;a=w8m%I!?e8Ou^EkA zX|J~NSan6D(?tHP)+H^H#J841nWTE842*0P;ZGGkEmtGI%Xj4m z!(@bU9m_4hJjjs{=0>u|Gv>>PVS8<6takmKuZzph#uCHndI9g|+=%;jHl=040Do$swxR3;faw~jA#EY!M^zGuTHmRsMA+;Me+9sU93YIA)2ff(f~MeO9@*p==y zFmp#~p1i5BISGL}){OY45o8>;PYe!F`RxTX9b=u;)wS!&M6E9Wx?P_h1RMW-HC^}- zPvcIp=*UU;qm>J9#?~v1tO=R0 zDmyaupM4*rH~H!t8rH$TX`pnnVDk6Mx3O#GUlrTFV1f$DeYml$*SU9>|G`Va?wWTT z1Q^$Y4&QS9sSXcJwJs~c98XtGjpt62*28x&hR{ty=O%#GZp(}`#u_+rg1 zHdBsWzngcS(EQ^xZn;WW;xu4B#-{Hn{pR5+d1r#Mr!KjY^&ER!#~yr_$(n6vQzJKr z*423p4M_Q=h%^ed7^#U4N3bde+r@A}R9uKG_%^aS!QoE^fR@c)5m3;-ii(PQ2{bzV zLKX_AGL)<#f_VOk>ul_;2ApOeFAq;J7KPBwD!@0gkr<1aj%S&3%V(^)QgCPgmX5}t zX=zz1-~j)09`Y1@h&+`4m-|0icu!6jHgg4KtoimR2a115hP~{nyy~CD z{Hs<{PDS!SK@hLKLXNLz3*kYDPdy>e*bBF&3Tx;x(H{R7oLGs-_UgrDNUeKHXn>g+ zXt#r42iOweB=jJ&8}O)dTuXc7Z@xFKp_l7XFSNQIvlPEi!@FHR_PIU30 z>q%75^lY?SjW$mas$f6aEWBN`B66{$i1uVlpjC{QTzcwKRsr-rm;l=cC-ClQ{Dxsa zr{8-+D1Aa}mb;;7qb}H|g7`lu;eMvZN?JH5Uao{FAg6~J-2W^1IHgWHRTvh6NsOmv zGvXHYuW4k{amUZx(Q~SHW!U}I8&wP}bsoHUd!W&gGeu!`wIQlufE64ic5Vi)zg`3V5IL3o{;?zAT6DCJRNB598w? z{seT{RQ7&Kg6iJl>$R##?x@{FB;PMTjCne4tL&Ib@#gO?l04jIbxvjyl=I5IYf-D-`3_Nt_1s$N zlpa-&%-V1FKP5|wpzy$z7F6j##tLK#ekUUGI4q30Ku9-Dlz)7r_!fB(GrOWj(eH?bm=2}mu z|1E>*-PlC?VdS`%C$bsu*uTL=dCQK_X!m9}5vna5dc9nv_S@0E-K5cWVb0u>H6GD$ zVYjJT@Vm0y0jCCO%%a2K8mxn;xK+=Xw~7%qeZRf^BAJo-Z`LHAC-X_58JsVr>EgvXIDSN=mATv zjzCeY89FmU{2S_G6j)!4_aYDMb{=}Tb*z1^{A)wWudEmK4<3QBCI!6^k=1-sEg^Jr66iztdvJ#e=?_(4g{(#57vYICb57 zTBUsU)!(#qd5GS9zgH)Rn)~s}cjV+ufjzIe3@x8g98by_6PtLU5}oCHN$n;*1VEDw1&Mnx65v4suZ~xrEI_ScflizD zZ+FcwU_D54l^x!!CU#c6TAABJXL0dVcfGUjp>f%MV}A6Iosf{QWhx-2^qCWmI&RYM7iwvJ61r7G)kh?$ z`b!lZTf5nkCauHi#YMx#)#I}9g1*Bl8Z12)Ds?1+56E)x#DhX{I8^8M@d@wRj$5w{ zI-ytf!N;ygm)Q_GS*>hd^W%n&X&uk1 zU9DqbOAf)-=NEmCiRpP}tc8ur(l0mASGR{(1L@Htwa*lU)6r*N8zsF>_j^t;_y_jR zo9AR1C}0s0qJ~vkoFyoezrZ|O`&RM^v@B8pvSYod)p3?ItEJ~4<>}%Od(fomz-QaN z{|{n+{@B3s?MME@vmeY0=rLdgq;_qmzTy9s9Xsqb)bErleNCKC>QIUNOrw9)`Q=Dp z3CIiow3r=Qo5O^6Fds7E(=~C5S#ALcBxrw1mttgOB)WKIj?9EVvtR|n@z8&F3;KQH zVy{*_94?A6h_(4BiBN3l#!lDxB_@MBqJ$CYw%UTfA1`3{7<2HG_$%cZ3$L|BwuRK? z#yOb{Z=>2<3iv`z437})&DQDER@QOh;`#bP_3@!%)_zCZ{PkcoIu^XjVD_pLGZma% zCZW+#cZn=h0u^d6PzHWm0VR#v^{VgF2*}=kdv;ZI{==Z^-WNhLBK9pje_Y?_i?9Y8 zzv;!ZvmpMuV^{czpfKf-wzT|>UN!$c&AY0!>qoQr!Vp;RzQ~hwK^SWW?GlMpt;ZQ4D zHJ1AV=b}} z?Wi1P&UkdF?nm{V1M>QYhq_B`$PW>O7Kw*)`W%|fD>_t9(0yCHFm&c!V^u#-`(eV? z?xBE&U5$}0i2ghyNzfN7$KRf^v6H;2;{PvLb%}!S^RLwJ&z+qoC`8zL?pq&d3Ore> zP~j?Rp>%OQ1|du=FYD{Jy0Ajt-vSlR#ZxrJyW)}dJ#0oRGpl)SWGU~L%uYJ{_)K-W z59y)lxCTDkW?uKYt3U4=4j-^@Er$lRm|nQHCGVu$5x|dr#^>$aT~6ij5z7CZ@b|V< z=Cb%a7;jwUo-BQ|xZ$pou1}8$J|KtHZ<_JY-hd*T5B$u*r-b66RH)s6e+`0IRcKb-mHyM^-RtlRs}`1iIrlS4|xO<|)D%22uCvL{FT+TcF{@oD}|EhW9{1 zB300pZ3Rs9iX@(r-_Ci8smjk-ag*^+Ma))c<^e}R>b)zIfXH(4;&3%KdCnxlbQkXM zgl!>)L1cUtXSa{oPt)O_!LB8xzRi?j%iV*hmZ6bnE*nJAGIaW5gW}2z#y@x@5YZk{ z?SB8fvZ-d%Y4Q7_qxZ-spnf!djK$}%#rQb>^aynWF$>%g#3*S52LhTnIc+>~>h$F> z@y}t=Y~me^_NNI?VFdT?lv1E6a!R4MqAEe3d}K-tb(# zr=YmsEzzz0eZ|>fOqAc*N!bJ&LUEYY(9*$zc$X@BTs;M2lG=~Q z_ylrbWZnLEYCat$gh?r_xE947p`O>Q!?`C_6i@{-jJbrs%aSkEcHHM{An?(9%p;E9 z`mVa)zfwA(nQnhYwv+2)jqoO%z_$PvZKp5Fa#|#Gwcvm8XU)Xh`%kU&3*bzRvQ#S( z7k?E24|iEZ1$S`clu;7cYDx_v>+VuS!7Lpht8UYN(*0r)Pz6~CIb-bdZIyc%={p>w}@B3alhY+6SO`a@{ln~PZEU;>P!Ep`%TKt zI76X$5*g}2gP%1~of|!^rO9Ld%WC->&YYH!IPjO!2x=vYJ(#?0G{VQ7KMqoO9wAf- zh_**ZAKR_Cqsb3>zdQz*Ee)<@CF#-y6S>e^y78lWu}_4LdP-w>Q>6HFTFOaKkOU1^ zM`$UWmgAdZo}Z6Jr(%9YBYPs2o1YCy^mIFzC7G*ng2fFVx?>Ckr~m;1^OP@oW^DNOuc zr`uDQY9fDID(cg$9N*i=Z?#pvP4LC5Jou17CPK!`=LU7-|xd)(=Dd)Ao+ zW=z@!=Z~hjq%|#5!r`B|NSwJ2FonR7=EpVkpOklLv`xv^pjCMUXX8WkfZ+%lim7OZ z9mY+*eh}n65$wDdoaYf~QXOf3;J|PIv;>%Mz7>WFDeVQ@ZOtWfjE6`Vle*LGSKL;R z2-g@Lak?^jYbnnq#&E`Fd*Br34Zf^RhK8p?5ugLP9}c9`aJ^(n%6~dX_U;Al*;&k? z-QQ~?D~Hva)oo9j+0V+ojC4pbJEc^7LLAWQDSj$a5+&8F)`_z7JzV_bdm{Q4k7^U~ z7EWFjqN=TC#*O=QmrjXy@eKutECF};&DH>vT(+viYfgsP_AQlg9G*|cK11U`*7~~x z_{(;%eJ=_YzqMX1j6>`VP7(}^)!x19a_2 z23ZENTz7|3whm*YsBgjeeFPZ=C8v2e+tcNZPJJnObvh885Cm8={icl(yDB;1rkeAN zK#HURC^ZD`PC}q6{fq| z&r;piP2$U1zu|p2)y~!rjlmA=68z$ZVr6Ul7tLqqih+p^Sna-Bp3`GoT)&$`Tx@3u zun00vA;FlxFDLOWiVCv0Z60U7Ur31Lm1}S`SXn*D&3~l@D6lmn2K4^!Bv{tO z6Njjf0nH>kRkX=wy3w-zg8V{ql4iGn{B1soY8G^g@#KC=H<^KS&5%Km4> zqXf`7&S(&qmDLor<~3|7q;V}6Zw4mWzoR}-`_jDe{Y-4Nli8_iei>{-HytIPY*)qg zY2m%f$u?zzQY=42%h3BJ2-JlZJDkM06n_m)XM>RK9S>ZQFHXk=bnnT@^U;mw3B3p~ zmW*T9>#z|hhWk!n9W-fvF2?uz6{A>)XX9IaFO}h{S()XNW`9#3E{-^JdC6!jH3K2T z7MsgEqY-f*xstqVS*n!DZXRqv+tBFa}yR{`PmA#WILdv?esVY6Ae;@crM9@2TZ zT+N3UOlCL5yzOOI7t_kTPgizNS9cMh<1r1P3ekOKCBb-MIe*Ts3A@H#gFc-@Pe($0 z)4DAxmPepEYLf}vK00l0R&^}f^bFoCsGDf5fpr&TRvj+6wF-#m`*S`gu|hDW91&dp zd6oTm7%%&iCHyF)Z?#_^KV?h+!6ri2jHu#~)@$Z~QgHev{)&;U+hcr>5nwv>*SIYt zi4aAR;S0-{XcA5CaCcFY(Vm>P@Cg|~>Xqq;I;Zi0Jqvk2ve9~Ak?D97N=LmTL&s@n z=hq{_Mw+c?)iv-e?IBBZaqaE9M$iU4r1sAh4A|D+wP%t8g)L5Lp&UF5*y&a8g zKU^L;-h>E3f27{t-t$VK{~h_U@zS)Wv-4!};_+aRafFj$R4&Tm7BV|1W}{c-zh^#} zvL<=ojLPrGw>^|*KB98g>&%Bhn>jX#CPDHc#h7a$p?)gVMT)Ol{Y0Da!~9MZA)C*~ zdR-E3+0FATIO}Y~fagNr1N-`Qw27n~Vj8f5;|)5k`?*pjzZHjk4U7a)LB7prsBINu zkpq1EjDfk0$Q^k-q_c1}uO7D@g@7`o6b=U%Bkt|MsX!lfAQ{SQNjG?b$a4Abj&JDI z19!&9)-S9bPwmH>CqE@n18@$wyVxYg%Sz=D^u^|irRy0b!uC(MD{$b!N&?E_u2Pi( zoPpch6`oNpo%Kw92z%!R9ykE(2Z>cK1%6V;q)Ew;FZf1UF2C7LM3X=}x9LJEfF&6AMdpne{HI$}G?Yd(3 z(>}rs6vU$3yH#FwHyQ!R%!Dsh5ym*sur&gjllN)c@NxTk41`s6)k-yAJCGUfPqp71!; z<3r(j@m2^n(IdMNsCQ8!%q%TwCz^=b63?_=>Q?+6t1cwBq{Nz>t$7PC@-I;sEzk91 ziTu|we@UD|tlpLOdz{a6l%B-q^8G?l%)0cHJhs?Gvl}FO7BoSe%2gzCK}pj$9=NJ> zg+6+0OEXDUG9&wRlwygepBTeNB)?^oaIo#x`n~P_ezSKZV;!>81KhoGaIv8Ij)kdpfRqI~mg3?dQoQcVAs)b=FC{)OHD znuaUh*UP>?zAU%&$48`O@~FYt(W3Z~-eJFD)tLe5tlAwYtOwe*xpY2uZ-M;shZS-T z?Hj+P8wQ7X);nt>@a8(RS#5vG^Qad=)Bt1qYZZb>{#W03XayPzE zU^DRdX#cWwHxSCKa-nm^6bPg?1X@&!{P}2_MfZ-$)|kmARo9N3WW@gC9Fi+suxSH;)M zEcp#&j4^n9L(np;SlQZ#M+buSBoKxxpnOEFZZplikZI5xYQr+Q8LMzu&^}WYFjGJ9 z-bHcymBEV*U@TzvVGMq1zI{|DOga_A$yKpio5%~;NAsh&rAex}E8m{YgQ0)rYp9yI zek+uoRQF#Oaz%kczMQ?Dc1z;NsU7pn-?N?W^BT2W+m0jFLzD7GI9aO!arKcCi^WnS zmV1|}<#%P%PA8l8ABq3NkshkaihOkwTT_T5$vKb{Fu2f`crzaW?J0;638#(5<)-Nk zb1}og4Yiat$AK%1WO$#-u?@5jch-=j=b=~}IdKf{iFy}lA6yL_2?0yK|p8Gq&x%5zIj4-_oig zDCLX#tPqu@x@4A#0}mQAga%I3;6jZkzGzk=%oN4}aIl%13(5PL_X*SQn`n{dD;MCp z(8FwD?x^}xZiI2Tk6RvP*Z|j%ODi@f@upSpFXAlPi36>hl9vyohe6)93fyonqurR( z4@{bM$vro9;bg3IpHp=ejU${Io#-ZmT%$Ce1$mU5_?Q?|FxgB+(j9Sg%&*t63?^2}V%SQWx zox5<_y=$XI@umb<5(OmA)Vmn9Es0Sx;RB~>2(rgY@RAEhHg#(=5@{v0l0y)W{&bML z`X1l&)!0#P(6Tj>Dlk#cv{8g;C#ThR>=(Owe}Ubc)@cE)9MI(}WZ~Qdb3AsD_Q2I# z!4Ocb8*~5}3^DqZ<+^g)tWqHDTqyD6GncH=FClEmY#XruvU8vR2^aJ&{D8TQ@L}Li zc;|tM4NI!4cb@B(FDog75aq?z;?rvq_B7J~+ThPj;dqVkBZMOP<9Bu7h^ zgpDrlZ0E_!Lx-VX71FcHap4pjGK?k5_78%EP9rO!!kQJ=PzueyIWx z!NYKoSQQ26mU13yCCKRqlK7842-Jy!S}H{GT8CWl0zK%iiwK( zkK4`{T>k!RJw|1GNtSCLiv;|%y9PBCB6heOYKL1g=*sOl_ zOS_&U5!B#jLRu3}?B^1#MXUe)XY>#3EGKC9O)t0@Wr{**@)aYJI+tdeKDE$93}f`( zQ9!v~W-U+4oJROkeCgE3KOE|@HIi@DScl6Kj09g4s}%WQX=8{;EpxhS!uTwg^p}95f+l)2@cHu`X`5Av4dJKz!%^E(;YK)|?$Lx$|lnB7> zc=)L;pglhm(c{|}5Myxf;1TEpM6i3DXN`7thd0GjwCYHe_(m1 z7j967F(oZL0gYXOFC9EomH!6Qwyd1~5ch4v^TNm?z%(O652|Cz%4$WTnqDhE!WoTB zd5C{c^kI*y%zT>a+_dO)dOIJpHB4etV<%{Tbn%kfJFoAgltqsu%zO@tA5x&v*Msq% z<;u#gxH9RC5u$$hX8lLow1cWVqH!o_@vi?nUXB6=X(~C|d_F^LKk;3HAL*7m*q*%d z4DCE_>gGq$g8y#pApJZfQMsBG4%To=`}WL&kl)$9rj!;}f9JU&>vO;#qpXz4VT|ra zFed~`vgyizkl9JIwWRe#$t`YPt}aQ+=l;? zEJVtrv@@3FIgjmo({tTTP_k2O?Y|C)Ex4R5*$ zziHQ7OrpHw>-0h|s+DCFgd28Yr^7LCf=Q|1gR9_vDnD*AfU$c_ZA#Ykn53Ca-^4x7 z?^mSBebdIUAe_*ARDg630R06wO72pI^;Cf+oM^j&vJB4qfiUYTjdmZloM7XoBwoIV zg1Bq$J1S#Ex9(VCFSoeDus#@Lrl!AqNjRZO$_JlUJ|FtTB>Id;mMxkz=AHOsMfN52 zryR(g_{p$!1cZuS49XxyF%fB2r=GqyyPzOH(ZGu4iFA6pqQMsSXUM+6G<{uH;9sW# zsbi~eqx*n8XW)5M0^-!du6^Mzhro9&4x!$wgHsx^F9(N`?oRN21W8Cp!1;Y|T6S49 z7Z4Og*z$bTeI0QY4^14U>WpKKo@Vj6M?pTFV5jIl(5S#getG^V?`1|;Y4PE6=Bsh6 zb=)>xu1C>ENX_K4%wuwPHVJE0@C)zO7@}Vy&TDeTiJcRSzJlGaD#Hfo%VQDIhd0bh z1!ldn5#%qbYqdPDDshs3R+P%QJFD;Mjv-x2q^+Xo3`!_{Gx(Sjm1o>5;DE}PauV1h zJBKOL(kJYB&UNKMUVM{S?<8P`F(+L*k&Z7atZJJenYS5|x2XR5_IH@ME_jwgQHa&W@#!tCgDM{+ zDEjRl!K;N;brgs_c;kI;rgI)I%*KVU_0A*TjSV&-bx^-T%mz=v==c`9wWXySxEttz zokVaGgi!C*6=n+mdy~5}z0dku?vtcS%ZVL$p>FoQuT7phyisRNY_#b(PP8X>lbAbdj!4I;;#A5T!3yi`-aTOj<)8TY|LW>tu(ZPZ~K>+ znjW|Lb5g9MvA+lgnU}+P>=Ke(GAnE$Huv=w#O^LrCB*!jjC$soXY`VOh7V_2ZU!m+l5j6{r-c0{x-l+rMuInvlzB= z-Ql_jM!Dv2q3h0jF@?~a_u5-WTypXG*HAvbP#Fpz#PMp6?5yh&61M3|*23X~eT z2SjI=)@*s1e2zb$^_u$5T&ZR6Z4D`5$;GT2H6qn?m7jRrPld4W!WDX&GPeSieyxOt z9h%YTj4D3JACx&Ug=uX2u}OTw6_1ZjVvQ$KAPa_GwE_F?96b|xN9NWXgquV^k8xVCn z8RP%!0*L1IlkH4Ese`NGgueAlo_sra zWjVAT>#a3M^gfLfNriw|*!2r;E;)PM4F_tyiQkK~O!S0M-cOa4Q@_7{Dlu9N?ioOx z6n4t^j5|WYkDf9fB8MNJYX@Tt>3Q#3m(Xi?z#l<Tu8>c zzOE)b^%flnM!;YMYHm{(#2OEON;-<^#B*e_;0Y{qF4MV}9~!0_E=8*MG4&NyK2Knp zsG>1cSwIm6iNpgK%+-gKl_ngd2vj-R)MWw~Om~9V1Xq5({-m9)vD)|8F~-S>L21^d zBmd6hiiE(DHt(n7#V8^BpV#whPal*kzd4+k+|>M#PMiM$-+8xPU1PIfYr~u?L<-?- zWf7GsygS6Q;}9fc%m(?y{Aa&$3>ts5 z%+N?vI^FVR!J2b&2teuIkoj@NIneU;WUvS@O|GmMP-Og_cdSjK&m;SRQBKP`%G7W% ztOCbCzBqBx?nN>XXfKFGrkKz%J|1Mqnz2JRF(IV*D#g!yB$3UxmU_o-?5wghBMEu9 zD?xx3$xMSOy6L6iuny+P$emTc9JkUGr!4+hzKqE09CSXf8;<9@=>9G@@#-Jo%|(>TwG@B?->0_6c#0(Wo$Wum^m{>-N?wf-A~l4yin56!*_1sud%G{pkw zMIQEpHn=mEN_YN>;pG&kcjCbnH0|m!;#}LjVB-*0N=%y{Zq8QKnec{DJ}mIMAz|G8!|eA3e~0__E8hFj&F-mKc)QE8kZesmzBf>ex^#+T)@`(JHFrCr2xnM-Q>7Kv~f zqdnu0=*an0V3cnfV|~c~CK5 zUtfsPlHzg8KE8GgmvPz0ujTRi7O>`1JFe-BN6jj|pptAi`JXclk|Rmkq_6At-iG&! z#l$fq6ljzp6it*nG$Pf2djo%T&lUDoy(!+<JH%;6lK|-ki?>D9hd8G>cW=g_;S2n~g3-81h71c<_-?J%dXE{0X+wiqS^6NC# z<+iC0nbmu@vf+{GpC*qP9SCS8{PwWknjTUUL+`)%d6y{+%I z_K4zeVL=&BD@+QJgQt3jxg)@?CT>oHD-M5p1EN2^`wE6Fn0~4Jj>XmrYjVe^ndqQ` zAo4X`d;~U^q@Cwp&1#0A-9I|N)@!>a(ma@5*O1LWXgH1x?$y85kesK#yL?`IH9n)& z{%RdKn0^OUx z;AU=%Z>jW|pW3*khhXjIoB5j4WU;(b4pFyLe*dte(_VeLZN!p^E1)Y`+4Pm3g8ZU< z(ZSkY3}>y3A*Xj*8LIyV^?egk`E7jPB&4P9TjSikIs_Q>E<$`SWPF|0fyQJrJ3AW| zYIIIkp_YYz>IY;Hh%+o?=c{_a_RtCbIGX){LB7{(Q)fuW(5C<aMrxy6p1zgEefzV^|ZjnZVh=QaVb&BeTpRI z{q|8=lmc1Z!dyJfe~^GFx@w|*swnMC*B?FO{;B>2uE491GM-{K9gWWNlY@z0P;#iw zT5iG4;%QaxiT%&h)t|X0Ljw-nI85O#$%$_HB6VZzVx2E#{1e7;b)4auox=j4be*(# zSwA?F_)+06E-wLDUM$#9m>9ZQ`YVPuc9svDQdGQOU*AN=d*@wFGr6cOPz{v)W$YUI z2Z1;pL&4?L7U!+25C0VFK933zioT}0Y5T~{5F@MQGkAt8YPAQrUxEQQ<9i;Vvk4j^ z5|ZJoig%-oq}9w_ADli5&`l4HOGVlSHy7d+3hpPFoNwasxPhWNbX za6|#oxHMERL-JMgwgXfBU0x2I@*#6!z{!x1zD!Pz;7w$XvFfolmPJrt_AOG9<8DbG ztE~J?QCsGIFk>;VVd?o}ehHe?4`Or?TEyk!#aL#bY4S zq)vU(Z`tX+wznB4)1JLcKAUb!!;jzwPy&G~io^;4GBB_$3>Pcv^RHsi;zcwSdB$Qo z7$hI=#Q4HC6#zVn4{n}Zc9uMmBUSp0(v^8n2=T|IkwyeDDFiZEeCi} zvc6Of2Y4V6566W}-8kMEhT+SS`!zPYfc7e!gA1K~Mfmiu@T$OJyKyG(mC_!#O%BmQ z45z#wN3%0|OWhKb-WYTnDYYxt9b||Q@sb+#yU=;rV^QOBn`U&uFb+-N90{JUV(#7m8sK8gxXNjShxi!RIP8ch@^ z*@3rYR>A6y=;wB^H+e*P|7YL}(gcrjH{vYZU>4-%V<84Z9RlRf{bK;Q&t?Xo90jdF zMqZru?sKNwP00NJX91j?Wu!!={&_b$u+uQXJ)j$D|00!gh>RlT0bgl0b7KNQdX3kn zM9_e>>blww2Kce*o5Lve1&89ZA&L#YpJ45Zz=iz2c^9mdAPSLt4Kh{9-`$u(oCd1Gh2XXMw^}2u1XDpdj{GOzQZ(f@wcgtd5vE zJpE(yC!}On{oq`ClGL3I-Tks*>-;;9NsN{>`6?}=8SHVEn31-F?mCVwLotIj z>cefnHG6;qf6Wsw_oX6}ZG3gyPM71Q2;GYUg(_n2)EY0JJ&GEpklXVYL4QAs6h7+B z%*s!(x$m{?rts4BC60E#HPUYJcw{2c^B%QyjG8+_JUPP9WS)IgxEbbIpPYPI1ne2>HMHeQl>HOL}?o(VYrWP9~Z`klbFi$}ix zP6Sj0;+dQU5QYi_qbgVqIBNd^!h0CNRqh%AS)3JeA?QBLPHe|!*q2Q~gv*@)`o13@ z|JDJ?wXGHGTz;d}bxi&}lMZLJ1rBXC?FNRM7LCIRPpE}`vy5=9W1&=zkY*~*gnIUC zNc#M?I1K@cpV$Y}ymwoFmo*r@hP5a6;-%$f)-gL-p~3=28z<5Uu4H`a%(io$)?y>d zMepgM{=vyQn0C`+E3(>u&uB^Qt}GliEbzUwLuAY9oxVht%uS=&4@#iWJp71kMcY+~ zh+oX5QpRc8!Z(P}!W8l~# zk&Yn_;FG%u@=M%8B)9y!7@RxHC8Do&(>MuVNi<8OJs;1ppFZRhyi#Z~Rawr!oOwJ> z9rc3}p!|(f-1L7X8G{>O#vb)JQzCL10)hXm zU=-$n7nA_n7sK$wFF2lU3_pN(XeXr;wq5ZIYq=wT&bl+#gwqNdn)8!oieqg0KQx_X zR8;TVg@+-eJBIG=F6r(T1f)Z{Q@XoT=@w}aP?7GAA0pBvEhR|vKK$2uKeGlFXU;j# z9eZE7p@F!mZO*U5aOZfs8I(AE4VWk3*z|x-X@w`t%C!YB1XQSTR?i?fCB;IfA8QIF$ zs=SXr1*;m~N6LCB)(c_-g`^emF6V z)KGs;eyc!(NAksx_O*r*ZYc=a^!Pn4)jTu3Kv>m~WwD#-QhdbRY4zT7eV^w73577v z;crL;eV1tDkS_M?-%{nT6SRMNH{UCfua)UIb-!^Z^|RT}6|#Qg*+=SvNxaWlG$t># zW?A7+$j98S2MW+p7u1(_i4v~v!jKRXMK}nz*TH{g zK$$dC2{y;{U{T)55vii1$sL3|V9GH!iPbkM1+Fm5q ztZ&i5Mso%{_(+H(7$@SWrQ>|C5%!58C_O5;=UQ$^9|D|uI{Z8?-Srz|pdKW?(m|v{ zitx#mBywD^>eSgCdL|XrtP-^au1T&7nDJYTU-1$u+9gdd6=UD}5+$%l_vpLt$Gs#B zOyXe7X4@LJ8ORZx)zZJ7GeGUcQP_vguIt=((@23WqMf_f=+@GQf4!yL5!h8&7#rYK zj|$cn@@d1Ho{}HxoErn#+Qx4S!B*#y5_Az$hRrr=vx|#LiO}n`fH%D(n~)q2IyeY* z1oB`qB_*^992h50;XqOQAEdvENfM|(W1ilD)Po9>vFuuW}cA$9?CoQ5)ZWZ zzX)ZPa}+wtpd(5kokA^v3||>o~ej9-aZqku3`+VJh+a z9RJ)k!gjkp`Iiq%YlK7J$LPAA=+_81qOA*>kN1=`G^T3m19n)uUQiW)!a=DEc53AY zfqYBp2PE1)P}UDFfIyPhVA=lr#M(8J=M%l^{F&~)3Y}5{Z~=W^?FJ~S2B$-ThiS@Y zs5S!YOK;wpMJ~3;jRnm-`JN45%@e(8DeMM2J=A1eda>5#Sa2knZ}!K=Yzck$=qV3~ zZ$F23o0QIka}8XsUVZ6Ol>nQ?J|EQOFYwlMYhQCi;PlK!N2s!h@#|C1NKv?%rN!c7 z=g9YfczjLk^O)f+GT2q~YPv)aO(H?5f{m1J8c--p-M+=(nKfVj*wN6Wi(uQClZs4@ zaBwN|Yzq0=?86Bfw{)R91m+ga8U+=0r5mPfWA~9eh-Vi_!+PuBu=N9)ho96vvJHXU zUUhMOb9nvuylYhhiN0i5$*Wz;Oi!!?qX)~uWZ-Rq0Nh}`Ku`-!^cVXOZqrh)@o#KK zIS*sGZ6hTvG4xa$GXdH}q9rm&5G9|P`UAdJHCBkq$<>Xl)WMNx(!^Kb;PPvbMXB93 ziiz=!E=wS^Uh_5rSKlpPW}J=O2s5+bTXg-V@If&uPo61(GvYgvo)FjH^GPk_H1EZq z-Wczr(nXK$EXXB)k|6N&7GV6~8t;ZLhD9-1&^8ZIS}~W%JgIPQ>T0fMOQO z<4oOOwZ){v-0~zx5q{C{5o1YNO%Ts&L4-X9$1k4t3m^9nR##VJ4A5M)Bh!1}xHKNo zAPD5-1xwG2$&tUh~7g#&>a$HoV#!TnIUkU4!6}J{MH(X2EA!Ih=c5EdY0?s^rvM2*h zmE>2yQIq)G${v-cTij3(<{MI7^^oGjb}{x((f_(3WkyHG=t<6(Lj7CXWaLoaRCrw1 zSia>~#~dQ(baE3KB+-eBY%$vp@Nnu+Td*=!l8D~=oK(m9QAk9{ntRKT3=u|By)|Ph z9tTu%G!pO(Yq-coM|ZP|>UTS=a+Q(ah+mB4ko-|*3XP5u362xB-r1iSCa zHqT8SL(&uY^}h%HYO(W3F}*D%$fcwF?30`Rf~xs@;bLO2QtQdn$I1d}u@85GVuqA& zlz+bb^jdg-n}Shl-WqE3t*~gEXx_Dhq+E4Dl}i*Nme55bd6u)Xs^_u^6kmM+Z-FzE z8eYe@=^6CZxla2JY6jn!q2azj7Ds7q0P)s;aWkc9W9#jPVbmL~|8aWxH_5 z$|K+Qf63?dX>BuLNZoAn;3H~eQ(J5-pBknqxFha;A~f8zgI@nOZGBZI0CHyjd}qZk zk1QUMobeje2_U;1L$%UaOsplAZ_d|g#~fO#MPspQ!}fcx4vVJQ-nqZ}7rx;My#1@v?0LLS=~J#<=pn~REqAaZrHCt!xQ2qLsq@IjoIH#4z+3Wc9ccrKq!O1fy##Fk8NM%~ z316_*vZP#dL1^n?_3i&*Wdm5 zK}120vlA~#dGl9c-2BWOo_<2b!$heC_JkeRb^VPUW~HXsbUwxjG{8!C9#F3XJL63FNzQq(8 zPN6X+*RcuZZs;D0VHVkNi_l=3<;7kV-f~+ip%0PJ$cN7p1my75-@hf+pp*_vPB(2^ z=_IrhdL||(IVtIjkP$WuypRg%?6mZ~Zya(11ZzipDlaIVS&sFbyr_|7kh&TVD4KGg zu9Kg9#X|n4C@IVTnQkw&9_tAi563|W0h#N(23r7BA{`6jO9xo(McP*x#7Id zz)Y@TLm--DiH?a*g^b{I&;HdWO50GtOoWq&pZ0mrx|<&?9u*Z8$NM6$uU7YExs?>N zp?WH*d}!Iei*C&x>Q)t_@`FMsR}1nAe~si!&8vov@l2l7%BGxcpQ6i4FFsw3DAKbaiz_ zIM=O|N0mBiAsKok`Q=>fffiQWQoIX+0B`wK7Er!ll&pO75d?C!aW(k72G2A{q`mP`;za;tfQf#QppOvG1F;+hd{5_KVrf&N!e zJXeMER^~?_2ReGlL_sJKQeRMc`{Gg&Ns1&m?vo&T1`mf4?lU*(oRvc(AD!85z%cGO zMsPd>v7QvF?1caIhdEP+$VS9~6^318Pj>&5jTU`s%MJb;%D7TdqdZm50$&m<=*T?P zv2wxV4hFEx=cPl5IIFdqu`=g1B+_jJ(?~z(!RN;E{{B!n z!9qW{Ag+}kz7!`UXLF3hk&6ZVpfH4vY<@VCNRm%->2{wCQz3h0lb)2Qqc$8vCr8tn zy*&rR=oZOn-L}@sJ+oReGqbzwY12`*6S=A2zl%AgVl=PmuBMR&Rl z-FjIr6cizN464X*5rQiL7otO+i6|+Yj~D2zZo`|_Uq^Y9ES_-?H{eRYTX6nYE+I}Z zQ4Bv3x7(jCPhfH0o%Xh;A$^g?Y+Sy8a&hV1w&GVC=}`BNgM5QOD6D2sie?;vcK*e) zP#nBT5Pttbm!?)>?F{%U_~=?jJlLCRLQo#hsd7kbXYe&{Cz8o4v?Gd^5A}!WRJtTp zKJ?&DbnjLK>KJBn@pec}gAv8vLU;ZJ-fFk`x>D|Sg|hsCSB%SqGfqt9hBu5%Jp{wP z*ME;Wy4U^UZwk{ESa}xs*)RjACcR?pre}ACY*3V{U16m8C*kkxb7fY*gvQ%>?-T69 zAHpQMlJ%8pFCqeO1EN`=!DZ1OV+P(oO8WW>EjJgx-_&>er|D8Q55RaXHmv-Ad*t>1 zY;&P}&WNZYhO4s-4t%Nc>HH#dawSI9vDaUh_VeJJWrS2X&i-&@1VTk&4#OSf=Qzro%NkqVgZ*^D1KWfO8!|2~M6^e z%)&oEAy7yU7y(N|&eonD)=Y)L0rXn2OGo8AGjQN&ni4XM=@(N11N&Q0r#D#MCAIw1 z_<&q@sn@5t$JOVK%+%LCGBP%>E2&|1hMXgb{P*gLCT^7A)~fzvm~b05Hd7@WNBb#s zyikI3$qPFr?B?Gel^|86SELE3;g&XQ@{JfJ+h+W}KIHDRRp?|cHxyRo#W!=>g!vh;VKUdOIVQ&#cUxfBvJGKF@_rA)m)-7MW!E*FS8 z3cWPA(gKkV!?@>tTg}6(T*mmiD!zqFUYXwco zsGq}t@6zA9m=7C^n(X~?|wR3+*2&X3UKuPHC1iYd)rXurql3 z5GlYDHDMW{;GL0>_c1AbTYhOuAXBNs=tRJ4^+%v;bf{Gx&9I8M7D>fqq-o1qe^0mP z(|y9;K}=Jf2PPF8xAB?s67j1CkG?cFD(Jin!xnm7=j}kT!YF>aDXZ5c{11hb*SB*o zq8aca;4uGPSuD<4!^p_SIgm#=P@1^TH#7<=dZZxTy^(13FSs@U80u9$ia>tI!aT81 z>JlWLIfR(k1`KElulpJ6_W%5$cv1NM>0lE5vKt1uX-z!;wFZ-s3HSZ;g+Qp`3F7xB zSgCvFRF|QkxO`OUE55ToyVj-M(ddV2p;?>|?7M%*Q~oJYnwl6Nm_GYnR&7Inf~5(1WEE?gvXyLRF`db-tt8Ra|CW|W0eaPB1=F`z z9;$v#(`{Hw2qM&AkguRE?`X5;`r}fAJsUQ@)tt;hUm_%*)(?V;unJ5cUT)GcE1(Py zn$7;jj@%zfKW-)X+VTD@tC*mgF8@dkirkC+{PX~VAWwK+6_o%5eS(bSaSf#GlgHZ4 z-AVuOWNM7%1>fGQ%r***ENf>|A>uc!vixCuad5Wj3{KhBr^i&erh2YDikq)_rq~c| zeuZSFx+3MdlA#k9(|tKWJ3C%Xg}lIG>GT>3Nte=KSc(W$%*-+})$$K{q+s6{li{#^ zTb$mz)`&rlc`GZU6jXxyk*=0FP8?Ldp zBUxVwZ-(iC4Cw)`*>F%|F+M|SbD!xe9<1)K3*|E~cvJUlgWd3pXB;gaZZUlAcGV-K zB!m8gHl}53l-Ja`64L1@2{R-`*+9?YLgHwde!GLIH?n5EV1w@vB1)VqGPL3Xm{IDcOKA_BLZ)P-?0BQaqsT3G$7EdD=B>G95oq9*RbR!^gZR zxXAc^w=7RWx7ETHahq6IeJo^q?6cO@zeu$!82L@_lvKg!BQ0~(mawN!u;55Mfq&$A-sbb+G`#~2cgw| z-PLlE@3ewZ>Prp!Orzt@{puC6S5B4&T5a_c;P0a+t(&85lPVoKtmk8Nj8@gabq zfsc22z;oEuS+wXlxU#as7$CYH`x8+U_5`T9AR$^D0`s|AwC&;#Pqk^BjjSoeuA~^$ z9DiWk-|Wg4^|1g8mEY3p;jKo+@+vBEsye)2VBT zd=;SGgRHiaLcZHd&o(jt-P-;ow)Bd&m0Ts%Tvf&lDiP-g8lv z6@~~0me6Qh4;~UqXKZ+HKQZ_JzZQT4G8XEyftzQFm#gY;4%rnfj#~e9S{(4Elate)f~1*xzQ=^3qe|-x35R=jKXi*a3S0AORwg|8IlEIS;sBG? z>O$u1k%br0HjcaTX==&3ZpPLQyWbC%ghJSHhWS_uZV-|t+b=g8W<QH1g|<2)*92(0=w{i}iRLTh+)DN!@*i^e0k1^RjvAl=Kt9`y)Z zcHm1rm(_sJ8)gnM1LY-k$o0cX@RJ@mXJIHd*QVHe+QMfVkWs~&@r2+oI(666X{j2& zAdL3|U(VAUq>glMe`)K{n2yk)4Mc9Q4S#aY^JIEpobzh{@S1p0AHufFT(p0VFL?xL zU)L&yQFS+3KD6J*u{Ho)E7{l|8ZI~`L}%s$oMccryWrVe{Ai!uje{VTnV+o&i$VMQ|#>Mxv5ff zBO3xXjVT3)`tVFQqXkY81RK2E+yKxN#q0~&ijdES72n}8<`{qUUp^rX_(&M*}(j-xN{9zI4W}CX% z6>xT#3wMwB;X4*Av4t3Bm1(EKj`NDBS_MIN_7T)r`q}Lwr3dtIuKEM7{!ucS%=*?B z*$RA%I~fq?_8*^!pQSD6`Q#zD)e^DKkCCD#c6Dh9LjGW6Fx3S(I^{b6D>N`y+pMeO za0A#QV|Yoh2#TgT&h0c`fTZ-1f}Knc5HP2;eLr^c0Mc)cnv5Ymz5`*1_Trr>W$)L| zhpyYMF29U+GGICzfeE$EslMlDXdT5C)X@X=`ASI5%^?;c5f9eZOY~bRk>RMWt7Og#UnRv zt>M3>RO&E&xxmdtQN@5xV`DB35?tNIxhe{;=orotS$`uNnyn-C8_x+@7%p@i z1nMxV5Nw(yV)4AB=%oNRjf&!{ok4}01z@kEhkn?$K7H__i_eun4h3>`b3j~9f#BxC z=tt&*>GN&aAZFn7(1+%ROE9-41X=^C_{93QUoA^s7V(#Q;+27@v`w-?i(rp02oBr`5iV8i24h2xJZtw19mfpcY zo=-0*VP?RJSiUrrDURA2iV{jSOpXxR#QS!b?wD*ks-=l5x{{SGP=4leI{kH6rq$?l zSdZY1uvcAf?_LQP`q?OJy%`7?-S>94CYOveRuw1xdGj(vlM`Dv7Zn2LZ6?pjmIZI> z7t+CgFt5QbkA~K2Prdd%bb;!O5$bF{KU>jE(z0Oo|bK z_8$yQj!#M)jtu(U`lo$ZJr3ptNOU{YO0{#fdbs=7P&)Rt$Q*eq`p z%{FPHhTTeQV5r+j4jO-&o>l3r_@%h(_=S&#QF|gR>{EZVc}S_k-RYx50HyQyV}ZXE z;tBrWlR`t`(9zM?4c55Hg#8h~q<^3%JlfUlDd;&>B>LUEnW_b?dO=smx5(PcEuZI~ zzXPrsYHYYLE=f1me7+r3Q%_>AoK0@{S&zc3JkqG27ZqnbZZ){$uE;QoSS(_7=H?d| zI`W^onNsN@6sM?PgB;gY|6`*W{tDK^va-X1*{xl;))oY6!$tkXp+r*sZ)by~oOizk zyKee{66qH}W#{~RYq9`Nv0reSyt+bgwDJka&yXz8y9R?^X)fDpKWQWQvu`GenQnm7 zb+9Y);X?h;46Q=#{FC~HonJ|Yq+Y=;7qoJnyydmV2OfKN-=7RgsOfaLKvem&!;Tl+ zOgKO6Q(_yj8&baf(n`&6OfcKW>5*yQ38(DeN`4>#?>RXmBhfO|VK#Tqs_P9{@aT#; zIcii8t1E)wlvx#{T?blh$+ykopt3VTh33o-ixL#0lFh7C0(lc|76$eS9L>BC8urR5 z5o7`WY(R6~Pn`$b*KP%gTP@Z1FHu%j@ZyUfsj&CgxV}#`&Ai3$0y7#A83X}M$`B~U zPC@w3I%tOm<3&xM0XE11r{r(-}*vSdmfj`0=W7xlVlHE&OK8deDR*X6iNNc_;?u)vWZ?0}v|$6Vo>axr>PJ+1W}22ArO?2xbE zY6}^Bnh#@&8{c@^;$arwkWGNZ{Q2|86&yEbAd@cW5kwU;lr}9VpxRuqxyxjhJ(H3^ zSBWj)yc{Exq~$Q@@_2z#@!mF(onQn#QZEOSF_tlZwlWM_0=;aT+>R{U98R#LXVGiCO&?tjasX8A>Wx`osZUsGpzvcsSxIYy_X7E z`KhBG@;@7VTi~{U>V9q*t-@?%<8qA7R|YD{yBHi07AxuFBM5ZO35G_b01vvpP6gzc zjUbY}#t z?2LmvbiUq73<3gCOuncoP4rXz`o{~!vaO!thsZ3&#=!y`t)3Jv|13P(cnNT758Wo2 zgYrn|Vw{(oj}HOom0qAUP1Ej)H(4*2r0E)W$HhOF{%33fW1%lDSne?YL0uPqkCZI< zxT+3E&AD%(EBr#PEIM3zv(x6bCs{vIhpI+#p4#p4SrCP$`(jGyb>13=V)XGSuE- ztb=Rtv(<_8Ipq5J00Ui|knbr3>BZgjsyGxpx5O^F9HSG#dPt^fqg#eB9>H*>?D7%n z6j?Rn;P5qEN<3ZVm?NQc{waxM5VTAP{*B2z_SfE1Vq=5I(2yf=N8bYCO#AkJWueL3 z+?>wRS1*T7OsbFF!SlTi`;aOM)eT4s5S;=zt}?`P3gmcONIGqL7j@+`rIbl(ZKNL^ ztQGr{lP0Fqr`lcH!IQgXxW|#~~ z8YhZ2%8Vf}>FH4nz&oAdUM`3Hk0+0 z#ZoJgD<5g#9&AyIY%ZVaYs^DXUqdBf^}I(V&+!Noyk*x5C5FW9e8K{sOkHVY$6xIz zKoG$p4|o1S>6mrKh-DbWBAo&t$anni73ml{WN0c~tT6QSz5l=Z1!T!bK^QO~0-%2J zc9xNIuAtjyKcZb2H-jP-5jE#7o0Qe9j&iR552Y~RjdJ|eL9B&e`r`-E@U|XLMunRT zVy@pmNdYy@%QzbF@}yn30Bh5spif>W`ZElmdAnZ z0qY#(cN)L*a%>_Vgv;qy3!1~{U)$_Ptw(?M_K?9cOn0juH%RS$`)K3QFRgO_R#N|0QZ?KRrxN&q#V*_$p>-lIVGV!Ih zH^-2IF&PPrrL@(ZyZQ(I3U`M@bpKpkrA=lx?F<+KF7y8GZppM%?cn;?zU)(Q*uOc^ z`<^-05E#GDpXK|5H9}sdSu9%vwp_ZHrsk(M=S6Uf$MprX{qEz9P$#KPuRdDIJ`u-+ zsx=2xHn8T!p|hjQkcs&teA2I#ABw@GQ|gVev+aLRQ}K{Y2v;%U5)7QL@z^;35l+OQzM*r=_QYFQLB zy0j!z=28Un9|e$?oBzrC(tg}(bXISAqaSeTU^<}S^27ubr?HQZLEu?Lq7oaELEgk9 zNbWws<&dGU%ShsaRFC-W3%p1E!$ni&V7Ri$V(S-i-&pie%Co;P z2wfvEN`5_d2c@RK5a%~B4Bvs@>3xfS$B@=YI_qVYbmRRkk<-*wJd5phn0zU90(Y9p z#sN=3pXdU<^msZq%CvnhH#ExJZ0h?}eR5(w&A|Mq2m@b>stpf&iyHK~G>B-#S$C?MoXN zO#eYMGqbP=W57kZ;woa+gSPH!b&qy;hoE)?VlS^Zd#19-%NsakTmSzo|7)*&B1Rej z2+qcXd_7R>#fz=(ZlVb`N@jI4y}u6)Wl7|*YKr~5QYF%%a1rpxm&IusdUjU(+Yf?+ z{$c8=Hv@s>J(B%98V0w`A@%-|$~-GX8Rm`>oCvPtGZa+RYR!ReQ~m!ubF9aA-f<&{ zXWqxNunta^MsqHgA_)2JAdKpSAg$tU?WE#ey&Q2$Y(A$`7vG`FyKcne78c%5;+1+h zHSPUTW@!_q)B9JuD(*{@QT>7zM|SGiZ`b>?p{-wsbv;f#%Sjr+zj<>6oJdA@cnptQ zzqDXuBWk0o-sVTN@V5ViN=IzY^W~0yA^c%Xw{S4OG4!G(4 zE`Aqle=M>dz`G7xLI|K=ZBJueSusdlU*h(y)0}Atdiy>~aLpG!a|9JK2f^LIk~N5t z6NC%V{>XQ~uMzBzmBWbe%LuN|;R>a~9i_Vs-E0N$Y~g|EX35B4P6s7qr@T>LKde$y zOY`tb0>0J;&80Q~?egpX`>k)f1Ja6B%-;Md*Hav$if4G=*aaAMsblOFO3?62istRN zZgvaftqf+v@~K0{4025)us!)OZYdD1KoIUkFzBaTL9RS$Gg zG2R%)xjzpRca%ME7&a%I|4Vc}J+(XE)08J5Yu$dcEKrgOnOVY>llrzY*wP~vaADdR zD_sn!E%obNdubWL`>MRMpugorp1BBuT&zhjx3cy&XBTxmha28DKaeo!x>VF{aiKE` z*weqpg}B&-BpWHlnzeh;X2{jX+!zwsM|59X;_2!2fG~;tvC#2%aJ|>CGh29qlT<3#9tF>1B5<80jDuKatjdQJ%yYDNXw%ypS&@s4^6dk&<<(D;azqD@WLgUjp)e(`bET zm&FuOav2QxXT4eN^i`BE+8kq=@LmlT5|yE~@(?Iu{b-y!ig~BDaQ1nuE2{h(yWDpZ z4}2A87nIK2e?)T_uSi_PWv863uGEq+p$D-x&EKK<>M!u{thX=x0&aN=d$EFi89N^@ z#~S-E(KI=uBxS>a)vVN3(_=S*TwrVDEl49)P7BW`M9=VU$1V%$`o1n*yDkcSN zc$IpI19Y5{C3azOgnx%qUFz`TRindwx$)%l4WS4^^&?T?Z_AweQn>-!r0cnGy_{Gk zyx$cPBpyKci%A)p1*iTq!NT4B+eHz z>ly+UQ5{GrZ1oi4QV|Pf>w-D_?Z4WO?eK_u9!0xeeX%Vy{1gi>m5X#sd;j34CCy-f zRIDuBew~SPdQSC9hiO-%qBGOJi&iIV3Cmna%!6Jzb{%EF>^l_)^m$sJ=P@}5#&A-q z{#SR=G??_p#)iu4(U~|lh`h+$^~^uK$ama{_dQ9T{*GvDPExMMs&IaX@!E__f)m1d zZ_?cm-)r;tjdD0!|K@xR9DxkA+o$V8O-<~`qM5gwi3@9z&GVkuh4tir1cn;2P|QZw z!IJjqD@Zmgihj-;IuW!xaD%0EAg_tt@#66)l$j*Li9f8HI**?0>T$z`^+jZ&(eyxUa z?C!hsh;J@z%^p9~8TkLv#O%Dh;P{QZKt1U_=>8?aQ6PG5N{Af1;qtHVi8@16MZ%R1 z+2HhWV&WC+A%$UeRNYg)53CDSu;?nP2LE~c3`T|;`%RS13i(k-C}hCGZSOxFjTHh2 zISgi&4Of9)VgLMIE2?JssLKM;%n@{1rRYqfDf&-8X2@a!K4g3>*#1&vHR#ZZ?*se7 z0UmmmFE+>cL%Gn7B|ibT?>pF$BQ^JTbH%WPF)nngCgPWO2cw5eJj=>=Q)?ORS<6%-46r87P~IV! zGP}pPj>h9hVzacfqv-VAri@^vz+PwTM6>3+F}Cl;e~hMdL*RHg(4V#!fY((>981|O zrrJeBmOAdJFPcJ_OT{*T3$IWxbG@Yki!z_|ktstF{ar>Ezb@Wr$X zVz%A zvj+vs1eJ)6bLtr+(I*N-)Ml^R(`O=zG9L6;%&hY#}<=Ioi+i#m8~c7OmK z+7whW_ZMYkCFlTAq_5CJzL8IVNk__q&uUkJmjYa#$vVh6LArk9F5GZY42BLlLKTE6 zS5+QPQUjy|+!Q=KQ4}o~b~Y1K`Sj-~7-}m^)zXImo3X`KRN|lG;HHlG1*?D3KLe8( zBkn$8A-DlmrHw5ai9uxd!%HNJz`t6Ix8ex}TmdC~99kXjKQ!*D)-4)iK6M51gSk$G z{pW)vis4d~{xx{%cnPZwMfo5t5X(%U5~CDt?AJ4Px0Q+YiAX&;nZYe~4}UUEGN*(p zpYbMe;y8!^;D&iY^*Rq@!Yo>8@=1>o7h+xZ+cGdtdP;JH?ixiiCr@_RFt(3J2s+?6 z*nw3rn2>J8&?a{u`z%Ls=KrIo)3MY2q{kG8>Kf)qyMKuLv!3adKm>thOV1`>xb$(p z&4fJKBMj2rNjv?fBnt@0 zv*TRKh)^E{M$n<85J!bN3d9pV-P1geU_2T6$WmF7IRn%(vj_E&nN$U z{a^0;5enLGjha$}GPKSJC!0>pFFaUea@EMWVv zwY=$p(8{rG8ic8ukIe*Xe+mCylnaRF*xM^4_{TSFaF8Hr0%>HW$~go{3&0#xNgttu zz&aLC=w8>6ZvL(O_m{GpYn-ZEzBEbiq(*w|+v7=AOVD2_0|l1p?`_m?^o#OjnUPm! z3qkFl#)A3Mz~qC zIRg@!ao$GHU^v}*w|6I?JxZWJh8iipa~~V;myOcyaGS?N)xT3N7qd}AWRl=N>R$s>S$IBm|DEUl#ZCK1>1{IN zh9Kt#kLa^#O|LaD7D!YJ&QO_(g-WC;1PT zpJv78Cx{Ip2KCgo-|!$U25bz;!oPD?;9vz8kfZx9G(;*%SDI8Ra0B9Z^G)4$ZI9)& zsR+&ezZRf3&*ybR;5{cl69XfwL7Q`62ORDW#Yr?hf!XbbP-%pZ?u6?jo(EKxC+J#a zs)I%-QQ`5F2?!{{tkQ%@gnmTeX|E7eRmUlRjvD|S>DR;hz9ZpgcBGTfe>x`OBkW=% zQA=}ggX1E*x<0!0a8^yCdRBO%r*1rst)o#^1>E+Z;MS18(o#pk7JyY*vc@#;j`w;k zn8pvp6XoJORihRxnReiJGel-v@>*}fmr(sR4uKi?PF*!g0&Ya*T#v29zt_Ok?DW%R zEfDluaB=G!MkbE9mfh+H-mA?Ilph`Yd(<|3W#kuX5I;kw^JPNw2xHj4Ks}fd5_DN2 z!Jj!nl^n6+iOBg;qc8vO4bEOAv>P=T8bX(@RBl*1D;H0wMoB?o(w~tjeMneA7~ZT7 zX@Nq91_vA5`jD9Dte%4DWSJ2HA*ty2Z?Cj^KhSIK(WKKbOXtar(~#=SBt5xX-1XCEU^QqO!woozIx+7FLrmJCq1TnY&$Ij(cYeK2 z^bWs-#bUn4CTGLpG!W`yL``?Lw=;y|I*O#h(L4(|T00JS|Cx3_-`j z;zZqF@W0;o8gmHdGvghK3U3e~B)tHyGRNhH8N;Vt zxr0F`IZa+=O!XpCeWmF+@rh zIh)43LJ}f2e!r6m*?*Y~9jsW&Rs^nPH0O?e`J;}vwz_%wG+{m)fo3CatTy=0B!WNa zQ>bY7m@bmng_{~(HT4L99Hs~#m_I4%o>skb#B4Dc7#VA~BHs9y!h)z?^SDd2=+ea@ zmrFyC%380gj`HM%Tn!0+EAbiu(;T;s5mWnl> z(YFGDer9&I#SUWTyC$*s%eG*{JKG0a%i*DG#m*9hpTGU@LnwSmM6Y4lSyZRCuH98` zge~t*34W||PDjRcRu7d{o;E9tD|-NVDNlPZICD%O`93^mut#hgm~`~Ev0!G1A%Vy4 z#K7~_g2xns?8G)*r{HPTYhl%fz6@5@Y(c!Zfu=san*F-tja10fJ#i@^?+1VVz>-ZQ zA6i|_q@lVFKGzLC9s`h$C9i4`TVvf3D}0CD56}G)eG^{9CTS-4>d}qL>f4xROhx1C zMM^P)f&x?QNOL)8$mw}P#Pzx$;!CZj`9b3r)g&zW#sViD8dvQ~o9I#C*3N6Yyds!Y0cli#b+x7uZ8edKeIiw#jGKrY`-D4s;lHMTv zkPy(mGs;yR6oJHO|L5QZ2H_N6Y2R*lv3$zBD$O2nH04T#@1ezknif$tOo5=5k)xJu8;%~*!^36~`?5e<9vMyx zA0)Amy<2W<|MPCa0;C>X`mbERLQzaLMP2_Mn$VrCk7_lE5>rFVye{{!VYe*&%Gcc; zS4O6<{r4{i*B`>R#fy&8b0R%XJs~@D)@Od}&*oc5$(pQm4TI=Qv z;WGCBVkwg0{(;RB%!n6*pB}s$gDvB*;NQ=_0f$={pmFs;G)c(FmSZObLj@aau#Okc zcQ25&JYGQf#~nBXotF2$K3Rt}S>M2yb9M-D$O^CsYmu9Yp%>%E#S3avpO(R*k^;n| zD+~Ndz$gg0ABw=p|AJLPZnyjkGmc%JfUch0r+I0XOt7DL6Z+h{%-dspHIG=PBMyiy z!rZ)4syZbq`rV=UPr9(TUbrDO6tnM~1&(5%26p2ll-$i zMrEO!NL|ORGMx@HpAq|1681L}B2H2fn*o9{gwA`bWDXvB)d|_^YL;KegnYH(7n4u8 zbsY!F+wQ`<2vXF{Kazzi!fuAoDTO=93#Qi1vS^Op!2t{JkGj`rZx-^IH(}KXRNi8y zm&1Z(;Pk)P<@|~Aii#=>Rnn9oG>94E*boMlY)pbw@Y;^EsTGO`QtM>-p$8FOslB<9 zmHLR;SM;;V8f|cJa2XV zd-_4jc$tbNx@(2$GsbD$uf)*t08BGXEk%5}P$_1Hv2>wF+M%uAMlOR%qDS3iLlcZF z2X_k6`W9OKPyOIE@A-?o_;pfrvlV=8gTIxk`;S;_eVH-XiwWCyp>kpsZmu z@%o-+QbGFEZuCyI_75Acrq}_32~r5Nr%YcKyAfa>t1dW2K=vqT_%uQpMy=44aJS6Q zORt8M5s@#EweS!^>JEo@FRr&NgZo)IyDZ4DYQEtO-0e~|2DzQO)jna1T=#`cnZ#HW zu)MC-b^B#kxe-O$8NJwS`vraMQ+EVvu-4s%WvISL*3lnns&_Ll^pe*-td`RHA!5-mlwYyaB^r@ z6)(7;z0^X92K1p*!6gY00lmgnw5&1fqt+$o_MN-7_4RP9LD?@y6eKNTzIbiuW4yFk z1kz@6gKA*^+|ZeVg~h!BIi)aWlq!Ql!o$Nwx0}9zk+WNMb+r$x*%~}t}a}O-6?Z*6# z99e=E)9Vyo%~JL1)Aiz1P?RhUoF4zv_!_FNUSvrX*Os*ER}6cTg%V>fd&@ptilEY?pBUwk1Fo)W4C#l>J7En)*Jxgsby3nf+RB5nYW&YU;-Z zGgNy8CWDe1`wp1}4rlE>TqIM`f9UF>M=^bFOXO`^A@Ki({*R`wjEb`R-X6L`x;uuD zkPcx$>H(x%x)B5fq+{qt5RmRry1Pq2Ns*Ec5v04}J^bGFUu!<_g;{gQIcM*ywv1PA zZO^&^3r_Vl%8#JcCt_O=|i(QL-B zDVioHAp>AbyG7wlI%~*Pfr%9)<#&MhTNng>meX`x0>#Uj{~MAgLu@hU~} zKepn>|Hdm8#VarwTH{>)vvGG^1AO;sYUCrLcu_L!o`?0fWzRtD^fN&#LN?>KZ| zZZdXy0m7;b-p$u$wkvN?W)_z6sQwG;JB^Nye<7pwoiwDPls!F`1%^H*am(p@UR#x~ z2E7Wpk7x=C9=OC;CN32D+{PS1EE`zLuE0c~P^39zSv&OCb;z1eZ93#nMIq>`(u?z^ zCMO)5Cc)=z1NdXqE8l*MD)8KR*peVk8THziCe7hnI%}y@$!O)P=ZYBgQosGG4#zJq z#crwk%}9@48rVhU!>0H_A#Wk!%n!~(wjH%}pPthkOJwbzI<33i zOi?8|T)6a`GYhAtaw0sQZd!>`NbPh~YOQ3Ilb6r&1&oIYEftm4+lF-98k!YCrkhao zkxw1@&_X-of;4g(isi{SP+aLof}-LgTQ#I=X!1@7ELnac3Fdd}i#Zas`Eu33O zxRp~czFsD_dLfj4CHIk*;#KFeNMTlTKGx;xp%14DGH8fW`NG(Y{#~~J?_`hGDYCE< zbYVj{O@`f$Ju=oB2@IqKS57J9*gH2z67%t+#qP|zRL&IjY|a9Ml02Zz0(QG4$AT%x zKAx}4oWx9t7)+non0_G>3?tXhTyYacr|Phq(oXlSL?R4GDkA7J~z>2 z4a2mF;*x2R7%;F(F#q%F$F{;$t{EoW6S&CxjkOzy4b=d1 zSc?QHP=hiFzWYry5jnxioJ)%dZ!+1l`J}*8L4A8On1#+IKF%!GTR zT#Vmv=*sgPx)fRZnpsn!=+o0$_&mtBs}HcB<05h9AZy>%^1Tml>l=45g6fSSKwGhfGoBO({ocF}MPb2elryMQ7gf!cx1qG&02O}q< zpY;BQ9`(En!w&+d@F9dQuauoW1Yfb_ovF{GH^~RhH3T|H#2=|p@)S#9+aEj1|7QLK|c5?a^eee<8KH%Bwylc7YI z#WRN%hGu@PFGD1=e)sGb$@3((M8OM%aw%0I>O^Y0pVDQcN!Qv>#|J0nX+w3&{z%S$ zR4S9lI4Vq|KooeXgvq;fu!BuB5M!?ohEf~*oC~x++)(OWZz+}=cVGgt5r~Qb!22Ri z<|l!J4820p)R1MJsmPn7jzwJb*40SYh0NOp?OgkFQTA!fW&c}hH&F_(lex5TqN1W! zZ}d{vSRNB_E`4BgUwWrIk}W_99w_!(z$r@Y+HU;}{m0c*)85Qsia!_lJ6RDW=WbJy zK2c?(l%oojbKpBqOl8HjvnW|0ji;P0_gl3nP>k@uc@4^A8-HgXZfnXS4LdZ*0Fd}- zX=^j57kjF%y4_Wzn~qnb`5r!Hd@N=buYpf%1D<&4wEw57Jbt7 zfZK5ZiRX8`*^*2Mq@n!X`5m)r4Zek+KJxsnDz+S*#zO0vURwExW&|R09sq>tGkzez zi^%Ga5B^Pip2i9ti>Yn8ih#9ZcDZ!fZR~Rx8Q{5cTW#9o3wCW=>QPI#bW}Z(6carB z%i7Y?iF#boeZe@9EhyNYjlm4c%T?0Hdn_$`0YJ>*W0L)H>N$IJg;h2&u?SPw$L-}r zc8>RwdHF?6?ODVHs#-OdG%sPp^I3z6Fb9_4@$3f?--EMpGfPY4X~hA<#B-xRlsy3@ zBDmOnaA~4K4K~XcTI2lY~H;x$6X6nlaw#t2(45sRQ7#Hj&P zBfOK8n4rMqJkV4LFHTd8UhC&VZlz-iq-OQSGN$ot2%1Bu2NOlHN|V`(@l-sns8X~T zDv zl+8~te&rh6hOPD%95@zzVL03MVhT)&aZ2-Od)}SVxQp0=X5R~)kd5xv>9?=0*3}6M zUYa~>e8;xKV)RX)wvcFyw3tXnJwdrd!R7^gz*NZTcZ`5NP46qbmmxqKzSn-LGWQ6- zf4?e)X12rCma9bpYnzg)n|J$+f&N2Yn1}!$Kh)HvHb}Y#uG9&LB=LXHr&)g8b>kqS z|3#oO%SPlkT(7br-7nIjz27ku6^cTqt$w&>^+?#!ZjFR5Aehl02R5jhEeqs9G1Es; zMX9OLsNL|#L~o<#Ji7cQ9qKdpGaNs}xt-(7N=a3m-^siPl+IgSx3uQ;!uH! zw*to}QN*6dzd^O(qu6Z+7QT zr0GXDPLfjS@d6wB4N?+G*oEh9%Bm}ORR+Ks&=SQ_WUE&}be7|53u z+W^xwdh)`l0SZjQs5u09-P|QRq$55NlQ-FDvf2P?1n^nSxa3|irBgcs-pea||C7X= z^@=7PhQv{ZWW}Bdobl$fvyegtQg!HAopRdP_a55Re5sv%CbS=lO2Uv|wJyZ<3xKV} z0W=PJ&i$Uv`^Doy`ZQ(pT5&tsl{Wbs%s*VKdP7n9bQwSB^GctZbsC>NUh(5EtViq7 z0X?8e&D}+^Vc`Np8A=M;iGM_I_g@Mbr_p2 zUSwf`V7pxP=(?q*@ja4?i5ckza){`@IAN;W!!Aic?wFE^QpASM>y{Gez1Tq^nm$9$ zbneBN5T&SWJz;0!L{W9*I)el`lCTqJo?Jx2fv!L;IEcNHn>SaY8k45W>&sjA^)u{yOhjy3=nQXID&VyDK>G*j5redM$fgCLn9_byubKsE z8J^fIy$HixF$9YYbXu#1jj?oI1XrV%9SmjAYB0^d;cILzB<1VKG;#miWGS}X3^8kT z`k}4bp-)t9MuTNlonhd;-&oFBj(*bO3{tsb7&#r zV?W?^Fhdc6Mfo%Ln=pAAFCtZmW2Tu(Bdk}mSrzUf__Thj&1bM3GxHT*azqS%Hse)4c~(vWgUt33gW(E;ZZn6{)-Z4duIOOr&txk zvZnUvsgTOU)i9_ruMRvOs6nTMxt4`CB__P@+AzENS#>60Es8C1u-@Q#Ozb;^B2=ir zMy78~raMlReDtTUYf`Q)R8J$2#3B3{qrVsSI*x??AXEoJtRbdF8hH>GK<>G|Qi|4M zQ4vvm?Ll2cl$e;9IwxpG&BQ1BJ0DWvV4r|Wd+1gAT9`Znf7wArtKIBzXw8&Fj7a(0 zJ7z0B^37U(AZY-4epJ<$=lq}~ie~&MPB2Il!0B$(%7M-+;N9nPi1DTok&+F5vdQGq zLIqN@IjJt!ERLg19tgUC0jS#UR4#|XG~@*z#ffE6|G@^nY9J9?z~4 zWOx#1_@BG#v4I&N&4HxFa4d$cmSG?1;EtzAHj`PGU0Uy(OFD9VL_6}Y`AN_U@bIW1 zq6~!LP##U?+!29X?t8FRc08@&jiWxizO$WIwhigF4Zn*Sx;^bn8pgO0b>8JwtJrg@ zt^@nU|yvO+EQFC-JYtB4-1_1~bkdY6I>_T{y|D?6;^|K>M z^SHERirO43&L43=%4?QDPO>(_YK0QMw)QZ%k!*138okUuwjYC#?wR;KDWx=WGqpZ- z2is>2@l4TAY;wCGx(OP|Jr^c}*}hI)6^Tt)gfcuv`x{adZ7SaSUl!jU`VDwSPx^5782@*8Kq zEzY|>-LJL6MAcp(pN!C{+gV&BBUw8XbZWifOT+$|h7H>9<6AV^Uhr!|JeK_z&WNn) z0GYaV%@DR0fHsrG{oK==_qd`r=4fmEnII#+sjrC5L*dr(KX}J!k9gY#Fm(1kow|Vb zT5I0;*ayy$r^X8CD+;C^yxsARmixQ>xuYoQLUJ`qR!7EjB8L@GJRY~XKn*7^61gl% zGs(NRi&f8IoDv@BNH!|E*AoUg&rSQ_KwOQ@7vzw)fmB zu_?dZ3Km2ODD7YBq4i=u&>H}J6%|+X%5?L@Yc~eX;|B{? zAQ4MX!3`TQwET7fDw6xE($XyJIy<%7WwU4>&xh79HW9S$D1$;U>6>fIW&Jr^=4c^9 zp`=2nW$+zEYv6GWFF3+?3uO52;Z~y(O5WJ{n+4G-TJSs$xRCTJ+CMDWpor>;Aa&f} z%7ybyxwZIjr1=~}zO0I)`yj0en<$zG8)^LIEhj*yyxIZddpxXqPYBK|wXc!L{~9PD zdP*vHGkbMNBd`XRu<-EX0d9YUFPSbcvOU-T6F*xG)@yVS`JRkn`Du%{@~E6QyhGqp zp!iotUk(?-`DD#%@I68OnbGOE`?03tq+QcwxlqD5i8D}|t%J2N2+*#mygYh-5t1%i zr|!a#${mGb4Jb*p9_R)Aud(d)t*2N%HaK0i^BR|pKW2*)#wN1Z@p@y0>zYNbrF^y% z{P`~iW`3zDnk6TgF)(vnakZ8u*?;JN`o{MxgmAWWk=M8*>gDsu(Jq+A zVj@DWef^PP?tm})psgKQx!V|XuDfGadO3w;`dBhx&O2$#?|dgOQTrDyGGBHGTYv^rT(@1LHxQu(Q|)#cJmMt@$R8IC{+rkSATa0*CTiybsG zYK7)52|lNm-*z^<+Uj{#DNmQIV)60iWjC7SmDFC>;{%i=YOe7E<+s7XNn4>j{DGFs zZfrih0LDqNkk5yV=w0tarY#OfS?K^m+aL!(T8rhy7U8u5)>WmyS>d4F)8FeHJb=Kx zJ8*9u{7iD{D&`RUKi_uhahlf}e(Sh=K z8;A7t*lpJ@bm~FK@!*$D`z;~>wLSvFQO!Y4vci`Um~7Yd(3!F_mc#|$hCsEdkTMg2 z@KG)jul$Bq*pY{R(p6hR2q$ZUcFIockx0 zFBlMu@M|n{Y)y;|K@MC=dn3xv2D|FrzV-d zSsjw`xGl{WH(pBktoa?@qGkGj+j$BI0yi$Z`^hQ@s^e~WoEt+d*ar0mFDglCU8Ejm zIOMT4Pb7rv_zgl`iKrdJR=zhS)A#XA?6^(*OjnEV9vVWATfVe?A|~blamn-I#nUcp zE0_tXTczl_#a(WyLAnDkB4{%%R~{k$H&2a1GKW$0igWOi^a>ACspRTp1b@eWmgJ;3 zW`svRpFfE+<;M-H!-RDI;4tnf2=GJ(M=6+a%z=s;Wh?N8r3E2x7)tZGEdw%E z;RI^ew*5xRIMEv@)oGwbwPlJCpexi$v_>GaIykM?!jYk~w-o7QK-xQs-IT~gL2 zPWGrLJFRhGnx$MDt~7?6^+@+xICb3kU5@Go@Za_P$LfdOwpZJ4c9o?vD1JRz$1>8` zlU^3~;mom9ts6*ufts)#^K$u?_N!Lu*89=VCp|CBXTEO~9hdveyO@`K@*>B;lJp(T z)`qlOwSWFVmHFb(?3m)Cys57P$CJ_zICxC}lxg@B=^JJS+qLa9HK=Mh$Y)$eJH<`V z!2&kd?(#Ig;~`oa-oNlFnyG*pvB8F;mmRBhN5SL%RD#c=q^l48Z_Ot2vqp~XSA_o~ z)29Z2JwUVmaa6URbb+P!#t2HJP&Z#+5#U4xQ=P!WgU{0a54HrBi3w-+wEFK6=gI*& zbLs^bw=L46xHI6NQ!_bL_er|D%-c`@gR5!J7x{3!t31z03=T>k6x=q#FmW zt(j%I{Tw`+{OFHtVPSE#Yhp4`n_}Dx4&&;#=Wn&xiEZnVh}l13@I_3t??nhlMJd%T zJ@ZQ^H5QMv!b=Sb7j{(Z1pRkUw1=>|31r9tkc8#xj$Q?31R># ziG=-Ror?^oZfl_k%N$Lh8%r$qPtSvSn;PG17vFVS$-pVqN2;*!(X#HWW9i-!h8e7q z(kSqXZ^TLBg1`{mgXVqQq~hd|W5}}o#>=HY%)cIuDrhU6KmMhx8Ak-j67b}5Urh+)6%ZJuWhftYeo-8~)G z#uv`8pU)79ih0zvQHnG$oZ633xmu6RGu=-9yIR*b;UCXwA~%FH-1ex3c8EjX%1LDC z;uC^`mg>W=1mUgJ0`&@=8nX?#_KSaIp7#y2H7BFVdJS)h*^@EZW7(Ch^rfW#gK2C1 zwY69&30t1S7BoD~nf7bNvm$B8|0~+?!@(M3SAigPu1fU!0e{{;qwsh--9|txx)c|EWE7 zA1k%n;vKRVvV7; zNc6a1E3OfahRUBf9G{Soumb^zJ9?AW$B|}I6!`H#YC!fY_zNJF7VToEoqJ$~*CnZx z_>uESHE75dOrkd%hj!56XO{QkE9!AyD~s5Pw%wJR-z}{r$`W}D!t_*xOV+DMN;zc< zkN;_ys89VIwyx!Y4gkJ9TvGB8vD0@AMfl%#!Nk|uJiByPh4_($Ih~Mwsp_Vd-<8uK z%F%3cOl%{BPz45pr8YO2mi;b`0K-CYSg9X8IjJ5~*-)ANu3<#!WA62DIv%7$B0UdU z9xnIWC0KBDdNJ1FQe%uxj4CcYf0ehpKfPVqKe3Td%lX8>yBE(yaw4e-0;@h0_VGjxe13)S7Lr6^E0{jkImN?9 zS3x0*ymyW%jpd7(?t9olL4)5WXucZ%l}MvF;C{{g=W}{Z3aHusHnh`ar%3p`e##l- zU*)MmQ+rgGA%Y0c#A`cz^4W^UK1+9tyV-(tb24)B9x!p;VL!tOD5rA`DxL)2|C7uN z4%WO|5CqC7W#uIGGHo!&3DlnxfT-Sa%iV3=P!u@)s+(nDRyWUia!qv-il|sbP@Jp? z#ow)VN=^B2e`j-PHm~%4RYe|Q0qM&R}FBU;u>8Odn`F&)t-6#zt4Vep@ z(2=^M4$r&Z<@T-QBB7&J#5|c-WAd{Vb=Nq3-S3)~!rB7d@c4~h*>xM@39c~q*MBUO z(xHlSwX#%7vigTq6Bg)hGro=ggYr}LsK7GH5z&We z_znm{02(H|chq;(G9?)<=GfII&NksuyQTHtRW*z9z!!~@{OK3LL2TzFR>?#m6J*M& zU#6xYmH-|thGOz!2cIL(5x1=u-%FqmO>TpkplM^4FFqr}nk}Xeav==2+Ntm0f>WhB z9NTXEZjZ}rh~l1^?2O&z)&mvtc4P|(=)B1+>VkJ2u(V9LSWZIvvH6JkxrU7XXJdWe zb&5`^z}uW0Ta$9cq^PN*?;HUGDP#9GzSq52&ff=B!N@vzrQO|!gKAvtVHa`aY$toH zKkjYs@<`AxLNFOzzIh$Zd(1U0yaT`?t;B^%+J@)SZ2sc)K|6oApwTSQ7}Pl4d;4B4 ztn}n$ds2lOBBWWOrWY$g^_C(|h3Nu|u=e4^M+ih~j&YaRGM3w|ThRAS%Pt&XKqRs6 z{>cwsj{_=DCNTYyQLBEDy5|Y!8xu$3?l>bbzB`THIv?FT{;i2AMK6agRblX6{DaC- z!xvl9oG=LR+OZ)h%`Q(I6Xt8UBkkCa_K)^`FWmW}23E$qV|=4MZ;!9zH?9o9HX?5%6Rj zRm}t|_ZJ>893;D4@eg*QUsrT{D~6!%#`K2`tHkDxFYAeIUh(Dbrf(1*-OqpSs1zf0 zoNuP~CGkIefs)hBbJEutD}P-(bE{NDkR9;gmFaWQ;&~Uzs>c3#@q^hj8WqBXl|(UX zrX(=L8eRJ*9;Z`G=?mf!R$LYG&@hCBcP8w~M^_skLHRm1a610dN&lpkZ>xEmQ_7x} z!0nis)B@-w;)+9mApAE&Fu;3<`iT~u{_FI=ud}-<`fv3kI{sPhxOqch-*I19XB;31 zz?X?3>(?oeYCO*FcCS;+($eDcWm6c`Z$y4DLtdPA!csBang+4|t+Zbjp88oQX*ZWx zHIwWTIX)p|wS z6?rDXoi`*6;ED+(=}@5}dH2r1Ri_^T8uEa(*W0Fb5A@ zhY$X7%8S$z?`sI0>&rDMr!)6=6A<5cok{1MA}13&(EslAJx9DdumeB&kwBXllCyH@ zN)#n4-yNQ(ON0Uh&OUoJXq{IB-03~5%eij?tq45^1_lP34E!*KNBZDEJZb9mqk+iF z9PcOB;A_#WF+yn#oso>$`xVmNrWG0p6Twz#Z@GRQDp2XNd3;lvP$^kCR(W0q6PkG2 z*iZI|YWX1m{;WKRD^aMu$!V~9=3`PFOLsSuE$$?kH7~IKmBN>8U9WP$&J#EkkBb5x z2#o!&t!#Eb%@ywrC?2PLBA%(C*Ju?Z3i*sxpdvI;lxnW{n%Ilf1lmL-`*1zL%d*ee(0r682qTN|Pk5$PIz5O8dM@By73`1DZn zA>e;hlF=px%P7D|ML#jf9RLMGLi1K1;Vk4Mz$1a19N@Dgkq#s z?(irrDM!2_vn$a1{ReSl7hmpxLKaO!WEjl25s0+48;(w)A59yVtw>ZSJ^>Yy$M5x< z0Hj@04gA!;J(z{fDbdsvhr%i5#Xy0{$RCU2X7}b3B1dvLrq-4q2csz-HK_UiZ*|}6 zcJ7S?c^ZL_h0`A@A%$i`5St8RqWAmdSf$F~GK&RXGr1=up)^ZI4--YPZLoW%S?q+xsQ1dSwtc=Ycg}h_ZxLRh{#)!Nx&G}81HdA z6lI@>JjN_VV49*>^z&MyB#B?WMjq<|7qrEQqF;+3rU2m$)nd2$y!L4%0Po-CiDXRW zuB|{u;LZJp&wWjwWm$`Bsue@PEr39We}r&nfV+x5MZ-c)|L;SLzI87 zr?EN{)n>pXvl)Obb1u`8?Tx0tm@wn@OPsXBRbfHBJ!I*5@AKcPMyV(M-u}nG2|kpu z8VO=hG(EA|{h4si3+&s4k#_m!Y^ z*{fp12Ox>W^>9FMw$DMJcgKO4E?v3$+KK7N52deGEC15N-Zof&XX8Na7IKfc-Nwnp z>a)*EEg1tDY7hvpHDmTO)N*X=;^!hYqUEY_ZYDOO!5~ENwzQS;*I&_Enz+T%!>sGb zYqN%e#Zdc`nR}(EYo^S6PIe}YNU}}#QawVc&9W;_o*|HS4E@QzvQ#DBsPgzO=+q|4 zH;?^f+*tJfQ2`$Jn8*!($c_w#P@=j5LGQ1+FPT0pup}AM)ksc8RM!j>1TfqPF!ka9%A^~ z+8W~Ow^?4xvrUw3S%hl4%jaXzD6eNOqFE|$mLY3m1Y%QBDi`?-5I(r_XzA2W?$X;g ze+FXOXLGxu70Jl{bAOsvIEn3b*7%W;J9$igdgnx;k939eZFu=lpcO#WG}<5#{u^wnB4yvT)7JrPo5jG zLhmO-N!ONu&2ES^*S__z<8H3+CaOrOG?lZ}x!`~Ixi}9AK6#wm3s@~}Vg`wNp zfafXDLPAM$F^HtKwL$;;etRu^J(%`}u5?K_tI#25*=U3xV)ua)Cq{THV&vjY4 z1BK2RwhBWm!^;E4zHG}w)(9kZ#R$4^InvE7wDf4UQ^e0oLUg%lVaaQPvu*$3kEasi zs~Ae@j}SlYdOfd&mO~7=Y;-ozlNELi*af>eNr}_O%gAknq@Zk>%5Sc^fH>=a$1I3B z1&|_t!VjTRKjWs;7Dro+OS&@H3Eij2d!xYkCIiX2W>yk@`~Pf#si}E$UFmq{dlTXt zJAKe}eaZ1cMg5}c@iFpJKrj@BjvbtDz;}pv?2SNmbQUTm7lQRn#f2dOCXq7^{3{Oo zKYn-N_^qJY`am>Fv_#{{{~hKAny3hT#BapoqK^^Xsj*14@t2Hzel=k^R+mKd4+pUd zKkt`JJkgtO`6N=^L;7lUxt#>5*^hPvsz|H4b!kCXfuydoi{~3{$Mxnm9ymYcmHTgq2bS2FomD*3a$Q(<{l{3E{iAZM}|fPfr)bfQCd&ZRJj;H3~A=L z#Z1z0`~6Ps5JmQ)zu;)cv%M@xmi`^!&=Q9&_g1JpRf8Zet2U8tzk;zWOh2=0bt+k9 zkIa;(!@MyOa}R#UIc7<58QTB-L?L(w>g|DXF1G&U%6oW(AmaF)$5k6DIeq^dr4+Me zBUp-P;I%Erv~(j}EWnNRpyif^2oohIK8B=~V5V!c(~mrUArc=a%#1E$)Eo$vn)k|5 z%=TLwhP)U$Wie@p3x8<-Jgnz}*(P1&k}2snBppNdUzt*ehr_B`F4mPK9r^=c`3|r(|qzIDFh}e6u66)MIKV0wH_6{J+H@Oa?V5g`|kTx zmmZVy8TRsN`Djhy)qIwp7e|WD8HDutpu_^ldij6Py6XN(9Kj$f+B;YfTj`}tb@#? zYp8No3H&R>$huO4Uh`S*J`zuYv2+^xIu~(A!^v~QhlZ*GjS@QW4VDmka$Xp|c%L_YVF!gK zj&zx{nz;3KbBIi3ImW*MHd6bh_*~ASEn$@}Kd&wieMtdD#Dl7cG~tGmKkcC@i`z6< zyiEmTNIxvE3Yr@kNT%R7Nk-}bZo7R zUg7E!uS*=edNL|zryoe5ndG9C+lY{}JO1e~q{g0LkDIMM#Jk#{eT=Ywyf-MEVp}=x zhTFm2MX3P{yg{paE6+jZjJZuxO5Gp1Vr%uXU;bp zk9qf@LV_L-VM-udrpVy`KcJiQ-K;Xr4MF^v`ry{n*KaR;BrF=Os9|b-V78 zy>dz9)pdwa8oa4#ACAInKt%TG2$>F6{2ixysaY<^SA<*q+||WeEt{2ADSnKZcbOOv zO_$quD|m>t<*u^ot?H1)SL!3Ug{IoVmlFgE{zC^dp`m5lns1FFZSk>%?UVy(Ct|fn zcM~1I3@2e=MS8b70wI9;`ETv!lc=)H43v$EKT6(B;@DBY$N9tBqofl3ntw0?N~USW z9nEE@-St4~>r2AdGK-jZq8+w4>eU^U9G5U8HfOrdFy+S|V1h`# z8f)r8lBHKGpTZ0N5Hi%AlbE@@7bx(;TFwn6&HrCMcfn1-Z5a_K_rJgGf4gGK43V$t ziikq+p_n}$o%ht`hAjj@5A^qWodtr#*&3cX&}zRxLKOL)>SlsTgz9dczLBmsX7^rF z_5@FCC4XM9_|@4k7YV8I_N@qHW;5=W78oN1F~$AzFjOKPQIx zdR0G&G)P}zwlIsFh2%!ocoR|D4rvlSK@hF*$GfV>?kd`=hLzqO^#>)|M1b?DrAkWx zIs~NHL^&A+*1WUWdhYb=C@B$TxP~NBt(;#?#o&;!Jtb~)HtV0jEuFsv_diwPbi>E$ z*FgRUG*30pEt=O?Dgng&iXp-IrIHROATY719w$HMPCH7eCrNzA30#*hsgv1Xy4&*5 zo7$GJ(B=I4xZ*FpLJW!4f2N907hso+>WXwj(|}f3fj;-xa$O@ealQ6ky9%EWZOsR= z9DUUX+tnn~+W1F8V8R$el?HomhX;c`JH}LEL;dU|k*u(Gvs*d%gS#unSHj6anEw8! z(=weanv^Y9k;0d$X5f^oS+C+y8!}k9zbUplA64a6#xkK!O`jJ=30$syp7DsBs-jgs ze<3u8{(eL8;UuMN^&6$5jA$Zi=nB?^gW&tU_k-qRWf!zs|EC4mn+HAs-kAwT@%m@e z=z-j@1bmN1HF`;Wx)`os4d-~ufrzGAy2?0x)woV1dnwN@ieFxny^eJGX_;%YVaARs zyuciQ)!4ZR&H1!L;|z~TG1D7Yw@sT9@_GN$`vbA(_ZziPUspvokSD`pPyB?1$u<}W zJg54Ofw_kPJ*^TC`A|V#W7O~De^0sixOYmBc>&*XiHl=Ay4n z{0QjG$BguSB#zD1FGF|(;3BYVMA8D+?T9Yff087cc)HCf7hmk3<9vYG6pi7%KM+xZAs}-W)*{E zn)jFp=pOXvD4SZ(ejG*a&}4hBy3&6p9j=?dN@S&zYF9B=Oi(Oxo_Z~0@#njcA1li5 zzdxx0?%o42N}vzRI{RC$0CQqO*M#5ocjSl9=qssCC#fe>N83gcodh6E($BWvTAocK znJ$?)+!wZHu`pz)>DpJzX8%DTo~6g^twN03b+jRD>{3!f?nU_h_?`~u#9h_bn&w9b z`+2%ADnn3-8#~%hw#zSgU_ar?8hoN_2t9Z1%$w)$LWF6Ds7z7j|o+9>%8QyfB*4lPn()+D2) z9Xy{kax$iN&|i;|25)E$NZ>VpyuXz1^O1*y%aI@E%R+?GpqP7a-uZp*j2%kc7VCq} z#k1kzC>5k(s=_3RA(+BV2pWAnBc_mQ>e1s!Z}Hpy8(M_-qGM(6*fH12l`xK6151F> z^BHn=Th)qfXd@AUEc3QNooq$BK>E(zB=^qq))lK>C>V#H{M5M#39d-A{-3uQy5 zRKj;SC`xm?wUW10kG*`{;<3_=wo8Aw5Ece%_X{-WG+Dw}m1Zt!J_^SGt znkMM!J^ie+dORs@Y za|ApM{hEQTj}%)3@Qv4pLT6&Aa04lLm>C&0VJtIoyT`&mSC{@3k) zF?o(=K0^V4>0@srq(gJjJe^3zkvsD`TP}TUR1C3eM4xBYe1~@s9d+O1tq!whW4BOt zNGh>T4#ql*qt1}it#qC1D9^AaD6^noD-B)b??iFZf9$c^I+n@*ab|VLW~9bRbc9$! zNZ6_!IiN(SiO#T;Hg);GAVg(l<<*%tj-h6$e^Kz82a#ys_pjMD7*so!A2x8@=R~E> zp0^sKzKPeTkB(EK`-&a`*LX|oCu!x-TBQWJXxV~bzn&6-??c<|^E`ht?%pm;f1nOEM|c{HcKoYO^_=QlH3ZLiH6}~pHzN|7 zZ{$n79IkJXrvD0@>LgVHPQE+oMoYOI=#7UpAfNXWs;)P6-B%Pq38g0{MTOUg7)SVH zTf9XN!mY1)1rolNV&?M@Dy^#I)Ad*(u`yb&Y@iC?UIM;B1W`{a&F6tWGD`3Oe<-ZVws-lKbYxxb2FpW zHd~h8UZNxSsx}IXUFSDl4?0wnq7Qv{p6o&5gYrbS`MZ%mf#YtX?;J548moW&G^)O( z@p;lh1R)f?7jt`Ym#T)3$VBj;jaQ&B;g8B~lW{tCa;~{hs8I!|b`gyPmr}_OaOeFihCp%p?ii-$*xw`WrY4c)#gL?UJW?a5f zKNh2oK3@ryqe^j%3$G3^!l5&7XyiDwxO3{8v06!pATP#GB!y~9PK56*W`18AXnfxO zN9!(+tq53c^y;i}4(@k4wssP?L`7Y$sc>JO;gfyVrQ-?_d4-PtX*eW0g@>8Z!tm=C z+=i7G{|p-8G1Wh7&>U9Nc~H$&o2jWLgMzzg?PlMu7MyS)XfMD5EvyQRvg zAFou46+?Dk#E)+A-3jHYRgYpAqH6YESF@3b28EB9?9;>K3MP>sRKy{TpME+vy-6>o z&u&m$&ES$C{?UK*SaZ4c=uSM_vD$P&egHLEDkFcb0A_!r@}~C0^~6!*?WnNce@Z#~ zlK2q4{6%s}WpUW&-tI*7pguDA)re16c>eg0R2o)`;}m z$l6s^C(2_K19Os(ALXiM3%cA`3=_g7KGmqh6eB?#Wc$VMUC-;H6yD!dvW8}1y$y~$ zV`d4Z<1A&dJH$9yQtdgWyZ`;jzHTq|9#1#aKMsevFH1&l(S2T?u=4 z&;a+&(dsKa>2C*4_F9UHc_h<3FT_95p!^d4Zy?O5)l3gxL1p~h`=x-8P!sm15(FvB ztnwGa(A;>c#(OFbNNP@w$)3BIXbfwK`JicW+Y5{5KkFVL>91Y^Aq3k6-Ej=oS#K^ei@e{-G&)uUpWRw(jotmo@7G=+z*@PsWg&-x;H&krIrSrM>V&B$k zeW|V4M>g5U&mH>>$j9}Q{#CqkT1!5A0QS{Tqc(T5!{v@`H_-@RT(}doDCH5v2gSt1 zy$y{-W-I#cVwApiqe(S*0z#XxATm|69M6HR^xtca_mCHVza)M75Mb)=?%tUw2T{zC zb2Be|>2aTd&;ae7P)oFwd$gkHD$RECbS}mAdWnd*JVUOm@1oBeMes1JVb&c!U5dGD z`Q>AzjWdPYt8&phDg$s?dFoXDf2nPVa=(`ODf}2vI^a1{WEayp|Mhswr~qZnStjyi zT>l=3{u2LWey7t5U4b(je#tLvj-={a#~#M2Y_x3iNn^c)_GPGh8%_}sP)olDC9=+) z3{MUb{Ht-_gc{!?UAz_XnbU?g4?%}er5F+z<30}hdSE?p;L*&*yoVj!-=7iWk`%K|d)MobU5y@=|~q+TOAU1K>w zodk$x*_nR+Kbo#NIIi#QZ#K3Yv$56Kw$(U|(YR5Ajcq%P(^!pdvq2l%Nn_`|-`~u; z|79jSv-h5Jp7Y=XHH6&n@cl4ZQnX(^gb*Xh-sj_jQa-y2=p7lqPHTu{X?4Cmop>Is zd9eKQ&`C{q0C4i%+6`A-FFJSG|8ymS32*rUPrb360`&|W)hEwOSQ5=UM3Vsb_D(Jy zp0tghQ7-q@Ezr;3K)fQ-7N(I$M@NsRL82exLkGkwIKJl*BP;sRjLC`R1IM19zi*nM zLYc>RuZxA7$0Jy~n!QRk9vm@BmUARZAv~tm$uYD zSG(YOZ6_?Q5yc_ev%_og7+u-SDqRxiM@5_GeukCyo*9Mrn6To_+w;ZFOja11_{3Y) ziudNgLFi=|iGgZwuq|f58}An{1W%)XyJ4S&C$U|ny!_STg@>yzWE<(uD|m1ADLUTQ zpRKtvbp`4^z&nUQl-pzGbift0*)+vC(!)b+!ou3sln|;H2Id6@QX2 z@KOGaW>I<9n5aRU+1K=^uzZc&mG@5LWyr@CBYMr*dabI10Q|Gfzwd${t>SihTW zRF0yR$gHjj0&r&exQiC1PX_Yrq(v0hb@9iez;f0AACz~1VH4P1gtCf%% z22yzme9d*bQ_t9}ol8B^Ba)6dGELOwfnfRHmwHE&XfdTQ?>2ss6`Gmt?R-;wNQz^l z!d?Zas#JIj6m@bd1I=#WVckC3rI{!)Bv5LazD_(ZDDd;T{;UGwgOup!7Ff~mHa1>p z;_aI}ypnP-DZvnoQDz0R9W(2Nei454oOStm@@-#SR4b`rt3l#=B<`qX^Xb&!JC#VL zU4?S5BBHb`n$~hA98=L>pYgYM5JB?tTm>|Kx8ufJfcdXAcNR=Hqg(_B5h>wNRJSLl z&OVDBnkVGK)`GWyR)Gt?q#*|o??iVXDqoZ;++v#LUeW#3BxnN++0*N(ZKJVA07c<4> zAW9^NV%k(%YN{oaTjpem2{%+V9U)tp5Qy)iJpCtreo7#Ht=d~D;@T6uF{D5yD4XlS zu%R<`bHkZDr89{q*HKLQl$kMhVMgw~Y=C7VXuj^_L^!PZq4DZ6f}}gBl@-;n^BgHL zFY+%ZLhEw?r&kxv{7^%7MRK2NLqr4K)aGv;|Ee>GMfbb=I#UjTIhX`Vf@-6j!D;_D zxOvBExprZqz4o zM30|2Hzm=UoABXr+CN}C&5G^Qrx1OIPS~~hXAp`vY@;|83~=|%v2IzSsvTWytE5N< zt>dRJw*)@}?1$FY*3abKCXTRUDat`@h-@nI^wH7LfJK)b_c*eP+JwMGLzpI!#h5*y zJp%N7?)|ak?)DL4mPmbLNwMM_bi3XV2-_G0sQIC$S5f|4 z8Wx{FFmheNaq;*eh~0f5-IXlJGu?CUnRN4Yhp7@ZN?#%C4u~(o|LILHA~v?G5HKt( z5Zb)%O{Afr0XUAKx2;w0=(dhRT4l&vVV!LILvLJrWR-^yOidAwI+=dLi88UW?u?}{ z>Oh4%G~3nl#CL*#9D)umSzW$3XC*5yuRGBY1*RArt+^e782J~XPbRR*dAog!<4^stsIz_wb;9+IV=H7!@fep0~xsQg#Gl&#h zP)JGBh$AuXiABly4k$Y#KzN>45gEL^tUdUWzgjbOR&R$@l-&0pWd~pAN&h(b)#Em{ zrn9TQcIzOtU`W~;RX8fFoRTBebXUlq<6h6E$b^3}0xVGtJ-ICZ7gq}NPY$==<2LJ$ zlMCpUEbv;p1@s$B%&WY|iiU%H~6CQ9JD;cK%#l z!kTN;jwMm~*PqOaKW?P{T9-YZU16*daB)`9VI|vCIiO_A_KPx{ztGBAvjI>x7B33`|!}`++GV}Z%M68rZcRg}K zaIrrZDL=qr&4}yd`ZaqtqLRyyHjY|g>}gToap&fs6mb9T@)>0sktQHlr}h8=mJ}am zIrG_mi?5KGxR7hfTjVX^p1JGi3Vypn1_5Nt87-Z3xF*C}oWw)-b|g?qX{l9eTH23f zfTIs#c&AkoXVfZ>mE(aD&sVE4v$u~vV3^rCUxH{V(U7d;83>tmw3XY2!JE0I){lir z)E2}p2>H|GRUJbJlCjCL_S7U$yZ!dGCZ?xL?GhOkTKfcVK9sO37gmIV%~+X@%XkI11=DhDd3#0(R;Ne#6y|ig z%JzfR-}Cngd?<+bw`)iT&hE9Q=<|HL6m<>Y0QS3tnxMFLy*d?gj2rj3zIo zN|HKd&}m$WwuT8^b(v4%^el``h&33Jh*i^d#Q;$D+|O1&KUKPisPbXlhb3^;1e|OFbItKJi;l;kAGoa0bj7NUTx>!@G53DgS-Ynq0de2p83^A~ zy0974cwgPA%zd<6+4|Q>nX&ihJ|mR)nPh(g$JA7We`~6YfLWSigWXxgKJv>*!0d2+ zAOY$$)BAOy(PpknyDFjjk1$1=x*|=afV#{`H3{4)v!lgQV7NQ-Zu@(KuNyKlGUxR* z@;Gpu)Ug^4po+wdVPMEb>LGwv5)1G#f+{e;oPCnLPO5h?mEphnlzL0FShzww=b`lH z5Ih%ihy!zNj+DBz`Q5#^{tB1d@UVgvzPMA|H*j-D_&noBdp>%0#~JyFE88fCD2XEp ze{e`hhz|kzu7n897a;o!4f~h1(GeEKnWjoh968fP@we|usZk+8>-wNwAW2@p zzwzN9A!(c}nR%MSj8K}}Q158jrNdNGQmrNCrK^yDK;t_*JK{9YV7DM%j4+epQqUo| zShH{z19&6j$jH}e1p^K0a`3Gf5p#5GH3XQH^6uowCT9aM($b+vClL5q1jffuk59*p zq^G{rvFUbU$0l^U4H;s5RPlUiT{ZX3#q1ffiZD0(6o<2%;vpmB4+?XHUQ!RSdiSnbxY2jHm-j8 z#U`VN4rWnF86aq+(v*pZ;kg-dT8LeoKJx(&IuA+(LO{6!1V8p?@o!zQs=E9qc6JI7 z)70dou`E#v@8{1{q7@UJJxn4OG67VIg^l=S8hMK#VU)rE zGa?(t9rbV}i#ipmLrQoG?;H zWoH9kkK7HFo=Z_0aO0{klrrhR-4I%4!2|+9cdxVnEW9`b*hKk%HL^2DW8BJmcr7_O zseHdGS zgS~5QY6@jFotpzk{^yLl>Dr&X6bfz&WUfSCrBXx-wQzHD0~}9S5XO@Xlnv%wHgRVu z*C1wbo&zxfY46i*Qf$?JV+sAt9S=6af*e>QeIme=kZqB%=}vpD0d29v%M$+ekcsni zhk$Pd5)Oj{9MeBBew<{r%l^ zy5fPkxkP^V)8)^}BoGveCiMIouJG?5*=Z^Zy=+0RtJA(d!DRIW(YpBr@#JrDb18*g ztZXOd2!L(#e@3iL-j8(oj-X06x|&qF8DXB6nwDc_-MiTv$tSr0)?T&WhGFjV9k=2n zuv6qoC&Sf9eg{24RU;Uug-}_kkpnT+KxN+QhBzEYBGa%^dHSF8qjp8i#@k&pC_m@5 z6jdZovX^KH?aM_FcAj?3m{reft(azcpKWXz>TI|E*&xN3h^|ml(4*&(3Uqzb1j&0P z&(}rH`k+{IECdko^C@R^4S~uqZ()BpRm#A?01P0)r17C%AJh=v6i~K{kMC1fKjhe? z7v{%Fyqq9Q+^ST9`1 z-hy&8rAM?=4KREE+w@6!1Eh;Of5tA$NKRi#wnTSZ6BRz(%XV>&{6^w=P51eNlgmB` zh97GEo2wY1Y$+#Xmv>X$3Xu%pIamI67SyBzNte0$F6U`Mu#7sl)_hWf%14Qt$hTgwo5pU%BQOZJstP^ zG)x@{Xh#0`W(#NO(w?7`fCABerH%RpF zNlBHv+li;BI7=#YZm9d)_d?8zL+fpQ^WszebvaHlv|!XwdbdPR-H>DlNscLTRfB=1LM#lIkR-noc zy^iYKe;%E`=Jc;RWFAbIUG)50H_K2589B~{`H(SjN_c1D>rEUmzaZZHPEDZ_KVT@v zkiy(QIH;?wh0sNMwojk_8?(oVA5D?>0MVoxc^GJ_#aLpF6k;?qG#nHqBLm8mvvHtf z8*=3yauP^l8fCF2AX3_(Q_VCnHI=I%;#y{p9xhP?*d1cyjDG7;OI~HH^?Stu5ac?( zzMRh#(N;A)cBxr|L4+M#BySnlnK_3m2fawNlO4ePvZ#mY4Qo7CJFEo&SRVfSO1 z@|Z=>pY~*|Nj>a5zbE<+A3gxGRzD<7hd6&H6Zyt``2}_PUm2s$sQ<3>f#i?e57hUb z=IE%OB^ql(|PEiK`$R@2l;qo)JW zO=)AEDhv^cU)S0`iP&Z>^XhTXannUrCvrksT=vH3LCAJX)wq93A(IF_sC9^JW<|-{ zyxJ#=RZ*Y`<5rhF#iud95<9i4fEs)nilXwMj{cax>}6c*U=)%^co8m@_AU zxQ0RY8`#)~JqQ1vs1`-ADDg0_-zh#$WTDzQ_utUG{k(%L7Vf8@6MZ zfIo&x^WU?ZU&Z!@lDwGI<*I^U***qw@<=QTZhrnqW_|AKRrb(i+}St#UeH|3g}1)% z2aeIr1pH}=$XPW>2Ipqy%goOX_x=!POp96$R$&DO`}{-{RUdU~VB{jDYh*MOu|wNv z*YHXoNy)^_yfqM!R90B+s9NBKigrDx3^pD<59-qB!HN}_j%kqpjd4HVQnq3RZ=>DC zt6;ZzcdXKo&Z3sdNJ0=7&TnC(QB;(+uR3{gnX6vW4qWz_Yo}8$l5irZ1QV>fWzD$^GJ zq%ObOct%rc*v>7LFdFgY*xakH-uG$f;RdGXCgGz@H3&FX`$_i~g@eobu# z1-p6ozx#_<0xNetgCSKjb@as*LelZpo zVSqU~(^T%gJH5U_=byu@&M1H&`zW&XzXAWV7Vq2RRbbdtJ|`_&fj9A*s;4h?Re44} zp#rwo?S=xRg03B4n6OZHzJF%g*D|owtt$H7W!|MVVcjl|6ia2(6Rhhe5N8_#-7z4$ z0&&h}dkmP$UqY;Pk!7mH?%5jcd&HYE7X99;)itToFe%l!+uh`iiZ_)fqcgXJ(4HMz z1Zg}5VlIT?9^e1Y5M!sefvo4mQgdDLI390tS-=GDhf7DGP2(&_ATG_w-bN&zX2zWlOG3_N(tpmJbpvfv${8)rXnil3Yy?ng_hpUA59X!o7`i{=HnI)t@`W0F?%^} z$<28gr~y&cVX6jQV9)hT)kZ$25#CRkZ;a>HgK-$4rEa5|Elwxmch)Yr@J+aAO}QKF zO-R-<0A2e}Of1aXa}(6h{Kh}4F%G|dZ($Qs+IG9b$C(lxeavsYyP*!M45m0Komm+6 z!~9u|D$_J7R)J1%6DcH9@a1!BM?q{^StL@6Q>=AI(0y)E@_)Dvz@|GBrGbl(`D)MB z43DV$)ObuyUP`CNXb|YVtAOKyn!!L%W@W0Ht|}B-^$Ac5!V8;Zj3? z-kYJ+7^ZFZ&i#u3+n_ef4w98H)2u#+NoItXoE-mVh?@*FdjT5@psf|ug&1!Dwc3X_ zZ@fzj(sg&SqDr@M91#gZyu;0zz$DN0nCgYYs1#(8Fb@ku+=@w6W3(RaXxEz0sK^gu zY(w0_jYzI~cUAE z{7q_gppeh_-W7;2JFlk%WUOkY7>YH`1=w<-5CXm+l22lSDdintf%&b~KRy{~SL29T zjt-64gYas8iG!I+!dgT2BJU>4&qnBKlL0*qLl?d>MWj`m_`9kGB!$t9DZ)wkec-R* zg>>SjYeU6RX&q>y?o>bjo_*O@M{<-;lzqqM|GV_li-a6`XujsOr2Fk!OqAC@qkqXV zdC;^MUn=*995ieP7V>~zGOjnbLh$df=<1O?M~H<+9*fn6W7~4K*)SS#HfQe8OFW>8 zA`*XLIRw}nXM-V+kB=ETMqZCx@&3@FB5=GjDb1r-2KKHsYdS2Om2 zHT1J#n<(s8(56>bAfqcUie}e|F9+A_&V=zk={pq#p28z*#ix|L=o-RG^eU@1ipp7w zpI%GvI#L&@-!j#a0NMEV`_rMzODDp$7?;Ali)dL7uqD&y`FpTws17296EKU$)~PmV zT2#!|x|e9-CgDoW7GbJKK6!616sF$h}#3-^VmK;@dKA)v+Jh zGM9p78B}Wf93Os};?&w&aC?LVx!xUX4REj!U9a}jkgdSvq70qg)DY-UmIVScMv`q8u^+L+t8T?%mS}~vREj4D^$jAqS zR%Ou_%!|2hFTu^cKzdM^sdviwuOs-#dcAz-Ry~!rc%Wo5tJe1~w>c&^=u!GM(ddjM2aNc7k~&g?Y?o&$!M8~H)cTsj+crb2RE2ZZZbQ+N z5fjdxvx}~49S%A~MtpV7qAag4@_~p=idPG+#ID10k!+W@q{$4&n0q$@w0Rg)Fh<$$L>l|Qzfgf8Kb;}Mz!*w~546`F&2kvU za>FNAf*&Xj{*Y7-uM?}#ZJHfbb0*lr5?Z$t-=J3N>iBFs9HV(~1H5Pd$iiX{R46V5 z%!jlO6g#}|?2|5c?Lbzorzbhx@*%~nT)s?Xyrh37_kI`U8GYmdJ}$489hWe9PClqk zr=dMM#UfQ4K7q-1i@y2zdnc9NaEKdpR?eHMwphY(4FBEvTF}-OCTMiY20JSGt=NZ# zXa2i=BgV=pnK*(0P#3MT`af21f+X8K&OL;|`Jgg~W=5#+G$Ze$31Ij&wZ{YApZCJZ zVRpfhqY}lj1`J6N5`wu>`T*bPI=dz15#Eyc6)#~GH?6iTs-uGt_Dh%&F()&@5Ls@5#hG0SJm*SjR?_@MnziQmOU)1Ks^R2e#^s&n>OYj`*mZ2z7rFl2qa=~l%)dtGf> zp2B{2320((F_t4b)_Z6*iQHV7d($rq!ssmEnJLM4-~rQjwUQ^%P+C zOA*D2U3DxB(dvc=L6X(S%FF37hySG<9U9P({UQH6QaLTw z_tKCqvbG|)-^8>saK5wQN#5u>Ji9^)SYap;Fn_4kn7+UH{_zC`;oFl!I|x0OX>FGLgzhJxNp(xf zb-pPEdLFjMoKZD+T_WVVAzB__6lFuWj|2ISF7>uP;l}v>FvH^cYv<&ATFJMLNHr=) z6iB+V!&nfxq=OZ_^Ceei7=u<;h92rTpQjCfkt-zv6V7S&TIlT1uHWWg4_4+@L`s$qI%l(X{Nw#v-!WTyl6Venx~kBhztMGo z;(f@wm3dxy?u1Z=Rgn8Q&YTpDcBG&*+ZHg7%gsu`#b99vawF4b-(NRxRH4sbu{POw z^97KIN@9{6oXk~oQT4WFiXPn8)3F1_IXO9A=dA~^;PAPc!#44Eo>MH*yPDf2hh}Qf zzlJ;^4|{j_JLG`fe$mwsQA&y8%C|`hoH-59QCI*spzPn-d>V2?%!+?Xjz$zsELU1x zHijHDe}6jgTgIXxR^O3BdeOTC$0VE`mFt+83~C&T#ehmBIK?g9r%N)Pdp2+)rfuxE z24cfw+G!zstChKwt|8`xNJMl<-M^M~jb#C<&5CGr@Mot(5lIB0i{Eis68*eLzEs+2 zPpKk0<~d3RtxZm;I>c@($EyFtT3hl3I^}`n22=6MbFuFvhR5z z&sB4~8+3u9E@VAl;T&sGiv60fA=4HzX8Q8feygm}1hoyB#(-N^kZj0b$mD%RU1wz}Qia zzRv8x{j|-C7IO3UE-1UNN{VtU_{3Zc^vxe$77oZj=|p5?(~IxdLT=ulP?j-utD+$F z$vl$s^fJ<+P$qA?qZ%^60Vgk36uvy@v=2TW{IVq)j!^J{B9iiSqZ|E%W-;X)+6j$c zpD3jPTEaLb(e-On;7oepiI%2j4#OEIt{Q9*S=o&^k;Ikg8R+2qg0-OVVjhnAAw)?B9R59$sQPchvmLLWpBKH~7w z1ob_^pi_(^>{*FPxVWcIShbD}EQW2t&FEx)=%DE9_xInDARJI@Z7n`@xRL)^(><`};k>aQVN`{%fwkbnCrDu$Y|Mei{-nTX9}82bC%;y<|hW z`e{f2c}x<#3PD;SQ;}f*S6*Zl>D+y3(Yrs5T@MbDAJjOWPiJo>AmlMi@+lQ5Yi(2n|PQvPQT zeK1553($V%^;{AI5aPHyJk-Vdm3^20+?k#U9e%<1Jzni8Obb7s4ucBz_V$u3u5_Q|y|EWfX)wnBr6+WJI8F14CVS&dU3N)W$pa8(a_OHL_I67S3v#`8~ zMznDs3c5lI%7dATcd@IHBy}_Wvc>G;l+#r$h8dSPi$S!=IAq!Eo8fa7jR~x2No`3H zJJ%(tfeiU6VB01_N{Y>w4o4(|ir)zEJs0*Z3TiGKW!~Hd1SOhN@{)N&8xwd^T}uy~ z*>D8KqQkx5BlwcQYTX2h%|RQ%tUZG%(FgRIN$DTaX21xJ;Q8(>3_@2uKXp+*-&|M0 zWvolnzDq~d;7=fhIh7-D-#Zv(Y@m4Y&a?Dh^^;|>q68RAVwg~ZfU>^4yxiK$>v0aS zCK3T9K7+t4u#M{U;6f~T@<~k5DEe*x$);h^Y=O2%1yiI@qE^wpf}MOl20`=Suh)tOvwXV);}k z1mdSWbtH*N8cH>VWL*$7D~#F9s5eS>MaPZiqc6wfy2_%hSsG#5l%R-CaRJUXug|sLP8PYgvtv0a=Kf_EX{0&SmzQk7@^|tr zLP!WnryQ4y4*4F7A1s~XFX8-MhQx6!Y?$CSi#t-r{see~i3n=Y}hAik<&Z z?ilOAa_FF0GKwZeWCl-Vhg$J5GS!&896&di9e{GWj5nMW;^q$Zu1aOaOCQQDJ>Q=` zCyT$N@()m8t@co5*s&AoCA6izUB9 zY7<8oQ2%24isK;QL?l@h@=mb#b+gaZ4G^m znP?zY;W2Wk5x`5%t|`;=sMt8?WjpG~EL`%*%jB3p-=)BEa)mHjb?_iSMq1kWe$B(o z+zm&b;X7NE33GWKl+8yoPWeW0r;MN+U-#<5=OTatENPh}jl3{6|SVW&q$Zp%tC3qFzzC6NI=)NtCt5`~K zj`n@N+uG0apy0zzT6>fE%lN|>rHJ-xX_n&BdvP+a@6&`MXacCWDD>ZX;@&f`j{-{U zoU!xQF@VN^LAPHg$mwx+G%4)+no$=~y((|OI4Q#;oJNUHeGN1c6fv(8NWa-ZjA@7A zu8Wm6V6Yc~`E`NJ!8H#Y*QuH#EKBJ(*Y7Ts~!PdStS0b3?aZA@U*0y|H3YCq?< zeK@m6YF2WY9i_kMDww8DrFE!^=X<{MVdTiWo zz{qsXVC&Hgcoyi!{9FIt?L{OikOQM}|9((r zsC2|*7-a?#ZB^sw#AWllp83)I-xbIkYxf5bS~bpM5P#0ZnhuvzgMkgna%e*Yu+{NK zkUsq?dFa(XF?9YUXdcMvsZiRoYkI3@Rf)%Mt1OR#@nK5vh-Xd_fc*RqFyKUbhpyT~WWPp=`-QyYJN8h8g0Nx@m+Jvd(nOes8Ee4){8{xdfmdU+e;|DR;g5zY4ShV7X}s-C(wy&8TrUEM_6^Lc@D#O*mVXQtBMze~)4=w65}BSr>ua873X|YX zkRue1#gXLZk*jn0!pC}d#(oDpuCPxQi^&O8A35(ZOnfj5RSs-hMaQ&MsN}x8v%k)l znlfvm1JzI1<7O^OxNWx8+s$hMJ8#{y*`CXK!xY?jH~GL}17%``%Y+dIC!A z{7^7#Bn}P^zJm(>HTy6O~2^a7@9ON zyPcum7e5M@OsEU*&))zR)w;T;ljlDSBwX)HUfjfsX;~K zAOOM;A^ddMoCg?qKfSs&yR@P7G_}8s;lwk4D$dD!p-ux+lZmvx-uXf{%?6I4FwX?KTyQ~-Ra=( z%p2o29)!E+jn!&j8v;O0Py7%@pnstWv*`iUK0MDlvZ*h7Gcy<Z`%z3kXGwy7%QFKSY#R;lE-4ebryYwrMxgOVnnOTJDrUD9D`fahe$N2bd4< zA+vT|5`qBg=~MLk#VHo3=K@xhjv4x8oakUGM?Y1)q!}D&1Sea(*cOUeTL=fnNa(qo zVIzH*g9hffUu_8TA;|15P<;h;vVFk_=sNf~KMRjE)vc$D|1hJ|Sa@<(R}y~Zef6SBibl9h9${zop+olpRP6`a|op7hP^f+($(RJmY$yb(}!TRIBoTn z{;vQ8D5gmN34y7}CRbodObHasJmzTT+Z~C(46$v=u&G?_ z_BVt8qt*V!tY2Szd}6k^hP1d{{0%72D+XpmI4=JmfKF6$R0*z!g>H18Y*8k`MvA05inrjAbpO4?zFC#6zR>62;Wkz>Cw8&HeLx9SzU*>4Gd->5Kz06%lp=epVlI{&jy z`a2R70qrxK`}t~wLrt2Gp1=uo-{+$!nS$ID2}$h3F4lRLG}mt#@OG=#LyH1KP5%@& z(F`d!hZ^33QklhR5%Ik`)+q;SHEnOdmMh~bS$8ckM8CO+o*8vT}4v2Jk!G-(I{rfP|?VE`RRX$v^>AagJh8@g2AqcWoemkK7Z` z0GVPQbS56;(2h?W(iP9e_e>D6qt^n2)qxnc^COJ3uVj*FHY#Esq=$Q5J_ouF9Vp|c zL3$tAhy7hd1_=tRn9=5s(s2qji9hzZ!|UY@nU^*g>Ye5C2aLiAiL%Tp!qPw24E{tr zi8yURsY=HB__6SRl$jid>Ip21+$!tTcU;-y7{L7aK6edZNbIKLOpuM2n+>TnufZR9 z%3EwyPD5*$w=T8}Q$90F(lKXKd?T?~V9HGR)*Oq(Q*N|&fIpYwRuyMdcFvg#3q$Jt zKw-bpg(^=yJe-1<1P7z|z3^`V6Mb>MIp&*)|J%#3ipc8?{H=HAqHUA+X3X2C2(UpM z0>p1C;MBd{1%Um}wqJC8KLy-;R9Ijmizm{cGzlUe_2aj-lW9G`pzn95y}d#3KZ>5Y zAo`2Z6G&n}0Nl?rOQAFq!aFa&Cke>xx80 za+PVb$otCQWq%}|T4MhGqf8^lvjd0K7kvsgoq&yQZZKP;L$aTrN!Q-EcT! zrlFyjq4niS?v=9$IQIA0bF45(tKTm=mm56Ly`AF> z;pj>+BoqRSze- zKfmFdJg5;$8w4bwVLyI+;qjM^lni!g4*QRo{u+9FXh8%8jpB=O!9OjTkg^kng3p@3 zWF>F76T;PxJCycHqokV%4oV_|msAQ{&155H`4CX7ABd9+y#Zo~m=l@e*CYd2kkoi4 zzPA;|gS)$$|6bFv988(vTN2fg)B!9EBZ${`ezFajY?hG zbLZG3|AHUp`yFSeN zJBY9+`U%-StzP$&Ef)dL^Y>JZWiF;clB4#jQFBKcZI3F{TmC>su|1OTkrgVPgoudq z;;oxy%YX9^3zYe&3k4fhxy#t0HK-Np@)*Kbt>xw&G?v_dHa~eGPtH}rI5Z?snaHK==GtosJiuY_e&sgH- zMXl%m**?%f2nZwdg%SAWsM1A}6zge$Mg{ftN;i=ox^Rpe#6Z7u2yFQ%z+`k`98|!y zeTMvQoaEL?2};GTt*xDbR(<~4{o2l>0_4<$kZ_C2B6m)cRx7#``e>z~kZX+MhPTe| zj=ex?2~D7Y6-MHLOcspeXnzI@F*uSG4Zq$FmsDk=$yT5aV`MI1FpbZMpd5u>a#>7( z*odG=P1Ak9p0!ltaW_8;l&$@kkE?o%;Ag8A4DiB(;4gaGzMWuLc|2ulFHx24Mabs= zJ=z4yqM?Te-78b_NS-JawKrr3GdRa?)A63xbtt(W&u*5ta(-eK%DitZ_>XLLDJx6n z0Y42W;+C{vqVIZ+(ZZbO3>8mCaF1Bw8?mK#INOzri+wc+yj>KJ`c+EZI361XM^|+E5}`D-bKJdThKt?u#H${qSQx zHC|9bI)(O^8jJ2$N9r_UZ4-1QsOi4@!wOP*o8Sg_X^C+JHQns(#tx`p25e-5cGT$ zOr24U6pNpROE3{+G2-LBHxJQB#zAaWXC`uJAoCLZwf#Yoz;sBl(8j86C}zSun%GWM zsSHPkiTb~g(e->TQM#rq9KCmtAFO~7(1K^SHoy<4MF;_Ko^abwF~jzk|6Z=XR?R5G z+gpbUF|xu8?~%LgBoKY~bDkMWaeWCJ4SvpnI|MV-6!>i(30x6mDtCz+1d+=~T>Rag ziuz=?vUS|~yzBfQO;^DdRoAtLZfR-hMp9`RN?JM<7&;}DMuzT^?(Pl| zq=u4iR6wLdknRD7neROBb$!18*qpugTI(**ch}30e!)4FweK$>o>yAj>@wg__ebkU zM(BfN@NWd7`}CsFIj7-TNcPdk4ndjnBqlR6GXPK`%>k_js=r#Vat$lEW06-BBgc6D zFI${=Z;EZzdqMW8)h5eVy*F*^gQKH@pI$kETd#S9H;3p<*^@yL!6f3?!zNlJenDH9 zc;i)`YE5drF1TF2@2WHVk4jodixHtbN|OT+;TBf~M|nU^0tm2s0&cBO_9$wqRb;f5SD-D#uSngVW(VoVq;Dh@_m6q+}3O65NR zrtm>Xo7C|K;DU(35I$yFAkd-@USBLAy0K|p)(7pa1zt{}JiT0>0G%s^ZgWDLTe344 zi{z(MbO|o;r#t4O)h?VKb7FZ#jw(9RO#6hJU`e!!1qLj^1kd2jUj=#SySU8;2DC`Q z?usEEu7&i!IBCmYM+fNi)xx{6aB5pEqPUG`k1%9`sA8t!#duCC&~+*%$xoIqZznqb zt_3uCY%5*O+kz_l7YVLza!R)9PKBpTZMV!Mhx0^Ob{M1@Jgjvl3ZpB|uT{|q(vB&o zuc~+Ilxf~`MCB>^)_TBQx~KiI;{ilSK>^4p`A-aX|EHfF^1(IW#fpY-8Waxv_Kn^^ z7{Z#!jPxaXEUwt?dEsG7!^r=}*XUL$G~~%C@=?98Q1Ut=O*nSjMuZ9F5{&M+miZg3 z3StsrLk}K!Bj)m*HR)CC+virDV2jywrfhq=KBK3aK__y-PWVxSWOWuA?(JLdZ6QSY zWF3!3l=ZC&*U~-oYqdKH|7h~aGCse|2)Cpdf>i4R+zMU~dO&kb2nm804}J4;L4J5- z{+#aG7&Y}ybQ>`JSE~If|C~ih-){M;>a|1k`up5-4ztxawsv(*y90_kgzkUgR*GTt z%Iw1t26bS#ZD@r$lTB;Q^wkt(x}wAVok9cvY-h@!WfcvHKQC6%96#!@6?o>qUo0&U z@_|pBx8N&}H`2Zx0kHhZ-C$<}1j@haO!U+lKu*UfSPWnv#eEFv!Dx{}+X5&x9(Na$ z2T8zZWCVzQ%|6k{f=MktW_nm#Bj4@AD47()l|JD9mqiW$;!p0aib&+}DRJ#UQdp~8 zAE$-sMFMU6!hf(*mNf!yc%k~fX#*5&w)Ry8JIe!8o1Sw`hu>K6C|k5egS@vADuz|= zsbX-CHE{ymmg4}Zne*^wZd}n+H3pz-D>DWV>eOev4{EWyG4QK&PuqOe7#v<^|M=V2 zYW%%_Z?lDH?%*zsv*|P2F1~DXQQ8FVSe#TE-R}p24PPI0u~^?+_f$K}pJ_)_yq$yo za(XF^gl{i>`@or&;xAUo9fwQufL>KrkMijp<;g=DY3DHdrS8j1c6Dw|VTj(i!J*BUY=3s@ z5*fX6Oxr^v^5Ruqo1Qx9*x6(6^ChEagTG%or*1@u}%)p+s`(_AOjiCbL$sC z9AM37*}I^wa()|8@3(;yr&)9}70>V(=+bOG_C9=BhIljJzsb(S(M&f++xsi^_b|B8g`Xd(qdT?_pgs1zsK--S zC_wNajySK(3kL4cB5;T3aTGgg+yeuMEVV7YqArB!0v1 zr_;H}2e{SdEik!Va>{E=EQ(-=(}~&Cw8G9 zh8=79`^8Mai8b3EK9z=VggHDH_*;n|u9I1|XWU}ZITSH@Z{qR`GTeNzubm#4D6qI> zE6hL&ZbuN%C6cuk#SlaO{cg3%=(xty;tn^eNuW)I|62?q1}0F|%nYm|#S^Bq_h8ef zk_jrjSk)9;HpdX}-wfSA0R|^+!=;<=X8vFjn{p~|D2ZdMxM0TvXd${FYF=I7Rlwba zEwHL09r5t5{N`5#`YMW^9LR6Xl>11vh))pBwY(d+@pa|wjFotS5qCaW0w-(bMI{qr zwvokG^WoTCnonA$v*PS?Z0ME(i5EBf-VYb{x&am*Ph;J0CLI!C7FCSX$u?e7*wkM6 z!=tHFv)^BSDaY^Wv8wRt~nBXfbnmwf2&>F>3uv;RYd{_=Gp4^syvxehty?IH4nXR(LE$P|H<>qZ8dCc>PJt103y(pT}42B7Bj9xaNRv8Fkq_<@_3doy*wG~eDUyV}sb#Ij-W({`c%7jha2DTh z+aHtNrZZ79pWPCq@PAEzMDbW~|5CrO=dL7q)px1X%20&HV4bwswJG^sT|*VIw%8j9 z6gUNLtVhDE@##lf^4HIEjt1&Mqc3!ymk-UADnlQK>D&UpxUMSxaBZ)if_9R-ug)hl zVHQQnUrc`%PF$>ihXaDp%OGx`=5OE1w=XIHu~+9pg@i7`GWvencrGtx*>r+|)_sUI zNhXhLDp)&kw#b`WU7;;mkpGV>NddHdn%L0?>gUQn#3ioOoN*cJ?a>*WKxSh79Y6oLs$$EAD83KMw4Mr4*UEqk2PWyO$v27xn#=8M%Uu1Z13vRHl zsWrb2vigvuZ~-h(>fSpXXmA3Z*sKnSC?Z>>cp7@9(E7WtA}t@1yNumHY)SkCb?!BB zN3lw>%XJ~$u>0p|szLijpZ&f`*uaI=yLOVO{&Bm6GNOzK6zm4R*aXy?hu`KU{GNJR z=}Uly4hUG6KELK{@)*Rv(F~UDng&rz*r4bt^s^o(`UI~`C(vG)lWa%Qw$dh$MeC8l zJ(by-e!Nu^4I_MCgsc&MAf6+C!Sb{MverWJnzZ-)VZE>S)5B#OG@bUg#QcuSFYyb4 zX1*kH6NeGey>eW# z_(q;@&py;|l_mDJ8qL-tgb&|+F(2oAv^42V2b8K=k1z1+U=aMzGM`^4*=*-v$0bNqwTU)3(&)(*w1yOa%WJHy^fQ zuT|*od>G6jk?%^d-N_@=kqou`o%=W4Ba*{pX0u4#Ply3sIAUMeN2+xhm1rh){$J%P z)tR{yt>w93pH*cxc}N0W+cc?KTaC`gc?D3s!4n6%^e-R8#877KA6fa(_F?s0|Hq9;5`(*K?R-`bhG$S> zjq1rd$c*NeyGP*iqbcB-RcTYra6nycw~+?Y@XM+jLDNR@LcRH2N!#-G4i5b^X~L*L z{l1Op7at|fqOg9(0=%7!jLiS)`*vRlhOws4fmG@&((94bt1JyZh&mw_5|#`#9+o@0 z2VI$Tx1XwX2%F!(B0tS3+4diqgr6nQ#gRE1|LN28D^qD0^{|k@R|nQr_5RY4LGIAw z^Yy{{_7mPhHr~@K)*7CB?)J7JB$QjatEMuZSm1UnGR+=_A(5}d!Zb>EG z6(1F+{EC+?uzsO5Atg*Hr!0k<)@dsVxetp{IYqRi`hNK=keC1Es^IRPr1Qi&$SzB}NJh|;8x)D+9-Quyaylf+Byjnp* zMrEpo?(R{(?Y1mEt*gW6WJ#mUzaMX4A8>lF>)qH}QCDjyn?{Jg8sS~BOxm$XghYP5 z8XgVrRp+b!WUuw@YxmV@=ijQU$By<3UEwGcJBY@jqlo5Uyj~d@jm(Nao6D>lhY5Q)RSbLVuvEf<5q9K7|;zA!OyU9vVq>#`k zpt%BOJAus*flqkq6M1q^Z9XBBYI5cE*4_S}3&2)R0Yn5Mqrk}R9o+Q!O>ufv!(a9Y z!n}Qj7O;ajRs4j|=tPY00|Rz^eNkWm6?s;Qnc6RepT1cEWh%dfr2G{gjr19ZI)ioqX1*mNf3R03B92L{75c~na4Kh2F~-l&nkk!yc0B4~4*ve!Zs#`|yM z1^vK|a--XVJKZbvxD8*G6m*i;FA=%mM@FF+KQ!!LGNGht7qY{D0g+!#U%o^uKl7u$ zU4=qvv8tA*)KHW*j5^1h`=3uE_Mv-^2XF`MZ})Fs)l3?Vj8Yl)PMP{e>k6xDFMP~< z1V+2(f2mr2X*-$XGBX=6Oj3+yC4@>L1K!XYfoe|Oe@jKba|f24}qQnGu}5S zKn_r^1sOq4L#(R`-(s?@dqsimJI z=`GkD{0yKy@JSAydq46(?WEFmscD}L6h1B&K7Da5X$ZBY&vLQ3|Gp3<(T$<$CV;oi z6IS_bnLp^L8U56~^Vlh^`{KH`FT3Sn5l^&{uUomn_}QC3yG9nw7Kh9pZ((=!ZX8mm z^3Wh8dlwh~UE^2Gjfb;5-Q+Q4sDArOeZIJd)g~BGh}xt9c{)1Ah#-GVhaKj)V&Vkn zJRZWVLFWSl@@_UZWyqaWtzJN?G{-8O!P50nj)!J8cUOb~KbWlM3}2T3usvgFUk~Bz zJ;$6Ez*(*e#f*Tz#@FbW^@wGu3K}JZEYz?w6Sy|ME7tKt8lJ`eOrJTYJmDmbjCx6H zi#W;d{miQo^v&NE7b^nbse@5*;1M8u-d~ClOPxkrnX~B{|BsIT;MM@yvxjk`vvPq~ z!Qa1QAr)oulIDW?uc-#b@F0;B>&*ni|J-r~THQqmey}wwrXHd~BvCgVWga}bucN%M%9{)(UxHg)u4o8w@NSW30ZTmr zV)~tm8Fc7Y8I9F~lH5DPU;ryS_~^u`nP(gX7>_~Yw|?Y&yF^72JlY>9d4f6?0F_%T z;Ec02JFESO1BUm<{YOQL@}Q4%ZFGK(M!gV<(3ia@LrYggYhgtn+agIzS!#D6b)6b}3UziNY3X>b7 zXu%Q486viDa{4r;BYJ4?^8Q}xJ=yA7e{9e(Vb4VUXCSeAeRa6Y zDhg_L4g2DSvq2jv3r$NgCxAf^CF`2N=q?_h9P@m3JW5R1jIXh<-I;9`=fa|a9# z4&wLi{_m3l1U5joIwY(~4|*ID2OTMh46m$+T^TYq)^fK@=cYTNf=Tx?l%Cq(D z-53Q&V+d=+IO5X6eyv{IEEdNgpbQ0FI0WFg8t0+!{e$S?5Xl zJOvou!pdxa4m_@sTHU^UZSI(O-1}#gsM+RE`zrNQsk}k9`#9qWGdJ{UJG&%DQ6q!g z*$W?bQ_W`)20N^n(KexCKsyCSrh1_K>RsS)*Y`} zJRaMzVmej?NvRwyI-P>v+@QZ-|4IbCQig_qp8LGDAB|>TTDc}5z@+*4Cj8zwq^O2e zT>cVqMZ->ZWbA(Ryi5XL?uEI60?SzjUgu=OO2y1zdV1&jQnobyRj1j1h7r(f7_hIl z9&Ttwg=ylQ48fEBGiT}}Mr}p`UN_#)GPmIK4-?0HBwm5;M*;5XnTM&iiT+3Qa&#-^ zCbI=L$cFbZrUZ`Z*Wh+MZ`KO0Za767tuiGxm-LieJeuGE|x!1d$Wb}PV*E>H8I z#mc(71DN1kN$5jDX}9{ar2n{6R7}yaY}WBeB);@*Ye^>pySub?f@JHtp!$Cnm1M<| zW23>99OWd0rRgl3Btn6al4#g`KMM(+x+3cx)omNNLMI${u3a=$#BnvW zRWqE!Ab$Y0Uj<@Fe?w5$5EKGP^*F}ypjQTnK`12|Al(o9?vK4XzJAr}PReQ5rhJ zc#wj30UXglPH>fq@RS6z;D_S2 zP~#DX%gXohkuZfHovsF~uliT7Gxiu57#ZAt!qXr#-(B$gR0~Y824-H<)p`b%(lXNo z7N406n?&O(`WWQE-QZDmsg0!x9M(!YV5 z?)toMm23%w~>l z5a%H^a%G4{3f-j%x6a*o2H8ssRtcvE!lh8$^eYuWpK99UWbKWL4MB9*M+Y%=SQ!^qp?9J3p zipRwM!dncA0XBZc9dvn4wktuWKu7zb3da)N>Ww8W`Z(702Xp;e1L_Ly{+WzaUCDC7 zG=FoPch3pL0uQm%aK=aNT8p{U4Fs>QV=(qtSPPWRGq4lGRMq~%QeQkTdTB`h%&~i& zXN!FJb&XKjLE$m1x#}l^`*V_tpW*KgiXV7^k-D*_%1;U<&-Jo|ZShSPBP@M0VIK3c zoQWpNXqNg-H>ZFH%s0SYOA9r#aLWhj4SWrO6!zym7@6I5E%2a8)uJ=GW|7o`5>FXs zv9!NkHb_AMuBGIQ+&6<=tg@TW#1(0a6XP@4FmbKIf@G`XdTw&!x3$EdnXRrCxH5ag zzl2I(2a{QrjCxP}Am%f;`_~~(#BE8#{oFdyqhMM@+9cab#cE$Iq=-U>=MNv!m&U1| zzcRa?jDXU#PsHn=exBbi8i%$V&kDZhQDwe1?&so*UO;ZW$4ZjhAh1G>kY^@d^wNKs zvazk|tc-?Vg8Okj_>TQ?y3>t&gp^gX?>VcSH!QH2G2r>1A(Z*ubg=xPd|E8N8JQurSP#4keD9=jpZr?4LBzp({<>8I0Ao~Lnp`95oRi@OsUuK<4`Ah>ng(OJI+GuljjSPS72 zvNoeFPD=mjYR(s>bGne{m+{fG+Fd`5VS*F)bphk>IK{T_Je!AfaE9h9qPa$95%H2} zD(rGXb3)J7rnEX=N4$Zs-1_11=*-e#Dk@LQ^sK=Ca)ZBS?lMXVv=N52yHifMJ4s(& zf0P@){{~6iE;8oZIv;s92z|VQB@>GpI;P`4V^D{@lP|2#-0KRzFcJlnWb>!}l?3^G zYKt!$>XPuYe<%FiDzny!Wc8bLoS4TlU|=5*FECo4`Pj|-S&S5Y#6!?-(jjoK6-E5r zij?20%}m&@46gS&wr^JeFc*IZFvuL{)=vU~pl3tpK;$V|D-=Ys8g;A@A_xr|vDhL6 zMLOn0+M0Nm>n=+IbU0!vg6|p}xy6xk(U#P>n4P`R9lKHRj9`lgT&&sU{<*jA= zLOCc(b|0y~(pDZ;(WA*9rar_OliH@R)f7*p!cw_;Y!l4GE{n(bnY+n{KX;y%q$nMq zK4zAbAfv5SSC-p-P~Mmod7V=W2DOp<;vx2xBkN9P%sL%FoO)qUqJ7}&Zq1P@zE<kHK-F92W&|$@)V+bDzstSNy4D%R9`j6*-D4uP{=I9TK|$a=SH&6ze=q(jX>BEMIQ;7PiD?od zq2FR#o-g`$GcA;1?pAwqcIJM&!zSWlpe~`g4^%Z=Q0H_oT+If}%1eQac}}EZny6&` zbl%P?>)T%crg47Kz!+W{M@1>UO-WL||HO>C$tE4?V zwq&U$n-^6({xh0sj43mjuS}Uzl{E{U?5e5T-LjBt&DtqKo|NFJd1Rmr@rZX|64cXa zJ&~mD0ksbf3J3NsnB`V;?{2jWx{V>`Io1EB2MMs)2Sl26#e0w08ProDis=pD520D3 zTa+DH%_}FqpTCcJs`_b!4iuHMhMYQXM(XmP-&CaetBdc7R9Z}C2_dtg=sG84JdQYq z@DYy4{@*Z+i5An9uL*!YOW@(e2X^_8nE0gClpjYED(-W=kq+W&I(=bjZwLxydW$AL zA`8khthK$qzW!`bYY4kq1gxu#fao3Ib-d2Q&^awB5(T5-o#3AZ`LdDB@V)S#nG?TT z;Iy91nsYaOOz&sA8Q`eF4#F2tFUlYzu}@T!+%6vy<`5}!sH(;ea;o^6?`NYrpQqdZ z{D&-0FeWu=VH!pC72YOM7+~M&kuCGvujrC*Hw@eAge={z7iZCB2Dv1BM3K%e41i;ZC|h zzq$aQRPy!V;&wyV2@)^ZW%>QD^0b;sWC@l<zv5uWkOlPcPo^R~yrs_W} zd>4rM@+2UwwH;M%%$EZgjTzr+vBG_v*e&dhK%pcrSPso%-eJ4bkMO6fxtsl+dN_F) zgt{)bPyz$U5I7FO+_Ixi=GGyHe~1q8Y$MQZi;q zDv=K^n$t6~+gEf-^DSuDNhU8w{Ov+_!aAX7tttz&iYAB<=U;uVD;El)8>wtz$+E-V z^~8!|3=*G~LP@{)m%t!enu(Pi=w;@%uny>N-G8P-l*&z&G?%VM1_=BWV`+Lz%!(!& z-6r%yAyMket+RMr-_PY!!t{p^N0Ps&Z8@VqktwKrO*qcau{W2Xg_T#wWU_jNh(pPS zq`c*#Jj@-!UEY_mig!x-`yrn{a&w- z<$leP8e(hGaX(>XeO+U)e{vA1e)k5-)FK-5Q}Y6sW)w zm!+bV^9ARe8*L*Rl{6IP>U#t{gd1+qhm}Su0ds^Y&~E}6ZQt9V%X=^qvrc^EX+-`V zH4|O*j=+N}oOsNxwZhh<9zfPz@&F!vRNQwfj@&6AP&HZ(VI4A`$hrFDpAS83J9WcX zRsX8PV;e}_IYWGy0la%f9Xi^-gvkx7qz&i?rW$b?CYHR?_w_C24f`qhnmf@3{`I1Y z0 z$ht}LxxU+NT%JgwVcx#YXsi-(rhL1M#uBM|ES3<)s(%1o(Dq%Hh|d%Lj(z#7>l^=H z+=CXg#q5&rXuQ7)41x{?PLg%Y6eFMMDLG=^z;R7MT6^%_9Qgw*4H277z>T8gl^Wt-L**K2lqr?}F zhH2%Y@ObpZi1ZI{Nv!%jqz`|j8=1ubtwXugsX*z904)C%=*Z_3ILHKW2Pv%UuTSrp zmgfy?wf@-~6~t$ZIPvVXp};w}g)EMkJ#uC=nyw$8_gHKgB+ z@cyt)inEsqO?x9N^D9hvYumVwJi3;prirI$d$JA2yNeoXN`<#iNcPov6y6*4YUbQKPh_MClzqQoPp6@v z!-!GmYLk%*A9(<~|CJIq9`yTL7?leCM8=b@CVqK)rs{osy0IbtM6HMYkH-T;$L7kq zoR3c?gpep#Ng`K^jnn*=8gs!_F@!HU84mj*Zoz17O_?z zv!9>dx9!k}w=2(1q&;g+K-jh@5KB=|nYyC^yMk_8-a)B+va`$mXH~7z zVIx5dx#0tROn&nhVwj!%)gYrb3MOtMjY7+ysQ74Y6042Ukw6M%)Fsf0ae6RKYz<8} zTIEW5aO`R6X-apQ#-DfC*s7S*C{bq?`ww$i9?<5%sr^iHyx&-IJKBG(&R8s1E+h$c zYKs+_Mo4Qu{;}7oH{5A$F@9;Y7nro$Mi<(Og!MeM!+%|`16!F&v5At-dR;}|#aHs0 zWYC?CA9rlD&lrnc0z?UjDU{W1s_x`J`q?`2l*>}HRu9*!B|C+~q6L}>x%pqZ(U1XI zo>jgK%$SKT)`%fI@yj|9Abxa+H8)k9uXiX02dd9-9fX=%1Irgvz zq~l(Ew)pYYf}quo%85f!{PWSyawn#;gq1b9N!^q{MQutg1ma(RL2NbI=z+n`-0nh~ zv$R=!GESR&T(d6;S@vUXI=+n0?`rYWZh;aR<693JrU6(xZ%0F%ZgqG7)JLEJfR?Ev z@fIf88Xi7S;+xxsnzM(LjbkS9=7&;#f)6eCs#IaVqaclnARjV7x~nlJmIdi-uAONd=H>;*fR_`)lISmW))t`}E9V(?Tyw^aTqv?h1kjMXtN>=> z*$z>x|IY;&26P(kBY@)op@o7>p%25z=lnbSB-C+BE7W4~3kWoKYm16K#RirBbn1Wc z=5jNN(fqFWwg=VJY(0`FOgIQsd_}2Z6aAnfLJsdT9T3}FM|`(jTn}xHEJ3scJBAs7 zPiwh4F$_KR+1-5nt9_)t<@hFiV1mqPt>79$tOaenXy&Pc7w?t0cr}#?-{n2)(cg}n z$9*nOsI3OTQD=7|GD#m`zWji#Ay?Hv39RoP3W>_wf=xx(B(VR%b(7cIoK`P zgbwcg#Be8dM%zzek|T$W_$6eNsGM_z<((>J1_{r1jsu+YAhv@E(;$;IUsS4rtc}q8 zIm0Y-=^HG@6i&L8gW(+7j7o@>HAU%n?ys!pCqr2xZv6b;cC6y5g;<0i$$d|Lt8V17 z_cb0+2C^zwKu4774L|(5?mW+gA$E!XD_zhGwbyAA=}&_#E+O;7Ff-w!8hjAR>FJR; z%b6CYwwkJsPy4b{n^Ra=Tf)OBY(44%@c+EW7W7@HB9qD-o}68pKHdhViY!Slo;BZX zqPn+j<10(-(+yt(yakx`UTWLA;nBXzR3&4asicLs6yony)?q-Da=sXw8ECvEAyf2a z8eqrF?zN!CHeGz(y4?3)u?INE5We{s}K z1o)YAs|PxA++iv&NVnJ}a8dgn7dqTt&A7&#KNPIe%GDm;=py%VVhou5RZm6&O#)FA z+ht$wnlEhG3tW2xKbteuJ6 zB;ttXM-kMu|0+P4zKi@%PyvJ%eX2d|{Ce0~w+#6MZ&!czKt6a<0*cOy+6E3d?&{`d z55Tb_7VW2UeoB41zk1YI_$abmN9z0bGQ3kpG1YIb-2ID4GRcgAyOxg_hepG)@_fPaozqFKmEgn1MId~JjeZcAw!3G8;#7;2(3k-Kz73DZZ#_z zbQ=wf=M(5$-$N&;Hg>cM+!dVdaG%c%|tlu23%`bgLNVUTEl z)C=y6EUB)|S2nt{&4C=c z5|>(WS`wE=2_Js?IyTlhtb&36k_-kw$%}eqKw-ZN)KAKa^1=W5Bg<%`@9MO(iH`s< z9tSK(nZS<+P&1gr90X;^lMbj*ZJt0wmto_V>-<#SwpgHz2RhQN&=HU<-vmpV?til2 z`v)Cm2Ct!gfFPPvagYnoKa>3nnBCjRcq-|4TY#xRGd3n_x0LeyDDFo~QFP`kdu>#njKL?etac`*AA&-4 z1MN(@;-Xmgx~1GR`7zTMCUUS%bf=$?B&T-rg2rJPTau$n6;{JYqa4m*JlZgkfZ zzUBi;{b9UXE*MrabJKGmCo(*gX?tz+0Wfn0Y2|IjFt{?f_e)#}TubbE!^u*<6Jc?Rk} zORTQ2+-vdH#yvHjn#XMXRPDo{?L7CGs@D!0#xBB?{t%t&YW{oYRujr z4mb_T+1qmh_v|I4=P9gn`w0bpL{p(!gimZ#{a5LxIwPGiCc_XZAueIAhY-O|{QS)X5>X{AsLe@%-fVk!%x8lG~m?n$_Luq5! z=!+2Z{#zX!LTaw-Z`5elrrKmddd1me7Qeq#FLk;Q0M@kZZhOktVY2bt&biXj17b8! z98Kl~h`Ow_xwfo#s~il5+WSfFyytc5#wNC&Qg=L2isg(JBczTDx#$Bu0VCW^eIC93 zE37=}_`-JB<0idJ@3z}4B>gY`!M4SLfAZ27M{<6My9Dy+;imFO!Yzy$>$AmY-?2<4 zdA+X+;YxbZFX=*_@+TUhDZoG41{D*Uysex0T2IJ1yIH9cRpEVvyPM1Zet+Ou;NYOp4+|kJq0sU@zgqsZv4}yD0~KF2mjl8Lx+3RDGJ$m_za3@ zeqF9R2E5O2h87=vazCJ~mhve;=VA|HzGA?A4k!8VKKmbxvL;FY$IvVV>fltoDz1<$ z;hipMv0N22csi5lizP+EEh*I{2tYx=^FiC#bJIBh4vs(MB+juh_6p)R-{6sMP5E45 zC5NtQ(Ct0UR&1gYw@?tN!%JO^4KuY+t3?Sd|BdpNc{1H}# zMeAHX1tepoL^AyU3eN?utpIyN_@X-hcc79N4zxqNlETe7UfWBkFucg#PJ_ePLS=uC z%yWH0_kPB#MYRswc9b?(nNK+zyJ&J)z=OI-FL)ooU%xeeMKn~o?qx<1i)H%pJVJbH zlojy?8q|e}H$mdVUZ7y%jJDR>s+Sd)t&2#-8v$40yAn?KK9xhf_(fi4PYZlN3RGxKKxxrx`D{}1f{~;~ra@T$Wkd(cG=BRj@R}srGFK&#s&kfPoP`7?q^{JBXM)aRRo;YO zD_fUVhNLu-f(!rFT(~wN>LpQ}+i=S~v)@`cr}%{Ii;B$_I{&tyHF!S4h~up)8h#52 zTQ#Oo{P$Z-eulFn5q7wKd1KFSc8{R5&-c19`08@~(1l#ihN;0%MfacqF;I9$6~ zeXxaZesMAPUGrA?B0!+Z0xs!rfN*wv?wAci4=r+rmm*Z_CXP(eN1Gf(k!Jgpf?CRs zP$`~1hJA+rTnsB`Bafi%C9bOfk~ek8lc;!Z@@qcBVa$~At=}x{A_<#6ggr!GVZ~z- z6WC|14NrT1i+}ukl+*~_UU&ZiMqu!y*JQd*zz^V?4r`BJCicvk6A{(nOjkYVB8v7 zVLnrCwLeSJ)xP^aUN3rFPPGSKM^F_&fJ_kW#>KFg(4WKLxgFR$F0`&(bf!4g6d#}| zE-o!)y#&~Ju#W)jGMO!CiNKU69MMA$JSLA8VUHNb8#s}+rAD(<0x@Sv`;loZ45ip- z_Ibab8TwwLAw+%mTQw_DH+Y1u#AR33(2ood6z#KdbTBybROE}VDA$=S;3k9kt#g2c zICpv+&ip{KhWiTxYH*M=&rf`f!L*YrBQ?HdR-#bJja)?BcNS6CrRYY}Ysx=&hmg{) zF1kp^;Ld+oP_h6&It6ijJQOa|Z;n}G`}shzyW6N|97E(YPm#JeGnjw4@4IVw_c^$v za?cj~X@^#>e3nR~HdCNIm_0o`Yj%!b2Ypx|I1&37>d(eX&)c0zRA8*F&>P~V!~EztfA0<-Dk5m_-4ucn>19#;8J zrss7mG8RrOHNniPu^U;&DSa8ccnkU(IwSigmVIz#Z+(dcA0Fb2|_CPSU`CROG7NGFr@tHP}s22uIuN?jDjR{qiaDLxc`c5$+@z~BE);&0xEddD2E^+nj^ z%DSu19^|RF7#1ixNsna+2S&8=RTWqaH2{W)*2>BweMif!N&iN@m{O<6!oKlwO|d@@ z#396{Ed72DlM9}nelnRS1c;hkhsM)urA3m}twX&_nq&)uxq>o6N0<19d#6hEmURt2N5vWP$;o9MVvXo?~^Ka?| zgFwaZrc|q|tL|o0?Q5RAy?0ZG+SU5A3k%zBH!C_JLtyzIOc%-u{$~xY=ub1*OfoX3 zD!Edl;>gNBam{&BoR+e^d7I2XMWvM*F)K`?H9O1Nzud5~W_}m~0-ng@r7S^9^rw8? zgg^)@hOOA+cc(GaNsiAq=c4C-+5&o!E-!M4-d5`;0FHUT{%h76fNacb=g!W~KZdoG z9S;V__v3xd8qnxg^ij|9`xM>kv`+x9$7niF;=_+0)d4f2goD6N9Gf}(o2Twe zwnsmi1d%$XmtR;g!-COY-96LD6{V~-svkaVZ1)fjwj6B^dFBklQZN+iI~L@=n2Ti? zn@5mgv#65WCO@rAM8k1m3DMa;$Anq0R2kMT*{^?c|DFyRsMNZ90X!`LuYNL~+k%N_ z(sP;n7dT@*3^=%8u|w!xd8P*dt_;7r{S!7lt!dM0B4GBmPn)rL3Rm1;U(PVVkw3vM zZ8mg1@ZODPgsk+nZV-JEhgk&J<5%PUA$^vsw(9Wb_rC;YC3fK9!d~at%(HpM+pPY3DP?^=$8ztEfui+%lL;u|j8#)8b3528ReB%&5LiKgsj> zMI(M94jqB^2_WvH+jdV(tuu^muSR}7;^5>1h`@hr0_3(4?EYsiegt|t{hhk?{rWLQ z$f^MvXlpy|aq7{_EECAu%*wF+x#nxV$_rRD-BnsOXAvY~ld*iDtfOg&2jNOpyDPk& z{XP;;6Xvl!V!cct@E=Bx{4VE+{Zznq{)r9cHVaF)hP#^GJJRItSGo@bDmF#JlAlBU zCOk&Y2tMXSqM=*3yOUHnZm}}n2{v^9Q$t+hAZY+Rc=>&wAbMjE+2`-0lz!wSV_{9F zO*Y>e!Zd~zn~nQosf;#ijUWk9L3g|)=pVhjJd_bO#_NC=kV^MfcueZfk1bm|xl%rr z%b|cyOi?@z^9Bm#s{^)e(P{|qStQpB_2hw$jcs<{Hnnct#u_Td#^-uf(2ebNL8811 zlxDxZ)*~MfE08u4MJ)Cx5{Ep`NNYD?LzpcU)IxmRo9qA+@i(bjntU zK}<*d%;GT``r+8L^WW#C1Eua6#)u1N$rb#R_ejQH<(;&T=1 zwm>z$nMSgk6whtH&&dUSXEE*lNhG2D{j{cw-#3-%6C((E)B}OiU-Od(7pQ{UW^&J2 zUQ2MeJ&}PO-iyKgM$*nwQ3yQb@gXYGpw_5Vn=6x8j}ME%{8lLejj|a)-Eu6Q zQxSPsuYZG*Tgs;dT3Z<_M&h-w2X@ucImK%IN#uVE;r}M80L|rq@%LfWnjE=8LvKQA z7J@ew3l$sCq}%&RzqCusvK|+p2OPIi0lR*_Pp;gw*LFJ@S+xmeipNJxYq(CT-KEsA z-MIfp(^p1C*?n&hAss_VcY`!MfOJW>bR#873(^dY(j6ioB1)$;NQaVwlyphM&^_}W ze((CT_=d}wJI*YZ~95kuc42M9oZd+SglJ>ScZ4;#usw{*4L%?rkI{7sd zJz8qxgCp2gL~xfG6TiGvji2J_bbtb~(X+PWHaS$?9<`fCUhZ7xC25!=FPU{)YBlfl zbD0GVu?AkMe{vc;o`26gdv;ln7>Gq~?!_1riOLaQd5OO%qxvd`q4%k~?@)fnAvpJI z{7xM_Zx0%m-GQm{eNAKI40q;GXqlDnRcm*hSq>sR>3W%M#f89`4}02cW#B2oidr{c z2%sKOHc)f_ogx_u;NSWkC@cbHglxqlpd^a*^b9z^_ocbshghL7K+0%+C9LrO2iE{9 zg2ySLn_Qt~v<<~E^*q+<1-1?wZ>RPnV_s?N45kD{Njf`j>9)Ifw5)PP>+AhFkKBuD zn<*BEa#yu$2$C`x3!hpth5F!r0WOcaoS6@#Nd-bFbimI90h@k znu!I6Qfxg^JiaNaua$T{V|k>NYfU~enZbxT*?FMCYEf!ULg2290~@M!-?&|lJsp)H zY%faL%J1gCdvo{OC0K4*b9Y&M)y;$lkAOfNV0U-Gt;}@5t($i$kTJ^3bx3g_bO}Hf$AZ9EnmvY=Bv5Oe!S?5>7WNA3FNoN#>OFGP0{ru z%yBLhMUKu(eIloNOBhZDWHz2J%ElVJ%n)<#02@l@f%g(PCpZa4T3O96ZM={5`a6;D zx`tko{(=@xnxRbySfZLNRr3?{pH)1~u(tuXYSCg5a0ZoE!*E9j9uAnD$!`UqOrLY^ z=Eti(ESTg)sd+X~2R$qhZfR)L;dlWuMw$+k8&_}kss>K^+CmNwUG3hOG$s5kNt~7b zGQ#eX*E^wL{q-becx~l5t_P}#LaEaWo75YO6c4u1t97w2XRnfe(fG_SGq7?bag+<67ygx-eVHuByBpgKZf>-oWC)v?CZR_ZsZ=4II=3sesFI( zsT&pFdj$zkSU0ZR+tom}p8LTJd__4RvTe)IMXRA&l^WyWhk`KNe$H*=7@2bKDN2A0jiRxFg6sq2S2>LdNMK?yVtaB1ZAmM?GJibGM$#w1TWZo;KDt@m`F#ZCn1M??i|7#1~Z% zcn*F|ZI&wqJ5vUQ{==EUMu(x?-=kfDjVJyALH6B9R~B*zNEH~Lw4IW2 zU_CFw$$hV@|CI4bMj4UEnvX8d^T>Z+9)F_2pD=}2<9!v=^1c=@weL<-*Z#!HVsRdH zB~VH|bI|{0UA>glnLfCO-`a%i-*9etq*V*e5<3u9=+fW0^(ZNfwg5T zg;Ene!^rQJPZskMQClO1fQHh7`^fq4cWj>}KlZ~Pd%Wc)?HIJQwA-5emqX+_5tkzA zAsZ1*A?N-s`$_7*7cU`%hCvRiM?)-{7{r`#m{@PLhdS>k#z@9tW z?@rW*z@i8H9<`3!Rlp1mY-OHOyw@RN8&S+GB9!Hfqu8c8y=3SCn$ld#3-6r zf?wYs=Fv4JguklaUDVG6%)%i*)8svz00Q4T0UPe;sJ^@1rm=o6TsFV`RI*4B>=NYZ z5$>UsE7Ohs|9li_(2yl7)4d0P8S$i}hBAOmU!79{{12isbEVJ{a%G8!@2ulgBO-VE z*30V?;ArNExB!>VrAYAAamx%Vn){G`$^j(L-ED# zd1e~?y}T41HTf4V0oEs4XvuoOrQ8b#`|_)vm)G<=bYE0A?s`)mK!GR0<+Ta{?!|m3 zC*(P^rwh-`*Dw2jG^wn}>EYy7lu zg=79TnDx1r&FVm3YUx06Q!v~`H6 zVm%<(pkuOx-Fr*I2QcB$%SaU90xc(!sQ~^`yyrh~Goe>2R9liZ#8g6=_==K^$S=mk zm#Rs)?)r~VFrUG^qC)nfP`6|*9Co{gwki|!&&&L{=m|dz5m<$!UIUsB_Q$v-6VSLP z^9|v&NUdgbk)sYILdHDsxNm0T)bF*sd$lTOJ)~L!**6p%c8;`pv|>SQ%-Si7!}_VU zwYAW&j1r8mFNdjJn^rNwybehhDc^Mf*l4JsoE9Wn^M#y{?!D%&zZr6PU~v-3++F6t zTLTUsap3cy$W3Dg zrvgqVEZ{1%1zrkUgQ@qa6(y-5)}xo@N^)2Zx8P}7SXh9^_r$Qg^Zs%%F&cTt4}7~c z$B4&&V`rXZWYTsAiIITx5sCn{;qTo_IMYQ17YAq7zbFI5&0js~8IO2NW0m6h=^LNT zbX89Bw~rOy>PWx8S9hFC%D%5416LFkX~f+dib=PfeQ#6Uj#J(@=@=w{_=daY(E%1L z5NVvgXU{YC-!1uOiT+6@^`~)z3ua|--w14txU*I73hhV>ARfE43k$>?D3ly&AunoX zO?WeRek&s<1ArKc?|S;X)1)U@1Fk^(lKj)|kD4qu{G*UKggF{d12ad&wRc3+PYrUO z>-fbGRZ&j;os92?!7R$*4^p^*Qp~dSW+ed3K@BU+Bf->Rt#6a>1;~5{I9jnN6_DJ_ zX<$$aq4k{IGgVI$T0MnZQp%MYpi;!gIkj3RVWKTcEZIhGt%Ew2-~a5TM!oFiXdw6rNH#+;asdYb*EupsOnnNdd%Vw$jVViGAFDFiZ;O5d;yHbjoW+$06K;u zdH!b-G`&mL2y-oCPbW8}4k1rY!~9tyc^M*QiRn;Ur^n+sud#oz`M{-2hGt6AO+>Q`D#ndLK zFWL=D(IIQxQg9)iswwMw?zmZ7bOMQe{=k)R`p!1L)A7`!t_O?EwZ6y3^{@T|F;CuY z*Z*>iI}n&+KKxhnFT~kg;qncROC;D$Ko<;g%qVGjpYvdP=isvY;VUR>t&f zrfCUi5%52w0L%h95_ZjQU@*mR|BEz^PKruON@{z1TNb#W4ZZ5B$sOOw>-F^(@};cC z6C}Nf3jL_<2B1U`OcSS!JRz67*|zod9(m$kvNf>Guj%H*8ehb?aI-9JMa;e}uF>L4 zS;-h3aU`H0S~Xmz!<+U>Ax8*7%XyXhE5OX*Qrz5y`&6p(*gC_7wLA#jRxITik5S(FFTg))tY*b?l|t;dQS=8KKNxd00MeJV-;bc(m|+hp_0v?V%_b%J+N0hioGGw0A%r=jzP`_jm{|n zr~Z8Zui8Bp18I70*}X>k02G`4(LxrBxDb^*z9M zen)GtN@bswwu!7*v#4NE3QZy~UgqQJF3yx0wBZfC?5^ZNzmPrpag^NnE0e7DF=y_k zz)+uxsty}UhPx$pRq}+GiL&g1$Po|gIT7%g2S-Qj;Op`(2x5AH`vaDZv~*wetynPHbMX! zk>rmZpcY*;=Q1ZhUF9sG8C!1grVhSar(YelTEw1zE6>U>NYq|DozDD7ZvVXNYl~r7 zq-lGCId3%f`oMZ;I6*I%--R`wZrS{yD;%Gi!X9c+u-O(Ppqa-WId53+^rjul52|kF zF`pD|E0e)To#Y3+7L6W7wp46UMt`w=sG~LQCt=TmVJ$eZec-D)nJS!!0J`EWeE!s* z3^U2$PFclXxnd`+n_flH2ybK)h1|+ z-F=Se)awyAIB+D9ke@B{D0zH%(17=y^Nx}B(lfBdzc(FKS|qn~`b&|qw8}3MvFkb5 zD5A-E)qWO+yjQT7+_qBe{mF~6?W6Q&U(lG~JM(IyW{wE(DZV2Il7h>QODnKb_AVb+ zksx~Z5c0l_Pau%OVR;v9>q|6XCFvaPGH;^hbUbb5cHa;F{f}_m$gt-eq{HUCC1 zGtC8!6xEx5`-y(wJ|8alQHZ*7`=3ZL3Snu$Lp$*@QBeE0pP&}p(C$-XWukrd_&K(1 z5?b_Xilt!JO<0cu&2k3Z0XPr8HzfQf{|NIpm9$u$# zoO>p$nw!DJ3g2Y`qup*hmkaNG@)zjU-|SZTe8azx1~d_m=?=7!`?4w5WO7z!=a=4?<9U(4EmBJ9RW(Y}V4S9|#E+HX76%Vvx|7P>=HrPdh!lFw^ zl9j>Dp^l1GcJBB6TyPfm@N~CKa#EQ-*HzybEHodvzg29AVe>&LAR`2wmEjemo2HC= z{am@IEMC})ns=(H?6%0Kx3(Aib6NidI5hF*k2u)Wb8Bg7grt+m;eARrg6K&e}-*Ms9@6?h#|M?1s>|URWr#W zn^2CxG`*%j@qZ;AnW>@|)MPR~Y8a}?dIv#!_Ll(LRBl`;d){fy4O2vS8;qUUb_Eoc zHUh509(l==IOBeBB^AJ!sVptL^nRV|5tDrLA#Byh9xk-DLnA zz}QXOpJOX`_V=B?gEnXVpO5dP6}62q3Oq~$TgL?lFCBFIhoivS15h?buSW4j7x;Y; z3m%byT|WWogE#XuQjI_M@mxju4kc>{#y=QY3eg7QH8@H@5fJ;pTQO^NK8?_I56)Mp z&_hLUi;-mXMg)hB&uNuKI+R6%l@F*w#{l;2kK1{m9(>ixjrkd%sZ?W(=hlEb>HCwLs;uNPd|lX>n>@Q#3?x#wL2QWE646)(|iHV`r2s4ob$7@hW5|ye<0u1+4nV? zVUwMH-Yy2oVfVjy{8y(JO2iNj1cL@dj)?$wC5#DjU&%pMj+S~^Xc(npnrgxf|{H(dSF+RPf08cv4ZK|^=Qi$@C)2I zBjuRz3Ur^(C;beHklH(Xdc!G^{;DtVu4W)#d_7Kx3@ZOCTcf{$Tm9pG9RbWv2o%tr zggrR2q>8Mv<~aOBoR9)y9S%})&~g1615uq%tge6I16WwQM2Dm4I**Q-nhWIzD+9h2 zo;@fKFRRElFjF0QFEqA!orn7A>93ewZ!UAH5hLe=c0x-oW;(cM#$!fbpVgVnasRG6 z43NG+>sopxf&vA>IuEl>+kyB#P!k9MvUFTNKMLR{_HF+T_-xF@m^s3fGZH1Vvl0g@ zrfA~j@b0-m53|7AXOq?55tI+r=EUc_bgJhD!H~`%)&60Y23V2se=X;}K%EKSEvxzr zGXoJPa)91Fc9uWVHJtV14GYe$U`)&_PC2SFS2rGGu3{1Bdo>03prSuCou0=R4&LRi zVf2_bQb?^ z`flzmcB}rKv&{J|(ebK&Jg4|gi9VNBz3G1sb|sJV@An<~3etW|&CTrq`AjyP5U?Ni zVa8YL-PNd_(JZX83#3_(lAffi<)WdX9Rfqq`=wakmhBu+Mjmme$GH0VY;dS_urnCv zCYWJ=Z$Ry5Ok5Z9-Tyt{4lZ^reX5Ef_2!pl02TC%vL*WRHQ?7+QCpj^rM30<)MBI-($}K8Jmse^UAe`~ z`6Z3m%Nx`mmTsRc%WSF*73%f6Iy)g47{6v~?QuaoZA5&Ih|T74U- z6oDob9dov%hN5y>yPtpmY}_eyUlT-K{JVGV-=O(>-NB^Gd$!J5vZKj-L2xH$}^-)m$vj!H76VCjCUyTIF@M`K(RCN0uL?P{e!O&g?nf<|M6$E4PdmCizwSHdV#8_gWkqM@Gz%Y5tL~RfL)|>m4P(%+x^oodN z;#E#{A;Mw=TUC3Q68mqOX*>Z0wtF&q?X%%IXs2N{8&DTG?_q65R-Uo>^zQhQ7X1eV zbx+>wCgdvT+VjUur>mFOTd)&E2;C)#{N%R!rJ`EizrbvV)$|S78`^vKi>_;xD6gTF z%c>zuXrz^@AThl#nTLnhOP>&vb#3YotCF^}u->28<42yC_@L>hZ8rBJ&FqrRic84}MByM+B`%U9MTCdD0w}_$+*knq zFuCcfwI9mkBgOe*S`i~}wE!1&zR;$OdJOSX>0|D_l*I;>-0={Ienxx#RpTl6_9<~r zdXvJ~uTfUgLSCkf;j{3?S4%R5MYEp~;VGg*mbm@KbPM?%4{5+D3>aWI3F{H^+I=5W zD70IIzl@R_M%GH46bhX(t_y~Z1VrT2=O7f~nj#^Hwuk|q`L0g{@55N-KB+%{x>~_2 zRR5B22PIMBf`3PVQS(Yz<%C759DVY8VM`Dy1bY$BvL?uVv4lcx<&nLfyCn>hkYWfr%9*^<=i4@=q@Ifff8 z32%G^5-35;=qUIRh|3Pb;4O(|lctxyKYxqqOVKS!>x&JVD8EGO>9(Q*x5it?1I1(5 z<0xJ!JVUK$`17;a3=o~$ZyBKZl*QITg)Oxp%4sk?1fD4GO;DJHZ-)1{tAR@XM7E`UdL9Eep1uFCN9 zTZ6|{>akAfdN-i}3*J7~E3wNMLhJg_A$B^GeINa(gAMNq<` zB(1p8_koK}P&jmfl6 zn$}R%p7n{eFS}XkA8k&3C)XW3ieDN<#H}uLPPltoXWs5Coq}?23`-yK^aVKaq%5&_KT9#ihvI(+0Gmm)3YogE_L3WuQumMA(9*1lsamd>pW!N+3kL5h5H70&6Ns9x9&yfIrY8K_9 zXUJ!cx8I4_#J-)QUpKVbJu?~yLe1ahM!;fiwkku zOg#TLGm+O_K19c=ar!%5KDy9?poqBiI824cN<&qJ@4_zW087))A&S5x*WN<&co1d1 zTemkgLw_ol=$GeX;Zq{ch&N)qlkr3Zwke-^o=D(Z&~O0W=OFpX)o@9Emx*Kk`($S2 zZf^^iD#Pr)a@xM~V&fVoc%pkZ2t|t34u=t{JsH>b^HOl`Y(3z;QAcY=!nc@0F!GZ{ zL{GQOGf?SIJfdyXDI%J0_&qsk3vAU3)o*TY0Ly6Ok|IhDaP%CpWvTJr^QiXYWuO1| zi~A82niUnQn#yJ~EnW9}c{#DZs`AnK*jYap13&XSCp=E^C}QTe5z09IckKTXdj_*T z;yQh?9HV!7V-1sP!k9Mqje3iU!g?xv`Fp?X`%Wp=s{Xpb4t;#}-$5#kWI;N@taK>; zHx>C5F~#;|dJM|4(U+o<6T!2+{*)P)&*unG;P(5Ge~czU93h{9G4}v7#O*!N_`<($ zbDQMqC)$VAuMzk6_x&IrbAR6T_SonUhy*YLSNSThW3_^#91-(G;Ax*R(stOO7NyNL z#Dk;w6qGSRmHJ#yQ`t1d$AvZBv_psvu-^@jX$Pw z-|%33wbdp9P~Il0lbgpBCWv#1v#w84a;N%|K|EgP%Tkjl7SOlVX$5naMZTI6y{)ob zyh%h}{WkDf4!T4Gd|w(0iX@jSYBGcbKUm!dk4#{OMQ9N0j{tKyqj+%9e{&u9<3Fc~ zFX2Mj8cpmB>BP`a>|JtZ{|egRgd&fW%G{P@zd~>#>GlD(O(EckZW(lL0Hm%Xt=-0U zonidxf&vB#Dh|AXRQYsOg={iH;lC%oTL1HO_@LGHy-pE2RQ6d;30LBYwvM5ln%rMU z+r2VZ_}7ie{S>lw=YoFC!-;W!CLX~`%0Lte0k+6mE7AdD@wOF7}1 zgSTZvemJi^aZBl=?nkWfcoqISIaYX~&)7iJdW(A?Aq-f)@MI441D*$!=yKptIxvTk zA{2Oya2mTN%s;1p0p9YzxZIBF>-3S3y#CkoB0v~(eYKv*cW`)EqWwYmILVn!9dFWo zT`h#5f+Ze498#^2j-KGlTL=$NRtin*|TGu|2C$Z?nJ+3XCQ$zfIZw;jmaOfcH{p$vwOBN6{0ZB{$bda6;! zgP|sMSG@RXdd3-%@&(q?$AFaSQgWlfxRx^b7&6wlD67^kBu|ZxA|m0WN;K4;Ile@JXkgA8C-2z`}3kuv+ILp zsCs9iA2F6MW_}2Vsx8|eWwZ}Wik7U|>WJE5Rj|JbZtf}*M4@e_w#_Q-AmGJWK8WjGC2~vN%V54fBT!yoYC6>Nvr*I>h zU8`v}Ht1J+%yU4t0R|#&?TDK_KG=TE0ODWObu4Im`Cx8u9!V7mBygT_0{l&G zkPF`DP}l5wvZ1OB_*ag2J?X+Q_n^2d45R+WvBh4e1dLC%8MM;zOh;JW?q)DhAji8iJhezaUO9Lb+1GsH^We znLKU-6Gx5slUfIi&>zW)D(u*=idR1Mm-J-**?Qr{`kUm!O**${41Kz6d0U{+W7p~1 zTAa{m=d6HjA#;ta3fe$Ym^F~b;WU}So-vX05BhBpE86qjEYc1#+kfMg0 z&9h+rcUHXdg$L`ms|8p8BNGLaf6ae#0q+y+nuHJ#^U1SY-?BgjVR?eWshO%Unvd1f zjlErO|0}ynkAWi%--0374~WMYU(_!p!wJZ@U{oge(c>xmJ`V8cP1)0mNvvQ^dSb4$ zN|{o3*}ObqSN#|?xsBOqy_idyGdlCYpUMcn^p3uI_OhrweP0dH*Tl}7 zA|(}cu#AOK-XeOtE>`cCu~3>VQ_+8oKpig_^{`xM6;FN!ax3U``)o2#oIO$Lh&8X? zOAANeMDb{;Umtec>Z}>V&KE~{P+>ca<(E#?s8!)c5 zyp%zn4hsQu>nw>Ez3-AkiJ|@nxQ@{{N}Y+OTJY1}>iKURWBRN2K-!)q;=*+?=SKhC zl&9_D@9UTK+(#$0M2tB`(-bk;WS8nWR`Cj!2?Pm5s4N79y(Q2eYRTMO=<`n+v13z= z@KD)Bd%p?XSUp>~z)+%08(#A>{Y>^MpHwOB%`!uzj>Gfk9B*{Lv=GW+eA9uhUHMW- zGijQ5wDee|n84f%m&$`2*BAJLQSxsxwF{~m2`l%QPU1VaV)G9jr%Lh7hQN3;wuZ>vvT1WV-IbU1lb&u3nOeaA7@x*gv>T1?30X zDC$lxf}@0)^q|7>{x7w4rU|Bb!=y*JG-Hk`oE>#{Qn0k_J0=4sDatd*>OG-_2A_vn z()+&E$=ufm&nx+8i#|C1hyfdWk3q)h&fzS`dF2YhFV=-6>2&zz(sjzza;^{Dw!pCL zcLg9LRG2j+0e(NcG1KDhTn7q=&)c^x=l;35$KM_!dU6nN-5+j(Glig$bG<1*mKx`Z zMN2zC=u3VUh;eQLWal@f>J~uS4BSU-zAT&Y63lPQG8`Y_S(DsrN~1HRMM&b2Ztos; zmz&h_2jtIzNuclgx#;+-dTYT;>@N=HG1(;K--YO8l)5C}Lo#p@3ZGcIey&c|mmkjI zF5i%UeTl0P&`0{YsoL>P(JN)mCF63XtS7h1ZEVD1nsd(W-e;VXn_hKwi0nFtH9))U z=lm8(Rj8bZQN+9QBrHvkg)2v*y)&^^rzsTf<7~`>8$KoHV}=y5?YU`m2Z#E?MSWG! zLB9su^cKP1fI+H3p7m;CKIOPnS7R3N?`0sz^In3|2G5D&79#P)fI$!TH zU+ZG_@wyHtB-UmXnGgl|5(n4{4pR*-%m9{f7Ez}U-dOExHm?*1mfQ+pFo6$Au%U|b z(M!@r|0^&+OzgA9D2|8v=6kdvr^TX0mQ;WUpdQgTRmO`h8AmG>cIA>L2`Wbvf8VLO zOZujs{rWSu5%26XW7g+dwoGJ0x@qiBgmk&1)IxS`pe$G>21GIR9$bBI-%n-sF;=yr zmwn~QKGFNmnh!<3eUZnZhpb=>*NlB061z&A7Vc{z|Fln&!h40EN311vY%Bg@>I=I1Y6no_U;=!C zSFOaBB90i6NcsI6dNDK8>d_qTy=cCX?aO@k_kc9XCF&qe?3!?ZTXR* zPtJN@ligxE&ULF7_L+So6A( z9v0|$>v|3vnE$PbrKO~(=fA{7iqZg0?)QThV>Oc8u*$e*tr-kW0$knPj<5ziXqI0y zY(3?N&i+jy=D>;UAXF(C;6{xNmwVSJ`l*a$^4r)0aUf}L(B|H3PnXl^(*g@eZ*ewT ziVbO8?m%i7N;I}-S2?LLXGF85vAX-aI$vz*#}=lcP2utqo9r{RCA)bzZrzPZTc@^q z=R{<@nlLr!Zi)4pH}>#7cv~}v*2a4XffY(_w07VL3B^-SLRdw<)Kc`zX?lb`uHHuy zy&y6Ak9tSZd+2-j546__z|1P>fOP`bFLturH=|FDTmee_cr^}2brf8V3i-LbS{?qs zcL0-?FAWEF0hA~c@&!pjKuIRL07KG~k7Nnho`A$s_U1}lFxkyoO7C`-`_+yj|Iw5l z9wCYEr`pRcX?d_I_%4(vi1`7(8_s%)HsXNmR$-OsP{+BCd6$Npe16myYSq>a$1|-^;gR322#m8aV>A-}2saY02Ds4S)D=Xg*v44fF}6cN3DS9k??d))IMU%WCzz7nhgR52|$7-TJ)`w|NuXUv0;5H7kF3 zgPsGz`{&1fDA*?&9`=b`?EP`Idc4jbx9sSGAk*0&d4DM*0OTc=0LihHvASyQ!bS-> zE>`Mg+FVmo7C1&K#mKU(81qFoo0E~SYjvX>J%t1$P{xCK_H3IU3C(<@6IBwgHb;b@ zwk$E;7kSp?>)7AFFy7%-C^9k+Jt5M6asBy|L4g4mra)JQss1-SX=#^hK}NnKpHg-V zP8{R-@kB@VKucA6-3#`}{}?fFpIZJP2-7j@!t#}TO^rWtKM=gOF1EG6w^CfVl$du& zr{gA0u$Fl5&Yt*(i?u`>`mA7(WYbm?(Xd{1nK|@8E;9nXeH);8b=-r756Rwa-eCWM z64(I=ci|$0CGMjmkrFfj+3?~0vO1_XnQ3n#oH<1K)#>kp?bHj1nNlKMkhPJK(X{vC zODhNjx)kqj?Dc_GTK^*_Jj|+T7j4Pp4PoiGZb*nzS6J3c4YI5{!4w4&vRKzy zud+M$10yOtRmhLmuzTYiHMc9DVLVNai)FKK@TGR|*gw6Iw#L2C6bF3PgDFNOp%=~H zn^13>F5=(+Wjv>KLjGQ}-|Ot@dr5X9(mJ|4HxeHia|KAoFoRPCh*h_@{6OH z3Hof2r;lnD zACCeH)lqb>_lbl^+^hh)qwRq|>s*GtnQd&Es>K)IcRjZsR?UM9XU;DY#cvF}4ZP2j zQheJ8w@YrP%69WEgF4pRw~+J_Q!nq`QX7zCWW>rBuWv1{p$rSI!3jgl!Ww_}wAwmM zE7Tq)=Gh(BIUU#9$eSwUjdO`U(}=?)zow98n+(kAp#Ih4Q85hNMT}M_o##n!O>ueuEhtXlr0VxdO^{gP{bsY6 z=!@=PGgs1YcIwP*=SsQk*n8)gNMy8ntCTU23R4Us=@`IDh6a_rtxZ7Q*UKnRxGgaU z^NsTTB!0U;8#;F1wFTc93#Q5ub%|n8tx&(;d-~tXa?f?uhH5@~Lt#FDV4DkD$JYcP zUgG2Bo!H#87Z(ybU0Y9-L1ab~(Egp7oKygF!Bm0i_jfK>CmqfV=$#VbUqmKuW$=*9 zVmqQ*o}U&cCkttwdTyKnui*0`d}@&d0s;bO>jq7%A`$N>f(Qk~VTV_7jjDl=7Uxwj zy@(L11(umh{$~yfdisGgmq|b{LZdQ8Bj+^_nI1=73b8I+SYq&*@BGTcXDIB&aB+dQ zqry%5w8c?5{pSoPtJy%Zw|Q;~mX!Hrf&qiTSobn_!RV@geQr8KmR`^R_VV+yAn`l4 z>yy-7qEer%E6Naj>aXyU>8Log#MXY~Y;fRcY0^e{AEzK7<4fFgpptC&}=jApJsocYshq%NCyjZ7^59mo8d_(g7 zX+KvY^T=R8Uy?pmSbX58Y6carOcGb2uohKcsl%@eZ$l&tccJ3!Q6E#67*K79awMH4l{8gchKi(0t6LWliem>^3 zbtd5ZX!o<$J-H`&M)2fB`W6fbAVF2TuwTlGdR4|+nTW&tLpR-m*K?5P^gj@U%Oeg# z;PtX>li`|NmJbvKcP0}B+!;rrb2rLN^vMi*K&yOC8u`!j>BawqAd$G_1o83lE_~@~ zQW^GhX*xQ8cUf2d`*r6fn{4ok8JRKP(Oz);fy!qAQgtb-f|VBY?19TZ*{Esjl8>`SFA-u@sX6?G!qw)Mdgxt*Nqn2XLvjBrD;7H@5#FDSqKy| z{`KV(tD6bf^-y+pw$B!L1}h!0)v{KeIVti!yBkR}PB zBAy`bZxaz412WfB-g}kQfQOcE7e~~&!wPA_$M*P;+#4ExWyogUS4^ic%sIbB^PP?4 zIF)GCi+X?Opik4xwfpQ$I4UX%=xnQ-zN7aD6GuRps81U(GFkFx8b}Vd`ZH*yKTNbF z@`^=S2gxTW5!bwB`YS9`E36W5v>`7)Hvj0i@-nl&64k_|mEOta><=O1nNidNpfNil zLXN}v=>oR_#lB_$UXvI^NzOvJr+N%MklvX2mR`S$~Sd^|k4yy-Dc>-QduchDMAUrex@Y@#4K1V>~P?Nk6? za4=o1=ppj-PTpY39|l7b7>xWp?35FZIcpEXE5MdpH$4&Y2)Rg(Z03R4$z3YQ%Cg2F zen)K@x@DD3h-{I$Ex#d2(=8zmUSn4$GU{{Op{-}mlXZFum6HUUgrzDnyXv+ zYpI>kd3D_Mzv&^y|21RMvHyGe_Z3-7`Q-iIUkOiHOFyP9KVGX;Ee(puVth%|vA)~L z-{wH^5OM@I5qhvc2GW4=lO)!;%g=t7o@6$n!Q(%VmN`AGVcqxfrSAN{$7;D9nezSP zd3gBhUKKGN%s0Xi)Y^gT9YObdNVs+__g>8B*>isdg}`Nm*a@N8eFy{6hejS^F8%KH z(b@^h-wVnpn^s1Y;o;$Zski%Auh-XYMK3Nc2&kgEIEuI_h(nG_kNBfL!I&oupBstw zejz~rAm5)%#F)g<*SQk%1pTW3_`*}Bk3q8JI6k{_=WEIEcdmD~s^RQ$@WbL%BL(ie z9;FnEty*yyT3~a5e^h=VOWG`gm5nBm@mPgVsW@1*@6BE=wv5Hx@6CuR%{TZ=RXg0! zu+5X7K@BdpBZE|u@bT~~V2=r6u&%|ve*OAs(6id7atmesr*t*e^tW^Y8m8L>YiqpF z)xew)Hm<3d_O><+ax(HkT)yEpIJp(Dd~E~i;hDfPa>{%0&2wTOKR%Xdpv0iRGtH zyuYfh`OZSn{CwK1VN@TzrgJvaVe-x2H2dV7ib6R8(ijrXY~=wydQ5+70o3w#3N3-$)}4#RMiM# z(Z2JxY?*ebLyvd9-rwRwZthgx{c&Rc)e-EraT!yGk1Q4(F1j_r(o4%u?)0lC9e9jj zgJJ9s{)W6jW4zSyZc5 z^mz|IiBX;S-4oO{C3c$SL~Bh;qC5XZC4Y;0TJyHd zO1ZG0My=?@%%1no)$#fTs4-%X02}w$$baTx;s`^5`bBrxqiqqC?IYdn_6~v0W%ae*^l?=O z;%4>1tr)r9yp?yG&OM}y}@zs=cNt-RJ%s^ZiH3HZ?Zkz%F!!2f1^b9yuu9{I|xD`AjW!<$x&tCp# zB=>a17k%8)vh&Wvwg9CnWO7Ah*L#@mX_!T;&$^LNM|T%mv`$HyoGcm%A!`iE5uJc{ z0<1XJ>oQ=?Asyo`eLAMH#Y1)V>9+IgKz{uPcT~57`3520V5Ohd2fd~X&8J!m_j#=~ z_x}#I>9>7e9TF6;kEH*`36H!Tgai^-v`QF1TE4mT-&JhOEVekLKs4C*2=Y*TMbUg+ z?b+N7%0H>#yQcp@8NS$YQ`T`q5Q1Bzr&>Do3V_Tu-P#`pm7DIWtid|iWN=XlVcN#V zia>{O^u5saXh+G`4n9dG4Bh~`{~jQ^sQS6-30xEE+ZP}v8ML@LAQ-gXMk37+`N&n&ZbOMGse0Qj7ZH5m)1r}akbKRB=m0XqVi^>qIi&y4|LrTsMP5KoK zZt+nn2H#b&*WBEt%+L1p38qbihEYR8jm-{IGuDSM;_+>Qr8)ew@mM7Q8O8Q-*b z*a~Xy#Zb@}Fl0VQ`5xieq|(3O8k9@dEA!)~@u2xOd&GLK0uKqc0RyhbB_Oq>9xyQ0 z&6aPbmq91lG5d`3nMh7Ob-dETpLSj5n@9_UWznN+D>*3Y$}jo9Ng?d0-R+M81;8@< z_|q;8{5SY`cp8YG`FA^xuB|RN;jae<3jUqG1uR2wI~0>2tJH1`EX=2(&#z}5zigH+ zjFc^F`$S<~p@U*tffs5U55C2N*krcOk?`%&-*^Q~LK}O(zOlzDkD1_v=-{2!2Mif8L#Tz~pS3ZU=YO=Mu2^Jx_<>)LNwc{FeuZ4(DBaY9eIe}Tm(hF?@h;p z_z=Jo;3CBdPWhz#RgQ7Ixj8|a*&cV`-D?j2jROMFeXDJ5$~SGOAWQ0juv#5(>DtzI zY5!5&59i!;k8g|YSO-e7fJws0pIy6Z+T%(Bj5cq+^VUSIG_%W;+g}$~!p|Y8j?@LXU-ImMCfetbZI1eQ<=%mGF5nP$5wreD7cUsv;JCdo)QI_4+?6 zp1Yp`Z7edAiq9vF>rLyyHut@$>0PE(@aD{eK^JlcNMLSCxrg*K?A0f~7rzhq%yz80 zeuBx{r)|9A5)OqJ@utY5wn0};QGK;TI(Ua_KMrlC0hde^ir~BB0T>XH9wB_5r=~tN z7^%_Y%wpltb=!xu?EY$um)t(Y-@^4J+`cC0HWGmJ)^*-3paA>vs7c3B;22`nmzNnF zeh;FMo9i44oHTXR7<9Gpnkw&lGoEyzYHKeEY-)GO>V zhY)Xli1j*s$iCExihxd1?D7m5aFc_)s({@Qh4`1~fBrZ{Z-0Hti!83WiyT67g@Q{L z`7DnbD8}Hl011XLQihb5mQFZ}yayO9uvfhRYRjjr;pY4R4+H(~*q{0LrSJEM>!+XcjyV9|l|$M;}rrrPJol19=Odb+h$;57QD zmpGlqh_^|p-d2j)ja-`fks%)rlNxrp4>Q$7-g^3&TNO^(85>LM`KolF&tZ^i%J}?D zY)J{{LhU-4#odEwwYrDBAP?6C&+od*8j=5d+S_^u(ql z*%T;NBGYy>I!BRbfzBW>sG$zI0l?EPV)>>bIv57<@tMaskATFq%$OUa8#RRw-IRq2 z*|R%d`)K&vbo0UCl>1W1IxWPu#?Ijbo1vkhJE#x$=Nk#ZgqDkdRx;vrYgFO2FoYpX z_8n;f>W?*At(IcLlb9it&S#N$dRD(n0_Yt#B^EE7XTgbg1fKt&DE!&r(~((^JpyxB z4?YDQl{{PO@fhJ!46Z`&eOzkrlJ1)IW9tU|`T85p$q1(pdT(Q4BT(}Nq4BDR=!-l-Z@VXeEZ#d1tLt>Wa$e+ z^DnPu;FbzS<|$dBdmh+vkCHZuc1kyRcauOW*(zE^gjhd?XK749fgHGeCFjKkeBeOP z2jqbQV#4p@CbVvXFQrhwW4ypIy+|gA#;P(H?;8uK`3yb&ILsNDc$)L|JtzplLI(U` zV1m91U+vifCt7~s77&JB5rufx7^ewq3TaH@X;02O33qUuDEI2E;6#CCLB7_8;uHzV zXW&3d0-7MbvMCNo!0A8g#=yfj`S*VS&EN<~wD(j*a_m9Btzj@_7%Xe|*;=xDWBItP zxIaw-_4`*%{9d#71M$CY!u;D4-fWGn4(2Gsfy=}Cc$b2h#~pe`Mh0kZp-By^Dedkk zRtr8AsO;HL<{^7KWs_rMW*6R(d5r88k&L1aSw|Ei zlsGu(d-?ng-}AVC@;DynzVGXLU9a_gK407oNQ(G|zfj6MIRsSm@(sFArc*gj!<9U zon5l(t~;t#0MWpnun{HXqX=T|CPMwCybu?F^V3lj&jB{__R(7j&EKJ*6*c*HVuIz? z?Ixi5W_CMMGI7>3b&wwWnK#+Yf|Z`}HCq;qR|;flXvVW5d@)LnF5hy%gWd+8P*$dw zf5o2~(-q8v*^Hzxc!2u`aFYL7xKBJcX89xww;Aw%FAUfEI?z&4l|ZiLLZ92&qC+=4 zp1l!h@l_^lTNKSU)dBW|@O7`8zH(y~CqVDX$dQX~_<#Zi9>q5LGKN3~3;zEpBj*Pg^qWq0|5#z*{YpBxTaC(YhuBS=rr z?R|VeD^Q@3s1wJ$w33ja>idJiEt4;ARGjB-7os>iR%2j32}a6;EZQZE*8UTFDH{6E z$6kcV^gQA7s0%}bv}lp#nJIt?Id|RqIWBpAO&%WJ1BFnX`l%<$r9qYk`Ua)?tH1jU zAWN0MhOsLNfod^_qPr*kX;gufT0h#4PI$?iZcTL6AB%l=cU8TKJ(xnk@z_=eee-q* zPnmhbmkoa)%@`5ewUZpagw%50bL%dLo)CaZ zI@dFVw4#sp~ppzM~DlhZRb#Zi9gy7PRSKaZf` z)mKx?5C`c&%WBC*p<)T z&hBlAPTc<4i12^MVBYho0IJDK`aP)6Dlv%8Woy0vEM&67Zt+iiQ4Q{i+q0~#!T~FWi z4;FA&eCL0(rMg%cN82Vi7DUl}zqG{FN-A3+@KoKTNb~9PyBoxn1a{TPQ}wYTHQb|C z24nX=dB5!F$EAp$8bJ;ektcMzBYC)%+~D*UPkx3dT`9m^p3*+ouk(aQkiY-nK=qPG zPT$uA+GHH=MP#wiKp1O-$e|bw+X18wvZ3ZEoq+iD@LkvV`$D&%q=~%O5_4rY+#d!s zS;UmP`hkVObRGWK1ZqEXBrq!!bHdSM4kjQ$rsteA&E5(<*n7vgCi?o+9S$N(HFy#P z!@ExzG;1b|twW?rl{HjWKB~45g=}K6y~9A=Y*#3^uxoGW)vH%sU^3V3+rP9+h;h_N z^kbx%>9@b#j-4!qiG%vKF6Zh)L|bAyDXX*lXJQJmf^|`?PqW7PF5#W8_}JWf&-r0| ze0i?b_so7w3e~YOF?!%HcbEELe2k1CcmVzg`$I?nywdf@40ezFq;0_${t&5sEQ~T8mMzebmEu zo}*k)XN#U4G47l^Pw`7XEZGeM z+{0c15D=_@ez=e*GTh+dp6Al$bNVLYxFFuq>V%*Q-0^zjhIva)*%%FA3_T&7Z%050gv zBhFw?YOjaxNK{-kL_}v-U9B9#Zdj1s84$Coal7`WMkSJT&yrz!W~P!WA64X|da=+m zY~;OrjQXHks-zU%+k2IPkx`^b4t4wXJw-Y=T0Asz@D8T5z=HE`zt%ie{xMCgidxz3 z*77yl_C3+0U6=Lcwq4#>sPSYzPj~sp%uLP?V`GIm;6-Hv$cy_semyo2=3&sfS8r)i zdl#A;0lp20>8S5Yt1DwFuwkV`i&d|HyD|gr>epjk>0k^>LYPGg?4I=7w=*?_V)~R8QMU8KgE#ma)CL)Xo4z>kU#mIlgA&9s9 z61V`|rMw)xxqkWUIkC`5C8xX;)jALUpwT9-0NJ4r9i9{m445(W$p{Te;cOlN9c-*9;7AdL0=%( z`V{W4n$k|!2XEeNVe?gF>lD4`HqZ9(yQ)i>lB6)oi@+UV^2Z z(+GL!;_@Tz1`uc(hkWQ2jsVk{`o&IFKdF3d*l+hI#Sy4#gyBG%%}M3>VsH}aiuY|Y$? zS5=$3q5e>~Pf+OGj*E0MUXwTb`UD-KSWM(Av2c4~mLQbuJ22}-P_P8|yYBMLRu+2M zepQ{@uN~q&BF^!wFLyJ+bnGW)G?y5UKhQPN3Kj7Qdy<6`Sx{ScxViD_g#`A`xW_I7 zm1EfSBit!t<5mzlx_l$}H)X!3wY`gC&Q1x_<$ z9AK9AdO>6|3)E!6Ia^#=Sunne2GDZs4x}Wa*X1@p>L=~E^|3UZ?u42x!zr7k%F-(cr>rYb$iBT>tgU_-q}U z;Bp6VP7Ku5VDYv;%DF33ULs_XJq`v~ZtuLmHgDg&fZ2DRdROw?;JNyuR6CUPld$KnSy`n|gHf=LLD3 zo?K@CJ8cp_&@7!3OFKVd{oRM(1p&taQvA*$=t$Xhk+ZacL<4D*65E=%x#om;`0!O_Lk z(Am6R;bTcQW=-w2U#;JIM~Jw~DnH*pj5U!(n5ZHbB~DfEdK+xqm2qEG1pX|~E3EtD zL(_hFc`Yp*s6vlz&EQvOw!^oJik=r0sX=?*ukNOM&RXDSl;&TbBKvTn5nT1jB4Jlg zVV*27R5AOvXvn{W5!N-1bE$CQbayBkWDYPCeayFm?c=fT&W#kctv47DRnEQ}90ZKN ztFqbnd0nMaNyW7L1H*>QPZg&XbW`8w-B8kfvcW1rR^RsYe{V%knNLzn>UfdJBf2ws+*`3YDsX`vo>S z&zMBS5T4#3_?Nr#RS`Dc$}^bP5`2#Sx_SoeuWfNV9DK)X9rQ9sP!M4dz;i(3UpwR3|cJ|A+m=d()Rs`Z-yE%38{|ogPl^t${^x!Zog^2`t20$ADb7E1G zN^U&Vz=e7P)x`{eXbC+9(7vM?(8Ok@c=)D7?qQybO%`Wn>$c}Ee%D{*^grf*|G{7h zXRBnJ8h<}uC2BEC`&zCoweDk;wqn=3mopwVkd@xv-UM9u=3O;_Z2dwq0iYToexaLF z`E+__R+>dEzB2;16%(~SIjQQhtnj7?TE8=WAc_cu3k{~%WJ;1HsPY(Wl;w2Lf76Ky`4Pmj8`2>eXth0vM1r?9pM8VTQ zdMQ29uo6Ng4H*D|i!o_2@?R(r>l$o-_wF5mnVGr$WDI|O^!p}&yIwSk9ZD2nqZpni zESUT0g$d?fXdIa*42XTCnVcoMHll40)s>qL|DltnEXCMlVIO`!rbI6SWm2`57#`|L zrBSab{&eTu2|mVrwZJjW(EUv;D3+hB0_079E|4z}q?m|a{$YThX=Lbs!}E#zt=vwQ zHXMRY8gPq-$idAMpy>3McCxF%R30tKy=QuyEt{ZYp#j`n3l7Y;Ra>ST8oJjY{SJIF z%jPb|YTwI9ielrut3aAp>?k(Ol#y3p^>x!~uH=I9{nTbMcHcF?6ZjH>Ut^Z|_Z@^z*eo zoV>3_sJ0Cp+1c440+?RMQY^^TGl%b?2ZwnP4mk*QA*?868v}QT4AbLlY5QEBF%s0! zPb;i`ZHVSXsUoSLjXdL6g9Gmeh?NHmcu$OU#FO>5F;JfX*PdY{z?}K+a+?Tf1o(|_ z*|!I1fE1Z5Gm=N+#HRfaV3gZxY-62~X%QStamz>N4&$gGhPoQt^(Bli@qZbxnKdg~)zo?7k8m`xl~`BhPU)7W3XW;B4S;*^?ySxXB*)a&nt2L1~1ApjCiC`(Ei29^p6 zLf0o7{pRa<@ELH;;rFEdfwZoivK#+v9KbNjrO^-~%bOO4IQXY{tf3h&V97ENNW6yx zDWsO%;tK;ZL@nznYF>*9%F0au__J~Z9>N23k3f~xdCG>*3PyIz_!a?3$3~lp`h`U$ zV`;TXx&P_S6Nl1fvLCoOOp}3TkRU}JWn6sOoqG7BlBYevc(ysi?!f1FSm90v_115= z1F(x(YG4-t8WBU?asO5!DxbxWjRG4U&62>PJ&%nw1k@WFE{koT*;o@g9=d=JUmj$% z1;>yVFbN@kKEB64Lugc)p?2GlG$busCQ6Ktj+@dK4i}~k|0&1VCy=|=wS6V7guJnM zK6;t-!G9f}8iN7Fko_)Kd3%BDyOC6YsA{tYd`PU>Am)q8OWS9 zL->V7m$4vn^LOjR4^2C{&OBY$78?Zm9Bu3xvMsa5i_`+QCeE5E+h3bD_U79Iiy(FI z9th$--nvwh4vd7jZ=Lns=E&@2k?y{aJ*jjKV0R+0BM=aJcHFdjYKibtEJX^VME1?J z;h9KqWLW1aP^;V`Dt{_lSXjUUolajcZBb=WNufSqcW7;GuTw)qCp}s&f0k^IH|G<7>C1PF5-a3?kQd{s$vY_ZX3vY0 zN*WCkvCgKGO5v7~t#*U&-zPS8bcF4L3QHUa9(}yG7dkN=e?MK5@UwTMo`@p1OL5RS zCD*&)U1yp-TRYKxMaTD$bZ`t)x7L1QNoy7n+6L=I%UXE3}Pf$=$KY$j01sL`Xpz?O<`JNsYi5`+nl#!yl znO}z`O!jb7N1IQW4k3VPowRDI$+*#-hy)oO|$$)zD z+`SZZyO6{9Kf#0uiErIJeK?~vknniVpnw+~p+##)Ic~2A8vbF?O@J+2L&1fgnV$y- zH>a46@nlrFYgm6FbV5y8Sy@#>qtnOD&Fv&lS#*PjhWg9i9_eW%;8nL&&>V&>kYGW5 zD1b#yzb8JDB1!C^dih-pfp`X^EcT8;NiNB6D#H__CoUjSD!yWhbzJYrjRSM*aZAkE z8=FjQYTa9rJp%5F65;gCqdBvjIG^Ynprygm$=xoOJp%M5KbMu2T?7!vcjKTHwh5Xb zcp&VBNf{j-m7%c0>da9>Mpt*E&EKo{JxHEWu!v|Wl5D^w{Ss|t@+ykzQDZvuoR%*+ zDa;a{EOlTd6?(SmnXhvku+V@N%p1Ok9P|L~k^ZOQOsMg=CTIL$O`Ujt8xW^J+ z|JvB#2i{XqhRf*5W#tNvq=6{;9V%ERVwf!hIh=*0L#M+_IM_;}jncC{3`?n{>5^S| zM9AJ&B~8DKp({SDzh#2!xU_;_Zn?q`hJRCHa9phy`9EMWqrTVf(xi)+^~&gfss2>p=Hlh9bhqe z{~$eZZO}h81x2Qbz$H=safRt4+Zw)G4GY-0_hk1iwQ?XM~6~l%A4D~;NS3Ch> z-U|>O@$&KT@JMEcP}9d8qe@KIEN?eSWRGMj=3ci*M_vZffX_#-WC-0?9GjyKlY8>= zWnc7VW_9^5V~$J^$-U8o5k7 z%=s6+ozK})8tJLriCQE`-Af&GpOBg&W^X6Lw57_tjX-ovsLA zV-3<5o|z|@pJ%re-jN#@3m-34fev~+w=Tb*+kBrl>pow(D?r9ffQzC3;ac0mgn#Fk zhf0-AHk}UD7UwTqxR5z8Q1)jA{~!yTMF4>|D$ij5WVwbwv?3k{LXD(`UGM>;qcd&Xmq+RGEmT)cdk`#SM%IlKb$peP9iZs1Fp{4 z8YmKm^f2f&xOxP${NLuyW91Yl5+>te+g4TxKat(3dqv;aYaEHZ>IFTRoz+y+?jCP1 zl;qa9$o?+b>Y?(OO^WEWmsSB>eb}>zqZ=neR+UQv_glqwcE8u)84}%EvVN)J(W$y$ z%?e|Zgiqc8Xsg1{%gg(}<3-vf=~Z2M4UWyoS6Bms)I5XHBxb_JEnX_h8aEJDRUavhBN5)Cz^6c=GzCK! z6Z|Z7h_1CaeE+=+iEFWb8&RwmMLDcHjCq&vSjhnzp)nflI)f)EpC~@(3eM%tP`6gStFQ-HJ0>i#;WEk4BbVZ?Hz|>g` zai8O1r_*lP1^arRC0rd_?&W+D?WwK3bT8Eu?kL&DUPE_-%eRUv=! zs?hjR!XE745rp{J<`u6w>H~#hInKUE{3I$P)d7&Bt-)`r@POLjT-lU?G z6+ZjHAad$TI!%h91?_tRq>St5qHjTKsuLR-qs62uRR6Hz_ipLj_hsVXvs za>{01? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index fb32fd29269aa4..70129fbd9bafc8 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -74,11 +74,9 @@ "builddir": true, "config-opts": [ "-DCMAKE_BUILD_TYPE=RelWithDebInfo", - "-DOBS_CMAKE_VERSION=3", "-DENABLE_WAYLAND=ON", "-DENABLE_BROWSER=ON", "-DCEF_ROOT_DIR=/app/cef", - "-DUSE_XDG=ON", "-DENABLE_ALSA=OFF", "-DENABLE_PULSEAUDIO=ON", "-DENABLE_JACK=ON", diff --git a/cmake/Modules/CompilerConfig.cmake b/cmake/Modules/CompilerConfig.cmake deleted file mode 100644 index 849d19de75ccf2..00000000000000 --- a/cmake/Modules/CompilerConfig.cmake +++ /dev/null @@ -1,192 +0,0 @@ -include(CheckCCompilerFlag) -include(CheckCXXCompilerFlag) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -# CMake < 3.21 only goes up to 11, but it's mostly identical to 17 anyway. -if(${CMAKE_VERSION} VERSION_LESS "3.21.0") - set(CMAKE_C_STANDARD 11) - set(CMAKE_C_STANDARD_REQUIRED ON) -else() - set(CMAKE_C_STANDARD 17) - set(CMAKE_C_STANDARD_REQUIRED ON) -endif() - -# TODO/FIXME: Investigate disabling C extensions on Linux/POSIX -if(OS_MACOS OR NOT OS_POSIX) - set(CMAKE_C_EXTENSIONS OFF) -endif() - -# Set compile options for MSVC -if(OS_WINDOWS AND MSVC) - if(NOT EXISTS "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user") - file( - GENERATE - OUTPUT "${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.user" - INPUT "${CMAKE_SOURCE_DIR}/cmake/bundle/windows/ALL_BUILD.vcxproj.user.in") - endif() - - # CMake 3.24 introduces a bug mistakenly interpreting MSVC as supporting `-pthread` - if(${CMAKE_VERSION} VERSION_EQUAL "3.24.0") - set(THREADS_HAVE_PTHREAD_ARG OFF) - endif() - - # Check for Win SDK version 10.0.20348 or above - obs_status(STATUS "Windows API version is ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") - string(REPLACE "." ";" WINAPI_VER "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}") - - list(GET WINAPI_VER 0 WINAPI_VER_MAJOR) - list(GET WINAPI_VER 1 WINAPI_VER_MINOR) - list(GET WINAPI_VER 2 WINAPI_VER_BUILD) - - set(WINAPI_COMPATIBLE FALSE) - if(WINAPI_VER_MAJOR EQUAL 10) - if(WINAPI_VER_MINOR EQUAL 0) - if(WINAPI_VER_BUILD GREATER_EQUAL 20348) - set(WINAPI_COMPATIBLE TRUE) - endif() - else() - set(WINAPI_COMPATIBLE TRUE) - endif() - elseif(WINAPI_VER_MAJOR GREATER 10) - set(WINAPI_COMPATIBLE TRUE) - endif() - - if(NOT WINAPI_COMPATIBLE) - obs_status(FATAL_ERROR "OBS requires Windows 10 SDK version 10.0.20348.0 and above to compile.\n" - "Please download the most recent Windows 10 SDK in order to compile.") - endif() - - add_compile_options( - /Brepro - /MP - /W3 - /WX - /wd4127 - /wd4201 - /wd4456 - /wd4457 - /wd4458 - /wd4459 - /wd4595 - "$<$:/DDEBUG=1;/D_DEBUG=1>" - "$<$:/Ob2>" - /DUNICODE - /D_UNICODE - /D_CRT_SECURE_NO_WARNINGS - /D_CRT_NONSTDC_NO_WARNINGS - /utf-8 - /permissive- - /Zc:__cplusplus - /Zc:preprocessor) - - add_link_options( - "LINKER:/Brepro" "LINKER:/OPT:REF" "LINKER:/WX" "$<$>:LINKER\:/SAFESEH\:NO>" - "$<$:LINKER\:/INCREMENTAL\:NO>" "$<$:LINKER\:/INCREMENTAL\:NO;/OPT:ICF>") -else() - find_program(CCACHE_PROGRAM "ccache") - set(CCACHE_SUPPORT - ON - CACHE BOOL "Enable ccache support") - mark_as_advanced(CCACHE_PROGRAM) - if(CCACHE_PROGRAM AND CCACHE_SUPPORT) - set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_OBJC_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_OBJCXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_CUDA_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") # CMake 3.9+ - endif() - - option(CALM_DEPRECATION "Keep deprecated-declarations as warnings" OFF) - #[[ - Note about -Wmaybe-uninitialized on GCC, this warning seems to be subject of various regressions and false positives. This - warning is set to not turn into an error. - - - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562 for 12.1.0 - - https://github.com/obsproject/obs-studio/issues/8850 for 13.1.1 - ]] - add_compile_options( - -Werror - -Wextra - -Wvla - -Wswitch - -Wno-error=switch - -Wformat - -Wformat-security - -Wunused-parameter - -Wno-unused-function - -Wno-missing-field-initializers - -fno-strict-aliasing - "$<$:-Werror-implicit-function-declaration;-Wno-missing-braces>" - "$<$:-stdlib=libc++>" - "$<$:-DDEBUG=1;-D_DEBUG=1>" - "$<$:-Wnull-conversion;-fcolor-diagnostics;-Wno-error=shorten-64-to-32>" - "$<$:-Wnull-conversion;-fcolor-diagnostics;-Wno-error=shorten-64-to-32>" - "$<$:-Wconversion-null;-Wno-error=maybe-uninitialized>" - "$<$:-Wno-error=maybe-uninitialized>" - "$<$:-Wno-error=deprecated-declarations>") - - # GCC on aarch64 emits type-limits warnings that do not appear on x86_64 - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") - add_compile_options(-Wno-error=type-limits) - endif() - - if(OBS_CODESIGN_LINKER) - add_link_options("LINKER:$<$:-adhoc_codesign>") - endif() - - if(MINGW) - set(CMAKE_WIDL - "widl" - CACHE INTERNAL "wine IDL header file generation program") - add_compile_definitions("_WIN32_WINNT=0x0600;WINVER=0x0600") - endif() -endif() - -if(MSVC_CXX_ARCHITECTURE_ID) - string(TOLOWER ${MSVC_CXX_ARCHITECTURE_ID} LOWERCASE_CMAKE_SYSTEM_PROCESSOR) -else() - string(TOLOWER ${CMAKE_SYSTEM_PROCESSOR} LOWERCASE_CMAKE_SYSTEM_PROCESSOR) -endif() - -if(LOWERCASE_CMAKE_SYSTEM_PROCESSOR MATCHES "(i[3-6]86|x86|x64|x86_64|amd64|e2k)") - if(NOT MSVC AND NOT CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") - set(ARCH_SIMD_FLAGS -mmmx -msse -msse2) - endif() -elseif(LOWERCASE_CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64(le)?") - set(ARCH_SIMD_DEFINES -DNO_WARN_X86_INTRINSICS) - set(ARCH_SIMD_FLAGS -mvsx) -else() - if(CMAKE_C_COMPILER_ID MATCHES "^(Apple)?Clang|GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "^(Apple)?Clang|GNU") - check_c_compiler_flag("-fopenmp-simd" C_COMPILER_SUPPORTS_OPENMP_SIMD) - check_cxx_compiler_flag("-fopenmp-simd" CXX_COMPILER_SUPPORTS_OPENMP_SIMD) - set(ARCH_SIMD_FLAGS - -DSIMDE_ENABLE_OPENMP "$<$,$>:-fopenmp-simd>" - "$<$,$>:-fopenmp-simd>") - endif() -endif() - -if(LOWERCASE_CMAKE_SYSTEM_PROCESSOR MATCHES "e2k") - foreach( - TEST_C_FLAG - "-Wno-unused-parameter" - "-Wno-ignored-qualifiers" - "-Wno-pointer-sign" - "-Wno-unused-variable" - "-Wno-sign-compare" - "-Wno-bad-return-value-type" - "-Wno-maybe-uninitialized") - check_c_compiler_flag(${TEST_C_FLAG} C_COMPILER_SUPPORTS_FLAG_${TEST_C_FLAG}) - if(C_COMPILER_SUPPORTS_FLAG_${TEST_C_FLAG}) - set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} ${TEST_C_FLAG}) - endif() - endforeach() - foreach(TEST_CXX_FLAG "-Wno-invalid-offsetof" "-Wno-maybe-uninitialized") - check_cxx_compiler_flag(${TEST_CXX_FLAG} CXX_COMPILER_SUPPORTS_FLAG_${TEST_CXX_FLAG}) - if(CXX_COMPILER_SUPPORTS_FLAG_${TEST_CXX_FLAG}) - set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${TEST_CXX_FLAG}) - endif() - endforeach() -endif() diff --git a/cmake/Modules/CopyMSVCBins.cmake b/cmake/Modules/CopyMSVCBins.cmake deleted file mode 100644 index 390a49edf51643..00000000000000 --- a/cmake/Modules/CopyMSVCBins.cmake +++ /dev/null @@ -1,364 +0,0 @@ -# Doesn't really make sense anywhere else -if(NOT MSVC) - return() -endif() - -# Internal variable to avoid copying more than once -if(COPIED_DEPENDENCIES) - return() -endif() - -option(COPY_DEPENDENCIES "Automatically try copying all dependencies" ON) -if(NOT COPY_DEPENDENCIES) - return() -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_bin_suffix 64) -else() - set(_bin_suffix 32) -endif() - -file( - GLOB - FFMPEG_BIN_FILES - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/avcodec-*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/avcodec-*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/avcodec-*.dll" - "${FFMPEG_avformat_INCLUDE_DIR}/../bin/avformat-*.dll" - "${FFMPEG_avformat_INCLUDE_DIR}/../bin${_bin_suffix}/avformat-*.dll" - "${FFMPEG_avformat_INCLUDE_DIR}/bin${_bin_suffix}/avformat-*.dll" - "${FFMPEG_avutil_INCLUDE_DIR}/../bin/avutil-*.dll" - "${FFMPEG_avutil_INCLUDE_DIR}/../bin${_bin_suffix}/avutil-*.dll" - "${FFMPEG_avutil_INCLUDE_DIR}/bin${_bin_suffix}/avutil-*.dll" - "${FFMPEG_avdevice_INCLUDE_DIR}/../bin/avdevice-*.dll" - "${FFMPEG_avdevice_INCLUDE_DIR}/../bin${_bin_suffix}/avdevice-*.dll" - "${FFMPEG_avdevice_INCLUDE_DIR}/bin${_bin_suffix}/avdevice-*.dll" - "${FFMPEG_avfilter_INCLUDE_DIR}/../bin/avfilter-*.dll" - "${FFMPEG_avfilter_INCLUDE_DIR}/../bin${_bin_suffix}/avfilter-*.dll" - "${FFMPEG_avfilter_INCLUDE_DIR}/bin${_bin_suffix}/avfilter-*.dll" - "${FFMPEG_postproc_INCLUDE_DIR}/../bin/postproc-*.dll" - "${FFMPEG_postproc_INCLUDE_DIR}/../bin${_bin_suffix}/postproc-*.dll" - "${FFMPEG_postproc_INCLUDE_DIR}/bin${_bin_suffix}/postproc-*.dll" - "${FFMPEG_swscale_INCLUDE_DIR}/../bin/swscale-*.dll" - "${FFMPEG_swscale_INCLUDE_DIR}/bin${_bin_suffix}/swscale-*.dll" - "${FFMPEG_swscale_INCLUDE_DIR}/../bin${_bin_suffix}/swscale-*.dll" - "${FFMPEG_swresample_INCLUDE_DIR}/../bin/swresample-*.dll" - "${FFMPEG_swresample_INCLUDE_DIR}/../bin${_bin_suffix}/swresample-*.dll" - "${FFMPEG_swresample_INCLUDE_DIR}/bin${_bin_suffix}/swresample-*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libopus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/opus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libopus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/opus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libogg*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libvorbis*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libogg*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libvorbis*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libvpx*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libvpx*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libsrt*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libsrt*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libmbedcrypto*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libmbedcrypto*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libmbedtls*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libmbedtls*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libmbedx509*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libmbedx509*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/libopus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/opus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/libopus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/opus*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libbz2*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/zlib*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libbz2*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/zlib*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/libSvtAv1Enc.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/libSvtAv1Enc.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libSvtAv1Enc.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libSvtAv1Enc.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/libaom.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/libaom.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/libaom.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/libaom.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/librist.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/librist.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/librist.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/librist.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/libbz2*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin${_bin_suffix}/zlib*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/libbz2*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin${_bin_suffix}/zlib*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/../bin/*datachannel*.dll" - "${FFMPEG_avcodec_INCLUDE_DIR}/bin/*datachannel*.dll") - -file(GLOB X264_BIN_FILES "${X264_INCLUDE_DIR}/../bin${_bin_suffix}/libx264-*.dll" - "${X264_INCLUDE_DIR}/../bin/libx264-*.dll" "${X264_INCLUDE_DIR}/bin/libx264-*.dll" - "${X264_INCLUDE_DIR}/bin${_bin_suffix}/libx264-*.dll") - -file( - GLOB - FREETYPE_BIN_FILES - "${FREETYPE_INCLUDE_DIR_ft2build}/../../bin${_bin_suffix}/libfreetype*-*.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../../bin/libfreetype*-*.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../bin${_bin_suffix}/libfreetype*-*.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../bin/libfreetype*-*.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/bin/libfreetype*-*.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/bin${_bin_suffix}/libfreetype*-*.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../../bin${_bin_suffix}/freetype.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../../bin/freetype.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../bin${_bin_suffix}/freetype.dll" - "${FREETYPE_INCLUDE_DIR_ft2build}/../bin/freetype.dll") - -file(GLOB LIBFDK_BIN_FILES "${Libfdk_INCLUDE_DIR}/../bin${_bin_suffix}/libfdk*-*.dll" - "${Libfdk_INCLUDE_DIR}/../bin/libfdk*-*.dll" "${Libfdk_INCLUDE_DIR}/bin/libfdk*-*.dll" - "${Libfdk_INCLUDE_DIR}/bin${_bin_suffix}/libfdk*-*.dll") - -file( - GLOB - SSL_BIN_FILES - "${SSL_INCLUDE_DIR}/../bin${_bin_suffix}/ssleay32*.dll" - "${SSL_INCLUDE_DIR}/../bin${_bin_suffix}/libeay32*.dll" - "${SSL_INCLUDE_DIR}/../bin/ssleay32*.dll" - "${SSL_INCLUDE_DIR}/../bin/libeay32*.dll" - "${SSL_INCLUDE_DIR}/bin${_bin_suffix}/ssleay32*.dll" - "${SSL_INCLUDE_DIR}/bin${_bin_suffix}/libeay32*.dll" - "${SSL_INCLUDE_DIR}/bin/ssleay32*.dll" - "${SSL_INCLUDE_DIR}/bin/libeay32*.dll") - -if(NOT DEFINED CURL_INCLUDE_DIR AND TARGET CURL::libcurl) - get_target_property(CURL_INCLUDE_DIR CURL::libcurl INTERFACE_INCLUDE_DIRECTORIES) -endif() - -file( - GLOB - CURL_BIN_FILES - "${CURL_INCLUDE_DIR}/../build/Win${_bin_suffix}/VC12/DLL Release - DLL Windows SSPI/libcurl.dll" - "${CURL_INCLUDE_DIR}/../bin${_bin_suffix}/libcurl*.dll" - "${CURL_INCLUDE_DIR}/../bin${_bin_suffix}/curl*.dll" - "${CURL_INCLUDE_DIR}/../bin/libcurl*.dll" - "${CURL_INCLUDE_DIR}/../bin/curl*.dll" - "${CURL_INCLUDE_DIR}/bin${_bin_suffix}/libcurl*.dll" - "${CURL_INCLUDE_DIR}/bin${_bin_suffix}/curl*.dll" - "${CURL_INCLUDE_DIR}/bin/libcurl*.dll" - "${CURL_INCLUDE_DIR}/bin/curl*.dll") - -file( - GLOB - LUA_BIN_FILES - "${LUAJIT_INCLUDE_DIR}/../../bin${_bin_suffix}/lua*.dll" - "${LUAJIT_INCLUDE_DIR}/../../bin/lua*.dll" - "${LUAJIT_INCLUDE_DIR}/../bin${_bin_suffix}/lua*.dll" - "${LUAJIT_INCLUDE_DIR}/../bin/lua*.dll" - "${LUAJIT_INCLUDE_DIR}/bin${_bin_suffix}/lua*.dll" - "${LUAJIT_INCLUDE_DIR}/bin/lua*.dll" - "${LUAJIT_INCLUDE_DIR}/lua*.dll") - -if(ZLIB_LIB) - get_filename_component(ZLIB_BIN_PATH ${ZLIB_LIB} PATH) -endif() -file(GLOB ZLIB_BIN_FILES "${ZLIB_BIN_PATH}/zlib*.dll") - -if(NOT ZLIB_BIN_FILES) - file(GLOB ZLIB_BIN_FILES "${ZLIB_INCLUDE_DIR}/../bin${_bin_suffix}/zlib*.dll" "${ZLIB_INCLUDE_DIR}/../bin/zlib*.dll" - "${ZLIB_INCLUDE_DIR}/bin${_bin_suffix}/zlib*.dll" "${ZLIB_INCLUDE_DIR}/bin/zlib*.dll") -endif() - -file(GLOB RNNOISE_BIN_FILES "${RNNOISE_INCLUDE_DIR}/../bin${_bin_suffix}/rnnoise*.dll" - "${RNNOISE_INCLUDE_DIR}/../bin/rnnoise*.dll") - -set(QtCore_DIR "${Qt6Core_DIR}") -cmake_path(SET QtCore_DIR_NORM NORMALIZE "${QtCore_DIR}/../../..") -set(QtCore_BIN_DIR "${QtCore_DIR_NORM}bin") -set(QtCore_PLUGIN_DIR "${QtCore_DIR_NORM}plugins") -obs_status(STATUS "QtCore_BIN_DIR: ${QtCore_BIN_DIR}") -obs_status(STATUS "QtCore_PLUGIN_DIR: ${QtCore_PLUGIN_DIR}") - -file( - GLOB - QT_DEBUG_BIN_FILES - "${QtCore_BIN_DIR}/Qt6Cored.dll" - "${QtCore_BIN_DIR}/Qt6Guid.dll" - "${QtCore_BIN_DIR}/Qt6Widgetsd.dll" - "${QtCore_BIN_DIR}/Qt6Svgd.dll" - "${QtCore_BIN_DIR}/Qt6Xmld.dll" - "${QtCore_BIN_DIR}/Qt6Networkd.dll" - "${QtCore_BIN_DIR}/libGLESv2d.dll" - "${QtCore_BIN_DIR}/libEGLd.dll") -file(GLOB QT_DEBUG_PLAT_BIN_FILES "${QtCore_PLUGIN_DIR}/platforms/qwindowsd.dll") -file(GLOB QT_DEBUG_STYLES_BIN_FILES "${QtCore_PLUGIN_DIR}/styles/qwindowsvistastyled.dll") -file(GLOB QT_DEBUG_ICONENGINE_BIN_FILES "${QtCore_PLUGIN_DIR}/iconengines/qsvgicond.dll") -file(GLOB QT_DEBUG_IMAGEFORMATS_BIN_FILES "${QtCore_PLUGIN_DIR}/imageformats/qsvgd.dll" - "${QtCore_PLUGIN_DIR}/imageformats/qgifd.dll" "${QtCore_PLUGIN_DIR}/imageformats/qjpegd.dll") - -file( - GLOB - QT_BIN_FILES - "${QtCore_BIN_DIR}/Qt6Core.dll" - "${QtCore_BIN_DIR}/Qt6Gui.dll" - "${QtCore_BIN_DIR}/Qt6Widgets.dll" - "${QtCore_BIN_DIR}/Qt6Svg.dll" - "${QtCore_BIN_DIR}/Qt6Xml.dll" - "${QtCore_BIN_DIR}/Qt6Network.dll" - "${QtCore_BIN_DIR}/libGLESv2.dll" - "${QtCore_BIN_DIR}/libEGL.dll") -file(GLOB QT_PLAT_BIN_FILES "${QtCore_PLUGIN_DIR}/platforms/qwindows.dll") -file(GLOB QT_STYLES_BIN_FILES "${QtCore_PLUGIN_DIR}/styles/qwindowsvistastyle.dll") -file(GLOB QT_ICONENGINE_BIN_FILES "${QtCore_PLUGIN_DIR}/iconengines/qsvgicon.dll") -file(GLOB QT_IMAGEFORMATS_BIN_FILES "${QtCore_PLUGIN_DIR}/imageformats/qsvg.dll" - "${QtCore_PLUGIN_DIR}/imageformats/qgif.dll" "${QtCore_PLUGIN_DIR}/imageformats/qjpeg.dll") - -file(GLOB QT_ICU_BIN_FILES "${QtCore_BIN_DIR}/icu*.dll") - -set(ALL_BASE_BIN_FILES - ${FFMPEG_BIN_FILES} - ${X264_BIN_FILES} - ${CURL_BIN_FILES} - ${LUA_BIN_FILES} - ${SSL_BIN_FILES} - ${ZLIB_BIN_FILES} - ${LIBFDK_BIN_FILES} - ${FREETYPE_BIN_FILES} - ${RNNOISE_BIN_FILES} - ${QT_ICU_BIN_FILES}) - -set(ALL_REL_BIN_FILES ${QT_BIN_FILES}) - -set(ALL_DBG_BIN_FILES ${QT_DEBUG_BIN_FILES}) - -set(ALL_PLATFORM_BIN_FILES) -set(ALL_PLATFORM_REL_BIN_FILES ${QT_PLAT_BIN_FILES}) -set(ALL_PLATFORM_DBG_BIN_FILES ${QT_DEBUG_PLAT_BIN_FILES}) - -set(ALL_STYLES_BIN_FILES) -set(ALL_STYLES_REL_BIN_FILES ${QT_STYLES_BIN_FILES}) -set(ALL_STYLES_DBG_BIN_FILES ${QT_DEBUG_STYLES_BIN_FILES}) - -set(ALL_ICONENGINE_BIN_FILES) -set(ALL_ICONENGINE_REL_BIN_FILES ${QT_ICONENGINE_BIN_FILES}) -set(ALL_ICONENGINE_DBG_BIN_FILES ${QT_DEBUG_ICONENGINE_BIN_FILES}) - -set(ALL_IMAGEFORMATS_BIN_FILES) -set(ALL_IMAGEFORMATS_REL_BIN_FILES ${QT_IMAGEFORMATS_BIN_FILES}) -set(ALL_IMAGEFORMATS_DBG_BIN_FILES ${QT_DEBUG_IMAGEFORMATS_BIN_FILES}) - -foreach( - list - ALL_BASE_BIN_FILES - ALL_REL_BIN_FILES - ALL_DBG_BIN_FILES - ALL_PLATFORM_BIN_FILES - ALL_PLATFORM_REL_BIN_FILES - ALL_PLATFORM_DBG_BIN_FILES - ALL_STYLES_BIN_FILES - ALL_STYLES_REL_BIN_FILES - ALL_STYLES_DBG_BIN_FILES - ALL_ICONENGINE_BIN_FILES - ALL_ICONENGINE_REL_BIN_FILES - ALL_ICONENGINE_DGB_BIN_FILES - ALL_IMAGEFORMATS_BIN_FILES - ALL_IMAGEFORMATS_REL_BIN_FILES - ALL_IMAGEFORMATS_DGB_BIN_FILES) - if(${list}) - list(REMOVE_DUPLICATES ${list}) - endif() -endforeach() - -obs_status(STATUS "FFmpeg files: ${FFMPEG_BIN_FILES}") -obs_status(STATUS "x264 files: ${X264_BIN_FILES}") -obs_status(STATUS "Libfdk files: ${LIBFDK_BIN_FILES}") -obs_status(STATUS "Freetype files: ${FREETYPE_BIN_FILES}") -obs_status(STATUS "rnnoise files: ${RNNOISE_BIN_FILES}") -obs_status(STATUS "curl files: ${CURL_BIN_FILES}") -obs_status(STATUS "lua files: ${LUA_BIN_FILES}") -obs_status(STATUS "ssl files: ${SSL_BIN_FILES}") -obs_status(STATUS "zlib files: ${ZLIB_BIN_FILES}") -obs_status(STATUS "Qt Debug files: ${QT_DEBUG_BIN_FILES}") -obs_status(STATUS "Qt Debug Platform files: ${QT_DEBUG_PLAT_BIN_FILES}") -obs_status(STATUS "Qt Debug Styles files: ${QT_DEBUG_STYLES_BIN_FILES}") -obs_status(STATUS "Qt Debug Iconengine files: ${QT_DEBUG_ICONENGINE_BIN_FILES}") -obs_status(STATUS "Qt Debug Imageformat files: ${QT_DEBUG_IMAGEFORMATS_BIN_FILES}") -obs_status(STATUS "Qt Release files: ${QT_BIN_FILES}") -obs_status(STATUS "Qt Release Platform files: ${QT_PLAT_BIN_FILES}") -obs_status(STATUS "Qt Release Styles files: ${QT_STYLES_BIN_FILES}") -obs_status(STATUS "Qt Release Iconengine files: ${QT_ICONENGINE_BIN_FILES}") -obs_status(STATUS "Qt Release Imageformat files: ${QT_IMAGEFORMATS_BIN_FILES}") -obs_status(STATUS "Qt ICU files: ${QT_ICU_BIN_FILES}") - -foreach(BinFile ${ALL_BASE_BIN_FILES}) - obs_status(STATUS "copying ${BinFile} to ${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/") -endforeach() - -foreach(BinFile ${ALL_REL_BIN_FILES}) - obs_status(STATUS "copying ${BinFile} to ${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/") -endforeach() - -foreach(BinFile ${ALL_DBG_BIN_FILES}) - obs_status(STATUS "copying ${BinFile} to ${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/") -endforeach() - -foreach(BinFile ${ALL_PLATFORM_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/platforms") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/platforms/") -endforeach() - -foreach(BinFile ${ALL_PLATFORM_REL_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/platforms") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/platforms/") -endforeach() - -foreach(BinFile ${ALL_PLATFORM_DBG_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/platforms") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/platforms/") -endforeach() - -foreach(BinFile ${ALL_STYLES_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/styles") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/styles/") -endforeach() - -foreach(BinFile ${ALL_STYLES_REL_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/styles") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/styles/") -endforeach() - -foreach(BinFile ${ALL_STYLES_DBG_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/styles") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/styles/") -endforeach() - -foreach(BinFile ${ALL_ICONENGINE_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/iconengines") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/iconengines/") -endforeach() - -foreach(BinFile ${ALL_ICONENGINE_REL_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/iconengines") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/iconengines/") -endforeach() - -foreach(BinFile ${ALL_ICONENGINE_DBG_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/iconengines") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/iconengines/") -endforeach() - -foreach(BinFile ${ALL_IMAGEFORMATS_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/imageformats") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}/imageformats/") -endforeach() - -foreach(BinFile ${ALL_IMAGEFORMATS_REL_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/imageformats") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}r/imageformats/") -endforeach() - -foreach(BinFile ${ALL_IMAGEFORMATS_DBG_BIN_FILES}) - make_directory("${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/imageformats") - file(COPY "${BinFile}" DESTINATION "${CMAKE_SOURCE_DIR}/additional_install_files/exec${_bin_suffix}d/imageformats/") -endforeach() - -set(COPIED_DEPENDENCIES - TRUE - CACHE BOOL "Dependencies have been copied, set to false to copy again" FORCE) diff --git a/cmake/Modules/DeprecationHelpers.cmake b/cmake/Modules/DeprecationHelpers.cmake deleted file mode 100644 index 6d1f8d9edfdf5b..00000000000000 --- a/cmake/Modules/DeprecationHelpers.cmake +++ /dev/null @@ -1,237 +0,0 @@ -function(upgrade_cmake_vars) - if(DEFINED BROWSER_LEGACY) - set(ENABLE_BROWSER_LEGACY - "${BROWSER_LEGACY}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED BROWSER_PANEL_SUPPORT_ENABLED) - set(ENABLE_BROWSER_PANELS - "${BROWSER_PANEL_SUPPORT_ENABLED}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED BUILD_BROWSER) - set(ENABLE_BROWSER - "${BUILD_BROWSER}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED BUILD_CA_ENCODER) - set(ENABLE_COREAUDIO_ENCODER - "${BUILD_CA_ENCODER}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED BUILD_VST) - set(ENABLE_VST - "${BUILD_VST}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED CHECK_FOR_SERVICE_UPDATES) - set(ENABLE_SERVICE_UPDATES - "${CHECK_FOR_SERVICE_UPDATES}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED DEBUG_FFMPEG_MUX) - set(ENABLE_FFMPEG_MUX_DEBUG - "${DEBUG_FFMPEG_MUX}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED DISABLE_IVCAM) - if(DISABLE_IVCAM) - set(ENABLE_IVCAM - OFF - CACHE BOOL "" FORCE) - else() - set(ENABLE_IVCAM - ON - CACHE BOOL "" FORCE) - endif() - endif() - - if(DEFINED DISABLE_PLUGINS) - if(DISABLE_PLUGINS) - set(ENABLE_PLUGINS - OFF - CACHE BOOL "" FORCE) - else() - set(ENABLE_PLUGINS - ON - CACHE BOOL "" FORCE) - endif() - endif() - - if(DEFINED DISABLE_PYTHON) - if(DISABLE_PYTHON) - set(ENABLE_SCRIPTING_PYTHON - OFF - CACHE BOOL "" FORCE) - else() - set(ENABLE_SCRIPTING_PYTHON - ON - CACHE BOOL "" FORCE) - endif() - endif() - - if(DEFINED DISABLE_LUA) - if(DISABLE_LUA) - set(ENABLE_SCRIPTING_LUA - OFF - CACHE BOOL "" FORCE) - else() - set(ENABLE_SCRIPTING_LUA - ON - CACHE BOOL "" FORCE) - endif() - endif() - - if(DEFINED DISABLE_SPEEXDSP) - if(DISABLE_SPEEXDSP) - set(ENABLE_SPEEXDSP - OFF - CACHE BOOL "" FORCE) - else() - set(ENABLE_SPEEXDSP - ON - CACHE BOOL "" FORCE) - endif() - endif() - - if(DEFINED DISABLE_UPDATE_MODULE) - if(DISABLE_UPDATE_MODULE) - set(ENABLE_UPDATER - OFF - CACHE BOOL "" FORCE) - else() - set(ENABLE_UPDATER - ON - CACHE BOOL "" FORCE) - endif() - endif() - - if(DEFINED SHARED_TEXTURE_SUPPORT_ENABLED) - set(ENABLE_BROWSER_SHARED_TEXTURE - "${SHARED_TEXTURE_SUPPORT_ENABLED}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED STATIC_MBEDTLS) - set(ENABLE_STATIC_MBEDTLS - "${STATIC_MBEDTLS}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED UNIX_STRUCTURE AND UNIX_STRUCTURE) - set(LINUX_PORTABLE - OFF - CACHE BOOL "" FORCE) - endif() - - if(DEFINED USE_QT_LOOP) - set(ENABLE_BROWSER_QT_LOOP - "${USE_QT_LOOP}" - CACHE BOOL "" FORCE) - endif() - - if(DEFINED WITH_RTMPS) - set(ENABLE_RTMPS - "${WITH_RTMPS}" - CACHE STRING "" FORCE) - endif() -endfunction() - -function(install_obs_plugin_with_data) - obs_status( - DEPRECATION - "The install_obs_plugin_with_data command is deprecated and will be removed soon. Use 'setup_plugin_target' instead." - ) - _install_obs_plugin_with_data(${ARGV}) -endfunction() - -function(install_obs_plugin) - obs_status( - DEPRECATION - "The install_obs_plugin command is deprecated and will be removed soon. Use 'setup_plugin_target' instead.") - _install_obs_plugin(${ARGV}) -endfunction() - -function(install_obs_datatarget) - obs_status( - DEPRECATION - "The install_obs_datatarget function is deprecated and will be removed soon. Use 'setup_target_resources' instead.") - _install_obs_datatarget(${ARGV}) -endfunction() - -function(__deprecated_var VAR ACCESS) - if(ACCESS STREQUAL "READ_ACCESS") - obs_status(DEPRECATION "The variable '${VAR}' is deprecated!") - endif() -endfunction() - -function(__deprecated_feature VAR ACCESS) - if(ACCESS STREQUAL "UNKNOWN_READ_ACCESS") - obs_status(DEPRECATION "The feature enabled by '${VAR}' is deprecated and will soon be removed from OBS.") - endif() -endfunction() - -set(_DEPRECATED_VARS - zlibPath - vulkanPath - SwigPath - PythonPath - mbedtlsPath - LuajitPath - x264Path - VlcPath - VLCPath - speexPath - rnnoisePath - LibfdkPath - curlPath - JanssonPath - FFmpegPath - DepsPath - DepsPath32 - DepsPath64 - QTDIR32 - QTDIR64 - DISABLE_UI - UI_ENABLED - UNIX_STRUCTURE - UPDATE_SPARKLE - LIBOBS_PREFER_IMAGEMAGICK - DEBUG_FFMPEG_MUX - ENABLE_WINMF - USE_QT_LOOP - SHARED_TEXTURE_SUPPORT_ENABLED - BROWSER_PANEL_SUPPORT_ENABLED - BROWSER_LEGACY - BUILD_BROWSER - BUILD_CAPTIONS - BUILD_CA_ENCODER - BUILD_VST - CHECK_FOR_SERVICE_UPDATES - DISABLE_IVCAM - DISABLE_LUA - DISABLE_PLUGINS - DISABLE_PYTHON - DISABLE_SPEEXDSP - DISABLE_UPDATE_MODULE - SHARED_TEXTURE_SUPPORT_ENABLED - STATIC_MBEDTLS - UNIX_STRUCTURE - USE_QT_LOOP - WITH_RTMPS) - -foreach(_DEPRECATED_VAR IN LISTS _DEPRECATED_VARS) - variable_watch(_DEPRECATED_VAR __deprecated_var) -endforeach() - -variable_watch(FTL_FOUND __deprecated_feature) - -# Upgrade pre-existing build variables to their new variants as best as possible -upgrade_cmake_vars() diff --git a/cmake/Modules/FindAMF.cmake b/cmake/Modules/FindAMF.cmake deleted file mode 100644 index b67c31a2613fb8..00000000000000 --- a/cmake/Modules/FindAMF.cmake +++ /dev/null @@ -1,92 +0,0 @@ -#[=======================================================================[.rst -FindAMF ----------- - -FindModule for AMF and associated headers - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 2.0 - -This module defines the :prop_tgt:`IMPORTED` target ``AMF::AMF``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``AMF_FOUND`` - True, if headers were found. -``AMF_VERSION`` - Detected version of found AMF headers. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``AMF_INCLUDE_DIR`` - Directory containing ``AMF/core/Factory.h``. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -include(FindPackageHandleStandardArgs) - -find_path( - AMF_INCLUDE_DIR - NAMES AMF/core/Factory.h - PATHS /usr/include /usr/local/include - DOC "AMF include directory") - -if(EXISTS "${AMF_INCLUDE_DIR}/AMF/core/Version.h") - file(STRINGS "${AMF_INCLUDE_DIR}/AMF/core/Version.h" _version_string - REGEX "^.*VERSION_(MAJOR|MINOR|RELEASE|BUILD_NUM)[ \t]+[0-9]+[ \t]*$") - - string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") - string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") - string(REGEX REPLACE ".*VERSION_RELEASE[ \t]+([0-9]+).*" "\\1" _version_release "${_version_string}") - - set(AMF_VERSION "${_version_major}.${_version_minor}.${_version_release}") - unset(_version_major) - unset(_version_minor) - unset(_version_release) -else() - if(NOT AMF_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find AMF version.") - endif() - set(AMF_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(AMF_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(AMF_ERROR_REASON "Ensure AMF headers are available in local library paths.") -endif() - -find_package_handle_standard_args( - AMF - REQUIRED_VARS AMF_INCLUDE_DIR - VERSION_VAR AMF_VERSION REASON_FAILURE_MESSAGE "${AMF_ERROR_REASON}") -mark_as_advanced(AMF_INCLUDE_DIR) -unset(AMF_ERROR_REASON) - -if(AMF_FOUND) - if(NOT TARGET AMF::AMF) - add_library(AMF::AMF INTERFACE IMPORTED) - set_target_properties(AMF::AMF PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${AMF_INCLUDE_DIR}") - endif() -endif() - -include(FeatureSummary) -set_package_properties( - AMF PROPERTIES - URL "https://github.com/GPUOpen-LibrariesAndSDKs/AMF" - DESCRIPTION - "AMF is a light-weight, portable multimedia framework that abstracts away most of the platform and API-specific details and allows for easy implementation of multimedia applications using a variety of technologies, such as DirectX 11, OpenGL, and OpenCL and facilitates an efficient interop between them." -) diff --git a/cmake/Modules/FindAsio.cmake b/cmake/Modules/FindAsio.cmake deleted file mode 100644 index 78229f96ce1697..00000000000000 --- a/cmake/Modules/FindAsio.cmake +++ /dev/null @@ -1,94 +0,0 @@ -#[=======================================================================[.rst -FindAsio ----------- - -FindModule for Asio and the associated library - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 2.0 - -This module defines the :prop_tgt:`IMPORTED` target ``Asio::Asio``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``Asio_FOUND`` - True, if the library was found. -``Asio_VERSION`` - Detected version of found Asio library. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``Asio_INCLUDE_DIR`` - Directory containing ``asio.hpp``. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -include(FindPackageHandleStandardArgs) - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(PC_Asio QUIET asio) -endif() - -find_path( - Asio_INCLUDE_DIR - NAMES asio.hpp - HINTS ${PC_Asio_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include - DOC "Asio include directory") - -if(PC_Asio_VERSION VERSION_GREATER 0) - set(Asio_VERSION ${PC_Asio_VERSION}) -elseif(EXISTS "${Asio_INCLUDE_DIR}/asio/version.hpp") - file(STRINGS "${Asio_INCLUDE_DIR}/asio/version.hpp" _version_string - REGEX "#define[ \t]+ASIO_VERSION[ \t]+[0-9]+[ \t]+\/\/[ \t][0-9]+\.[0-9]+\.[0-9]+") - - string(REGEX REPLACE "#define[ \t]+ASIO_VERSION[ \t]+[0-9]+[ \t]+\/\/[ \t]([0-9]+\.[0-9]+\.[0-9]+)" "\\1" - Asio_VERSION "${_version_string}") -else() - if(NOT Asio_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find Asio version.") - endif() - set(Asio_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(Asio_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(Asio_ERROR_REASON "Ensure Asio library is available in local include paths.") -endif() - -find_package_handle_standard_args( - Asio - REQUIRED_VARS Asio_INCLUDE_DIR - VERSION_VAR Asio_VERSION REASON_FAILURE_MESSAGE "${Asio_ERROR_REASON}") -mark_as_advanced(Asio_INCLUDE_DIR) -unset(Asio_ERROR_REASON) - -if(Asio_FOUND) - if(NOT TARGET Asio::Asio) - add_library(Asio::Asio INTERFACE IMPORTED) - set_target_properties(Asio::Asio PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Asio_INCLUDE_DIR}") - endif() -endif() - -include(FeatureSummary) -set_package_properties( - Asio PROPERTIES - URL "http://think-async.com/Asio" - DESCRIPTION - "Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach." -) diff --git a/cmake/Modules/FindCEF.cmake b/cmake/Modules/FindCEF.cmake deleted file mode 100644 index e39a5ed97eb537..00000000000000 --- a/cmake/Modules/FindCEF.cmake +++ /dev/null @@ -1,128 +0,0 @@ -include(FindPackageHandleStandardArgs) - -set(CEF_ROOT_DIR - "" - CACHE PATH "Path to CEF distributed build") - -if(NOT DEFINED CEF_ROOT_DIR OR CEF_ROOT_DIR STREQUAL "") - message( - FATAL_ERROR - "CEF_ROOT_DIR is not set - if ENABLE_BROWSER is enabled, a CEF distribution with compiled wrapper library is required.\n" - "Please download a CEF distribution for your appropriate architecture and specify CEF_ROOT_DIR to its location") -endif() - -find_path(CEF_INCLUDE_DIR "include/cef_version.h" HINTS ${CEF_ROOT_DIR}) - -if(OS_MACOS) - find_library( - CEF_LIBRARY - NAMES cef libcef cef.lib libcef.o "Chromium Embedded Framework" - NO_DEFAULT_PATH - PATHS ${CEF_ROOT_DIR} ${CEF_ROOT_DIR}/Release) - - find_library( - CEFWRAPPER_LIBRARY - NAMES cef_dll_wrapper libcef_dll_wrapper - NO_DEFAULT_PATH - PATHS ${CEF_ROOT_DIR}/build/libcef_dll/Release ${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release - ${CEF_ROOT_DIR}/build/libcef_dll ${CEF_ROOT_DIR}/build/libcef_dll_wrapper) - -elseif(OS_POSIX) - find_library( - CEF_LIBRARY - NAMES libcef.so "Chromium Embedded Framework" - NO_DEFAULT_PATH - PATHS ${CEF_ROOT_DIR} ${CEF_ROOT_DIR}/Release) - - find_library( - CEFWRAPPER_LIBRARY - NAMES libcef_dll_wrapper.a - NO_DEFAULT_PATH - PATHS ${CEF_ROOT_DIR}/build/libcef_dll_wrapper ${CEF_ROOT_DIR}/libcef_dll_wrapper) - -else() - find_library( - CEF_LIBRARY - NAMES cef libcef cef.lib libcef.o "Chromium Embedded Framework" - PATHS ${CEF_ROOT_DIR} ${CEF_ROOT_DIR}/Release) - - find_library( - CEFWRAPPER_LIBRARY - NAMES cef_dll_wrapper libcef_dll_wrapper - PATHS ${CEF_ROOT_DIR}/build/libcef_dll/Release ${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release - ${CEF_ROOT_DIR}/build/libcef_dll ${CEF_ROOT_DIR}/build/libcef_dll_wrapper) - - if(OS_WINDOWS) - find_library( - CEFWRAPPER_LIBRARY_DEBUG - NAMES cef_dll_wrapper libcef_dll_wrapper - PATHS ${CEF_ROOT_DIR}/build/libcef_dll/Debug ${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Debug) - endif() -endif() - -mark_as_advanced(CEFWRAPPER_LIBRARY CEFWRAPPER_LIBRARY_DEBUG) - -if(NOT CEF_LIBRARY) - message(WARNING "Could NOT find Chromium Embedded Framework library (missing: CEF_LIBRARY)") - set(CEF_FOUND FALSE) - return() -endif() - -if(NOT CEFWRAPPER_LIBRARY) - message(WARNING "Could NOT find Chromium Embedded Framework wrapper library (missing: CEFWRAPPER_LIBRARY)") - set(CEF_FOUND FALSE) - return() -endif() - -message(STATUS "Found Chromium Embedded Framework: ${CEF_LIBRARY};${CEF_WRAPPER_LIBRARY}") - -if(OS_WINDOWS) - set(CEF_LIBRARIES ${CEF_LIBRARY} ${CEFWRAPPER_LIBRARY}) - -elseif(OS_MACOS) - if(BROWSER_LEGACY) - set(CEF_LIBRARIES ${CEFWRAPPER_LIBRARY} ${CEF_LIBRARY}) - else() - set(CEF_LIBRARIES ${CEFWRAPPER_LIBRARY}) - endif() -else() - set(CEF_LIBRARIES ${CEF_LIBRARY} optimized ${CEFWRAPPER_LIBRARY}) - - if(CEFWRAPPER_LIBRARY_DEBUG) - list(APPEND CEF_LIBRARIES debug ${CEFWRAPPER_LIBRARY_DEBUG}) - endif() -endif() - -find_package_handle_standard_args(CEF DEFAULT_MSG CEF_LIBRARY CEFWRAPPER_LIBRARY CEF_INCLUDE_DIR) - -mark_as_advanced(CEF_LIBRARY CEF_WRAPPER_LIBRARY CEF_LIBRARIES CEF_INCLUDE_DIR) - -if(NOT TARGET CEF::Wrapper) - if(IS_ABSOLUTE "${CEF_LIBRARIES}") - add_library(CEF::Wrapper UNKNOWN IMPORTED) - add_library(CEF::Library UNKNOWN IMPORTED) - - set_target_properties(CEF::Wrapper PROPERTIES IMPORTED_LOCATION ${CEFWRAPPER_LIBRARY}) - - set_target_properties(CEF::Library PROPERTIES IMPORTED_LOCATION ${CEF_LIBRARY}) - - if(DEFINED CEFWRAPPER_LIBRARY_DEBUG) - set_target_properties(CEF::Wrapper PROPERTIES IMPORTED_LOCATION_DEBUG ${CEFWRAPPER_LIBRARY_DEBUG}) - endif() - else() - add_library(CEF::Wrapper INTERFACE IMPORTED) - add_library(CEF::Library INTERFACE IMPORTED) - - set_target_properties(CEF::Wrapper PROPERTIES IMPORTED_LIBNAME ${CEFWRAPPER_LIBRARY}) - - set_target_properties(CEF::Library PROPERTIES IMPORTED_LIBNAME ${CEF_LIBRARY}) - - if(DEFINED CEFWRAPPER_LIBRARY_DEBUG) - set_target_properties(CEF::Wrapper PROPERTIES IMPORTED_LIBNAME_DEBUG ${CEFWRAPPER_LIBRARY_DEBUG}) - endif() - endif() - - set_target_properties(CEF::Wrapper PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CEF_INCLUDE_DIR}") - - set_target_properties(CEF::Library PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CEF_INCLUDE_DIR}") -endif() diff --git a/cmake/Modules/FindDetours.cmake b/cmake/Modules/FindDetours.cmake deleted file mode 100644 index 1c661c255f1243..00000000000000 --- a/cmake/Modules/FindDetours.cmake +++ /dev/null @@ -1,68 +0,0 @@ -# Once done these will be defined: -# -# * DETOURS_FOUND -# * DETOURS_INCLUDE_DIRS -# * DETOURS_LIBRARIES -# -# For use in OBS: -# -# DETOURS_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_DETOURS QUIET detours) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - DETOURS_INCLUDE_DIR - NAMES detours.h - HINTS ENV DETOURS_PATH ${DETOURS_PATH} ${CMAKE_SOURCE_DIR}/${DETOURS_PATH} ${_DETOURS_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - DETOURS_LIB - NAMES ${_DETOURS_LIBRARIES} detours - HINTS ENV DETOURS_PATH ${DETOURS_PATH} ${CMAKE_SOURCE_DIR}/${DETOURS_PATH} ${_DETOURS_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Detours DEFAULT_MSG DETOURS_LIB DETOURS_INCLUDE_DIR) -mark_as_advanced(DETOURS_INCLUDE_DIR DETOURS_LIB) - -if(DETOURS_FOUND) - set(DETOURS_INCLUDE_DIRS ${DETOURS_INCLUDE_DIR}) - set(DETOURS_LIBRARIES ${DETOURS_LIB}) - - if(NOT TARGET Detours::Detours) - if(IS_ABSOLUTE "${DETOURS_LIBRARIES}") - add_library(Detours::Detours UNKNOWN IMPORTED) - set_target_properties(Detours::Detours PROPERTIES IMPORTED_LOCATION "${DETOURS_LIBRARIES}") - else() - add_library(Detours::Detours INTERFACE IMPORTED) - set_target_properties(Detours::Detours PROPERTIES IMPORTED_LIBNAME "${DETOURS_LIBRARIES}") - endif() - - set_target_properties(Detours::Detours PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${DETOURS_INCLUDE_DIRS}") - endif() - -endif() diff --git a/cmake/Modules/FindFFmpeg.cmake b/cmake/Modules/FindFFmpeg.cmake deleted file mode 100644 index 6c78827b77a415..00000000000000 --- a/cmake/Modules/FindFFmpeg.cmake +++ /dev/null @@ -1,189 +0,0 @@ -# cmake-format: off -# -# This module defines the following variables: -# -# FFMPEG_FOUND - All required components and the core library were found -# FFMPEG_INCLUDE_DIRS - Combined list of all components include dirs -# FFMPEG_LIBRARIES - Combined list of all components libraries -# FFMPEG_VERSION_STRING - Version of the first component requested -# -# For each requested component the following variables are defined: -# -# FFMPEG__FOUND - The component was found -# FFMPEG__INCLUDE_DIRS - The components include dirs -# FFMPEG__LIBRARIES - The components libraries -# FFMPEG__VERSION_STRING - The components version string -# FFMPEG__VERSION_MAJOR - The components major version -# FFMPEG__VERSION_MINOR - The components minor version -# FFMPEG__VERSION_MICRO - The components micro version -# -# is the uppercase name of the component -# cmake-format: on - -find_package(PkgConfig QUIET) - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -function(find_ffmpeg_library component header) - string(TOUPPER "${component}" component_u) - set(FFMPEG_${component_u}_FOUND - FALSE - PARENT_SCOPE) - set(FFmpeg_${component}_FOUND - FALSE - PARENT_SCOPE) - - if(PKG_CONFIG_FOUND) - pkg_check_modules(PC_FFMPEG_${component} QUIET lib${component}) - endif() - - find_path( - FFMPEG_${component}_INCLUDE_DIR - NAMES "lib${component}/${header}" "lib${component}/version.h" - HINTS ENV FFMPEG_PATH ${FFMPEG_PATH} ${CMAKE_SOURCE_DIR}/${FFMPEG_PATH} ${PC_FFMPEG_${component}_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES ffmpeg libav include) - - find_library( - FFMPEG_${component}_LIBRARY - NAMES "${component}" "lib${component}" - HINTS ENV FFMPEG_PATH ${FFMPEG_PATH} ${CMAKE_SOURCE_DIR}/${FFMPEG_PATH} ${PC_FFMPEG_${component}_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - - set(FFMPEG_${component_u}_INCLUDE_DIRS - ${FFMPEG_${component}_INCLUDE_DIR} - PARENT_SCOPE) - set(FFMPEG_${component_u}_LIBRARIES - ${FFMPEG_${component}_LIBRARY} - PARENT_SCOPE) - - mark_as_advanced(FFMPEG_${component}_INCLUDE_DIR FFMPEG_${component}_LIBRARY) - - if(FFMPEG_${component}_INCLUDE_DIR AND FFMPEG_${component}_LIBRARY) - set(FFMPEG_${component_u}_FOUND - TRUE - PARENT_SCOPE) - set(FFmpeg_${component}_FOUND - TRUE - PARENT_SCOPE) - - list(APPEND FFMPEG_INCLUDE_DIRS ${FFMPEG_${component}_INCLUDE_DIR}) - list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) - set(FFMPEG_INCLUDE_DIRS - "${FFMPEG_INCLUDE_DIRS}" - PARENT_SCOPE) - - list(APPEND FFMPEG_LIBRARIES ${FFMPEG_${component}_LIBRARY}) - list(REMOVE_DUPLICATES FFMPEG_LIBRARIES) - set(FFMPEG_LIBRARIES - "${FFMPEG_LIBRARIES}" - PARENT_SCOPE) - - set(FFMPEG_${component_u}_VERSION_STRING - "unknown" - PARENT_SCOPE) - set(_vfile "${FFMPEG_${component}_INCLUDE_DIR}/lib${component}/version.h") - - if(EXISTS "${_vfile}") - file(STRINGS "${_vfile}" _version_parse REGEX "^.*VERSION_(MAJOR|MINOR|MICRO)[ \t]+[0-9]+[ \t]*$") - string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _major "${_version_parse}") - string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _minor "${_version_parse}") - string(REGEX REPLACE ".*VERSION_MICRO[ \t]+([0-9]+).*" "\\1" _micro "${_version_parse}") - - set(FFMPEG_${component_u}_VERSION_MAJOR - "${_major}" - PARENT_SCOPE) - set(FFMPEG_${component_u}_VERSION_MINOR - "${_minor}" - PARENT_SCOPE) - set(FFMPEG_${component_u}_VERSION_MICRO - "${_micro}" - PARENT_SCOPE) - - set(FFMPEG_${component_u}_VERSION_STRING - "${_major}.${_minor}.${_micro}" - PARENT_SCOPE) - else() - message(STATUS "Failed parsing FFmpeg ${component} version") - endif() - endif() -endfunction() - -set(FFMPEG_INCLUDE_DIRS) -set(FFMPEG_LIBRARIES) - -if(NOT FFmpeg_FIND_COMPONENTS) - message(FATAL_ERROR "No FFmpeg components requested") -endif() - -list(GET FFmpeg_FIND_COMPONENTS 0 _first_comp) -string(TOUPPER "${_first_comp}" _first_comp) - -foreach(component ${FFmpeg_FIND_COMPONENTS}) - if(component STREQUAL "avcodec") - find_ffmpeg_library("${component}" "avcodec.h") - elseif(component STREQUAL "avdevice") - find_ffmpeg_library("${component}" "avdevice.h") - elseif(component STREQUAL "avfilter") - find_ffmpeg_library("${component}" "avfilter.h") - elseif(component STREQUAL "avformat") - find_ffmpeg_library("${component}" "avformat.h") - elseif(component STREQUAL "avresample") - find_ffmpeg_library("${component}" "avresample.h") - elseif(component STREQUAL "avutil") - find_ffmpeg_library("${component}" "avutil.h") - elseif(component STREQUAL "postproc") - find_ffmpeg_library("${component}" "postprocess.h") - elseif(component STREQUAL "swresample") - find_ffmpeg_library("${component}" "swresample.h") - elseif(component STREQUAL "swscale") - find_ffmpeg_library("${component}" "swscale.h") - else() - message(FATAL_ERROR "Unknown FFmpeg component requested: ${component}") - endif() -endforeach() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - FFmpeg - FOUND_VAR FFMPEG_FOUND - REQUIRED_VARS FFMPEG_${_first_comp}_LIBRARIES FFMPEG_${_first_comp}_INCLUDE_DIRS - VERSION_VAR FFMPEG_${_first_comp}_VERSION_STRING - HANDLE_COMPONENTS) - -if(FFMPEG_FOUND) - foreach(component ${FFmpeg_FIND_COMPONENTS}) - if(NOT TARGET FFmpeg::${component}) - string(TOUPPER ${component} component_u) - if(FFMPEG_${component_u}_FOUND) - if(IS_ABSOLUTE "${FFMPEG_${component_u}_LIBRARIES}") - add_library(FFmpeg::${component} UNKNOWN IMPORTED) - set_target_properties(FFmpeg::${component} PROPERTIES IMPORTED_LOCATION "${FFMPEG_${component_u}_LIBRARIES}") - else() - add_library(FFmpeg::${component} INTERFACE IMPORTED) - set_target_properties(FFmpeg::${component} PROPERTIES IMPORTED_LIBNAME "${FFMPEG_${component_u}_LIBRARIES}") - endif() - - set_target_properties(FFmpeg::${component} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${FFMPEG_${component_u}_INCLUDE_DIRS}") - endif() - endif() - endforeach() -endif() diff --git a/cmake/Modules/FindFFnvcodec.cmake b/cmake/Modules/FindFFnvcodec.cmake deleted file mode 100644 index 5a5c200971eaa3..00000000000000 --- a/cmake/Modules/FindFFnvcodec.cmake +++ /dev/null @@ -1,96 +0,0 @@ -#[=======================================================================[.rst -FindFFnvcodec -------------- - -FindModule for FFnvcodec and the associated headers - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 3.0 - -This module defines the :prop_tgt:`IMPORTED` target ``FFnvcodec::FFnvcodec``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``FFnvcodec_FOUND`` - True, if headers was found. -``FFnvcodec_VERSION`` - Detected version of found FFnvcodec headers. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``FFnvcodec_INCLUDE_DIR`` - Directory containing ``nvEncodeAPI.h``. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -include(FindPackageHandleStandardArgs) - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(PC_FFnvcodec QUIET ffnvcodec) -endif() - -find_path( - FFnvcodec_INCLUDE_DIR - NAMES ffnvcodec/nvEncodeAPI.h - HINTS ${PC_FFnvcodec_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include - DOC "FFnvcodec include directory") - -if(PC_FFnvcodec_VERSION VERSION_GREATER 0) - set(FFnvcodec_VERSION ${PC_FFnvcodec_VERSION}) -elseif(EXISTS "${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h") - file(STRINGS "${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h" _version_string - REGEX "^.*NVENCAPI_(MAJOR|MINOR)_VERSION[ \t]+[0-9]+[ \t]*$") - - string(REGEX REPLACE ".*MAJOR_VERSION[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") - string(REGEX REPLACE ".*MINOR_VERSION[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") - - set(FFnvcodec_VERSION "${_version_major}.${_version_minor}") - unset(_version_major) - unset(_version_minor) -else() - if(NOT FFnvcodec_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find FFnvcodec version.") - endif() - set(FFnvcodec_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(FFnvcodec_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(FFnvcodec_ERROR_REASON "Ensure FFnvcodec headers are available in local include paths.") -endif() - -find_package_handle_standard_args( - FFnvcodec - REQUIRED_VARS FFnvcodec_INCLUDE_DIR - VERSION_VAR FFnvcodec_VERSION HANDLE_VERSION_RANGE REASON_FAILURE_MESSAGE "${FFnvcodec_ERROR_REASON}") -mark_as_advanced(FFnvcodec_INCLUDE_DIR) -unset(FFnvcodec_ERROR_REASON) - -if(FFnvcodec_FOUND) - if(NOT TARGET FFnvcodec::FFnvcodec) - add_library(FFnvcodec::FFnvcodec INTERFACE IMPORTED) - set_target_properties(FFnvcodec::FFnvcodec PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFnvcodec_INCLUDE_DIR}") - endif() -endif() - -include(FeatureSummary) -set_package_properties( - FFnvcodec PROPERTIES - URL "https://github.com/FFmpeg/nv-codec-headers/" - DESCRIPTION "FFmpeg version of headers required to interface with NVIDIA's codec APIs.") diff --git a/cmake/Modules/FindGio.cmake b/cmake/Modules/FindGio.cmake deleted file mode 100644 index aa8d1d07d09044..00000000000000 --- a/cmake/Modules/FindGio.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# cmake-format: off -# * Try to find Gio Once done this will define -# -# GIO_FOUND - system has Gio GIO_INCLUDE_DIRS - the Gio include directory -# GIO_LIBRARIES - the libraries needed to use Gio GIO_DEFINITIONS - Compiler -# switches required for using Gio - -# Use pkg-config to get the directories and then use these values in the -# find_path() and find_library() calls -# cmake-format: on - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_GIO gio-2.0 gio-unix-2.0) -endif() - -find_path( - GIO_INCLUDE_DIR - NAMES gio.h - HINTS ${_GIO_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES glib-2.0/gio/) - -find_library( - GIO_LIB - NAMES gio-2.0 libgio-2.0 gio-unix-2.0 - HINTS ${_GIO_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Gio REQUIRED_VARS GIO_LIB GIO_INCLUDE_DIR) -mark_as_advanced(GIO_INCLUDE_DIR GIO_LIB) - -if(GIO_FOUND) - set(GIO_INCLUDE_DIRS ${GIO_INCLUDE_DIR}) - set(GIO_LIBRARIES ${GIO_LIB}) - - if(NOT TARGET GIO::GIO) - if(IS_ABSOLUTE "${GIO_LIBRARIES}") - add_library(GIO::GIO UNKNOWN IMPORTED) - set_target_properties(GIO::GIO PROPERTIES IMPORTED_LOCATION "${GIO_LIBRARIES}") - else() - add_library(GIO::GIO INTERFACE IMPORTED) - set_target_properties(GIO::GIO PROPERTIES IMPORTED_LIBNAME "${GIO_LIBRARIES}") - endif() - - # Special case for gio, as both the - set_target_properties(GIO::GIO PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_GIO_INCLUDE_DIRS}") - - target_compile_options(GIO::GIO INTERFACE ${_GIO_CFLAGS}) - endif() -endif() diff --git a/cmake/Modules/FindJack.cmake b/cmake/Modules/FindJack.cmake deleted file mode 100644 index 3ff3bd615948ce..00000000000000 --- a/cmake/Modules/FindJack.cmake +++ /dev/null @@ -1,74 +0,0 @@ -# cmake-format: off -# * Try to find jack-2.6 Once done this will define -# -# JACK_FOUND - system has jack JACK_INCLUDE_DIRS - the jack include directory -# JACK_LIBRARIES - Link these to use jack JACK_DEFINITIONS - Compiler switches -# required for using jack -# -# Copyright (c) 2008 Andreas Schneider Modified for other -# libraries by Lasse Kärkkäinen -# -# Redistribution and use is allowed according to the terms of the New BSD -# license. For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# -# cmake-format: on - -if(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) - # in cache already - set(JACK_FOUND TRUE) -else(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) - # use pkg-config to get the directories and then use these values in the FIND_PATH() and FIND_LIBRARY() calls - if(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) - include(UsePkgConfig) - pkgconfig(jack _JACK_INCLUDEDIR _JACK_LIBDIR _JACK_LDFLAGS _JACK_CFLAGS) - else(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) - find_package(PkgConfig) - if(PKG_CONFIG_FOUND) - pkg_check_modules(_JACK jack) - endif(PKG_CONFIG_FOUND) - endif(${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) - find_path( - JACK_INCLUDE_DIR - NAMES jack/jack.h - PATHS ${_JACK_INCLUDE_DIRS} /usr/include /usr/local/include /opt/local/include /sw/include) - - find_library( - JACK_LIBRARY - NAMES jack - PATHS ${_JACK_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib) - - if(JACK_LIBRARY AND JACK_INCLUDE_DIR) - set(JACK_FOUND TRUE) - - set(JACK_INCLUDE_DIRS ${JACK_INCLUDE_DIR}) - - set(JACK_LIBRARIES ${JACK_LIBRARIES} ${JACK_LIBRARY}) - - endif(JACK_LIBRARY AND JACK_INCLUDE_DIR) - - if(JACK_FOUND) - if(NOT JACK_FIND_QUIETLY) - message(STATUS "Found jack: ${JACK_LIBRARY}") - endif(NOT JACK_FIND_QUIETLY) - - if(NOT TARGET Jack::Jack) - if(IS_ABSOLUTE "${JACK_LIBRARIES}") - add_library(Jack::Jack UNKNOWN IMPORTED) - set_target_properties(Jack::Jack PROPERTIES IMPORTED_LOCATION "${JACK_LIBRARIES}") - else() - add_library(Jack::Jack INTERFACE IMPORTED) - set_target_properties(Jack::Jack PROPERTIES IMPORTED_LIBNAME "${JACK_LIBRARIES}") - endif() - - set_target_properties(Jack::Jack PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${JACK_INCLUDE_DIRS}") - endif() - else(JACK_FOUND) - if(JACK_FIND_REQUIRED) - message(FATAL_ERROR "Could not find JACK") - endif(JACK_FIND_REQUIRED) - endif(JACK_FOUND) - - # show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view - mark_as_advanced(JACK_INCLUDE_DIRS JACK_LIBRARIES) - -endif(JACK_LIBRARIES AND JACK_INCLUDE_DIRS) diff --git a/cmake/Modules/FindJansson.cmake b/cmake/Modules/FindJansson.cmake deleted file mode 100644 index f3224f0e2375e2..00000000000000 --- a/cmake/Modules/FindJansson.cmake +++ /dev/null @@ -1,80 +0,0 @@ -# Once done these will be defined: -# -# JANSSON_FOUND JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES JANSSON_VERSION - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_JANSSON QUIET jansson) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - Jansson_INCLUDE_DIR - NAMES jansson.h - HINTS ENV JANSSON_PATH ${JANSSON_PATH} ${CMAKE_SOURCE_DIR}/${JANSSON_PATH} ${_JANSSON_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include) - -find_library( - Jansson_LIB - NAMES ${_JANSSON_LIBRARIES} jansson libjansson - HINTS ENV JANSSON_PATH ${JANSSON_PATH} ${CMAKE_SOURCE_DIR}/${JANSSON_PATH} ${_JANSSON_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -if(JANSSON_VERSION) - set(_JANSSON_VERSION_STRING "${JANSSON_VERSION}") -elseif(_JANSSON_FOUND AND _JANSSON_VERSION) - set(_JANSSON_VERSION_STRING "${_JANSSON_VERSION}") -elseif(EXISTS "${Jansson_INCLUDE_DIR}/jansson.h") - file(STRINGS "${Jansson_INCLUDE_DIR}/jansson.h" _jansson_version_parse REGEX "#define[ \t]+JANSSON_VERSION[ \t]+.+") - string(REGEX REPLACE ".*#define[ \t]+JANSSON_VERSION[ \t]+\"(.+)\".*" "\\1" _JANSSON_VERSION_STRING - "${_jansson_version_parse}") -else() - if(NOT Jansson_FIND_QUIETLY) - message(WARNING "Failed to find Jansson version") - endif() - set(_JANSSON_VERSION_STRING "unknown") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - Jansson - FOUND_VAR JANSSON_FOUND - REQUIRED_VARS Jansson_LIB Jansson_INCLUDE_DIR - VERSION_VAR _JANSSON_VERSION_STRING) -mark_as_advanced(Jansson_INCLUDE_DIR Jansson_LIB) - -if(JANSSON_FOUND) - set(JANSSON_INCLUDE_DIRS ${Jansson_INCLUDE_DIR}) - set(JANSSON_LIBRARIES ${Jansson_LIB}) - set(JANSSON_VERSION ${_JANSSON_VERSION_STRING}) - - if(NOT TARGET Jansson::Jansson) - if(IS_ABSOLUTE "${JANSSON_LIBRARIES}") - add_library(Jansson::Jansson UNKNOWN IMPORTED GLOBAL) - set_target_properties(Jansson::Jansson PROPERTIES IMPORTED_LOCATION "${JANSSON_LIBRARIES}") - else() - add_library(Jansson::Jansson INTERFACE IMPORTED GLOBAL) - set_target_properties(Jansson::Jansson PROPERTIES IMPORTED_LIBNAME "${JANSSON_LIBRARIES}") - endif() - - set_target_properties(Jansson::Jansson PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${JANSSON_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibAJANTV2.cmake b/cmake/Modules/FindLibAJANTV2.cmake deleted file mode 100644 index 2ae1801ffdd5f8..00000000000000 --- a/cmake/Modules/FindLibAJANTV2.cmake +++ /dev/null @@ -1,179 +0,0 @@ -# Once done these will be defined: -# -# LIBAJANTV2_FOUND LIBAJANTV2_INCLUDE_DIRS LIBAJANTV2_LIBRARIES -# -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_AJA QUIET ajantv2) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - _LIBAJANTV2_NEW_INCLUDE_DIR - NAMES libajantv2 - HINTS ENV - AJASDKPath${_lib_suffix} - ENV - AJASDKPath - ENV - DepsPath${_lib_suffix} - ENV - DepsPath - ${AJASDKPath${_lib_suffix}} - ${AJASDKPath} - ${DepsPath${_lib_suffix}} - ${DepsPath} - ${_AJA_NTV2_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -if(${_LIBAJANTV2_NEW_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_NEW_INCLUDE_DIR-NOTFOUND") - find_path( - _LIBAJANTV2_OLD_INCLUDE_DIR - NAMES libajantv2 - HINTS ENV - AJASDKPath${_lib_suffix} - ENV - AJASDKPath - ENV - DepsPath${_lib_suffix} - ENV - DepsPath - ${AJASDKPath${_lib_suffix}} - ${AJASDKPath} - ${DepsPath${_lib_suffix}} - ${DepsPath} - ${_AJA_NTV2_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - if(${_LIBAJANTV2_OLD_INCLUDE_DIR} STREQUAL "_LIBAJANTV2_OLD_INCLUDE_DIR-NOTFOUND") - set(AJA_LIBRARIES_INCLUDE_DIR ${_LIBAJANTV2_OLD_INCLUDE_DIR}/ajalibraries) - if(NOT LibAJANTV2_FIND_QUIETLY) - message(DEPRECATION "aja: Using old ntv2 library") - endif() - endif() -else() - set(AJA_LIBRARIES_INCLUDE_DIR ${_LIBAJANTV2_NEW_INCLUDE_DIR}/libajantv2) - if(NOT LibAJANTV2_FIND_QUIETLY) - message(STATUS "aja: Using new libajantv2 library") - endif() -endif() - -find_library( - AJA_NTV2_LIB - NAMES ${_AJA_NTV2_LIBRARIES} ajantv2 libajantv2 - HINTS ENV - AJASDKPath${_lib_suffix} - ENV - AJASDKPath - ENV - DepsPath${_lib_suffix} - ENV - DepsPath - ${AJASDKPath${_lib_suffix}} - ${AJASDKPath} - ${DepsPath${_lib_suffix}} - ${DepsPath} - ${_AJA_NTV2_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -find_library( - AJA_NTV2_DEBUG_LIB - NAMES ajantv2d libajantv2d - HINTS ENV - AJASDKPath${_lib_suffix} - ENV - AJASDKPath - ENV - DepsPath${_lib_suffix} - ENV - DepsPath - ${AJASDKPath${_lib_suffix}} - ${AJASDKPath} - ${DepsPath${_lib_suffix}} - ${DepsPath} - ${_AJA_NTV2_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibAJANTV2 DEFAULT_MSG AJA_LIBRARIES_INCLUDE_DIR AJA_NTV2_LIB) -mark_as_advanced(AJA_LIBRARIES_INCLUDE_DIR AJA_NTV2_LIB) - -if(LIBAJANTV2_FOUND) - set(AJA_LIBRARIES_INCLUDE_DIR ${AJA_LIBRARIES_INCLUDE_DIR}) - set(AJA_LIBRARIES_INCLUDE_DIRS - ${AJA_LIBRARIES_INCLUDE_DIR} ${AJA_LIBRARIES_INCLUDE_DIR}/ajaanc ${AJA_LIBRARIES_INCLUDE_DIR}/ajabase - ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2 ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/includes) - if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") - list(APPEND LibAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/win) - elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") - list(APPEND LibAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/mac) - elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux") - list(APPEND LibAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIR}/ajantv2/src/lin) - endif() - - set(LIBAJANTV2_LIBRARIES ${AJA_NTV2_LIB}) - if(AJA_NTV2_DEBUG_LIB STREQUAL "AJA_NTV2_DEBUG_LIB-NOTFOUND") - set(AJA_NTV2_DEBUG_LIB ${AJA_NTV2_LIB}) - mark_as_advanced(AJA_NTV2_DEBUG_LIB) - endif() - set(LIBAJANTV2_DEBUG_LIBRARIES ${AJA_NTV2_DEBUG_LIB}) - set(LIBAJANTV2_INCLUDE_DIRS ${AJA_LIBRARIES_INCLUDE_DIRS}) - - if(NOT TARGET AJA::LibAJANTV2) - if(IS_ABSOLUTE "${LIBAJANTV2_LIBRARIES}") - add_library(AJA::LibAJANTV2 UNKNOWN IMPORTED) - set_target_properties(AJA::LibAJANTV2 PROPERTIES IMPORTED_LOCATION "${LIBAJANTV2_LIBRARIES}") - - if(DEFINED LIBAJANTV2_DEBUG_LIBRARIES) - set_target_properties(AJA::LibAJANTV2 PROPERTIES IMPORTED_LOCATION_DEBUG "${LIBAJANTV2_DEBUG_LIBRARIES}") - endif() - else() - add_library(AJA::LibAJANTV2 INTERFACE IMPORTED) - set_target_properties(AJA::LibAJANTV2 PROPERTIES IMPORTED_LIBNAME "${LIBAJANTV2_LIBRARIES}") - - if(DEFINED LIBAJANTV2_DEBUG_LIBRARIES) - set_target_properties(AJA::LibAJANTV2 PROPERTIES IMPORTED_LIBNAME_DEBUG "${LIBAJANTV2_DEBUG_LIBRARIES}") - endif() - endif() - - set_target_properties(AJA::LibAJANTV2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBAJANTV2_INCLUDE_DIRS}") - - target_compile_definitions( - AJA::LibAJANTV2 - INTERFACE "$<$:AJA_WINDOWS;_WINDOWS;WIN32;MSWindows>" - "$<$,$>:_DEBUG;_NDEBUG>" - "$<$:AJAMac;AJA_MAC>" "$<$:AJA_LINUX;AJALinux>") - endif() -endif() diff --git a/cmake/Modules/FindLibUUID.cmake b/cmake/Modules/FindLibUUID.cmake deleted file mode 100644 index 2b28f9891b7f11..00000000000000 --- a/cmake/Modules/FindLibUUID.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# Stripped down version of -# https://gitlab.kitware.com/cmake/cmake/blob/e1409101c99f7a3487990e9927e8bd0e275f564f/Source/Modules/FindLibUUID.cmake -# -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or -# https://cmake.org/licensing for details. -# -# Once done these will be defined: -# -# LibUUID_FOUND LibUUID_INCLUDE_DIRS LibUUID_LIBRARIES - -find_library(LibUUID_LIBRARY NAMES uuid) -mark_as_advanced(LibUUID_LIBRARY) - -find_path(LibUUID_INCLUDE_DIR NAMES uuid/uuid.h) -mark_as_advanced(LibUUID_INCLUDE_DIR) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - LibUUID - FOUND_VAR LibUUID_FOUND - REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR) -set(LIBUUID_FOUND ${LibUUID_FOUND}) - -if(LibUUID_FOUND) - set(LibUUID_INCLUDE_DIRS ${LibUUID_INCLUDE_DIR}) - set(LibUUID_LIBRARIES ${LibUUID_LIBRARY}) - if(NOT TARGET LibUUID::LibUUID) - add_library(LibUUID::LibUUID UNKNOWN IMPORTED) - set_target_properties(LibUUID::LibUUID PROPERTIES IMPORTED_LOCATION "${LibUUID_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibVLC.cmake b/cmake/Modules/FindLibVLC.cmake deleted file mode 100644 index 63fbd64ea017ed..00000000000000 --- a/cmake/Modules/FindLibVLC.cmake +++ /dev/null @@ -1,56 +0,0 @@ -# Once done these will be defined: -# -# LIBVLC_FOUND LIBVLC_INCLUDE_DIRS LIBVLC_LIBRARIES -# - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_VLC QUIET libvlc) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - VLC_INCLUDE_DIR - NAMES libvlc.h - HINTS ENV VLC_PATH ${VLC_PATH} ${CMAKE_SOURCE_DIR}/${VLC_PATH} ${_VLC_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES vlc include/vlc include) - -find_library( - VLC_LIB - NAMES ${_VLC_LIBRARIES} VLC libVLC - HINTS ENV VLC_PATH ${VLC_PATH} ${CMAKE_SOURCE_DIR}/${VLC_PATH} ${_VLC_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -# OBS doesnt depend on linking, so we dont include VLC_LIB here as required. -find_package_handle_standard_args(LibVLC DEFAULT_MSG VLC_INCLUDE_DIR) -mark_as_advanced(VLC_INCLUDE_DIR VLC_LIB) - -if(LIBVLC_FOUND) - set(LIBVLC_LIBRARIES ${VLC_LIB}) - set(LIBVLC_INCLUDE_DIRS ${VLC_INCLUDE_DIR}) - - if(NOT TARGET VLC::LibVLC) - add_library(VLC::LibVLC INTERFACE IMPORTED) - set_target_properties(VLC::LibVLC PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBVLC_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibdrm.cmake b/cmake/Modules/FindLibdrm.cmake deleted file mode 100644 index 56ea43e3aac9b6..00000000000000 --- a/cmake/Modules/FindLibdrm.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Once done these will be defined: -# -# LIBDRM_FOUND LIBDRM_INCLUDE_DIRS LIBDRM_LIBRARIES - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_LIBDRM QUIET libdrm) -endif() - -find_path( - LIBDRM_INCLUDE_DIR - NAMES libdrm/drm_fourcc.h - HINTS ${_LIBDRM_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include) - -find_library( - LIBDRM_LIB - NAMES drm libdrm - HINTS ${_LIBDRM_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libdrm DEFAULT_MSG LIBDRM_LIB LIBDRM_INCLUDE_DIR) -mark_as_advanced(LIBDRM_INCLUDE_DIR LIBDRM_LIB) - -if(LIBDRM_FOUND) - set(LIBDRM_INCLUDE_DIRS ${LIBDRM_INCLUDE_DIR}) - set(LIBDRM_LIBRARIES ${LIBDRM_LIB}) - - if(NOT TARGET Libdrm::Libdrm) - add_library(Libdrm::Libdrm INTERFACE IMPORTED) - set_target_properties(Libdrm::Libdrm PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBDRM_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibfdk.cmake b/cmake/Modules/FindLibfdk.cmake deleted file mode 100644 index 938cda18562c1c..00000000000000 --- a/cmake/Modules/FindLibfdk.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# Once done these will be defined: -# -# LIBFDK_FOUND LIBFDK_INCLUDE_DIRS LIBFDK_LIBRARIES -# -# For use in OBS: -# -# Libfdk_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_LIBFDK fdk-aac) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - Libfdk_INCLUDE_DIR - NAMES fdk-aac/aacenc_lib.h - HINTS ENV LIBFDK_PATH ${LIBFDK_PATH} ${CMAKE_SOURCE_DIR}/${LIBFDK_PATH} ${_LIBFDK_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - Libfdk_LIB - NAMES ${_LIBFDK_LIBRARIES} fdk-aac libfdk-aac - HINTS ENV LIBFDK_PATH ${LIBFDK_PATH} ${CMAKE_SOURCE_DIR}/${LIBFDK_PATH} ${_LIBFDK_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libfdk DEFAULT_MSG Libfdk_LIB Libfdk_INCLUDE_DIR) -mark_as_advanced(Libfdk_INCLUDE_DIR Libfdk_LIB) - -if(LIBFDK_FOUND) - set(LIBFDK_INCLUDE_DIRS ${Libfdk_INCLUDE_DIR}) - set(LIBFDK_LIBRARIES ${Libfdk_LIB}) - - if(NOT TARGET LibFDK::LibFDK) - if(IS_ABSOLUTE "${LIBFDK_LIBRARIES}") - add_library(LibFDK::LibFDK UNKNOWN IMPORTED) - set_target_properties(LibFDK::LibFDK PROPERTIES IMPORTED_LOCATION "${LIBFDK_LIBRARIES}") - else() - add_library(LibFDK::LibFDK INTERFACE IMPORTED) - set_target_properties(LibFDK::LibFDK PROPERTIES IMPORTED_LIBNAME "${LIBFDK_LIBRARIES}") - endif() - - set_target_properties(LibFDK::LibFDK PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBFDK_INCLUDE_DIRS}") - endif() - -endif() diff --git a/cmake/Modules/FindLibpci.cmake b/cmake/Modules/FindLibpci.cmake deleted file mode 100644 index cb7760c1d37254..00000000000000 --- a/cmake/Modules/FindLibpci.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# cmake-format: off -# * Try to find Libpci Once done this will define -# -# * LIBPCI_FOUND - system has Libpci -# * LIBPCI_INCLUDE_DIRS - the Libpci include directory -# * LIBPCI_LIBRARIES - the libraries needed to use Libpci -# * LIBPCI_DEFINITIONS - Compiler switches required for using Libpci - -# Use pkg-config to get the directories and then use these values in the -# find_path() and find_library() calls -# cmake-format: on - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_LIBPCI libpci) -endif() - -find_path( - LIBPCI_INCLUDE_DIR - NAMES pci.h - HINTS ${_LIBPCI_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include - PATH_SUFFIXES pci/) - -find_library( - LIBPCI_LIB - NAMES ${_LIBPCI_LIBRARIES} libpci - HINTS ${_LIBPCI_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libpci REQUIRED_VARS LIBPCI_LIB LIBPCI_INCLUDE_DIR) -mark_as_advanced(LIBPCI_INCLUDE_DIR LIBPCI_LIB) - -if(LIBPCI_FOUND) - set(LIBPCI_INCLUDE_DIRS ${LIBPCI_INCLUDE_DIR}) - set(LIBPCI_LIBRARIES ${LIBPCI_LIB}) - - if(NOT TARGET LIBPCI::LIBPCI) - if(IS_ABSOLUTE "${LIBPCI_LIBRARIES}") - add_library(LIBPCI::LIBPCI UNKNOWN IMPORTED) - set_target_properties(LIBPCI::LIBPCI PROPERTIES IMPORTED_LOCATION "${LIBPCI_LIBRARIES}") - else() - add_library(LIBPCI::LIBPCI INTERFACE IMPORTED) - set_target_properties(LIBPCI::LIBPCI PROPERTIES IMPORTED_LIBNAME "${LIBPCI_LIBRARIES}") - endif() - endif() -endif() diff --git a/cmake/Modules/FindLibrist.cmake b/cmake/Modules/FindLibrist.cmake deleted file mode 100644 index ddcb495b17b76a..00000000000000 --- a/cmake/Modules/FindLibrist.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Once done these will be defined: -# -# LIBRIST_FOUND LIBRIST_INCLUDE_DIRS LIBRIST_LIBRARIES -# -# For use in OBS: -# -# LIBRIST_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_LIBRIST QUIET librist) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - LIBRIST_INCLUDE_DIR - NAMES librist.h librist/librist.h - HINTS ENV LIBRIST_PATH ${LIBRIST_PATH} ${CMAKE_SOURCE_DIR}/${LIBRIST_PATH} ${_LIBRIST_INCLUDE_DIRS} ${DepsPath} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - LIBRIST_LIB - NAMES ${_LIBRIST_LIBRARIES} librist rist - HINTS ENV LIBRIST_PATH ${LIBRIST_PATH} ${CMAKE_SOURCE_DIR}/${LIBRIST_PATH} ${_LIBRIST_LIBRARY_DIRS} ${DepsPath} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Librist DEFAULT_MSG LIBRIST_LIB LIBRIST_INCLUDE_DIR) -mark_as_advanced(LIBRIST_INCLUDE_DIR LIBRIST_LIB) - -if(LIBRIST_FOUND) - set(LIBRIST_INCLUDE_DIRS ${LIBRIST_INCLUDE_DIR}) - set(LIBRIST_LIBRARIES ${LIBRIST_LIB}) - - if(NOT TARGET Librist::Librist) - if(IS_ABSOLUTE "${LIBRIST_LIBRARIES}") - add_library(Librist::Librist UNKNOWN IMPORTED) - set_target_properties(Librist::Librist PROPERTIES IMPORTED_LOCATION "${LIBRIST_LIBRARIES}") - else() - add_library(Librist::Librist INTERFACE IMPORTED) - set_target_properties(Librist::Librist PROPERTIES IMPORTED_LIBNAME "${LIBRIST_LIBRARIES}") - endif() - - set_target_properties(Librist::Librist PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBRIST_INCLUDE_DIRS}") - endif() -else() - message(STATUS "librist library not found") -endif() diff --git a/cmake/Modules/FindLibrnnoise.cmake b/cmake/Modules/FindLibrnnoise.cmake deleted file mode 100644 index 9cf98123670eef..00000000000000 --- a/cmake/Modules/FindLibrnnoise.cmake +++ /dev/null @@ -1,65 +0,0 @@ -# Once done these will be defined: -# -# LIBRNNOISE_FOUND LIBRNNOISE_INCLUDE_DIRS LIBRNNOISE_LIBRARIES -# -# For use in OBS: -# -# RNNOISE_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_RNNOISE QUIET rnnoise) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - RNNOISE_INCLUDE_DIR - NAMES rnnoise.h - HINTS ENV RNNOISE_PATH ${RNNOISE_PATH} ${CMAKE_SOURCE_DIR}/${RNNOISE_PATH} ${_RNNOISE_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - RNNOISE_LIB - NAMES ${_RNNOISE_LIBRARIES} rnnoise - HINTS ENV RNNOISE_PATH ${RNNOISE_PATH} ${CMAKE_SOURCE_DIR}/${RNNOISE_PATH} ${_RNNOISE_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Librnnoise DEFAULT_MSG RNNOISE_LIB RNNOISE_INCLUDE_DIR) -mark_as_advanced(RNNOISE_INCLUDE_DIR RNNOISE_LIB) - -if(LIBRNNOISE_FOUND) - set(LIBRNNOISE_INCLUDE_DIRS ${RNNOISE_INCLUDE_DIR}) - set(LIBRNNOISE_LIBRARIES ${RNNOISE_LIB}) - - if(NOT TARGET Librnnoise::Librnnoise) - if(IS_ABSOLUTE "${LIBRNNOISE_LIBRARIES}") - add_library(Librnnoise::Librnnoise UNKNOWN IMPORTED) - set_target_properties(Librnnoise::Librnnoise PROPERTIES IMPORTED_LOCATION "${LIBRNNOISE_LIBRARIES}") - else() - add_library(Librnnoise::Librnnoise INTERFACE IMPORTED) - set_target_properties(Librnnoise::Librnnoise PROPERTIES IMPORTED_LIBNAME "${LIBRNNOISE_LIBRARIES}") - endif() - - set_target_properties(Librnnoise::Librnnoise PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBRNNOISE_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibspeexdsp.cmake b/cmake/Modules/FindLibspeexdsp.cmake deleted file mode 100644 index 817e2750f45ff3..00000000000000 --- a/cmake/Modules/FindLibspeexdsp.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# Once done these will be defined: -# -# LIBSPEEXDSP_FOUND LIBSPEEXDSP_INCLUDE_DIRS LIBSPEEXDSP_LIBRARIES -# -# For use in OBS: -# -# SPEEXDSP_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_SPEEXDSP QUIET speexdsp libspeexdsp) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - SPEEXDSP_INCLUDE_DIR - NAMES speex/speex_preprocess.h - HINTS ENV SPEEX_PATH ${SPEEX_PATH} ${CMAKE_SOURCE_DIR}/${SPEEX_PATH} ${_SPEEXDSP_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - SPEEXDSP_LIB - NAMES ${_SPEEXDSP_LIBRARIES} speexdsp libspeexdsp - HINTS ENV SPEEX_PATH ${SPEEX_PATH} ${CMAKE_SOURCE_DIR}/${SPEEX_PATH} ${_SPEEXDSP_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libspeexdsp DEFAULT_MSG SPEEXDSP_LIB SPEEXDSP_INCLUDE_DIR) -mark_as_advanced(SPEEXDSP_INCLUDE_DIR SPEEXDSP_LIB) - -if(LIBSPEEXDSP_FOUND) - set(LIBSPEEXDSP_INCLUDE_DIRS ${SPEEXDSP_INCLUDE_DIR}) - set(LIBSPEEXDSP_LIBRARIES ${SPEEXDSP_LIB}) - - if(NOT TARGET LibspeexDSP::LibspeexDSP) - if(IS_ABSOLUTE "${LIBSPEEXDSP_LIBRARIES}") - add_library(LibspeexDSP::LibspeexDSP UNKNOWN IMPORTED) - set_target_properties(LibspeexDSP::LibspeexDSP PROPERTIES IMPORTED_LOCATION "${LIBSPEEXDSP_LIBRARIES}") - else() - add_library(LibspeexDSP::LibspeexDSP INTERFACE IMPORTED) - set_target_properties(LibspeexDSP::LibspeexDSP PROPERTIES IMPORTED_LIBNAME "${LIBSPEEXDSP_LIBRARIES}") - endif() - - set_target_properties(LibspeexDSP::LibspeexDSP PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${LIBSPEEXDSP_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibsrt.cmake b/cmake/Modules/FindLibsrt.cmake deleted file mode 100644 index 50112c3eb66078..00000000000000 --- a/cmake/Modules/FindLibsrt.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Once done these will be defined: -# -# LIBSRT_FOUND LIBSRT_INCLUDE_DIRS LIBSRT_LIBRARIES -# -# For use in OBS: -# -# LIBSRT_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_LIBSRT QUIET libsrt) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - LIBSRT_INCLUDE_DIR - NAMES srt.h srt/srt.h - HINTS ENV LIBSRT_PATH ${LIBSRT_PATH} ${CMAKE_SOURCE_DIR}/${LIBSRT_PATH} ${_LIBSRT_INCLUDE_DIRS} ${DepsPath} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - LIBSRT_LIB - NAMES ${_LIBSRT_LIBRARIES} srt libsrt - HINTS ENV LIBSRT_PATH ${LIBSRT_PATH} ${CMAKE_SOURCE_DIR}/${LIBSRT_PATH} ${_LIBSRT_LIBRARY_DIRS} ${DepsPath} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libsrt DEFAULT_MSG LIBSRT_LIB LIBSRT_INCLUDE_DIR) -mark_as_advanced(LIBSRT_INCLUDE_DIR LIBSRT_LIB) - -if(LIBSRT_FOUND) - set(LIBSRT_INCLUDE_DIRS ${LIBSRT_INCLUDE_DIR}) - set(LIBSRT_LIBRARIES ${LIBSRT_LIB}) - - if(NOT TARGET Libsrt::Libsrt) - if(IS_ABSOLUTE "${LIBSRT_LIBRARIES}") - add_library(Libsrt::Libsrt UNKNOWN IMPORTED) - set_target_properties(Libsrt::Libsrt PROPERTIES IMPORTED_LOCATION "${LIBSRT_LIBRARIES}") - else() - add_library(Libsrt::Libsrt INTERFACE IMPORTED) - set_target_properties(Libsrt::Libsrt PROPERTIES IMPORTED_LIBNAME "${LIBSRT_LIBRARIES}") - endif() - - set_target_properties(Libsrt::Libsrt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBSRT_INCLUDE_DIRS}") - endif() -else() - message(STATUS "libsrt library not found") -endif() diff --git a/cmake/Modules/FindLibv4l2.cmake b/cmake/Modules/FindLibv4l2.cmake deleted file mode 100644 index da35a0735560c5..00000000000000 --- a/cmake/Modules/FindLibv4l2.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# Once done these will be defined: -# -# LIBV4L2_FOUND LIBV4L2_INCLUDE_DIRS LIBV4L2_LIBRARIES - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_V4L2 QUIET v4l-utils) -endif() - -find_path( - V4L2_INCLUDE_DIR - NAMES libv4l2.h - HINTS ${_V4L2_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include) - -find_library( - V4L2_LIB - NAMES v4l2 - HINTS ${_V4L2_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libv4l2 DEFAULT_MSG V4L2_LIB V4L2_INCLUDE_DIR) -mark_as_advanced(V4L2_INCLUDE_DIR V4L2_LIB) - -if(LIBV4L2_FOUND) - set(LIBV4L2_INCLUDE_DIRS ${V4L2_INCLUDE_DIR}) - set(LIBV4L2_LIBRARIES ${V4L2_LIB}) - - if(NOT TARGET LIB4L2::LIB4L2) - if(IS_ABSOLUTE "${LIBV4L2_LIBRARIES}") - add_library(LIB4L2::LIB4L2 UNKNOWN IMPORTED) - set_target_properties(LIB4L2::LIB4L2 PROPERTIES IMPORTED_LOCATION "${LIBV4L2_LIBRARIES}") - else() - add_library(LIB4L2::LIB4L2 INTERFACE IMPORTED) - set_target_properties(LIB4L2::LIB4L2 PROPERTIES IMPORTED_LIBNAME "${LIBV4L2_LIBRARIES}") - endif() - - set_target_properties(LIB4L2::LIB4L2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBV4L2_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLibva.cmake b/cmake/Modules/FindLibva.cmake deleted file mode 100644 index c4280ba5753282..00000000000000 --- a/cmake/Modules/FindLibva.cmake +++ /dev/null @@ -1,71 +0,0 @@ -# cmake-format: off -# -# This module defines the following variables: -# -# * LIBVA_FOUND - The component was found -# * LIBVA_INCLUDE_DIRS - The component include directory -# * LIBVA_LIBRARIES - The component library Libva -# * LIBVA_DRM_LIBRARIES - The component library Libva DRM - -# Use pkg-config to get the directories and then use these values in the -# find_path() and find_library() calls -# cmake-format: on - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_LIBVA libva) - pkg_check_modules(_LIBVA_DRM libva-drm) -endif() - -find_path( - LIBVA_INCLUDE_DIR - NAMES va/va.h va/va_drm.h - HINTS ${_LIBVA_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include) - -find_library( - LIBVA_LIB - NAMES ${_LIBVA_LIBRARIES} libva - HINTS ${_LIBVA_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -find_library( - LIBVA_DRM_LIB - NAMES ${_LIBVA_DRM_LIBRARIES} libva-drm - HINTS ${_LIBVA_DRM_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libva REQUIRED_VARS LIBVA_INCLUDE_DIR LIBVA_LIB LIBVA_DRM_LIB) -mark_as_advanced(LIBVA_INCLUDE_DIR LIBVA_LIB LIBVA_DRM_LIB) - -if(LIBVA_FOUND) - set(LIBVA_INCLUDE_DIRS ${LIBVA_INCLUDE_DIR}) - set(LIBVA_LIBRARIES ${LIBVA_LIB}) - set(LIBVA_DRM_LIBRARIES ${LIBVA_DRM_LIB}) - - if(NOT TARGET Libva::va) - if(IS_ABSOLUTE "${LIBVA_LIBRARIES}") - add_library(Libva::va UNKNOWN IMPORTED) - set_target_properties(Libva::va PROPERTIES IMPORTED_LOCATION "${LIBVA_LIBRARIES}") - else() - add_library(Libva::va INTERFACE IMPORTED) - set_target_properties(Libva::va PROPERTIES IMPORTED_LIBNAME "${LIBVA_LIBRARIES}") - endif() - - set_target_properties(Libva::va PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBVA_INCLUDE_DIRS}") - endif() - - if(NOT TARGET Libva::drm) - if(IS_ABSOLUTE "${LIBVA_DRM_LIBRARIES}") - add_library(Libva::drm UNKNOWN IMPORTED) - set_target_properties(Libva::drm PROPERTIES IMPORTED_LOCATION "${LIBVA_DRM_LIBRARIES}") - else() - add_library(Libva::drm INTERFACE IMPORTED) - set_target_properties(Libva::drm PROPERTIES IMPORTED_LIBNAME "${LIBVA_DRM_LIBRARIES}") - endif() - - set_target_properties(Libva::drm PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBVA_INCLUDE_DIRS}") - endif() - -endif() diff --git a/cmake/Modules/FindLibx264.cmake b/cmake/Modules/FindLibx264.cmake deleted file mode 100644 index 8d025de6c34593..00000000000000 --- a/cmake/Modules/FindLibx264.cmake +++ /dev/null @@ -1,65 +0,0 @@ -# Once done these will be defined: -# -# LIBX264_FOUND LIBX264_INCLUDE_DIRS LIBX264_LIBRARIES -# -# For use in OBS: -# -# X264_INCLUDE_DIR - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_X264 QUIET x264) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -find_path( - X264_INCLUDE_DIR - NAMES x264.h - HINTS ENV X264_PATH ${X264_PATH} ${CMAKE_SOURCE_DIR}/${X264_PATH} ${_X264_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - X264_LIB - NAMES ${_X264_LIBRARIES} x264 libx264 - HINTS ENV X264_PATH ${X264_PATH} ${CMAKE_SOURCE_DIR}/${X264_PATH} ${_X264_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Libx264 DEFAULT_MSG X264_LIB X264_INCLUDE_DIR) -mark_as_advanced(X264_INCLUDE_DIR X264_LIB) - -if(LIBX264_FOUND) - set(LIBX264_INCLUDE_DIRS ${X264_INCLUDE_DIR}) - set(LIBX264_LIBRARIES ${X264_LIB}) - - if(NOT TARGET LIBX264::LIBX264) - if(IS_ABSOLUTE "${LIBX264_LIBRARIES}") - add_library(LIBX264::LIBX264 UNKNOWN IMPORTED) - set_target_properties(LIBX264::LIBX264 PROPERTIES IMPORTED_LOCATION "${LIBX264_LIBRARIES}") - else() - add_library(LIBX264::LIBX264 INTERFACE IMPORTED) - set_target_properties(LIBX264::LIBX264 PROPERTIES IMPORTED_LIBNAME "${LIBX264_LIBRARIES}") - endif() - - set_target_properties(LIBX264::LIBX264 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBX264_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindLuajit.cmake b/cmake/Modules/FindLuajit.cmake deleted file mode 100644 index a2ee5a5ca83a49..00000000000000 --- a/cmake/Modules/FindLuajit.cmake +++ /dev/null @@ -1,85 +0,0 @@ -# Once done these will be defined: -# -# LUAJIT_FOUND LUAJIT_INCLUDE_DIRS LUAJIT_LIBRARIES -# -# For use in OBS: -# -# LUAJIT_INCLUDE_DIR - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_LIB_SUFFIX 64) -else() - set(_LIB_SUFFIX 32) -endif() - -find_path( - LUAJIT_INCLUDE_DIR - NAMES lua.h lualib.h - HINTS ENV LUAJIT_PATH ${LUAJIT_PATH} ${CMAKE_SOURCE_DIR}/${LUAJIT_PATH} ${_LUAJIT_INCLUDE_DIRS} - PATHS /usr/include - /usr/local/include - /opt/local/include - /opt/local - /sw/include - ~/Library/Frameworks - /Library/Frameworks - PATH_SUFFIXES - include - luajit - luajit/src - include/luajit - include/luajit/src - luajit-2.0 - include/luajit-2.0 - luajit2.0 - include/luajit2.0 - luajit-2.1 - include/luajit-2.1 - luajit2.1 - include/luajit2.1) - -find_library( - LUAJIT_LIB - NAMES ${_LUAJIT_LIBRARIES} luajit luajit-51 luajit-5.1 lua51 - HINTS ENV LUAJIT_PATH ${LUAJIT_PATH} ${CMAKE_SOURCE_DIR}/${LUAJIT_PATH} ${_LUAJIT_LIBRARY_DIRS} - PATHS /usr/lib - /usr/local/lib - /opt/local/lib - /opt/local - /sw/lib - ~/Library/Frameworks - /Library/Frameworks - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Luajit DEFAULT_MSG LUAJIT_LIB LUAJIT_INCLUDE_DIR) -mark_as_advanced(LUAJIT_INCLUDE_DIR LUAJIT_LIB) - -if(LUAJIT_FOUND) - set(LUAJIT_INCLUDE_DIRS ${LUAJIT_INCLUDE_DIR}) - set(LUAJIT_LIBRARIES ${LUAJIT_LIB}) - - if(NOT TARGET Luajit::Luajit) - if(IS_ABSOLUTE "${LUAJIT_LIBRARIES}") - add_library(Luajit::Luajit UNKNOWN IMPORTED) - set_target_properties(Luajit::Luajit PROPERTIES IMPORTED_LOCATION "${LUAJIT_LIBRARIES}") - else() - add_library(Luajit::Luajit INTERFACE IMPORTED) - set_target_properties(Luajit::Luajit PROPERTIES IMPORTED_LIBNAME "${LUAJIT_LIBRARIES}") - endif() - - set_target_properties(Luajit::Luajit PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LUAJIT_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindMbedTLS.cmake b/cmake/Modules/FindMbedTLS.cmake deleted file mode 100644 index 9a095a1599214d..00000000000000 --- a/cmake/Modules/FindMbedTLS.cmake +++ /dev/null @@ -1,156 +0,0 @@ -# Once done these will be defined: -# -# * MBEDTLS_FOUND -# * MBEDTLS_INCLUDE_DIRS -# * MBEDTLS_LIBRARIES -# - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_MBEDTLS QUIET mbedtls) -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_lib_suffix 64) -else() - set(_lib_suffix 32) -endif() - -# If we're on MacOS or Linux, please try to statically-link mbedtls. -if(ENABLE_STATIC_MBEDTLS AND (APPLE OR UNIX)) - set(_MBEDTLS_LIBRARIES libmbedtls.a) - set(_MBEDCRYPTO_LIBRARIES libmbedcrypto.a) - set(_MBEDX509_LIBRARIES libmbedx509.a) -endif() - -find_path( - MBEDTLS_INCLUDE_DIR - NAMES mbedtls/ssl.h - HINTS ENV MBEDTLS_PATH ${MBEDTLS_PATH} ${CMAKE_SOURCE_DIR}/${MBEDTLS_PATH} ${_MBEDTLS_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - PATH_SUFFIXES include) - -find_library( - MBEDTLS_LIB - NAMES ${_MBEDTLS_LIBRARIES} mbedtls libmbedtls - HINTS ENV MBEDTLS_PATH ${MBEDTLS_PATH} ${CMAKE_SOURCE_DIR}/${MBEDTLS_PATH} ${_MBEDTLS_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -find_library( - MBEDCRYPTO_LIB - NAMES ${_MBEDCRYPTO_LIBRARIES} mbedcrypto libmbedcrypto - HINTS ENV MBEDCRYPTO_PATH ${MBEDCRYPTO_PATH} ${CMAKE_SOURCE_DIR}/${MBEDCRYPTO_PATH} ${_MBEDCRYPTO_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -find_library( - MBEDX509_LIB - NAMES ${_MBEDX509_LIBRARIES} mbedx509 libmbedx509 - HINTS ENV MBEDX509_PATH ${MBEDX509_PATH} ${CMAKE_SOURCE_DIR}/${MBEDX509_PATH} ${_MBEDX509_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -# Sometimes mbedtls is split between three libs, and sometimes it isn't. If it isn't, let's check if the symbols we need -# are all in MBEDTLS_LIB. -if(MBEDTLS_LIB - AND NOT MBEDCRYPTO_LIB - AND NOT MBEDX509_LIB) - set(CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIB}) - set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR}) - include(CheckSymbolExists) - check_symbol_exists(mbedtls_x509_crt_init "mbedtls/x509_crt.h" MBEDTLS_INCLUDES_X509) - check_symbol_exists(mbedtls_sha256_init "mbedtls/sha256.h" MBEDTLS_INCLUDES_CRYPTO) - unset(CMAKE_REQUIRED_INCLUDES) - unset(CMAKE_REQUIRED_LIBRARIES) -endif() - -# If we find all three libraries, then go ahead. -if(MBEDTLS_LIB - AND MBEDCRYPTO_LIB - AND MBEDX509_LIB) - set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) - set(MBEDTLS_LIBRARIES ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB} ${MBEDX509_LIB}) - - foreach(component TLS CRYPTO X509) - if(NOT TARGET Mbedtls::${component} AND MBED${component}_LIB) - if(IS_ABSOLUTE "${MBED${component}_LIB}") - add_library(Mbedtls::${component} UNKNOWN IMPORTED) - set_target_properties(Mbedtls::${component} PROPERTIES IMPORTED_LOCATION "${MBED${component}_LIB}") - else() - add_library(Mbedtls::${component} INTERFACE IMPORTED) - set_target_properties(Mbedtls::${component} PROPERTIES IMPORTED_LIBNAME "${MBED${component}_LIB}") - endif() - - set_target_properties(Mbedtls::${component} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${MBED${component}_INCLUDE_DIR}") - endif() - endforeach() - - if(NOT TARGET Mbedtls::Mbedtls) - add_library(Mbedtls::Mbedtls INTERFACE IMPORTED) - target_link_libraries(Mbedtls::Mbedtls INTERFACE Mbedtls::TLS Mbedtls::CRYPTO Mbedtls::X509) - endif() - - # Otherwise, if we find MBEDTLS_LIB, and it has both CRYPTO and x509 within the single lib (i.e. a windows build - # environment), then also feel free to go ahead. -elseif( - MBEDTLS_LIB - AND MBEDTLS_INCLUDES_CRYPTO - AND MBEDTLS_INCLUDES_X509) - set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) - set(MBEDTLS_LIBRARIES ${MBEDTLS_LIB}) - - if(NOT TARGET Mbedtls::Mbedtls) - if(IS_ABSOLUTE "${MBED${component}_LIB}") - add_library(Mbedtls::${component} UNKNOWN IMPORTED) - set_target_properties(Mbedtls::${component} PROPERTIES IMPORTED_LOCATION "${MBEDTLS_LIBRARIES}") - else() - add_library(Mbedtls::${component} INTERFACE IMPORTED) - set_target_properties(Mbedtls::${component} PROPERTIES IMPORTED_LIBNAME "${MBEDTLS_LIBRARIES}") - endif() - - set_target_properties(Mbedtls::${component} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIRS}") - endif() -endif() - -# Now we've accounted for the 3-vs-1 library case: -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) -mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIB MBEDCRYPTO_LIB MBEDX509_LIB) diff --git a/cmake/Modules/FindOSS.cmake b/cmake/Modules/FindOSS.cmake deleted file mode 100644 index 0474a013b40181..00000000000000 --- a/cmake/Modules/FindOSS.cmake +++ /dev/null @@ -1,36 +0,0 @@ -# cmake-format: off -# Try to find OSS on a *nix system -# -# OSS_FOUND - True if OSS is available OSS_INCLUDE_DIR - Include -# directory of OSS header OSS_HEADER_NAME - OSS header file name -# -# cmake-format: on - -if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - set(OSS_HEADER_NAME "sys/soundcard.h") -elseif(CMAKE_SYSTEM_NAME MATCHES "DragonFly") - set(OSS_HEADER_NAME "sys/soundcard.h") -endif() - -find_path(OSS_INCLUDE_DIR "${OSS_HEADER_NAME}" "/usr/include" "/usr/local/include") - -if(OSS_INCLUDE_DIR) - set(OSS_FOUND True) -else(OSS_INCLUDE_DIR) - set(OSS_FOUND) -endif(OSS_INCLUDE_DIR) - -if(OSS_FOUND) - message(STATUS "Found OSS header: ${OSS_INCLUDE_DIR}/${OSS_HEADER_NAME}") -else(OSS_FOUND) - if(OSS_FIND_REQUIRED) - message(FATAL_ERROR "Could not find OSS header file") - endif(OSS_FIND_REQUIRED) -endif(OSS_FOUND) - -mark_as_advanced(OSS_FOUND OSS_INCLUDE_DIR OSS_HEADER_NAME) - -if(OSS_FOUND AND NOT TARGET OSS::OSS) - add_library(OSS::OSS INTERFACE IMPORTED) - set_target_properties(OSS::OSS PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OSS_INCLUDE_DIR}") -endif() diff --git a/cmake/Modules/FindPipeWire.cmake b/cmake/Modules/FindPipeWire.cmake deleted file mode 100644 index 868acf5ec15bb9..00000000000000 --- a/cmake/Modules/FindPipeWire.cmake +++ /dev/null @@ -1,100 +0,0 @@ -# cmake-format: off -# .rst: FindPipeWire -# ------- -# -# Try to find PipeWire on a Unix system. -# -# This will define the following variables: -# -# ``PIPEWIRE_FOUND`` True if (the requested version of) PipeWire is available -# ``PIPEWIRE_VERSION`` The version of PipeWire ``PIPEWIRE_LIBRARIES`` This can -# be passed to target_link_libraries() instead of the ``PipeWire::PipeWire`` -# target ``PIPEWIRE_INCLUDE_DIRS`` This should be passed to -# target_include_directories() if the target is not used for linking -# ``PIPEWIRE_COMPILE_FLAGS`` This should be passed to target_compile_options() -# if the target is not used for linking -# -# If ``PIPEWIRE_FOUND`` is TRUE, it will also define the following imported -# target: -# -# ``PipeWire::PipeWire`` The PipeWire library -# -# In general we recommend using the imported target, as it is easier to use. -# Bear in mind, however, that if the target is in the link interface of an -# exported library, it must be made available by the package config file. - -# ============================================================================= -# Copyright 2014 Alex Merry Copyright 2014 Martin Gräßlin -# Copyright 2018-2020 Jan Grulich -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the copyright notice, this list -# of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright notice, this -# list of conditions and the following disclaimer in the documentation and/or -# other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER -# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ============================================================================= -# cmake-format: on - -# Use pkg-config to get the directories and then use these values in the FIND_PATH() and FIND_LIBRARY() calls -find_package(PkgConfig QUIET) - -pkg_search_module(PKG_PIPEWIRE QUIET libpipewire-0.3) -pkg_search_module(PKG_SPA QUIET libspa-0.2) - -set(PIPEWIRE_COMPILE_FLAGS "${PKG_PIPEWIRE_CFLAGS}" "${PKG_SPA_CFLAGS}") -set(PIPEWIRE_VERSION "${PKG_PIPEWIRE_VERSION}") - -find_path( - PIPEWIRE_INCLUDE_DIRS - NAMES pipewire/pipewire.h - HINTS ${PKG_PIPEWIRE_INCLUDE_DIRS} ${PKG_PIPEWIRE_INCLUDE_DIRS}/pipewire-0.3) - -find_path( - SPA_INCLUDE_DIRS - NAMES spa/param/props.h - HINTS ${PKG_SPA_INCLUDE_DIRS} ${PKG_SPA_INCLUDE_DIRS}/spa-0.2) - -find_library( - PIPEWIRE_LIBRARIES - NAMES pipewire-0.3 - HINTS ${PKG_PIPEWIRE_LIBRARY_DIRS}) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - PipeWire - FOUND_VAR PIPEWIRE_FOUND - REQUIRED_VARS PIPEWIRE_LIBRARIES PIPEWIRE_INCLUDE_DIRS SPA_INCLUDE_DIRS - VERSION_VAR PIPEWIRE_VERSION) - -if(PIPEWIRE_FOUND AND NOT TARGET PipeWire::PipeWire) - add_library(PipeWire::PipeWire UNKNOWN IMPORTED) - set_target_properties( - PipeWire::PipeWire - PROPERTIES IMPORTED_LOCATION "${PIPEWIRE_LIBRARIES}" - INTERFACE_COMPILE_OPTIONS "${PIPEWIRE_COMPILE_FLAGS}" - INTERFACE_INCLUDE_DIRECTORIES "${PIPEWIRE_INCLUDE_DIRS};${SPA_INCLUDE_DIRS}") -endif() - -mark_as_advanced(PIPEWIRE_LIBRARIES PIPEWIRE_INCLUDE_DIRS) - -include(FeatureSummary) -set_package_properties( - PipeWire PROPERTIES - URL "https://www.pipewire.org" - DESCRIPTION "PipeWire - multimedia processing") diff --git a/cmake/Modules/FindPythonWindows.cmake b/cmake/Modules/FindPythonWindows.cmake deleted file mode 100644 index 84a4fafd08ac4c..00000000000000 --- a/cmake/Modules/FindPythonWindows.cmake +++ /dev/null @@ -1,60 +0,0 @@ -# Once done these will be defined: -# -# PYTHON_FOUND PYTHON_INCLUDE_DIRS PYTHON_LIBRARIES -# -# For use in OBS: -# -# PYTHON_INCLUDE_DIR - -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_LIB_SUFFIX 64) -else() - set(_LIB_SUFFIX 32) -endif() - -find_path( - PYTHON_INCLUDE_DIR - NAMES Python.h - HINTS ${_PYTHON_INCLUDE_DIRS} - PATH_SUFFIXES include include/python) - -find_library( - PYTHON_LIB - NAMES ${_PYTHON_LIBRARIES} python3 - HINTS ${_PYTHON_LIBRARY_DIRS} - PATH_SUFFIXES - lib${_lib_suffix} - lib - libs${_lib_suffix} - libs - bin${_lib_suffix} - bin - ../lib${_lib_suffix} - ../lib - ../libs${_lib_suffix} - ../libs - ../bin${_lib_suffix} - ../bin) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(PythonWindows DEFAULT_MSG PYTHON_LIB PYTHON_INCLUDE_DIR) -mark_as_advanced(PYTHON_INCLUDE_DIR PYTHON_LIB) - -if(PYTHONWINDOWS_FOUND) - set(Python_FOUND TRUE) - set(Python_INCLUDE_DIRS ${PYTHON_INCLUDE_DIR}) - set(Python_LIBRARIES ${PYTHON_LIB}) - set(PYTHONLIBS_FOUND TRUE) - - if(NOT TARGET Python::Python) - if(IS_ABSOLUTE "${Python_LIBRARIES}") - add_library(Python::Python UNKNOWN IMPORTED) - set_target_properties(Python::Python PROPERTIES IMPORTED_LOCATION "${Python_LIBRARIES}") - else() - add_library(Python::Python INTERFACE IMPORTED) - set_target_properties(Python::Python PROPERTIES IMPORTED_LIBNAME "${Python_LIBRARIES}") - endif() - - set_target_properties(Python::Python PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Python_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindSndio.cmake b/cmake/Modules/FindSndio.cmake deleted file mode 100644 index 19db1145a0be48..00000000000000 --- a/cmake/Modules/FindSndio.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# cmake-format: off -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindSndio -------- - -Finds the Sndio library. - -Imported Targets -^^^^^^^^^^^^^^^^ - -This module provides the following imported targets, if found: - -``Sndio::Sndio`` - The Sndio library - -Result Variables -^^^^^^^^^^^^^^^^ - -This will define the following variables: - -``Sndio_FOUND`` - True if the system has the Sndio library. -``Sndio_VERSION`` - The version of the Sndio library which was found. -``Sndio_INCLUDE_DIRS`` - Include directories needed to use Sndio. -``Sndio_LIBRARIES`` - Libraries needed to link to Sndio. - -Cache Variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``Sndio_INCLUDE_DIR`` - The directory containing ``sndio.h``. -``Sndio_LIBRARY`` - The path to the Sndio library. - -#]=======================================================================] -# cmake-format: on - -find_path(Sndio_INCLUDE_DIR sndio.h) -find_library(Sndio_LIBRARY sndio) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - Sndio - FOUND_VAR Sndio_FOUND - REQUIRED_VARS Sndio_LIBRARY Sndio_INCLUDE_DIR) - -if(Sndio_FOUND) - set(Sndio_LIBRARIES ${Sndio_LIBRARY}) - set(Sndio_INCLUDE_DIRS ${Sndio_INCLUDE_DIR}) -endif() - -if(Sndio_FOUND AND NOT TARGET Sndio::Sndio) - add_library(Sndio::Sndio UNKNOWN IMPORTED) - set_target_properties(Sndio::Sndio PROPERTIES IMPORTED_LOCATION "${Sndio_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES - "${Sndio_INCLUDE_DIR}") -endif() - -mark_as_advanced(Sndio_INCLUDE_DIR Sndio_LIBRARY) diff --git a/cmake/Modules/FindSwigWindows.cmake b/cmake/Modules/FindSwigWindows.cmake deleted file mode 100644 index 95b135dda18904..00000000000000 --- a/cmake/Modules/FindSwigWindows.cmake +++ /dev/null @@ -1,19 +0,0 @@ -if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_LIB_SUFFIX 64) -else() - set(_LIB_SUFFIX 32) -endif() - -find_path( - SWIG_DIR - NAMES swigrun.i - HINTS ENV SWIG_PATH ${SWIG_PATH} ${CMAKE_SOURCE_DIR}/${SWIG_PATH} - PATH_SUFFIXES ../swig/Lib swig/Lib) - -find_program( - SWIG_EXECUTABLE - NAMES swig - HINTS ENV SWIG_PATH ${SWIG_PATH} ${CMAKE_SOURCE_DIR}/${SWIG_PATH} - PATH_SUFFIXES ../swig swig) - -find_package(SWIG QUIET 2) diff --git a/cmake/Modules/FindSysinfo.cmake b/cmake/Modules/FindSysinfo.cmake deleted file mode 100644 index 5da583095119cc..00000000000000 --- a/cmake/Modules/FindSysinfo.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Once done these will be defined: -# -# SYSINFO_FOUND SYSINFO_INCLUDE_DIRS SYSINFO_LIBRARIES - -find_path( - SYSINFO_INCLUDE_DIR - NAMES sys/sysinfo.h - PATHS /usr/include /usr/local/include /opt/local/include) - -find_library( - SYSINFO_LIB - NAMES sysinfo libsysinfo - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Sysinfo DEFAULT_MSG SYSINFO_LIB SYSINFO_INCLUDE_DIR) -mark_as_advanced(SYSINFO_INCLUDE_DIR SYSINFO_LIB) - -if(SYSINFO_FOUND) - set(SYSINFO_INCLUDE_DIRS ${SYSINFO_INCLUDE_DIR}) - set(SYSINFO_LIBRARIES ${SYSINFO_LIB}) - - if(NOT TARGET Sysinfo::Sysinfo) - if(IS_ABSOLUTE "${SYSINFO_LIBRARIES}") - add_library(Sysinfo::Sysinfo UNKNOWN IMPORTED) - set_target_properties(Sysinfo::Sysinfo PROPERTIES IMPORTED_LOCATION "${SYSINFO_LIBRARIES}") - else() - add_library(Sysinfo::Sysinfo INTERFACE IMPORTED) - set_target_properties(Sysinfo::Sysinfo PROPERTIES IMPORTED_LIBNAME "${SYSINFO_LIBRARIES}") - endif() - - set_target_properties(Sysinfo::Sysinfo PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${SYSINFO_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindUdev.cmake b/cmake/Modules/FindUdev.cmake deleted file mode 100644 index fca8c430812f31..00000000000000 --- a/cmake/Modules/FindUdev.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# Once done these will be defined: -# -# UDEV_FOUND UDEV_INCLUDE_DIRS UDEV_LIBRARIES - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_UDEV QUIET libudev) -endif() - -find_path( - UDEV_INCLUDE_DIR - NAMES libudev.h - HINTS ${_UDEV_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include) - -find_library( - UDEV_LIB - NAMES udev libudev - HINTS ${_UDEV_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Udev DEFAULT_MSG UDEV_LIB UDEV_INCLUDE_DIR) -mark_as_advanced(UDEV_INCLUDE_DIR UDEV_LIB) - -if(UDEV_FOUND) - set(UDEV_INCLUDE_DIRS ${UDEV_INCLUDE_DIR}) - set(UDEV_LIBRARIES ${UDEV_LIB}) - - if(NOT TARGET Udev::Udev) - if(IS_ABSOLUTE "${UDEV_LIBRARIES}") - add_library(Udev::Udev UNKNOWN IMPORTED) - set_target_properties(Udev::Udev PROPERTIES IMPORTED_LOCATION "${UDEV_LIBRARIES}") - else() - add_library(Udev::Udev INTERFACE IMPORTED) - set_target_properties(Udev::Udev PROPERTIES IMPORTED_LIBNAME "${UDEV_LIBRARIES}") - endif() - - set_target_properties(Udev::Udev PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${UDEV_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/FindUthash.cmake b/cmake/Modules/FindUthash.cmake deleted file mode 100644 index fde6bd5d123212..00000000000000 --- a/cmake/Modules/FindUthash.cmake +++ /dev/null @@ -1,84 +0,0 @@ -#[=======================================================================[.rst -FindUthash ----------- - -FindModule for uthash and the associated library - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 3.0 - -This module defines the :prop_tgt:`IMPORTED` target ``Uthash::Uthash``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``Uthash_FOUND`` - True, if the library was found. -``Uthash_VERSION`` - Detected version of found uthash library. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``Uthash_INCLUDE_DIR`` - Directory containing ``uthash.h``. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -include(FindPackageHandleStandardArgs) - -find_path( - Uthash_INCLUDE_DIR - NAMES uthash.h - PATHS /usr/include /usr/local/include - DOC "uthash include directory") - -if(EXISTS "${Uthash_INCLUDE_DIR}/uthash.h") - file(STRINGS "${Uthash_INCLUDE_DIR}/uthash.h" _version_string - REGEX "#define[ \t]+UTHASH_VERSION[ \t]+[0-9]+\\.[0-9]+\\.[0-9]+") - - string(REGEX REPLACE "#define[ \t]+UTHASH_VERSION[ \t]+([0-9]+\\.[0-9]+\\.[0-9]+)" "\\1" Uthash_VERSION - "${_version_string}") -else() - if(NOT Uthash_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find uthash version.") - endif() - set(Uthash_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(Uthash_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(Uthash_ERROR_REASON "Ensure uthash library is available in local include paths.") -endif() - -find_package_handle_standard_args( - Uthash - REQUIRED_VARS Uthash_INCLUDE_DIR - VERSION_VAR Uthash_VERSION REASON_FAILURE_MESSAGE "${Uthash_ERROR_REASON}") -mark_as_advanced(Uthash_INCLUDE_DIR) -unset(Uthash_ERROR_REASON) - -if(Uthash_FOUND) - if(NOT TARGET Uthash::Uthash) - add_library(Uthash::Uthash INTERFACE IMPORTED) - set_target_properties(Uthash::Uthash PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Uthash_INCLUDE_DIR}") - endif() -endif() - -include(FeatureSummary) -set_package_properties( - Uthash PROPERTIES - URL "https://troydhanson.github.io/uthash" - DESCRIPTION "A hash table for C structures") diff --git a/cmake/Modules/FindVPL.cmake b/cmake/Modules/FindVPL.cmake deleted file mode 100644 index 21faabd89d89b5..00000000000000 --- a/cmake/Modules/FindVPL.cmake +++ /dev/null @@ -1,147 +0,0 @@ -#[=======================================================================[.rst -FindVPL -------- - -FindModule for VPL and associated headers - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 3.0 - -This module defines the :prop_tgt:`IMPORTED` target ``VPL::VPL``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``VPL_FOUND`` - True, if headers were found. -``VPL_VERSION`` - Detected version of found VPL headers. -``VPL_LIBRARIES`` - Libraries needed to link to VPL. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``VPL_INCLUDE_DIR`` - Directory containing ``mfxdispatcher.h``. -``VPL_LIBRARY_RELEASE`` - Path to the library component of VPL in non-debug configuration. -``VPL_LIBRARY_DEBUG`` - Optional path to the library component of VPL in debug configuration. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(_VPL IMPORTED_TARGET QUIET vpl) - if(_VPL_FOUND) - add_library(VPL::VPL ALIAS PkgConfig::_VPL) - return() - endif() -endif() - -find_path( - VPL_INCLUDE_DIR - NAMES vpl/mfxstructures.h - HINTS ${_VPL_INCLUDE_DIRS} ${_VPL_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include /sw/include - DOC "VPL include directory") - -find_library( - VPL_LIBRARY_RELEASE - NAMES ${_VPL_LIBRARIES} ${_VPL_LIBRARIES} vpl - HINTS ${_VPL_LIBRARY_DIRS} ${_VPL_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - DOC "VPL library location") - -find_library( - VPL_LIBRARY_DEBUG - NAMES ${_VPL_LIBRARIES} ${_VPL_LIBRARIES} vpld - HINTS ${_VPL_LIBRARY_DIRS} ${_VPL_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib - DOC "VPL debug library location") - -include(SelectLibraryConfigurations) -select_library_configurations(VPL) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - VPL - REQUIRED_VARS VPL_LIBRARY VPL_INCLUDE_DIR - VERSION_VAR VPL_VERSION REASON_FAILURE_MESSAGE "${VPL_ERROR_REASON}") -mark_as_advanced(VPL_INCLUDE_DIR VPL_LIBRARY) -unset(VPL_ERROR_REASON) - -if(EXISTS "${VPL_INCLUDE_DIR}/vpl/mfxdefs.h") - file(STRINGS "${VPL_INCLUDE_DIR}/vpl/mfxdefs.h" _version_string REGEX "^.*VERSION_(MAJOR|MINOR)[ \t]+[0-9]+[ \t]*$") - - string(REGEX REPLACE ".*VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") - string(REGEX REPLACE ".*VERSION_MINOR[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") - - set(VPL_VERSION "${_version_major}.${_version_minor}") - unset(_version_major) - unset(_version_minor) -else() - if(NOT VPL_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find VPL version.") - endif() - set(VPL_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(VPL_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(VPL_ERROR_REASON "Ensure VPL headers are available in local library paths.") -endif() - -if(VPL_FOUND) - set(VPL_INCLUDE_DIRS ${VPL_INCLUDE_DIR}) - set(VPL_LIBRARIES ${VPL_LIBRARY}) - - if(NOT TARGET VPL::VPL) - if(IS_ABSOLUTE "${VPL_LIBRARY_RELEASE}") - add_library(VPL::VPL UNKNOWN IMPORTED) - set_target_properties(VPL::VPL PROPERTIES IMPORTED_LOCATION "${VPL_LIBRARY_RELEASE}") - else() - add_library(VPL::VPL INTERFACE IMPORTED) - set_target_properties(VPL::VPL PROPERTIES IMPORTED_LIBNAME "${VPL_LIBRARY_RELEASE}") - endif() - - set_target_properties( - VPL::VPL - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${VPL_INCLUDE_DIRS}" - VERSION ${VPL_VERSION} - IMPORTED_CONFIGURATIONS Release) - - if(VPL_LIBRARY_DEBUG) - if(IS_ABSOLUTE "${VPL_LIBRARY_DEBUG}") - set_target_properties(VPL::VPL PROPERTIES IMPORTED_LOCATION_DEBUG "${VPL_LIBRARY_DEBUG}") - else() - set_target_properties(VPL::VPL PROPERTIES IMPORTED_LIBNAME_DEBUG "${VPL_LIBRARY_DEBUG}") - endif() - set_property( - TARGET VPL::VPL - APPEND - PROPERTY IMPORTED_CONFIGURATIONS Debug) - endif() - endif() -endif() - -include(FeatureSummary) -set_package_properties( - VPL PROPERTIES - URL "https://github.com/oneapi-src/oneVPL" - DESCRIPTION - "Intel® oneAPI Video Processing Library (oneVPL) supports AI visual inference, media delivery, cloud gaming, and virtual desktop infrastructure use cases by providing access to hardware accelerated video decode, encode, and frame processing capabilities on Intel® GPUs." -) diff --git a/cmake/Modules/FindWayland.cmake b/cmake/Modules/FindWayland.cmake deleted file mode 100644 index b674a0ba36e6ea..00000000000000 --- a/cmake/Modules/FindWayland.cmake +++ /dev/null @@ -1,125 +0,0 @@ -# cmake-format: off -# Try to find Wayland on a Unix system -# -# This will define: -# -# WAYLAND_FOUND - True if Wayland is found WAYLAND_LIBRARIES - Link -# these to use Wayland WAYLAND_INCLUDE_DIRS - Include directory for Wayland -# WAYLAND_COMPILE_FLAGS - Compiler flags for using Wayland -# -# In addition the following more fine grained variables will be defined: -# -# Wayland_Client_FOUND WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES -# Wayland_Server_FOUND WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES -# Wayland_EGL_FOUND WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES -# Wayland_Cursor_FOUND WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES -# -# Copyright (c) 2013 Martin Gräßlin 2020 Georges Basile -# Stavracas Neto -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -# Use pkg-config to get the directories and then use these values in the -# find_path() and find_library() calls -# cmake-format: on - -find_package(PkgConfig) -pkg_check_modules(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor) - -set(WAYLAND_COMPILE_FLAGS ${PKG_WAYLAND_CFLAGS}) - -find_path( - WAYLAND_CLIENT_INCLUDE_DIRS - NAMES wayland-client.h - HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) -find_library( - WAYLAND_CLIENT_LIBRARIES - NAMES wayland-client - HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) -if(WAYLAND_CLIENT_INCLUDE_DIRS AND WAYLAND_CLIENT_LIBRARIES) - set(Wayland_Client_FOUND TRUE) -else() - set(Wayland_Client_FOUND FALSE) -endif() -mark_as_advanced(WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES) - -find_path( - WAYLAND_CURSOR_INCLUDE_DIRS - NAMES wayland-cursor.h - HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) -find_library( - WAYLAND_CURSOR_LIBRARIES - NAMES wayland-cursor - HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) -if(WAYLAND_CURSOR_INCLUDE_DIRS AND WAYLAND_CURSOR_LIBRARIES) - set(Wayland_Cursor_FOUND TRUE) -else() - set(Wayland_Cursor_FOUND FALSE) -endif() -mark_as_advanced(WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES) - -find_path( - WAYLAND_EGL_INCLUDE_DIRS - NAMES wayland-egl.h - HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) -find_library( - WAYLAND_EGL_LIBRARIES - NAMES wayland-egl - HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) -if(WAYLAND_EGL_INCLUDE_DIRS AND WAYLAND_EGL_LIBRARIES) - set(Wayland_EGL_FOUND TRUE) -else() - set(Wayland_EGL_FOUND FALSE) -endif() -mark_as_advanced(WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES) - -find_path( - WAYLAND_SERVER_INCLUDE_DIRS - NAMES wayland-server.h - HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) -find_library( - WAYLAND_SERVER_LIBRARIES - NAMES wayland-server - HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) -if(WAYLAND_SERVER_INCLUDE_DIRS AND WAYLAND_SERVER_LIBRARIES) - set(Wayland_Server_FOUND TRUE) -else() - set(Wayland_Server_FOUND FALSE) -endif() -mark_as_advanced(WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES) - -set(WAYLAND_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIRS} ${WAYLAND_SERVER_INCLUDE_DIRS} ${WAYLAND_EGL_INCLUDE_DIRS} - ${WAYLAND_CURSOR_INCLUDE_DIRS}) -set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} - ${WAYLAND_CURSOR_LIBRARIES}) -mark_as_advanced(WAYLAND_INCLUDE_DIRS WAYLAND_LIBRARIES) - -list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIRS) - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args( - Wayland - REQUIRED_VARS WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIRS - HANDLE_COMPONENTS) - -foreach(component "Client" "Server" "EGL" "Cursor") - if(NOT TARGET Wayland::${component}) - string(TOUPPER ${component} component_u) - if(Wayland_${component}_FOUND) - if(IS_ABSOLUTE "${WAYLAND_${component_u}_LIBRARIES}") - add_library(Wayland::${component} UNKNOWN IMPORTED) - set_target_properties(Wayland::${component} PROPERTIES IMPORTED_LOCATION "${WAYLAND_${component_u}_LIBRARIES}") - else() - add_library(Wayland::${component} INTERFACE IMPORTED) - set_target_properties(Wayland::${component} PROPERTIES IMPORTED_LIBNAME "${WAYLAND_${component_u}_LIBRARIES}") - endif() - - set_target_properties(Wayland::${component} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${WAYLAND_${component_u}_INCLUDE_DIRS}") - - set_target_properties(Wayland::${component} PROPERTIES INTERFACE_COMPILE_OPTIONS "${WAYLAND_COMPILE_FLAGS}") - endif() - endif() -endforeach() diff --git a/cmake/Modules/FindWebsocketpp.cmake b/cmake/Modules/FindWebsocketpp.cmake deleted file mode 100644 index edbd1f64cf3c39..00000000000000 --- a/cmake/Modules/FindWebsocketpp.cmake +++ /dev/null @@ -1,91 +0,0 @@ -#[=======================================================================[.rst -FindWebsocketpp ----------- - -FindModule for WebSocket++ and the associated library - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 2.0 - -This module defines the :prop_tgt:`IMPORTED` target ``Websocketpp::Websocketpp``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``Websocketpp_FOUND`` - True, if the library was found. -``Websocketpp_VERSION`` - Detected version of found Websocketpp library. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``Websocketpp_INCLUDE_DIR`` - Directory containing ``websocketpp/client.hpp``. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -include(FindPackageHandleStandardArgs) - -find_path( - Websocketpp_INCLUDE_DIR - NAMES websocketpp/client.hpp - PATHS /usr/include /usr/local/include - DOC "WebSocket++ include directory") - -if(EXISTS "${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp") - file(STRINGS "${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp" _version_string - REGEX "^.*(major|minor|patch)_version[ \t]+=[ \t]+[0-9]+") - - string(REGEX REPLACE ".*major_version[ \t]+=[ \t]+([0-9]+).*" "\\1" _version_major "${_version_string}") - string(REGEX REPLACE ".*minor_version[ \t]+=[ \t]+([0-9]+).*" "\\1" _version_minor "${_version_string}") - string(REGEX REPLACE ".*patch_version[ \t]+=[ \t]+([0-9]+).*" "\\1" _version_patch "${_version_string}") - - set(Websocketpp_VERSION "${_version_major}.${_version_minor}.${_version_patch}") - unset(_version_major) - unset(_version_minor) - unset(_version_patch) -else() - if(NOT Websocketpp_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find WebSocket++ version.") - endif() - set(Websocketpp_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(Websocketpp_ERROR_REASON "Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(Websocketpp_ERROR_REASON "Ensure WebSocket++ library is available in local include paths.") -endif() - -find_package_handle_standard_args( - Websocketpp - REQUIRED_VARS Websocketpp_INCLUDE_DIR - VERSION_VAR Websocketpp_VERSION REASON_FAILURE_MESSAGE "${Websocketpp_ERROR_REASON}") -mark_as_advanced(Websocketpp_INCLUDE_DIR) -unset(Websocketpp_ERROR_REASON) - -if(Websocketpp_FOUND) - if(NOT TARGET Websocketpp::Websocketpp) - add_library(Websocketpp::Websocketpp INTERFACE IMPORTED) - set_target_properties(Websocketpp::Websocketpp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${Websocketpp_INCLUDE_DIR}") - endif() -endif() - -include(FeatureSummary) -set_package_properties( - Websocketpp PROPERTIES - URL "https://www.zaphoyd.com/websocketpp/" - DESCRIPTION "WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket Protocol.") diff --git a/cmake/Modules/FindX11_XCB.cmake b/cmake/Modules/FindX11_XCB.cmake deleted file mode 100644 index bf6ab50291faa1..00000000000000 --- a/cmake/Modules/FindX11_XCB.cmake +++ /dev/null @@ -1,45 +0,0 @@ -# cmake-format: off -# * Try to find libX11-xcb Once done this will define -# -# X11_XCB_FOUND - system has libX11-xcb -# X11_XCB_LIBRARIES - Link these to use libX11-xcb -# X11_XCB_INCLUDE_DIR - the libX11-xcb include dir -# X11_XCB_DEFINITIONS - compiler switches required for using libX11-xcb - -# Copyright (c) 2011 Fredrik Höglund -# Copyright (c) 2008 Helio Chissini de Castro, -# Copyright (c) 2007 Matthias Kretz, -# -# Redistribution and use is allowed according to the terms of the BSD license. For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# cmake-format: on - -if(NOT WIN32) - # use pkg-config to get the directories and then use these values in the FIND_PATH() and FIND_LIBRARY() calls - find_package(PkgConfig) - pkg_check_modules(PKG_X11_XCB QUIET x11-xcb) - - set(X11_XCB_DEFINITIONS ${PKG_X11_XCB_CFLAGS}) - - find_path( - X11_XCB_INCLUDE_DIR - NAMES X11/Xlib-xcb.h - HINTS ${PKG_X11_XCB_INCLUDE_DIRS}) - find_library( - X11_XCB_LIBRARIES - NAMES X11-xcb - HINTS ${PKG_X11_XCB_LIBRARY_DIRS}) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(X11_XCB DEFAULT_MSG X11_XCB_LIBRARIES X11_XCB_INCLUDE_DIR) - - mark_as_advanced(X11_XCB_INCLUDE_DIR X11_XCB_LIBRARIES) - - if(X11_XCB_FOUND AND NOT TARGET X11::X11_xcb) - add_library(X11::X11_xcb UNKNOWN IMPORTED) - set_target_properties( - X11::X11_xcb - PROPERTIES IMPORTED_LOCATION "${X11_XCB_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${X11_XCB_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "XCB::XCB;X11::X11") - endif() -endif() diff --git a/cmake/Modules/FindXCB.cmake b/cmake/Modules/FindXCB.cmake deleted file mode 100644 index 23579b43770b9b..00000000000000 --- a/cmake/Modules/FindXCB.cmake +++ /dev/null @@ -1,267 +0,0 @@ -# cmake-format: off -# Try to find XCB on a Unix system -# -# This will define: -# -# XCB_FOUND - True if xcb is available XCB_LIBRARIES - Link these to -# use xcb XCB_INCLUDE_DIRS - Include directory for xcb XCB_DEFINITIONS - -# Compiler flags for using xcb -# -# In addition the following more fine grained variables will be defined: -# -# XCB_XCB_FOUND XCB_XCB_INCLUDE_DIR XCB_XCB_LIBRARY XCB_UTIL_FOUND -# XCB_UTIL_INCLUDE_DIR XCB_UTIL_LIBRARY XCB_COMPOSITE_FOUND -# XCB_COMPOSITE_INCLUDE_DIR XCB_COMPOSITE_LIBRARY XCB_DAMAGE_FOUND -# XCB_DAMAGE_INCLUDE_DIR XCB_DAMAGE_LIBRARY XCB_XFIXES_FOUND -# XCB_XFIXES_INCLUDE_DIR XCB_XFIXES_LIBRARY XCB_RENDER_FOUND -# XCB_RENDER_INCLUDE_DIR XCB_RENDER_LIBRARY XCB_RANDR_FOUND -# XCB_RANDR_INCLUDE_DIR XCB_RANDR_LIBRARY XCB_SHAPE_FOUND -# XCB_SHAPE_INCLUDE_DIR XCB_SHAPE_LIBRARY XCB_DRI2_FOUND -# XCB_DRI2_INCLUDE_DIR XCB_DRI2_LIBRARY XCB_GLX_FOUND XCB_GLX_INCLUDE_DIR -# XCB_GLX_LIBRARY XCB_SHM_FOUND XCB_SHM_INCLUDE_DIR XCB_SHM_LIBRARY -# XCB_XV_FOUND XCB_XV_INCLUDE_DIR XCB_XV_LIBRARY XCB_XINPUT_FOUND -# XCB_XINPUT_INCLUDE_DIR XCB_XINPUT_LIBRARY XCB_SYNC_FOUND -# XCB_SYNC_INCLUDE_DIR XCB_SYNC_LIBRARY XCB_XTEST_FOUND -# XCB_XTEST_INCLUDE_DIR XCB_XTEST_LIBRARY XCB_ICCCM_FOUND -# XCB_ICCCM_INCLUDE_DIR XCB_ICCCM_LIBRARY XCB_EWMH_FOUND -# XCB_EWMH_INCLUDE_DIR XCB_EWMH_LIBRARY XCB_IMAGE_FOUND -# XCB_IMAGE_INCLUDE_DIR XCB_IMAGE_LIBRARY XCB_RENDERUTIL_FOUND -# XCB_RENDERUTIL_INCLUDE_DIR XCB_RENDERUTIL_LIBRARY XCB_KEYSYMS_FOUND -# XCB_KEYSYMS_INCLUDE_DIR XCB_KEYSYMS_LIBRARY -# -# Copyright (c) 2011 Fredrik Höglund Copyright (c) 2013 Martin -# Gräßlin -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# cmake-format: on - -set(knownComponents - XCB - COMPOSITE - DAMAGE - DRI2 - EWMH - GLX - ICCCM - IMAGE - KEYSYMS - RANDR - RENDER - RENDERUTIL - SHAPE - SHM - SYNC - UTIL - XFIXES - XTEST - XV - XINPUT - XINERAMA) - -unset(unknownComponents) - -set(pkgConfigModules) -set(requiredComponents) - -if(XCB_FIND_COMPONENTS) - set(comps ${XCB_FIND_COMPONENTS}) -else() - set(comps ${knownComponents}) -endif() - -# iterate through the list of requested components, and check that we know them all. If not, fail. -foreach(comp ${comps}) - list(FIND knownComponents ${comp} index) - if("${index}" STREQUAL "-1") - list(APPEND unknownComponents "${comp}") - else() - if("${comp}" STREQUAL "XCB") - list(APPEND pkgConfigModules "xcb") - elseif("${comp}" STREQUAL "COMPOSITE") - list(APPEND pkgConfigModules "xcb-composite") - elseif("${comp}" STREQUAL "DAMAGE") - list(APPEND pkgConfigModules "xcb-damage") - elseif("${comp}" STREQUAL "DRI2") - list(APPEND pkgConfigModules "xcb-dri2") - elseif("${comp}" STREQUAL "EWMH") - list(APPEND pkgConfigModules "xcb-ewmh") - elseif("${comp}" STREQUAL "GLX") - list(APPEND pkgConfigModules "xcb-glx") - elseif("${comp}" STREQUAL "ICCCM") - list(APPEND pkgConfigModules "xcb-icccm") - elseif("${comp}" STREQUAL "IMAGE") - list(APPEND pkgConfigModules "xcb-image") - elseif("${comp}" STREQUAL "KEYSYMS") - list(APPEND pkgConfigModules "xcb-keysyms") - elseif("${comp}" STREQUAL "RANDR") - list(APPEND pkgConfigModules "xcb-randr") - elseif("${comp}" STREQUAL "RENDER") - list(APPEND pkgConfigModules "xcb-render") - elseif("${comp}" STREQUAL "RENDERUTIL") - list(APPEND pkgConfigModules "xcb-renderutil") - elseif("${comp}" STREQUAL "SHAPE") - list(APPEND pkgConfigModules "xcb-shape") - elseif("${comp}" STREQUAL "SHM") - list(APPEND pkgConfigModules "xcb-shm") - elseif("${comp}" STREQUAL "SYNC") - list(APPEND pkgConfigModules "xcb-sync") - elseif("${comp}" STREQUAL "UTIL") - list(APPEND pkgConfigModules "xcb-util") - elseif("${comp}" STREQUAL "XFIXES") - list(APPEND pkgConfigModules "xcb-xfixes") - elseif("${comp}" STREQUAL "XTEST") - list(APPEND pkgConfigModules "xcb-xtest") - elseif("${comp}" STREQUAL "XV") - list(APPEND pkgConfigModules "xcb-xv") - elseif("${comp}" STREQUAL "XINPUT") - list(APPEND pkgConfigModules "xcb-xinput") - elseif("${comp}" STREQUAL "XINERAMA") - list(APPEND pkgConfigModules "xcb-xinerama") - endif() - endif() -endforeach() - -if(DEFINED unknownComponents) - set(msgType STATUS) - if(XCB_FIND_REQUIRED) - set(msgType FATAL_ERROR) - endif() - if(NOT XCB_FIND_QUIETLY) - message(${msgType} "XCB: requested unknown components ${unknownComponents}") - endif() - return() -endif() - -macro(_XCB_HANDLE_COMPONENT _comp) - set(_header) - set(_lib) - if("${_comp}" STREQUAL "XCB") - set(_header "xcb/xcb.h") - set(_lib "xcb") - elseif("${_comp}" STREQUAL "COMPOSITE") - set(_header "xcb/composite.h") - set(_lib "xcb-composite") - elseif("${_comp}" STREQUAL "DAMAGE") - set(_header "xcb/damage.h") - set(_lib "xcb-damage") - elseif("${_comp}" STREQUAL "DRI2") - set(_header "xcb/dri2.h") - set(_lib "xcb-dri2") - elseif("${_comp}" STREQUAL "EWMH") - set(_header "xcb/xcb_ewmh.h") - set(_lib "xcb-ewmh") - elseif("${_comp}" STREQUAL "GLX") - set(_header "xcb/glx.h") - set(_lib "xcb-glx") - elseif("${_comp}" STREQUAL "ICCCM") - set(_header "xcb/xcb_icccm.h") - set(_lib "xcb-icccm") - elseif("${_comp}" STREQUAL "IMAGE") - set(_header "xcb/xcb_image.h") - set(_lib "xcb-image") - elseif("${_comp}" STREQUAL "KEYSYMS") - set(_header "xcb/xcb_keysyms.h") - set(_lib "xcb-keysyms") - elseif("${_comp}" STREQUAL "RANDR") - set(_header "xcb/randr.h") - set(_lib "xcb-randr") - elseif("${_comp}" STREQUAL "RENDER") - set(_header "xcb/render.h") - set(_lib "xcb-render") - elseif("${_comp}" STREQUAL "RENDERUTIL") - set(_header "xcb/xcb_renderutil.h") - set(_lib "xcb-render-util") - elseif("${_comp}" STREQUAL "SHAPE") - set(_header "xcb/shape.h") - set(_lib "xcb-shape") - elseif("${_comp}" STREQUAL "SHM") - set(_header "xcb/shm.h") - set(_lib "xcb-shm") - elseif("${_comp}" STREQUAL "SYNC") - set(_header "xcb/sync.h") - set(_lib "xcb-sync") - elseif("${_comp}" STREQUAL "UTIL") - set(_header "xcb/xcb_util.h") - set(_lib "xcb-util") - elseif("${_comp}" STREQUAL "XFIXES") - set(_header "xcb/xfixes.h") - set(_lib "xcb-xfixes") - elseif("${_comp}" STREQUAL "XTEST") - set(_header "xcb/xtest.h") - set(_lib "xcb-xtest") - elseif("${_comp}" STREQUAL "XV") - set(_header "xcb/xv.h") - set(_lib "xcb-xv") - elseif("${_comp}" STREQUAL "XINPUT") - set(_header "xcb/xinput.h") - set(_lib "xcb-xinput") - elseif("${_comp}" STREQUAL "XINERAMA") - set(_header "xcb/xinerama.h") - set(_lib "xcb-xinerama") - endif() - - find_path( - XCB_${_comp}_INCLUDE_DIR - NAMES ${_header} - HINTS ${PKG_XCB_INCLUDE_DIRS}) - find_library( - XCB_${_comp}_LIBRARY - NAMES ${_lib} - HINTS ${PKG_XCB_LIBRARY_DIRS}) - mark_as_advanced(XCB_${_comp}_LIBRARY XCB_${_comp}_INCLUDE_DIR) - - if(XCB_${_comp}_INCLUDE_DIR AND XCB_${_comp}_LIBRARY) - set(XCB_${_comp}_FOUND TRUE) - list(APPEND XCB_INCLUDE_DIRS ${XCB_${_comp}_INCLUDE_DIR}) - list(APPEND XCB_LIBRARIES ${XCB_${_comp}_LIBRARY}) - if(NOT XCB_FIND_QUIETLY) - message(STATUS "XCB[${_comp}]: Found component ${_comp}") - endif() - endif() -endmacro() - -if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") - # Use pkg-config to get the directories and then use these values in the FIND_PATH() and FIND_LIBRARY() calls - find_package(PkgConfig) - pkg_check_modules(PKG_XCB QUIET ${pkgConfigModules}) - - set(XCB_DEFINITIONS ${PKG_XCB_CFLAGS}) - - foreach(comp ${comps}) - _xcb_handle_component(${comp}) - endforeach() - - if(XCB_INCLUDE_DIRS) - list(REMOVE_DUPLICATES XCB_INCLUDE_DIRS) - endif() - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args( - XCB - REQUIRED_VARS XCB_LIBRARIES XCB_INCLUDE_DIRS - HANDLE_COMPONENTS) - - # compatibility for old variable naming - set(XCB_INCLUDE_DIR ${XCB_INCLUDE_DIRS}) -endif() - -if(XCB_FOUND AND NOT TARGET XCB::XCB) - foreach(component ${comps}) - if(NOT TARGET XCB::${component}) - string(TOUPPER ${component} component_u) - if(XCB_${component_u}_FOUND) - if(IS_ABSOLUTE "${XCB_${component_u}_LIBRARY}") - add_library(XCB::${component} UNKNOWN IMPORTED) - set_target_properties(XCB::${component} PROPERTIES IMPORTED_LOCATION "${XCB_${component_u}_LIBRARY}") - else() - add_library(XCB::${component} INTERFACE IMPORTED) - set_target_properties(XCB::${component} PROPERTIES IMPORTED_LIBNAME "${XCB_${component_u}_LIBRARY}") - endif() - - set_target_properties(XCB::${component} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${XCB_${component_u}_INCLUDE_DIR}") - endif() - endif() - endforeach() -endif() diff --git a/cmake/Modules/FindXkbcommon.cmake b/cmake/Modules/FindXkbcommon.cmake deleted file mode 100644 index e2205eaa696db3..00000000000000 --- a/cmake/Modules/FindXkbcommon.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# Once done these will be defined: -# -# XKBCOMMON_FOUND XKBCOMMON_INCLUDE_DIRS XKBCOMMON_LIBRARIES - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(_XKBCOMMON QUIET xkbcommon) -endif() - -find_path( - XKBCOMMON_INCLUDE_DIR - NAMES xkbcommon/xkbcommon.h - HINTS ${_XKBCOMMON_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include /opt/local/include) - -find_library( - XKBCOMMON_LIB - NAMES xkbcommon libxkbcommon - HINTS ${_XKBCOMMON_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib /opt/local/lib) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Xkbcommon DEFAULT_MSG XKBCOMMON_LIB XKBCOMMON_INCLUDE_DIR) -mark_as_advanced(XKBCOMMON_INCLUDE_DIR XKBCOMMON_LIB) - -if(XKBCOMMON_FOUND) - set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR}) - set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIB}) - - if(NOT TARGET Xkbcommon::Xkbcommon) - if(IS_ABSOLUTE "${XKBCOMMON_LIBRARIES}") - add_library(Xkbcommon::Xkbcommon UNKNOWN IMPORTED) - set_target_properties(Xkbcommon::Xkbcommon PROPERTIES IMPORTED_LOCATION "${XKBCOMMON_LIBRARIES}") - else() - add_library(Xkbcommon::Xkbcommon INTERFACE IMPORTED) - set_target_properties(Xkbcommon::Xkbcommon PROPERTIES IMPORTED_LIBNAME "${XKBCOMMON_LIBRARIES}") - endif() - - set_target_properties(Xkbcommon::Xkbcommon PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${XKBCOMMON_INCLUDE_DIRS}") - endif() -endif() diff --git a/cmake/Modules/Findqrcodegencpp.cmake b/cmake/Modules/Findqrcodegencpp.cmake deleted file mode 100644 index 6ee0638595f27e..00000000000000 --- a/cmake/Modules/Findqrcodegencpp.cmake +++ /dev/null @@ -1,174 +0,0 @@ -#[=======================================================================[.rst -Findqrcodegencpp ----------------- - -FindModule for qrcodegencpp and associated libraries - -Imported Targets -^^^^^^^^^^^^^^^^ - -.. versionadded:: 3.0 - -This module defines the :prop_tgt:`IMPORTED` target ``qrcodegencpp::qrcodegencpp``. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module sets the following variables: - -``qrcodegencpp_FOUND`` - True, if all required components and the core library were found. -``qrcodegencpp_VERSION`` - Detected version of found qrcodegencpp libraries. - -Cache variables -^^^^^^^^^^^^^^^ - -The following cache variables may also be set: - -``qrcodegencpp_LIBRARY`` - Path to the library component of qrcodegencpp. -``qrcodegencpp_INCLUDE_DIR`` - Directory containing ``qrcodegen.hpp``. - -#]=======================================================================] - -# cmake-format: off -# cmake-lint: disable=C0103 -# cmake-lint: disable=C0301 -# cmake-format: on - -include(FindPackageHandleStandardArgs) - -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_search_module(PC_qrcodegencpp QUIET qrcodegencpp) -endif() - -# qrcodegencpp_set_soname: Set SONAME on imported library target -macro(qrcodegencpp_set_soname) - if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") - execute_process( - COMMAND sh -c "otool -D '${qrcodegencpp_LIBRARY}' | grep -v '${qrcodegencpp_LIBRARY}'" - OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) - - if(_result EQUAL 0 AND _output MATCHES "^@rpath/") - set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_SONAME "${_output}") - endif() - elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - execute_process( - COMMAND sh -c "${CMAKE_OBJDUMP} -p '${qrcodegencpp_LIBRARY}' | grep SONAME" - OUTPUT_VARIABLE _output - RESULT_VARIABLE _result) - - if(_result EQUAL 0) - string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") - set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_SONAME "${_soname}") - unset(_soname) - endif() - endif() - unset(_output) - unset(_result) -endmacro() - -# qrcodegencpp_find_dll: Find DLL for corresponding import library -macro(qrcodegencpp_find_dll) - cmake_path(GET qrcodegencpp_IMPLIB PARENT_PATH _implib_path) - cmake_path(SET _bin_path NORMALIZE "${_implib_path}/../bin") - - string(REGEX REPLACE "[0-9]+\\.([0-9]+)\\.[0-9]+" "\\1" _dll_version "${qrcodegencpp_VERSION}") - - find_program( - qrcodegencpp_LIBRARY - NAMES qrcodegencpp.dll - HINTS ${_implib_path} ${_bin_path} - DOC "qrcodegencpp DLL location") - - if(NOT qrcodegencpp_LIBRARY) - set(qrcodegencpp_LIBRARY "${qrcodegencpp_IMPLIB}") - endif() - unset(_implib_path) - unset(_bin_path) - unset(_dll_version) -endmacro() - -find_path( - qrcodegencpp_INCLUDE_DIR - NAMES qrcodegen.hpp - HINTS ${PC_qrcodegencpp_INCLUDE_DIRS} - PATHS /usr/include /usr/local/include - PATH_SUFFIXES qrcodegencpp qrcodegen - DOC "qrcodegencpp include directory") - -if(PC_qrcodegencpp_VERSION VERSION_GREATER 0) - set(qrcodegencpp_VERSION ${PC_qrcodegencpp_VERSION}) -else() - if(NOT qrcodegencpp_FIND_QUIETLY) - message(AUTHOR_WARNING "Failed to find qrcodegencpp version.") - endif() - set(qrcodegencpp_VERSION 0.0.0) -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") - find_library( - qrcodegencpp_IMPLIB - NAMES qrcodegencpp qrcodegencpp - DOC "qrcodegencpp import library location") - - qrcodegencpp_find_dll() -else() - find_library( - qrcodegencpp_LIBRARY - NAMES qrcodegencpp qrcodegencpp - HINTS ${PC_qrcodegencpp_LIBRARY_DIRS} - PATHS /usr/lib /usr/local/lib - DOC "qrcodegencpp location") -endif() - -if(CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin|Windows") - set(qrcodegencpp_ERROR_REASON "Ensure that a qrcodegencpp distribution is provided as part of CMAKE_PREFIX_PATH.") -elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") - set(qrcodegencpp_ERROR_REASON "Ensure that qrcodegencpp is installed on the system.") -endif() - -find_package_handle_standard_args( - qrcodegencpp - REQUIRED_VARS qrcodegencpp_LIBRARY qrcodegencpp_INCLUDE_DIR - VERSION_VAR qrcodegencpp_VERSION REASON_FAILURE_MESSAGE "${qrcodegencpp_ERROR_REASON}") -mark_as_advanced(qrcodegencpp_INCLUDE_DIR qrcodegencpp_LIBRARY qrcodegencpp_IMPLIB) -unset(qrcodegencpp_ERROR_REASON) - -if(qrcodegencpp_FOUND) - if(NOT TARGET qrcodegencpp::qrcodegencpp) - if(IS_ABSOLUTE "${qrcodegencpp_LIBRARY}") - if(DEFINED qrcodegencpp_IMPLIB) - if(qrcodegencpp_IMPLIB STREQUAL qrcodegencpp_LIBRARY) - add_library(qrcodegencpp::qrcodegencpp STATIC IMPORTED) - else() - add_library(qrcodegencpp::qrcodegencpp SHARED IMPORTED) - set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_IMPLIB "${qrcodegencpp_IMPLIB}") - endif() - else() - add_library(qrcodegencpp::qrcodegencpp UNKNOWN IMPORTED) - endif() - set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_LOCATION "${qrcodegencpp_LIBRARY}") - else() - add_library(qrcodegencpp::qrcodegencpp INTERFACE IMPORTED) - set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_LIBNAME "${qrcodegencpp_LIBRARY}") - endif() - - qrcodegencpp_set_soname() - set_target_properties( - qrcodegencpp::qrcodegencpp - PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_qrcodegencpp_CFLAGS_OTHER}" - INTERFACE_INCLUDE_DIRECTORIES "${qrcodegencpp_INCLUDE_DIR}" - VERSION ${qrcodegencpp_VERSION}) - endif() -endif() - -include(FeatureSummary) -set_package_properties( - qrcodegencpp PROPERTIES - URL "https://www.nayuki.io/page/qr-code-generator-library" - DESCRIPTION "This project aims to be the best, clearest library for generating QR Codes in C++.") diff --git a/cmake/Modules/IDLFileHelper.cmake b/cmake/Modules/IDLFileHelper.cmake deleted file mode 100644 index 594481b51358b4..00000000000000 --- a/cmake/Modules/IDLFileHelper.cmake +++ /dev/null @@ -1,74 +0,0 @@ -macro(add_idl_files_base generated_files with_tlb) - foreach(filename ${ARGN}) - get_filename_component(file_we ${filename} NAME_WE) - get_filename_component(file_path ${filename} PATH) - - set(file_c ${file_we}_i.c) - set(file_h ${file_we}.h) - set(bin_file_h ${CMAKE_CURRENT_BINARY_DIR}/${file_h}) - set(bin_file_c ${CMAKE_CURRENT_BINARY_DIR}/${file_c}) - - if(MSVC) - if(${with_tlb}) - set(file_tlb ${file_we}.tlb) - set(bin_file_tlb ${CMAKE_CURRENT_BINARY_DIR}/${file_tlb}) - set(tlb_opt "") - else() - set(tlb_opt "/notlb") - endif() - - add_custom_command( - OUTPUT ${bin_file_h} ${bin_file_c} - DEPENDS ${filename} - COMMAND midl /h ${file_h} /iid ${file_c} ${tlb_opt} $,/win64,/win32> - ${CMAKE_CURRENT_SOURCE_DIR}/${filename} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - else() - execute_process( - COMMAND echo - COMMAND ${CMAKE_C_COMPILER} -v -x c++ -E - - ERROR_VARIABLE cpp_inc_output - OUTPUT_QUIET ERROR_STRIP_TRAILING_WHITESPACE) - - string(REPLACE ";" " " include_dirs ${cpp_inc_output}) - string(REPLACE "\n" ";" include_dirs ${cpp_inc_output}) - - set(include_params) - foreach(include_dir ${include_dirs}) - string(SUBSTRING ${include_dir} 0 1 first_char) - if(${first_char} STREQUAL " ") - string(LENGTH "${include_dir}" include_dir_len) - math(EXPR include_dir_len "${include_dir_len} - 1") - string(SUBSTRING ${include_dir} 1 ${include_dir_len} include_dir) - set(include_params "-I\"${include_dir}\" ${include_params}") - endif() - endforeach() - - if(WIN32) - separate_arguments(include_params WINDOWS_COMMAND ${include_params}) - endif() - - add_custom_command( - OUTPUT ${file_h} - DEPENDS ${filename} - COMMAND ${CMAKE_WIDL} ${include_params} -h -o ${file_h} ${CMAKE_CURRENT_SOURCE_DIR}/${filename} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - file(WRITE ${bin_file_c} "#include \n#include <${file_h}>\n") - endif() - - set_source_files_properties(${bin_file_h} ${bin_file_c} PROPERTIES GENERATED TRUE) - - set(${generated_files} ${${generated_file}} ${bin_file_h} ${bin_file_c}) - - set_source_files_properties(${filename} PROPERTIES HEADER_FILE_ONLY TRUE) - endforeach(filename ${ARGN}) -endmacro(add_idl_files_base) - -macro(add_idl_files generated_files) - add_idl_files_base(${generated_files} FALSE ${ARGN}) -endmacro(add_idl_files) - -macro(add_idl_files_with_tlb generated_files) - add_idl_files_base(${generated_files} TRUE ${ARGN}) -endmacro(add_idl_files_with_tlb) diff --git a/cmake/Modules/ObsDefaults_Linux.cmake b/cmake/Modules/ObsDefaults_Linux.cmake deleted file mode 100644 index fe8d72364e85c6..00000000000000 --- a/cmake/Modules/ObsDefaults_Linux.cmake +++ /dev/null @@ -1,175 +0,0 @@ -# Enable modern cmake policies -if(POLICY CMP0011) - cmake_policy(SET CMP0011 NEW) -endif() - -if(POLICY CMP0072) - cmake_policy(SET CMP0072 NEW) -endif() - -if(POLICY CMP0095) - cmake_policy(SET CMP0095 NEW) -endif() - -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND LINUX_PORTABLE) - set(CMAKE_INSTALL_PREFIX - "${CMAKE_BINARY_DIR}/install" - CACHE STRING "Directory to install OBS after building" FORCE) -endif() - -macro(setup_obs_project) - #[[ - POSIX directory setup (portable) - CMAKE_BINARY_DIR - └ rundir - └ CONFIG - └ bin - └ ARCH - └ data - └ libobs - └ obs-plugins - └ PLUGIN - └ obs-scripting - └ ARCH - └ obs-studio - └ obs-plugins - └ ARCH - - POSIX directory setup (non-portable) - /usr/local/ - └ bin - └ include - └ obs - └ libs - └ cmake - └ obs-plugins - └ obs-scripting - └ share - └ obs - └ libobs - └ obs-plugins - └ obs-studio - #]] - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_ARCH_SUFFIX 64) - else() - set(_ARCH_SUFFIX 32) - endif() - - if(NOT OBS_MULTIARCH_SUFFIX AND DEFINED ENV{OBS_MULTIARCH_SUFFIX}) - set(OBS_MULTIARCH_SUFFIX "$ENV{OBS_MULTIARCH_SUFFIX}") - endif() - - set(OBS_OUTPUT_DIR "${CMAKE_BINARY_DIR}/rundir") - - if(NOT LINUX_PORTABLE) - set(OBS_EXECUTABLE_DESTINATION "${CMAKE_INSTALL_BINDIR}") - set(OBS_INCLUDE_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/obs") - set(OBS_LIBRARY_DESTINATION "${CMAKE_INSTALL_LIBDIR}") - set(OBS_PLUGIN_DESTINATION "${OBS_LIBRARY_DESTINATION}/obs-plugins") - set(OBS_PLUGIN_PATH "${OBS_PLUGIN_DESTINATION}") - set(OBS_SCRIPT_PLUGIN_DESTINATION "${OBS_LIBRARY_DESTINATION}/obs-scripting") - set(OBS_DATA_DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/obs") - set(OBS_CMAKE_DESTINATION "${OBS_LIBRARY_DESTINATION}/cmake") - - set(OBS_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - set(OBS_DATA_PATH "${OBS_DATA_DESTINATION}") - - set(OBS_SCRIPT_PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/${OBS_SCRIPT_PLUGIN_DESTINATION}") - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${OBS_LIBRARY_DESTINATION}") - else() - set(OBS_EXECUTABLE_DESTINATION "bin/${_ARCH_SUFFIX}bit") - set(OBS_INCLUDE_DESTINATION "include") - set(OBS_LIBRARY_DESTINATION "bin/${_ARCH_SUFFIX}bit") - set(OBS_PLUGIN_DESTINATION "obs-plugins/${_ARCH_SUFFIX}bit") - set(OBS_PLUGIN_PATH "../../${OBS_PLUGIN_DESTINATION}") - set(OBS_SCRIPT_PLUGIN_DESTINATION "data/obs-scripting/${_ARCH_SUFFIX}bit") - set(OBS_DATA_DESTINATION "data") - set(OBS_CMAKE_DESTINATION "cmake") - - set(OBS_INSTALL_PREFIX "") - set(OBS_DATA_PATH "../../${OBS_DATA_DESTINATION}") - - set(OBS_SCRIPT_PLUGIN_PATH "../../${OBS_SCRIPT_PLUGIN_DESTINATION}") - set(CMAKE_INSTALL_RPATH "$ORIGIN/" "$ORIGIN/../../${OBS_LIBRARY_DESTINATION}") - endif() - - if(BUILD_FOR_PPA) - set_option(ENABLE_LIBFDK ON) - set_option(ENABLE_JACK ON) - set_option(ENABLE_RTMPS ON) - endif() - - if(BUILD_FOR_DISTRIBUTION OR DEFINED ENV{CI}) - set_option(ENABLE_RTMPS ON) - endif() - - set(CPACK_PACKAGE_NAME "obs-studio") - set(CPACK_PACKAGE_VENDOR "${OBS_WEBSITE}") - set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${OBS_COMPANY_NAME}") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${OBS_COMMENTS}") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/UI/data/license/gplv2.txt") - set(CPACK_PACKAGE_VERSION "${OBS_VERSION_CANONICAL}-${OBS_BUILD_NUMBER}") - set(CPACK_PACKAGE_EXECUTABLES "obs") - - if(OS_LINUX AND NOT LINUX_PORTABLE) - set(CPACK_GENERATOR "DEB") - set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) - set(CPACK_SET_DESTDIR ON) - set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON) - elseif(OS_FREEBSD) - option(ENABLE_CPACK_GENERATOR "Enable FreeBSD CPack generator (experimental)" OFF) - - if(ENABLE_CPACK_GENERATOR) - set(CPACK_GENERATOR "FreeBSD") - endif() - - set(CPACK_FREEBSD_PACKAGE_DEPS - "audio/fdk-aac" - "audio/jack" - "audio/pulseaudio" - "audio/sndio" - "audio/speexdsp" - "devel/cmake" - "devel/dbus" - "devel/jansson" - "devel/libsysinfo" - "devel/libudev-devd" - "devel/ninja" - "devel/pkgconf" - "devel/qt5-buildtools" - "devel/qt5-core" - "devel/qt5-qmake" - "devel/swig" - "ftp/curl" - "graphics/mesa-libs" - "graphics/qt5-imageformats" - "graphics/qt5-svg" - "lang/lua52" - "lang/luajit" - "lang/python37" - "multimedia/ffmpeg" - "multimedia/libv4l" - "multimedia/libx264" - "multimedia/v4l_compat" - "multimedia/vlc" - "print/freetype2" - "security/mbedtls" - "textproc/qt5-xml" - "x11/xorgproto" - "x11/libICE" - "x11/libSM" - "x11/libX11" - "x11/libxcb" - "x11/libXcomposite" - "x11/libXext" - "x11/libXfixes" - "x11/libXinerama" - "x11/libXrandr" - "x11-fonts/fontconfig" - "x11-toolkits/qt5-gui" - "x11-toolkits/qt5-widgets") - endif() - include(CPack) -endmacro() diff --git a/cmake/Modules/ObsDefaults_Windows.cmake b/cmake/Modules/ObsDefaults_Windows.cmake deleted file mode 100644 index ce9f27aa287877..00000000000000 --- a/cmake/Modules/ObsDefaults_Windows.cmake +++ /dev/null @@ -1,159 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# Enable modern cmake policies -if(POLICY CMP0009) - cmake_policy(SET CMP0009 NEW) -endif() - -if(POLICY CMP0011) - cmake_policy(SET CMP0011 NEW) -endif() - -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX - "${CMAKE_BINARY_DIR}/rundir" - CACHE STRING "Directory to install OBS after building" FORCE) -endif() - -# Enable building Windows modules with file descriptors -# https://github.com/obsproject/obs-studio/commit/51be039cf82fc347587d16b48f74e65e86bee301 -set(MODULE_DESCRIPTION "OBS Studio") - -macro(setup_obs_project) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_ARCH_SUFFIX 64) - else() - set(_ARCH_SUFFIX 32) - endif() - - set(OBS_OUTPUT_DIR "${CMAKE_BINARY_DIR}/rundir") - - set(OBS_EXECUTABLE_DESTINATION "bin/${_ARCH_SUFFIX}bit") - set(OBS_EXECUTABLE32_DESTINATION "bin/32bit") - set(OBS_EXECUTABLE64_DESTINATION "bin/64bit") - set(OBS_LIBRARY_DESTINATION "bin/${_ARCH_SUFFIX}bit") - set(OBS_LIBRARY32_DESTINATION "bin/32bit") - set(OBS_LIBRARY64_DESTINATION "bin/64bit") - - set(OBS_EXECUTABLE_EXPORT_DESTINATION "bin/${_ARCH_SUFFIX}bit") - set(OBS_LIBRARY_EXPORT_DESTINATION "bin/${_ARCH_SUFFIX}bit") - - set(OBS_PLUGIN_DESTINATION "obs-plugins/${_ARCH_SUFFIX}bit") - set(OBS_PLUGIN_PATH "../../${OBS_PLUGIN_DESTINATION}") - set(OBS_PLUGIN32_DESTINATION "obs-plugins/32bit") - set(OBS_PLUGIN64_DESTINATION "obs-plugins/64bit") - - set(OBS_INCLUDE_DESTINATION "include") - set(OBS_CMAKE_DESTINATION "cmake") - set(OBS_DATA_DESTINATION "data") - set(OBS_DATA_PATH "../../${OBS_DATA_DESTINATION}") - set(OBS_INSTALL_PREFIX "") - - set(OBS_SCRIPT_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-scripting/${_ARCH_SUFFIX}bit") - set(OBS_SCRIPT_PLUGIN_PATH "../../${OBS_SCRIPT_PLUGIN_DESTINATION}") - - string(REPLACE "-" ";" UI_VERSION_SPLIT ${OBS_VERSION}) - list(GET UI_VERSION_SPLIT 0 UI_VERSION) - string(REPLACE "." ";" UI_VERSION_SEMANTIC ${UI_VERSION}) - list(GET UI_VERSION_SEMANTIC 0 UI_VERSION_MAJOR) - list(GET UI_VERSION_SEMANTIC 1 UI_VERSION_MINOR) - list(GET UI_VERSION_SEMANTIC 2 UI_VERSION_PATCH) - - if(INSTALLER_RUN - AND NOT DEFINED ENV{OBS_InstallerTempDir} - AND NOT DEFINED ENV{obsInstallerTempDir}) - message(FATAL_ERROR "Environment variable obsInstallerTempDir is needed for multiarch installer generation") - endif() - - if(DEFINED ENV{OBS_DepsPath${_ARCH_SUFFIX}}) - set(DepsPath${_ARCH_SUFFIX} "$ENV{OBS_DepsPath${_ARCH_SUFFIX}}") - elseif(DEFINED ENV{OBS_DepsPath}) - set(DepsPath "$ENV{DepsPath}") - elseif(DEFINED ENV{DepsPath${_ARCH_SUFFIX}}) - set(DepsPath${_ARCH_SUFFIX} "$ENV{DepsPath${_ARCH_SUFFIX}}") - elseif(DEFINED ENV{DepsPath}) - set(DepsPath "$ENV{DepsPath}") - endif() - - if(DEFINED ENV{OBS_QTDIR${_ARCH_SUFFIX}}) - set(QTDIR${_ARCH_SUFFIX} "$ENV{OBS_QTDIR${_ARCH_SUFFIX}}") - elseif(DEFINED ENV{OBS_QTDIR}) - set(QTDIR "$ENV{OBS_QTDIR}") - elseif(DEFINED ENV{QTDIR${_ARCH_SUFFIX}}) - set(QTDIR${_ARCH_SUFFIX} "$ENV{QTDIR${_ARCH_SUFFIX}}") - elseif(DEFINED ENV{QTDIR}) - set(QTDIR "$ENV{QTDIR}") - endif() - - if(DEFINED DepsPath${_ARCH_SUFFIX}) - list(APPEND CMAKE_PREFIX_PATH "${DepsPath${_ARCH_SUFFIX}}" "${DepsPath${_ARCH_SUFFIX}}/bin") - elseif(DEFINED DepsPath) - list(APPEND CMAKE_PREFIX_PATH "${DepsPath}" "${DepsPath}/bin") - elseif(NOT DEFINED CMAKE_PREFIX_PATH) - message( - WARNING "No CMAKE_PREFIX_PATH set: OBS requires pre-built dependencies for building on Windows." - "Please download the appropriate obs-deps package for your architecture and set CMAKE_PREFIX_PATH " - "to the base directory and 'bin' directory inside it:\n" - "CMAKE_PREFIX_PATH=\"\"\n" - "Download pre-built OBS dependencies at https://github.com/obsproject/obs-deps/releases\n") - endif() - - if(DEFINED QTDIR${_ARCH_SUFFIX}) - list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_ARCH_SUFFIX}}") - elseif(DEFINED QTDIR) - list(APPEND CMAKE_PREFIX_PATH "${QTDIR}") - endif() - - if(DEFINED ENV{VLCPath}) - set(VLCPath "$ENV{VLCPath}") - elseif(DEFINED ENV{OBS_VLCPath}) - set(VLCPath "$ENV{OBS_VLCPath}") - endif() - - if(DEFINED VLCPath) - set(VLC_PATH "${VLCPath}") - endif() - - if(DEFINED ENV{CEF_ROOT_DIR}) - set(CEF_ROOT_DIR "$ENV{CEF_ROOT_DIR}") - elseif(DEFINED ENV{OBS_CEF_ROOT_DIR}) - set(CEF_ROOT_DIR "$ENV{OBS_CEF_ROOT_DIR}") - endif() - - if(DEFINED ENV{OBS_InstallerTempDir}) - file(TO_CMAKE_PATH "$ENV{OBS_InstallerTempDir}" _INSTALLER_TEMP_DIR) - elseif(DEFINED ENV{obsInstallerTempDir}) - file(TO_CMAKE_PATH "$ENV{obsInstallerTempDir}" _INSTALLER_TEMP_DIR) - endif() - - set(ENV{OBS_InstallerTempDir} "${_INSTALLER_TEMP_DIR}") - unset(_INSTALLER_TEMP_DIR) - - if(DEFINED ENV{OBS_AdditionalInstallFiles}) - file(TO_CMAKE_PATH "$ENV{OBS_AdditionalInstallFiles}" _ADDITIONAL_FILES) - elseif(DEFINED ENV{obsAdditionalInstallFiles}) - file(TO_CMAKE_PATH "$ENV{obsAdditionalInstallFiles}" _ADDITIONAL_FILES) - else() - set(_ADDITIONAL_FILES "${CMAKE_SOURCE_DIR}/additional_install_files") - endif() - - set(ENV{OBS_AdditionalInstallFiles} "${_ADDITIONAL_FILES}") - unset(_ADDITIONAL_FILES) - - list(APPEND CMAKE_INCLUDE_PATH "$ENV{OBS_AdditionalInstallFiles}/include${_ARCH_SUFFIX}" - "$ENV{OBS_AdditionalInstallFiles}/include") - - list( - APPEND - CMAKE_LIBRARY_PATH - "$ENV{OBS_AdditionalInstallFiles}/lib${_ARCH_SUFFIX}" - "$ENV{OBS_AdditionalInstallFiles}/lib" - "$ENV{OBS_AdditionalInstallFiles}/libs${_ARCH_SUFFIX}" - "$ENV{OBS_AdditionalInstallFiles}/libs" - "$ENV{OBS_AdditionalInstallFiles}/bin${_ARCH_SUFFIX}" - "$ENV{OBS_AdditionalInstallFiles}/bin") - - if(BUILD_FOR_DISTRIBUTION OR DEFINED ENV{CI}) - set_option(ENABLE_RTMPS ON) - endif() -endmacro() diff --git a/cmake/Modules/ObsDefaults_macOS.cmake b/cmake/Modules/ObsDefaults_macOS.cmake deleted file mode 100644 index e61d64d65686bd..00000000000000 --- a/cmake/Modules/ObsDefaults_macOS.cmake +++ /dev/null @@ -1,189 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -# Enable modern cmake policies -if(POLICY CMP0009) - cmake_policy(SET CMP0009 NEW) -endif() - -if(POLICY CMP0011) - cmake_policy(SET CMP0011 NEW) -endif() - -if(POLICY CMP0025) - cmake_policy(SET CMP0025 NEW) -endif() - -# Build options -if(NOT CMAKE_OSX_ARCHITECTURES) - set(CMAKE_OSX_ARCHITECTURES - "${CMAKE_HOST_SYSTEM_PROCESSOR}" - CACHE STRING "OBS build architecture for macOS - x86_64 required at least" FORCE) -endif() -set_property(CACHE CMAKE_OSX_ARCHITECTURES PROPERTY STRINGS arm64 x86_64 "x86_64;arm64") - -if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) - set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET[arch=arm64] "11.0") - set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET[arch=x86_64] "10.15") - - if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") - set(_MACOS_DEPLOYMENT_TARGET "11.0") - else() - set(_MACOS_DEPLOYMENT_TARGET "10.15") - endif() - - set(CMAKE_OSX_DEPLOYMENT_TARGET - "${_MACOS_DEPLOYMENT_TARGET}" - CACHE STRING "OBS deployment target for macOS - 10.15+ required" FORCE) - unset(_MACOS_DEPLOYMENT_TARGET) -endif() -set_property(CACHE CMAKE_OSX_DEPLOYMENT_TARGET PROPERTY STRINGS 10.15 11.0 12.0 13.0) - -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX - "${CMAKE_BINARY_DIR}/install" - CACHE STRING "Directory to install OBS after building" FORCE) -endif() - -if(NOT DEFINED CMAKE_PREFIX_PATH) - message( - WARNING "No CMAKE_PREFIX_PATH set: OBS supplies pre-built dependencies for building on macOS.\n" - "While OBS can be built using packages installed via Homebrew, pre-built dependencies " - "contain beneficial patches and fixes for use within OBS and is the suggested source " - "of these dependencies.\n" - "You can download the appropriate obs-deps package for your " - "architecture and set CMAKE_PREFIX_PATH to this directory:\n" - "CMAKE_PREFIX_PATH=\"\"\n" - "Download pre-built OBS dependencies at https://github.com/obsproject/obs-deps/releases\n") -endif() - -# SWIG hard codes the directory to its library directory at compile time. As obs-deps need to be relocatable, we need to -# force SWIG to look for its files in a directory relative to the PREFIX_PATH. The best way to ensure this is to set the -# SWIG_LIB environment variable. - -if(NOT DEFINED ENV{SWIG_LIB} AND EXISTS "${CMAKE_PREFIX_PATH}/bin/swig") - set(ENV{SWIG_LIB} "${CMAKE_PREFIX_PATH}/share/swig/CURRENT") -endif() - -macro(setup_obs_project) - set(CMAKE_XCODE_GENERATE_SCHEME ON) - - # Set code signing options - if(NOT OBS_BUNDLE_CODESIGN_TEAM) - set(OBS_BUNDLE_CODESIGN_TEAM - "" - CACHE STRING "OBS code signing team for macOS" FORCE) - if(NOT OBS_BUNDLE_CODESIGN_IDENTITY) - set(OBS_BUNDLE_CODESIGN_IDENTITY - "-" - CACHE STRING "OBS code signing identity for macOS" FORCE) - endif() - set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "${OBS_BUNDLE_CODESIGN_IDENTITY}") - else() - set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Automatic") - set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${OBS_BUNDLE_CODESIGN_TEAM}") - endif() - - set(OBS_CODESIGN_ENTITLEMENTS - "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/entitlements.plist" - CACHE INTERNAL "Path to codesign entitlements plist") - set(OBS_CODESIGN_LINKER - ON - CACHE BOOL "Enable linker code-signing on macOS (macOS 11+ required)") - - # Tell Xcode to pretend the linker signed binaries so that editing with install_name_tool preserves ad-hoc signatures. - # This option is supported by codesign on macOS 11 or higher. See CMake Issue 21854: - # https://gitlab.kitware.com/cmake/cmake/-/issues/21854 - - if(OBS_CODESIGN_LINKER) - set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed") - endif() - - # Set default options for bundling on macOS - set(CMAKE_MACOSX_RPATH ON) - set(CMAKE_SKIP_BUILD_RPATH OFF) - set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF) - set(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks/") - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF) - - # Set bundle parameters for cmake's automatic plist generation - set(MACOSX_BUNDLE_EXECUTABLE_NAME "OBS") - set(MACOSX_BUNDLE_BUNDLE_NAME "${OBS_PRODUCT_NAME}") - set(MACOSX_BUNDLE_BUNDLE_VERSION "${OBS_BUILD_NUMBER}") - set(MACOSX_BUNDLE_COPYRIGHT "${OBS_LEGAL_COPYRIGHT}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.obsproject.obs-studio") - set(MACOSX_BUNDLE_ICON_FILE "AppIcon") - set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${OBS_VERSION_CANONICAL}") - string(TIMESTAMP CURRENT_YEAR "%Y") - - # Set paths for distribution bundling - set(OBS_BUNDLE_NAME "OBS") - set(OBS_EXECUTABLE_DESTINATION "${CMAKE_INSTALL_BINDIR}") - set(OBS_INCLUDE_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/obs") - set(OBS_LIBRARY_DESTINATION "${CMAKE_INSTALL_LIBDIR}") - set(OBS_CMAKE_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake") - - if(BUILD_FOR_DISTRIBUTION) - set_option(CMAKE_BUILD_TYPE "Release") - set(CPACK_PACKAGE_VERSION "${OBS_VERSION_CANONICAL}") - else() - set(CPACK_PACKAGE_VERSION "${OBS_VERSION_CANONICAL}-${OBS_BUILD_NUMBER}") - endif() - - if(BUILD_FOR_DISTRIBUTION OR DEFINED ENV{CI}) - set_option(ENABLE_RTMPS ON) - endif() - - set(CPACK_PACKAGE_NAME "OBS") - set(CPACK_PACKAGE_VENDOR "${OBS_WEBSITE}") - set(CPACK_GENERATOR "DragNDrop") - set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${OBS_COMMENTS}") - - if(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64") - set(CPACK_ARCH_SUFFIX "Intel") - elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") - set(CPACK_ARCH_SUFFIX "Apple") - else() - set(CPACK_ARCH_SUFFIX "Universal") - endif() - - set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-macOS-${CPACK_ARCH_SUFFIX}") - - set(CPACK_COMPONENTS_ALL obs_app obs_frameworks obs_plugins obs_scripting_plugins obs_resources) - set(CPACK_COMPONENT_OBS_APP_DISPLAY_NAME "OBS Studio") - set(CPACK_COMPONENT_OBS_FRAMEWORKS_DISPLAY_NAME "OBS Frameworks") - set(CPACK_COMPONENT_OBS_PLUGINS_DISPLAY_NAME "OBS Plugins") - set(CPACK_COMPONENT_OBS_SCRIPTING_PLUGINS_DISPLAY_NAME "OBS Scripting Plugins") - set(CPACK_COMPONENT_OBS_RESOURCES_DISPLAY_NAME "OBS Resources") - - set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/background.tiff") - set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/AppIcon.icns") - get_filename_component(CPACK_DMG_BACKGROUND_FILENAME ${CPACK_DMG_BACKGROUND_IMAGE} NAME) - set(CPACK_DMG_FORMAT "UDZO") - set(CPACK_DMG_FILESYSTEM "APFS") - set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_BINARY_DIR}/package.applescript") - - set(_DMG_WINDOW_X "100") - set(_DMG_WINDOW_Y "100") - set(_DMG_WINDOW_WIDTH "540") - set(_DMG_WINDOW_HEIGHT "380") - set(_DMG_ICON_SIZE "96") - set(_DMG_TEXT_SIZE "16") - set(_DMG_OBS_X "124") - set(_DMG_OBS_Y "180") - set(_DMG_APP_LINK_X "416") - set(_DMG_APP_LINK_Y "180") - - configure_file("${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/package.applescript.in" - "${CMAKE_BINARY_DIR}/package.applescript" @ONLY) - - include(CPack) - - if(ENABLE_UI) - install( - CODE " - set(_BUNDLENAME \"$.app\") - if(EXISTS \"\${CMAKE_INSTALL_PREFIX}/\${_BUNDLENAME}\") - file(REMOVE_RECURSE \"\${CMAKE_INSTALL_PREFIX}/\${_BUNDLENAME}\") - endif()") - endif() -endmacro() diff --git a/cmake/Modules/ObsHelpers.cmake b/cmake/Modules/ObsHelpers.cmake deleted file mode 100644 index b4620ab56daf0b..00000000000000 --- a/cmake/Modules/ObsHelpers.cmake +++ /dev/null @@ -1,477 +0,0 @@ -# Set OS-specific constants in non-deprecated way -include(GNUInstallDirs) -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - include(ObsDefaults_macOS) - set(OS_MACOS ON) - set(OS_POSIX ON) -elseif(CMAKE_SYSTEM_NAME MATCHES "Linux|FreeBSD|OpenBSD") - include(ObsDefaults_Linux) - set(OS_POSIX ON) - string(TOUPPER "${CMAKE_SYSTEM_NAME}" _SYSTEM_NAME_U) - set(OS_${_SYSTEM_NAME_U} ON) -elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - include(ObsDefaults_Windows) - set(OS_WINDOWS ON) - set(OS_POSIX OFF) -endif() - -# Create global property to hold list of activated modules -set_property(GLOBAL PROPERTY OBS_MODULE_LIST "") - -# ###################################################################################################################### -# GLOBAL HELPER FUNCTIONS # -# ###################################################################################################################### - -# Helper function to set up runtime or library targets -function(setup_binary_target target) - # Set up installation paths for program install - install( - TARGETS ${target} - RUNTIME DESTINATION ${OBS_EXECUTABLE_DESTINATION} COMPONENT ${target}_Runtime - LIBRARY DESTINATION ${OBS_LIBRARY_DESTINATION} - COMPONENT ${target}_Runtime - NAMELINK_COMPONENT ${target}_Development - ARCHIVE DESTINATION ${OBS_LIBRARY_DESTINATION} COMPONENT ${target}_Development - PUBLIC_HEADER - DESTINATION ${OBS_INCLUDE_DESTINATION} - COMPONENT ${target}_Development - EXCLUDE_FROM_ALL) - - # Set up installation paths for development rundir - install( - TARGETS ${target} - RUNTIME DESTINATION ${OBS_EXECUTABLE_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - LIBRARY DESTINATION ${OBS_LIBRARY_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - PUBLIC_HEADER - DESTINATION ${OBS_INCLUDE_DESTINATION} - COMPONENT IGNORED - EXCLUDE_FROM_ALL) - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env DESTDIR= "${CMAKE_COMMAND}" --install .. --config $ --prefix - ${OBS_OUTPUT_DIR}/$ --component obs_${target} > "$,nul,/dev/null>" - COMMENT "Installing OBS rundir" - VERBATIM) - -endfunction() - -# Helper function to set up OBS plugin targets -function(setup_plugin_target target) - set_target_properties(${target} PROPERTIES PREFIX "") - - install( - TARGETS ${target} - RUNTIME DESTINATION ${OBS_PLUGIN_DESTINATION} COMPONENT ${target}_Runtime - LIBRARY DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime - NAMELINK_COMPONENT ${target}_Development) - - install( - TARGETS ${target} - RUNTIME DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - LIBRARY DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - setup_target_resources("${target}" "obs-plugins/${target}") - set_property(GLOBAL APPEND PROPERTY OBS_MODULE_LIST "${target}") - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env DESTDIR= "${CMAKE_COMMAND}" --install .. --config $ --prefix - ${OBS_OUTPUT_DIR}/$ --component obs_${target} > "$,nul,/dev/null>" - COMMENT "Installing ${target} to OBS rundir" - VERBATIM) - - obs_status(ENABLED "${target}") -endfunction() - -# Helper function to set up OBS scripting plugin targets -function(setup_script_plugin_target target) - install( - TARGETS ${target} - LIBRARY DESTINATION ${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime - NAMELINK_COMPONENT ${target}_Development) - - install( - TARGETS ${target} - LIBRARY DESTINATION ${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - if(${target} STREQUAL "obspython") - install( - FILES "$/$.py" - DESTINATION ${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime) - - install( - FILES "$/$.py" - DESTINATION ${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - endif() - set_property(GLOBAL APPEND PROPERTY OBS_SCRIPTING_MODULE_LIST "${target}") - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env DESTDIR= "${CMAKE_COMMAND}" --install .. --config $ --prefix - ${OBS_OUTPUT_DIR}/$ --component obs_${target} > "$,nul,/dev/null>" - COMMENT "Installing ${target} to OBS rundir" - VERBATIM) - - obs_status(ENABLED "${target}") -endfunction() - -# Helper function to set up target resources (e.g. L10N files) -function(setup_target_resources target destination) - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") - install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/ - DESTINATION ${OBS_DATA_DESTINATION}/${destination} - USE_SOURCE_PERMISSIONS - COMPONENT ${target}_Runtime) - - install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/ - DESTINATION ${OBS_DATA_DESTINATION}/${destination} - USE_SOURCE_PERMISSIONS - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - endif() -endfunction() - -# Helper function to set up specific resource files for targets -function(add_target_resource target resource destination) - install( - FILES ${resource} - DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT ${target}_Runtime) - - install( - FILES ${resource} - DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) -endfunction() - -# Helper function to set up OBS app target -function(setup_obs_app target) - setup_binary_target(${target}) - - get_property(OBS_MODULE_LIST GLOBAL PROPERTY OBS_MODULE_LIST) - list(LENGTH OBS_MODULE_LIST _LEN) - if(_LEN GREATER 0) - add_dependencies(${target} ${OBS_MODULE_LIST}) - endif() - - get_property(OBS_SCRIPTING_MODULE_LIST GLOBAL PROPERTY OBS_SCRIPTING_MODULE_LIST) - list(LENGTH OBS_SCRIPTING_MODULE_LIST _LEN) - if(_LEN GREATER 0) - add_dependencies(${target} ${OBS_SCRIPTING_MODULE_LIST}) - endif() - - if(TARGET OBS::browser) - setup_target_browser(${target}) - endif() - - if(TARGET OBS::ffmpeg-mux) - add_dependencies(${target} OBS::ffmpeg-mux) - endif() - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env DESTDIR= "${CMAKE_COMMAND}" --install .. --config $ --prefix - ${OBS_OUTPUT_DIR}/$ --component obs_rundir > "$,nul,/dev/null>" - COMMENT "Installing OBS rundir" - VERBATIM) -endfunction() - -# Helper function to do additional setup for browser source plugin -function(setup_target_browser target) - install( - DIRECTORY ${CEF_ROOT_DIR}/Resources/ - DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime) - - install( - DIRECTORY ${CEF_ROOT_DIR}/Release/ - DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime) - - install( - DIRECTORY ${CEF_ROOT_DIR}/Resources/ - DESTINATION ${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_rundir - EXCLUDE_FROM_ALL) - - install( - DIRECTORY ${CEF_ROOT_DIR}/Release/ - DESTINATION ${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_rundir - EXCLUDE_FROM_ALL) -endfunction() - -# Helper function to export target to build and install tree. Allows usage of `find_package(libobs)` by other build -# trees -function(export_target target) - set(CMAKE_EXPORT_PACKAGE_REGISTRY OFF) - - if(OS_LINUX OR OS_FREEBSD) - set(_EXCLUDE "") - else() - set(_EXCLUDE "EXCLUDE_FROM_ALL") - endif() - install( - TARGETS ${target} - EXPORT ${target}Targets - RUNTIME DESTINATION ${OBS_EXECUTABLE_DESTINATION} - COMPONENT obs_libraries - ${_EXCLUDE} - LIBRARY DESTINATION ${OBS_LIBRARY_DESTINATION} - COMPONENT obs_libraries - ${_EXCLUDE} - ARCHIVE DESTINATION ${OBS_LIBRARY_DESTINATION} - COMPONENT obs_libraries - ${_EXCLUDE} - INCLUDES - DESTINATION ${OBS_INCLUDE_DESTINATION} - PUBLIC_HEADER - DESTINATION ${OBS_INCLUDE_DESTINATION} - COMPONENT obs_libraries - ${_EXCLUDE}) - - get_target_property(target_type ${target} TYPE) - if(NOT target_type STREQUAL INTERFACE_LIBRARY) - include(GenerateExportHeader) - generate_export_header(${target} EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h) - - target_sources(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h) - endif() - - set(TARGETS_EXPORT_NAME "${target}Targets") - include(CMakePackageConfigHelpers) - configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${target}Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake - INSTALL_DESTINATION ${OBS_CMAKE_DESTINATION}/${target} - PATH_VARS OBS_PLUGIN_DESTINATION OBS_DATA_DESTINATION) - - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake - VERSION ${OBS_VERSION_CANONICAL} - COMPATIBILITY SameMajorVersion) - - export( - EXPORT ${target}Targets - FILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGETS_EXPORT_NAME}.cmake - NAMESPACE OBS::) - - export(PACKAGE "${target}") - - install( - EXPORT ${TARGETS_EXPORT_NAME} - FILE ${TARGETS_EXPORT_NAME}.cmake - NAMESPACE OBS:: - DESTINATION ${OBS_CMAKE_DESTINATION}/${target} - COMPONENT obs_libraries - ${_EXCLUDE}) - - install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake - DESTINATION ${OBS_CMAKE_DESTINATION}/${target} - COMPONENT obs_libraries - ${_EXCLUDE}) -endfunction() - -# Helper function to define available graphics modules for targets -function(define_graphic_modules target) - foreach(_GRAPHICS_API metal d3d11 opengl d3d9) - string(TOUPPER ${_GRAPHICS_API} _GRAPHICS_API_u) - if(TARGET OBS::libobs-${_GRAPHICS_API}) - if(OS_POSIX AND NOT LINUX_PORTABLE) - target_compile_definitions(${target} - PRIVATE DL_${_GRAPHICS_API_u}="$") - else() - target_compile_definitions(${target} - PRIVATE DL_${_GRAPHICS_API_u}="$") - endif() - add_dependencies(${target} OBS::libobs-${_GRAPHICS_API}) - else() - target_compile_definitions(${target} PRIVATE DL_${_GRAPHICS_API_u}="") - endif() - endforeach() -endfunction() - -macro(find_qt) - set(multiValueArgs COMPONENTS COMPONENTS_WIN COMPONENTS_MAC COMPONENTS_LINUX) - cmake_parse_arguments(FIND_QT "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - set(QT_NO_CREATE_VERSIONLESS_TARGETS ON) - find_package( - Qt6 - COMPONENTS Core - REQUIRED) - set(QT_NO_CREATE_VERSIONLESS_TARGETS OFF) - - if(OS_WINDOWS) - find_package( - Qt6 - COMPONENTS ${FIND_QT_COMPONENTS} ${FIND_QT_COMPONENTS_WIN} - REQUIRED) - elseif(OS_MACOS) - find_package( - Qt6 - COMPONENTS ${FIND_QT_COMPONENTS} ${FIND_QT_COMPONENTS_MAC} - REQUIRED) - else() - find_package( - Qt6 - COMPONENTS ${FIND_QT_COMPONENTS} ${FIND_QT_COMPONENTS_LINUX} - REQUIRED) - endif() - - list(APPEND FIND_QT_COMPONENTS "Core") - - if("Gui" IN_LIST FIND_QT_COMPONENTS_LINUX) - list(APPEND FIND_QT_COMPONENTS_LINUX "GuiPrivate") - endif() - - foreach(_COMPONENT IN LISTS FIND_QT_COMPONENTS FIND_QT_COMPONENTS_WIN FIND_QT_COMPONENTS_MAC FIND_QT_COMPONENTS_LINUX) - if(NOT TARGET Qt::${_COMPONENT} AND TARGET Qt6::${_COMPONENT}) - - add_library(Qt::${_COMPONENT} INTERFACE IMPORTED) - set_target_properties(Qt::${_COMPONENT} PROPERTIES INTERFACE_LINK_LIBRARIES "Qt6::${_COMPONENT}") - endif() - endforeach() -endmacro() - -# Idea adapted from: https://github.com/edsiper/cmake-options -macro(set_option option value) - set(${option} - ${value} - CACHE INTERNAL "") -endmacro() - -function(obs_status status text) - set(_OBS_STATUS_DISABLED "OBS: DISABLED ") - set(_OBS_STATUS_ENABLED "OBS: ENABLED ") - set(_OBS_STATUS "OBS: ") - if(status STREQUAL "DISABLED") - message(STATUS "${_OBS_STATUS_DISABLED}${text}") - elseif(status STREQUAL "ENABLED") - message(STATUS "${_OBS_STATUS_ENABLED}${text}") - else() - message(${status} "${_OBS_STATUS}${text}") - endif() -endfunction() - -if(OS_WINDOWS) - include(ObsHelpers_Windows) -elseif(OS_MACOS) - include(ObsHelpers_macOS) -elseif(OS_POSIX) - include(ObsHelpers_Linux) -endif() - -# ###################################################################################################################### -# LEGACY FALLBACKS # -# ###################################################################################################################### - -# Helper function to install OBS plugin with associated resource directory -function(_install_obs_plugin_with_data target source) - setup_plugin_target(${target}) - - if(NOT ${source} STREQUAL "data" - AND IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${source}" - AND NOT OS_MACOS) - install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${source}/ - DESTINATION ${OBS_DATA_DESTINATION}/obs-plugins/${target} - USE_SOURCE_PERMISSIONS - COMPONENT ${target}_Runtime) - - install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${source}/ - DESTINATION ${OBS_OUTPUT_DIR}/$/${OBS_DATA_DESTINATION}/obs-plugins/${target} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - if(OS_WINDOWS AND DEFINED ENV{obsInstallerTempDir}) - install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${source}/ - DESTINATION $ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/obs-plugins/${target} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - endif() - endif() -endfunction() - -# Helper function to install OBS plugin -function(_install_obs_plugin target) - setup_plugin_target(${target}) -endfunction() - -# Helper function to install data for a target only -function(_install_obs_datatarget target destination) - install( - TARGETS ${target} - LIBRARY DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT ${target}_Runtime - NAMELINK_COMPONENT ${target}_Development - RUNTIME DESTINATION ${OBS_DATA_DESTINATION}/${destination} COMPONENT ${target}_Runtime) - - install( - TARGETS ${target} - LIBRARY DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - RUNTIME DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - if(OS_WINDOWS) - if(MSVC) - add_target_resource(${target} "$" "${destination}" OPTIONAL) - endif() - - if(DEFINED ENV{obsInstallerTempDir}) - install( - TARGETS ${target} - RUNTIME - DESTINATION $ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/${destination}/$ - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - LIBRARY - DESTINATION $ENV{obsInstallerTempDir}/${OBS_DATA_DESTINATION}/${destination}/$ - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - endif() - endif() - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E env DESTDIR= "${CMAKE_COMMAND}" --install .. --config $ --prefix - ${OBS_OUTPUT_DIR}/$ --component obs_${target} > "$,nul,/dev/null>" - COMMENT "Installing ${target} to OBS rundir" - VERBATIM) -endfunction() - -# legacy_check: Macro to check for CMake framework version and include legacy list file -macro(legacy_check) - if(OBS_CMAKE_VERSION VERSION_LESS 3.0.0) - if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cmake/legacy.cmake) - include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/legacy.cmake) - endif() - return() - endif() -endmacro() diff --git a/cmake/Modules/ObsHelpers_Linux.cmake b/cmake/Modules/ObsHelpers_Linux.cmake deleted file mode 100644 index ced6c9ba3ec837..00000000000000 --- a/cmake/Modules/ObsHelpers_Linux.cmake +++ /dev/null @@ -1,104 +0,0 @@ -# Helper function to set up runtime or library targets -function(setup_binary_target target) - set_target_properties( - ${target} - PROPERTIES BUILD_RPATH - "${OBS_OUTPUT_DIR}/$/${OBS_EXECUTABLE_DESTINATION}$<$:/${_ARCH_SUFFIX}bit>") - - _setup_binary_target(${target}) -endfunction() - -# Helper function to export target to build and install tree Allows usage of `find_package(libobs)` by other build trees -function(export_target target) - _export_target(${ARGV}) - - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/pkgconfig") - export_target_pkgconf(${target}) - endif() -endfunction() - -# Helper function to build pkgconfig file for target -function(export_target_pkgconf target) - get_target_property(_TARGET_DEPENDENCIES ${target} INTERFACE_LINK_LIBRARIES) - get_target_property(_TARGET_DEFINITIONS ${target} INTERFACE_COMPILE_DEFINITIONS) - get_target_property(_TARGET_OPTIONS ${target} INTERFACE_COMPILE_OPTIONS) - - foreach(_LIBRARY IN LISTS _TARGET_DEPENDENCIES) - get_target_property(_LINK_LIBRARY ${_LIBRARY} INTERFACE_LINK_LIBRARIES) - get_target_property(_LINK_DEFINITIONS ${_LIBRARY} INTERFACE_COMPILE_DEFINITIONS) - get_target_property(_LINK_OPTIONS ${_LIBRARY} INTERFACE_COMPILE_OPTIONS) - - if(NOT "${_LINK_LIBRARY}" STREQUAL "_LINK_LIBRARY-NOTFOUND") - list(APPEND _LINKED_LIBRARIES "${_LINK_LIBRARY}") - endif() - - if(NOT "${_LINK_DEFINITIONS}" STREQUAL "_LINK_DEFINITIONS-NOTFOUND") - list(APPEND _LINKED_DEFINITIONS "${_LINK_DEFINITIONS}") - endif() - - if(NOT "${_LINK_OPTIONS}" STREQUAL "_LINK_OPTIONS-NOTFOUND") - list(APPEND _LINKED_OPTIONS "${_LINK_OPTIONS}") - endif() - endforeach() - - string(REPLACE ";" " " _LINKED_LIBRARIES "${_LINKED_LIBRARIES}") - string(REPLACE ";" " " _LINKED_DEFINITIONS "${_LINKED_DEFINITIONS}") - string(REPLACE ";" " " _LINKED_OPTIONS "${_LINKED_OPTIONS}") - - if(NOT "${_TARGET_DEFINITIONS}" STREQUAL "_TARGET_DEFINITIONS-NOTFOUND") - list(JOIN _TARGET_DEFINITIONS "-D" _TARGET_DEFINITIONS) - set(_TARGET_DEFINITIONS "-D${_TARGET_DEFINITIONS}") - else() - set(_TARGET_DEFINITIONS "") - endif() - - if(NOT "${_TARGET_OPTIONS}" STREQUAL "_TARGET_OPTIONS-NOTFOUND") - list(JOIN _TARGET_OPTIONS " " _TARGET_OPTIONS) - else() - set(_TARGET_OPTIONS "") - endif() - - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pkgconfig/${target}.pc.in" "${target}.pc" @ONLY) - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${target}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") -endfunction() - -# Helper function to install header files -function(install_headers target) - install( - DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${OBS_INCLUDE_DESTINATION} - COMPONENT obs_libraries - FILES_MATCHING - PATTERN "*.h" - PATTERN "*.hpp" - PATTERN "obs-hevc.h" EXCLUDE - PATTERN "*-windows.h" EXCLUDE - PATTERN "audio-monitoring" EXCLUDE - PATTERN "util/apple" EXCLUDE - PATTERN "util/windows" EXCLUDE - PATTERN "cmake" EXCLUDE - PATTERN "pkgconfig" EXCLUDE - PATTERN "data" EXCLUDE) - - if(ENABLE_PULSEAUDIO) - install( - FILES "${CMAKE_CURRENT_SOURCE_DIR}/audio-monitoring/pulse/pulseaudio-wrapper.h" - DESTINATION "${OBS_INCLUDE_DESTINATION}/audio-monitoring/pulse/" - COMPONENT obs_libraries) - endif() - - if(ENABLE_HEVC) - install( - FILES "${CMAKE_CURRENT_SOURCE_DIR}/obs-hevc.h" - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT obs_libraries) - endif() - - if(NOT EXISTS "${OBS_INCLUDE_DESTINATION}/obsconfig.h") - install( - FILES "${CMAKE_BINARY_DIR}/config/obsconfig.h" - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT obs_libraries) - endif() -endfunction() diff --git a/cmake/Modules/ObsHelpers_Windows.cmake b/cmake/Modules/ObsHelpers_Windows.cmake deleted file mode 100644 index b85751410466e5..00000000000000 --- a/cmake/Modules/ObsHelpers_Windows.cmake +++ /dev/null @@ -1,457 +0,0 @@ -# Helper function to set up runtime or library targets -function(setup_binary_target target) - _setup_binary_target(${ARGV}) - - if(DEFINED ENV{OBS_InstallerTempDir}) - install( - TARGETS ${target} - RUNTIME DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_EXECUTABLE_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - LIBRARY DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_LIBRARY_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - PUBLIC_HEADER - DESTINATION ${OBS_INCLUDE_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - if(MSVC) - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION - $ENV{OBS_InstallerTempDir}/$,EXECUTABLE>,${OBS_EXECUTABLE_DESTINATION},${OBS_LIBRARY_DESTINATION}> - COMPONENT obs_${target} - OPTIONAL EXCLUDE_FROM_ALL) - endif() - endif() - - if(MSVC) - target_link_options(${target} PRIVATE /PDBALTPATH:$) - - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION - $,EXECUTABLE>,${OBS_EXECUTABLE_DESTINATION},${OBS_LIBRARY_DESTINATION}> - COMPONENT ${target}_Runtime - OPTIONAL) - - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION - $,EXECUTABLE>,${OBS_EXECUTABLE_DESTINATION},${OBS_LIBRARY_DESTINATION}> - COMPONENT obs_${target} - OPTIONAL EXCLUDE_FROM_ALL) - endif() - - if(${target} STREQUAL "libobs") - setup_libobs_target(${target}) - endif() -endfunction() - -# Helper function to set up OBS plugin targets -function(setup_plugin_target target) - _setup_plugin_target(${ARGV}) - - if(MSVC) - target_link_options(${target} PRIVATE /PDBALTPATH:$) - - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime - OPTIONAL) - - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT obs_${target} - OPTIONAL EXCLUDE_FROM_ALL) - endif() - - if(DEFINED ENV{OBS_InstallerTempDir}) - install( - TARGETS ${target} - RUNTIME DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - LIBRARY DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - if(MSVC) - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_${target} - OPTIONAL EXCLUDE_FROM_ALL) - endif() - endif() -endfunction() - -# Helper function to set up OBS scripting plugin targets -function(setup_script_plugin_target target) - _setup_script_plugin_target(${ARGV}) - - if(MSVC) - target_link_options(${target} PRIVATE /PDBALTPATH:$) - - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION ${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - OPTIONAL EXCLUDE_FROM_ALL) - endif() - - if(DEFINED ENV{OBS_InstallerTempDir}) - install( - TARGETS ${target} - RUNTIME DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL - LIBRARY DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - - if(MSVC) - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - OPTIONAL EXCLUDE_FROM_ALL) - endif() - - if(${target} STREQUAL "obspython" AND ${_ARCH_SUFFIX} EQUAL 64) - install( - FILES "$/$.py" - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_SCRIPT_PLUGIN_DESTINATION} - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - endif() - endif() -endfunction() - -# Helper function to set up target resources (e.g. L10N files) -function(setup_target_resources target destination) - _setup_target_resources(${ARGV}) - - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") - if(${_ARCH_SUFFIX} EQUAL 64 AND DEFINED ENV{OBS_InstallerTempDir}) - - install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/ - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_DATA_DESTINATION}/${destination} - USE_SOURCE_PERMISSIONS - COMPONENT obs_${target} - EXCLUDE_FROM_ALL) - endif() - endif() -endfunction() - -# Helper function to set up specific resource files for targets -function(add_target_resource) - set(target ${ARGV0}) - set(resource ${ARGV1}) - set(destination ${ARGV2}) - if(${ARGC} EQUAL 4) - set(optional ${ARGV3}) - else() - set(optional "") - endif() - - install( - FILES ${resource} - DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT ${target}_Runtime - ${optional}) - - install( - FILES ${resource} - DESTINATION ${OBS_DATA_DESTINATION}/${destination} - COMPONENT obs_${target} - ${optional} EXCLUDE_FROM_ALL) - - if(DEFINED ENV{OBS_InstallerTempDir}) - install( - FILES ${resource} - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_DATA_DESTINATION}/${destination} - COMPONENT obs_${target} - ${optional} EXCLUDE_FROM_ALL) - endif() -endfunction() - -# Helper function to set up OBS app target -function(setup_obs_app target) - # detect outdated obs-browser submodule - if(NOT TARGET OBS::browser AND TARGET obs-browser) - if(MSVC) - target_compile_options(obs-browser PRIVATE $,/MTd,/MT>) - - target_compile_options(obs-browser-page PRIVATE $,/MTd,/MT>) - endif() - - target_link_options(obs-browser PRIVATE "LINKER:/IGNORE:4099") - - target_link_options(obs-browser-page PRIVATE "LINKER:/IGNORE:4099" "LINKER:/SUBSYSTEM:WINDOWS") - endif() - - _setup_obs_app(${ARGV}) - - if(MSVC) - include(CopyMSVCBins) - endif() -endfunction() - -# Helper function to export target to build and install tree. Allows usage of `find_package(libobs)` by other build -# trees -function(export_target target) - set(CMAKE_EXPORT_PACKAGE_REGISTRY OFF) - - install( - TARGETS ${target} - EXPORT ${target}Targets - RUNTIME DESTINATION "${OBS_EXECUTABLE_EXPORT_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL - LIBRARY DESTINATION "${OBS_LIBRARY_EXPORT_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL - ARCHIVE DESTINATION "${OBS_LIBRARY_EXPORT_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL - INCLUDES - DESTINATION "${OBS_INCLUDE_DESTINATION}" - PUBLIC_HEADER - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) - - get_target_property(target_type ${target} TYPE) - if(MSVC AND NOT target_type STREQUAL INTERFACE_LIBRARY) - install( - FILES $ - CONFIGURATIONS "RelWithDebInfo" "Debug" - DESTINATION "${OBS_EXECUTABLE_EXPORT_DESTINATION}" - COMPONENT obs_libraries - OPTIONAL EXCLUDE_FROM_ALL) - endif() - - include(GenerateExportHeader) - generate_export_header(${target} EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h") - - target_sources(${target} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h") - - set(TARGETS_EXPORT_NAME "${target}Targets") - include(CMakePackageConfigHelpers) - configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${target}Config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake" - INSTALL_DESTINATION ${OBS_CMAKE_DESTINATION} - PATH_VARS OBS_PLUGIN_DESTINATION OBS_DATA_DESTINATION) - - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake - VERSION ${OBS_VERSION_CANONICAL} - COMPATIBILITY SameMajorVersion) - - export( - EXPORT ${target}Targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGETS_EXPORT_NAME}.cmake" - NAMESPACE OBS::) - - export(PACKAGE "${target}") - - install( - EXPORT ${TARGETS_EXPORT_NAME} - FILE ${TARGETS_EXPORT_NAME}.cmake - NAMESPACE OBS:: - DESTINATION ${OBS_CMAKE_DESTINATION} - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) - - install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake - DESTINATION ${OBS_CMAKE_DESTINATION} - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) -endfunction() - -# Helper function to do additional setup for browser source plugin -function(setup_target_browser target) - install( - DIRECTORY ${CEF_ROOT_DIR}/Resources/ - DESTINATION ${OBS_PLUGIN_DESTINATION} - COMPONENT ${target}_Runtime) - - install( - DIRECTORY ${CEF_ROOT_DIR}/Resources/ - DESTINATION ${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_rundir - EXCLUDE_FROM_ALL) - - if(DEFINED ENV{OBS_InstallerTempDir}) - install( - DIRECTORY ${CEF_ROOT_DIR}/Resources/ - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_PLUGIN_DESTINATION} - COMPONENT obs_rundir - EXCLUDE_FROM_ALL) - endif() - - set(_ADDITIONAL_BROWSER_FILES - "libcef.dll" - "libEGL.dll" - "libGLESv2.dll" - "snapshot_blob.bin" - "v8_context_snapshot.bin" - "natives_blob.bin" - "chrome_elf.dll") - - foreach(_ADDITIONAL_BROWSER_FILE IN LISTS _ADDITIONAL_BROWSER_FILES) - list(REMOVE_ITEM _ADDITIONAL_BROWSER_FILES "${_ADDITIONAL_BROWSER_FILE}") - if(EXISTS "${CEF_ROOT_DIR}/Release/${_ADDITIONAL_BROWSER_FILE}") - list(APPEND _ADDITIONAL_BROWSER_FILES "${CEF_ROOT_DIR}/Release/${_ADDITIONAL_BROWSER_FILE}") - endif() - endforeach() - - install( - FILES ${_ADDITIONAL_BROWSER_FILES} - DESTINATION ${OBS_PLUGIN_DESTINATION}/ - COMPONENT ${target}_Runtime) - - install( - FILES ${_ADDITIONAL_BROWSER_FILES} - DESTINATION ${OBS_OUTPUT_DIR}/$/${OBS_PLUGIN_DESTINATION}/ - COMPONENT obs_rundir - EXCLUDE_FROM_ALL) - - if(DEFINED ENV{OBS_InstallerTempDir}) - install( - FILES ${_ADDITIONAL_BROWSER_FILES} - DESTINATION $ENV{OBS_InstallerTempDir}/${OBS_PLUGIN_DESTINATION}/ - COMPONENT obs_rundir - EXCLUDE_FROM_ALL) - endif() -endfunction() - -# Helper function to gather external libraries depended-on by libobs -function(setup_libobs_target target) - set(_ADDITIONAL_FILES "${CMAKE_SOURCE_DIR}/additional_install_files") - - if(DEFINED ENV{OBS_AdditionalInstallFiles}) - set(_ADDITIONAL_FILES "$ENV{OBS_AdditionalInstallFiles}") - endif() - - if(NOT INSTALLER_RUN) - list(APPEND _LIBOBS_FIXUPS "misc:." "data:${OBS_DATA_DESTINATION}" "libs${_ARCH_SUFFIX}:${OBS_LIBRARY_DESTINATION}" - "exec${_ARCH_SUFFIX}:${OBS_EXECUTABLE_DESTINATION}") - else() - list( - APPEND - _LIBOBS_FIXUPS - "misc:." - "data:${OBS_DATA_DESTINATION}" - "libs32:${OBS_LIBRARY32_DESTINATION}" - "libs64:${OBS_LIBRARY64_DESTINATION}" - "exec32:${OBS_EXECUTABLE32_DESTINATION}" - "exec64:${OBS_EXECUTABLE64_DESTINATION}") - endif() - - foreach(_FIXUP IN LISTS _LIBOBS_FIXUPS) - string(REPLACE ":" ";" _FIXUP ${_FIXUP}) - list(GET _FIXUP 0 _SOURCE) - list(GET _FIXUP 1 _DESTINATION) - - install( - DIRECTORY ${_ADDITIONAL_FILES}/${_SOURCE}/ - DESTINATION ${_DESTINATION} - USE_SOURCE_PERMISSIONS - COMPONENT ${target}_Runtime - PATTERN ".gitignore" EXCLUDE) - - install( - DIRECTORY ${_ADDITIONAL_FILES}/${_SOURCE}/ - DESTINATION ${_DESTINATION} - USE_SOURCE_PERMISSIONS - COMPONENT obs_rundir - EXCLUDE_FROM_ALL - PATTERN ".gitignore" EXCLUDE) - - if(_SOURCE MATCHES "(libs|exec)(32|64)?") - install( - DIRECTORY ${_ADDITIONAL_FILES}/${_SOURCE}$,d,r>/ - DESTINATION ${_DESTINATION} - USE_SOURCE_PERMISSIONS - COMPONENT ${target}_Runtime - PATTERN ".gitignore" EXCLUDE) - - install( - DIRECTORY ${_ADDITIONAL_FILES}/${_SOURCE}$,d,r>/ - DESTINATION ${_DESTINATION} - USE_SOURCE_PERMISSIONS - COMPONENT obs_rundir - EXCLUDE_FROM_ALL - PATTERN ".gitignore" EXCLUDE) - endif() - endforeach() -endfunction() - -# Helper function to compile artifacts for multi-architecture installer -function(generate_multiarch_installer) - if(NOT DEFINED ENV{OBS_InstallerTempDir} AND NOT DEFINED ENV{obsInstallerTempDir}) - obs_status(FATAL_ERROR - "Function generate_multiarch_installer requires environment variable 'OBS_InstallerTempDir' to be set") - endif() - - add_custom_target(installer_files ALL) - - setup_libobs_target(installer_files) - - install( - DIRECTORY "$ENV{OBS_InstallerTempDir}/" - DESTINATION "." - USE_SOURCE_PERMISSIONS) -endfunction() - -# Helper function to install header files -function(install_headers target) - install( - DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${OBS_INCLUDE_DESTINATION} - COMPONENT obs_libraries - EXCLUDE_FROM_ALL FILES_MATCHING - PATTERN "*.h" - PATTERN "*.hpp" - PATTERN "obs-hevc.h" EXCLUDE - PATTERN "obs-nix-*.h" EXCLUDE - PATTERN "*-posix.h" EXCLUDE - PATTERN "audio-monitoring/null" EXCLUDE - PATTERN "audio-monitoring/osx" EXCLUDE - PATTERN "audio-monitoring/pulse" EXCLUDE - PATTERN "util/apple" EXCLUDE - PATTERN "cmake" EXCLUDE - PATTERN "pkgconfig" EXCLUDE - PATTERN "data" EXCLUDE) - - if(ENABLE_HEVC) - install( - FILES "${CMAKE_CURRENT_SOURCE_DIR}/obs-hevc.h" - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) - endif() - - if(NOT EXISTS "${OBS_INCLUDE_DESTINATION}/obsconfig.h") - install( - FILES "${CMAKE_BINARY_DIR}/config/obsconfig.h" - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) - endif() -endfunction() diff --git a/cmake/Modules/ObsHelpers_macOS.cmake b/cmake/Modules/ObsHelpers_macOS.cmake deleted file mode 100644 index 91a7af183b82ce..00000000000000 --- a/cmake/Modules/ObsHelpers_macOS.cmake +++ /dev/null @@ -1,457 +0,0 @@ -# Helper function to set up runtime or library targets -function(setup_binary_target target) - set_target_properties( - ${target} - PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.obsproject.${target}" - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/entitlements.plist") - - set(MACOSX_PLUGIN_BUNDLE_NAME - "${target}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_GUI_IDENTIFIER - "com.obsproject.${target}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_BUNDLE_VERSION - "${MACOSX_BUNDLE_BUNDLE_VERSION}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_SHORT_VERSION_STRING - "${MACOSX_BUNDLE_SHORT_VERSION_STRING}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_EXECUTABLE_NAME - "${target}" - PARENT_SCOPE) - - if(${target} STREQUAL libobs) - setup_framework_target(${target}) - set_property(GLOBAL APPEND PROPERTY OBS_FRAMEWORK_LIST "${target}") - elseif(NOT ${target} STREQUAL obs-ffmpeg-mux AND NOT ${target} STREQUAL mac-dal-plugin) - set_property(GLOBAL APPEND PROPERTY OBS_FRAMEWORK_LIST "${target}") - endif() -endfunction() - -# Helper function to set-up framework targets on macOS -function(setup_framework_target target) - set_target_properties( - ${target} - PROPERTIES FRAMEWORK ON - FRAMEWORK_VERSION A - OUTPUT_NAME "${target}" - MACOSX_FRAMEWORK_IDENTIFIER "com.obsproject.${target}" - MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/Plugin-Info.plist.in" - XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.obsproject.${target}") - - install( - TARGETS ${target} - EXPORT "${target}Targets" - FRAMEWORK DESTINATION "Frameworks" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL - INCLUDES - DESTINATION Frameworks/$.framework/Headers - PUBLIC_HEADER - DESTINATION Frameworks/$.framework/Headers - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) -endfunction() - -# Helper function to set up OBS plugin targets -function(setup_plugin_target target) - set(MACOSX_PLUGIN_BUNDLE_NAME - "${target}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_GUI_IDENTIFIER - "com.obsproject.${target}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_BUNDLE_VERSION - "${MACOSX_BUNDLE_BUNDLE_VERSION}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_SHORT_VERSION_STRING - "${MACOSX_BUNDLE_SHORT_VERSION_STRING}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_EXECUTABLE_NAME - "${target}" - PARENT_SCOPE) - set(MACOSX_PLUGIN_BUNDLE_TYPE - "BNDL" - PARENT_SCOPE) - - set_target_properties( - ${target} - PROPERTIES BUNDLE ON - BUNDLE_EXTENSION "plugin" - OUTPUT_NAME "${target}" - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/Plugin-Info.plist.in" - XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.obsproject.${target}" - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/entitlements.plist") - - set_property(GLOBAL APPEND PROPERTY OBS_MODULE_LIST "${target}") - obs_status(ENABLED "${target}") - - install_bundle_resources(${target}) -endfunction() - -# Helper function to set up OBS scripting plugin targets -function(setup_script_plugin_target target) - set_target_properties( - ${target} - PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.obsproject.${target}" - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/entitlements.plist") - - set_property(GLOBAL APPEND PROPERTY OBS_SCRIPTING_MODULE_LIST "${target}") - obs_status(ENABLED "${target}") -endfunction() - -# Helper function to set up target resources (e.g. L10N files) -function(setup_target_resources target destination) - install_bundle_resources(${target}) -endfunction() - -# Helper function to set up plugin resources inside plugin bundle -function(install_bundle_resources target) - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") - file(GLOB_RECURSE _DATA_FILES "${CMAKE_CURRENT_SOURCE_DIR}/data/*") - foreach(_DATA_FILE IN LISTS _DATA_FILES) - file(RELATIVE_PATH _RELATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/data/ ${_DATA_FILE}) - get_filename_component(_RELATIVE_PATH "${_RELATIVE_PATH}" PATH) - target_sources(${target} PRIVATE ${_DATA_FILE}) - set_source_files_properties(${_DATA_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/${_RELATIVE_PATH}") - string(REPLACE "\\" "\\\\" _GROUP_NAME "${_RELATIVE_PATH}") - source_group("Resources\\${_GROUP_NAME}" FILES ${_DATA_FILE}) - endforeach() - endif() -endfunction() - -# Helper function to set up specific resource files for targets -function(add_target_resource target resource destination) - target_sources(${target} PRIVATE ${resource}) - set_source_files_properties(${resource} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) -endfunction() - -# Helper function to set up OBS app target -function(setup_obs_app target) - set_target_properties( - ${target} - PROPERTIES BUILD_WITH_INSTALL_RPATH OFF - XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/entitlements.plist" - XCODE_SCHEME_ENVIRONMENT "PYTHONDONTWRITEBYTECODE=1") - - install(TARGETS ${target} BUNDLE DESTINATION "." COMPONENT obs_app) - - if(TARGET OBS::browser) - setup_target_browser(${target}) - endif() - - setup_obs_frameworks(${target}) - setup_obs_modules(${target}) - setup_obs_bundle(${target}) -endfunction() - -# Helper function to do additional setup for browser source plugin -function(setup_target_browser target) - get_filename_component(_CEF_FRAMEWORK_NAME "${CEF_LIBRARY}" NAME) - - install( - DIRECTORY "${CEF_LIBRARY}" - DESTINATION "Frameworks" - USE_SOURCE_PERMISSIONS - COMPONENT obs_browser_dev - EXCLUDE_FROM_ALL) - - foreach(_CEF_LIBRARY IN ITEMS "libEGL" "libswiftshader_libEGL" "libGLESv2" "libswiftshader_libGLESv2" - "libvk_swiftshader") - set(_COMMAND - "/usr/bin/codesign --force --sign \\\"${OBS_BUNDLE_CODESIGN_IDENTITY}\\\" $<$:--options linker-signed > \\\"\${CMAKE_INSTALL_PREFIX}/Frameworks/${_CEF_FRAMEWORK_NAME}/Libraries/${_CEF_LIBRARY}.dylib\\\"" - ) - list(APPEND _CEF_CODESIGN_COMMANDS "execute_process(COMMAND /bin/sh -c \"${_COMMAND}\")") - endforeach() - - set(_COMMAND - "/usr/bin/codesign --force --sign \\\"${OBS_BUNDLE_CODESIGN_IDENTITY}\\\" $<$:--options linker-signed > --deep \\\"\${CMAKE_INSTALL_PREFIX}/Frameworks/${_CEF_FRAMEWORK_NAME}/Chromium Embedded Framework\\\"" - ) - - list(APPEND _CEF_CODESIGN_COMMANDS "execute_process(COMMAND /bin/sh -c \"${_COMMAND}\")") - string(REPLACE ";" "\n " _CEF_CODESIGN_COMMANDS "${_CEF_CODESIGN_COMMANDS}") - install( - CODE "${_CEF_CODESIGN_COMMANDS}" - COMPONENT obs_browser_dev - EXCLUDE_FROM_ALL) - - foreach(_SUFFIX IN ITEMS "_gpu" "_plugin" "_renderer" "") - if(TARGET OBS::browser-helper${_SUFFIX}) - add_dependencies(${target} OBS::browser-helper${_SUFFIX}) - - install( - DIRECTORY "$" - DESTINATION "Frameworks" - USE_SOURCE_PERMISSIONS - COMPONENT obs_browser_dev - EXCLUDE_FROM_ALL) - - if(NOT XCODE) - set(_COMMAND - "/usr/bin/codesign --force --sign \\\"${OBS_BUNDLE_CODESIGN_IDENTITY}\\\" $<$:--options linker-signed > \\\"\${CMAKE_INSTALL_PREFIX}/Frameworks/$.app\\\" > /dev/null" - ) - - install( - CODE "execute_process(COMMAND /bin/sh -c \"${_COMMAND}\")" - COMPONENT obs_browser_dev - EXCLUDE_FROM_ALL) - endif() - endif() - endforeach() - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" --install . --config $ --prefix $ - --component obs_browser_dev > /dev/null - COMMENT "Installing Chromium Embedded Framework for development" - VERBATIM) -endfunction() - -# Helper function to set-up OBS frameworks for macOS bundling -function(setup_obs_frameworks target) - get_property(OBS_FRAMEWORK_LIST GLOBAL PROPERTY OBS_FRAMEWORK_LIST) - install( - TARGETS ${OBS_FRAMEWORK_LIST} - RUNTIME DESTINATION "$.app/Contents/Frameworks/" COMPONENT obs_frameworks - LIBRARY DESTINATION "$.app/Contents/Frameworks/" COMPONENT obs_frameworks - FRAMEWORK DESTINATION "$.app/Contents/Frameworks/" COMPONENT obs_frameworks - PUBLIC_HEADER - DESTINATION "${OBS_INCLUDE_DESTINATION}" - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) -endfunction() - -# Helper function to set-up OBS plugins and helper binaries for macOS bundling -function(setup_obs_modules target) - - get_property(OBS_MODULE_LIST GLOBAL PROPERTY OBS_MODULE_LIST) - list(LENGTH OBS_MODULE_LIST _LEN) - if(_LEN GREATER 0) - add_dependencies(${target} ${OBS_MODULE_LIST}) - - install( - TARGETS ${OBS_MODULE_LIST} - LIBRARY DESTINATION "PlugIns" - COMPONENT obs_plugin_dev - EXCLUDE_FROM_ALL) - - install( - TARGETS ${OBS_MODULE_LIST} - LIBRARY DESTINATION $.app/Contents/PlugIns - COMPONENT obs_plugins - NAMELINK_COMPONENT ${target}_Development) - endif() - - get_property(OBS_SCRIPTING_MODULE_LIST GLOBAL PROPERTY OBS_SCRIPTING_MODULE_LIST) - list(LENGTH OBS_SCRIPTING_MODULE_LIST _LEN) - if(_LEN GREATER 0) - add_dependencies(${target} ${OBS_SCRIPTING_MODULE_LIST}) - - install( - TARGETS ${OBS_SCRIPTING_MODULE_LIST} - LIBRARY DESTINATION "PlugIns" - COMPONENT obs_plugin_dev - EXCLUDE_FROM_ALL) - - if(TARGET obspython) - install( - FILES "$/obspython.py" - DESTINATION "Resources" - COMPONENT obs_plugin_dev - EXCLUDE_FROM_ALL) - endif() - - install(TARGETS ${OBS_SCRIPTING_MODULE_LIST} LIBRARY DESTINATION $.app/Contents/PlugIns - COMPONENT obs_scripting_plugins) - endif() - - if(TARGET obs-ffmpeg-mux) - add_dependencies(${target} obs-ffmpeg-mux) - - install(TARGETS obs-ffmpeg-mux RUNTIME DESTINATION $.app/Contents/MacOS - COMPONENT obs_plugins) - - install( - PROGRAMS $ - DESTINATION "MacOS" - COMPONENT obs_plugin_dev - EXCLUDE_FROM_ALL) - - set(_COMMAND - "/usr/bin/codesign --force --sign \\\"${OBS_BUNDLE_CODESIGN_IDENTITY}\\\" $<$:--options linker-signed > \\\"\${CMAKE_INSTALL_PREFIX}/MacOS/$\\\" > /dev/null" - ) - - install( - CODE "execute_process(COMMAND /bin/sh -c \"${_COMMAND}\")" - COMPONENT obs_plugin_dev - EXCLUDE_FROM_ALL) - - endif() - - if(TARGET mac-dal-plugin) - add_dependencies(${target} mac-dal-plugin) - - install( - TARGETS mac-dal-plugin - LIBRARY DESTINATION "Resources" - COMPONENT obs_plugin_dev - EXCLUDE_FROM_ALL) - endif() - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND "${CMAKE_COMMAND}" --install .. --config $ --prefix $ - --component obs_plugin_dev > /dev/null - COMMENT "Installing OBS plugins for development" - VERBATIM) -endfunction() - -# Helper function to finalize macOS app bundles -function(setup_obs_bundle target) - install( - CODE " - set(_DEPENDENCY_PREFIX \"${CMAKE_PREFIX_PATH}\") - set(_BUILD_FOR_DISTRIBUTION \"${BUILD_FOR_DISTRIBUTION}\") - set(_BUNDLENAME \"$.app\") - set(_BUNDLER_COMMAND \"${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/dylibbundler\") - set(_CODESIGN_IDENTITY \"${OBS_BUNDLE_CODESIGN_IDENTITY}\") - set(_CODESIGN_ENTITLEMENTS \"${CMAKE_SOURCE_DIR}/cmake/bundle/macOS\")" - COMPONENT obs_resources) - - if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY) - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND - /bin/sh -c - "plutil -replace SUFeedURL -string ${SPARKLE_APPCAST_URL} \"$/Info.plist\"" - VERBATIM) - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND - /bin/sh -c - "plutil -replace SUPublicEDKey -string \"${SPARKLE_PUBLIC_KEY}\" \"$/Info.plist\"" - VERBATIM) - - install( - DIRECTORY ${SPARKLE} - DESTINATION $.app/Contents/Frameworks - USE_SOURCE_PERMISSIONS - COMPONENT obs_frameworks) - endif() - - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND - /usr/bin/sed -i '' 's/font-size: 10pt\;/font-size: 12pt\;/' - "$/Resources/themes/Acri.qss" - "$/Resources/themes/Grey.qss" - "$/Resources/themes/Light.qss" - "$/Resources/themes/Rachni.qss" - "$/Resources/themes/Yami.qss") - - install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/bundle/macOS/bundleutils.cmake" COMPONENT obs_resources) -endfunction() - -# Helper function to export target to build and install tree Allows usage of `find_package(libobs)` by other build trees -function(export_target target) - get_target_property(_IS_FRAMEWORK ${target} FRAMEWORK) - - set(OBS_PLUGIN_DESTINATION "") - set(OBS_DATA_DESTINATION "") - - if(_IS_FRAMEWORK) - export_framework_target(${target}) - else() - _export_target(${ARGV}) - endif() - set_target_properties(${target} PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h") -endfunction() - -# Helper function to export macOS framework targets -function(export_framework_target) - set(CMAKE_EXPORT_PACKAGE_REGISTRY OFF) - - include(GenerateExportHeader) - generate_export_header(${target} EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h") - - target_sources(${target} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h") - - set(TARGETS_EXPORT_NAME "${target}Targets") - include(CMakePackageConfigHelpers) - configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/${target}Config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake" - INSTALL_DESTINATION Frameworks/${target}.framework/Resources/cmake - PATH_VARS OBS_PLUGIN_DESTINATION OBS_DATA_DESTINATION) - - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake - VERSION ${OBS_VERSION_CANONICAL} - COMPATIBILITY SameMajorVersion) - - export( - EXPORT ${target}Targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/${TARGETS_EXPORT_NAME}.cmake" - NAMESPACE OBS::) - - export(PACKAGE "${target}") - - install( - EXPORT ${TARGETS_EXPORT_NAME} - FILE ${TARGETS_EXPORT_NAME}.cmake - NAMESPACE OBS:: - DESTINATION Frameworks/${target}.framework/Resources/cmake - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) - - install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake - DESTINATION Frameworks/$.framework/Resources/cmake - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) -endfunction() - -# Helper function to install header files -function(install_headers target) - install( - DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION - $>,Frameworks/$.framework/Headers,${OBS_INCLUDE_DESTINATION}> - COMPONENT obs_libraries - EXCLUDE_FROM_ALL FILES_MATCHING - PATTERN "*.h" - PATTERN "*.hpp" - PATTERN "obs-hevc.h" EXCLUDE - PATTERN "*-windows.h" EXCLUDE - PATTERN "*-x11.h" EXCLUDE - PATTERN "*-wayland.h" EXCLUDE - PATTERN "audio-monitoring/null" EXCLUDE - PATTERN "audio-monitoring/win32" EXCLUDE - PATTERN "audio-monitoring/pulse" EXCLUDE - PATTERN "util/windows" EXCLUDE - PATTERN "cmake" EXCLUDE - PATTERN "pkgconfig" EXCLUDE - PATTERN "data" EXCLUDE) - - if(ENABLE_HEVC) - install( - FILES "${CMAKE_CURRENT_SOURCE_DIR}/obs-hevc.h" - DESTINATION - $>,Frameworks/$.framework/Headers,${OBS_INCLUDE_DESTINATION}> - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) - endif() - - install( - FILES "${CMAKE_BINARY_DIR}/config/obsconfig.h" - DESTINATION - $>,Frameworks/$.framework/Headers,${OBS_INCLUDE_DESTINATION}> - COMPONENT obs_libraries - EXCLUDE_FROM_ALL) -endfunction() diff --git a/cmake/Modules/VersionConfig.cmake b/cmake/Modules/VersionConfig.cmake deleted file mode 100644 index b1d5dd1b977424..00000000000000 --- a/cmake/Modules/VersionConfig.cmake +++ /dev/null @@ -1,118 +0,0 @@ -set(OBS_COMPANY_NAME "OBS Project") -set(OBS_PRODUCT_NAME "OBS Studio") -set(OBS_WEBSITE "https://www.obsproject.com") -set(OBS_COMMENTS "Free and open source software for video recording and live streaming") -set(OBS_LEGAL_COPYRIGHT "(C) Lain Bailey") - -# Configure default version strings -set(_OBS_DEFAULT_VERSION "0" "0" "1") -set(_OBS_RELEASE_CANDIDATE "0" "0" "0" "0") -set(_OBS_BETA "0" "0" "0" "0") - -# Set full and canonical OBS version from current git tag or manual override -if(NOT DEFINED OBS_VERSION_OVERRIDE) - if(NOT DEFINED RELEASE_CANDIDATE - AND NOT DEFINED BETA - AND EXISTS "${CMAKE_SOURCE_DIR}/.git") - execute_process( - COMMAND git describe --always --tags --dirty=-modified - OUTPUT_VARIABLE _OBS_VERSION - ERROR_VARIABLE _GIT_DESCRIBE_ERR - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE _OBS_VERSION_RESULT - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(_GIT_DESCRIBE_ERR) - message(FATAL_ERROR "Could not fetch OBS version tag from git.\n" ${_GIT_DESCRIBE_ERR}) - endif() - - if(_OBS_VERSION_RESULT EQUAL 0) - if(${_OBS_VERSION} MATCHES "rc[0-9]+$") - set(RELEASE_CANDIDATE ${_OBS_VERSION}) - elseif(${_OBS_VERSION} MATCHES "beta[0-9]+$") - set(BETA ${_OBS_VERSION}) - else() - string(REPLACE "-" "." _CANONICAL_SPLIT ${_OBS_VERSION}) - string(REPLACE "." ";" _CANONICAL_SPLIT ${_CANONICAL_SPLIT}) - list(GET _CANONICAL_SPLIT 0 1 2 _OBS_VERSION_CANONICAL) - string(REPLACE "." ";" _OBS_VERSION ${_OBS_VERSION}) - # Get 8-character commit hash without "g" prefix - foreach(VERSION_PART ${_CANONICAL_SPLIT}) - if(VERSION_PART MATCHES "^g") - string(SUBSTRING ${VERSION_PART}, 1, 8, OBS_COMMIT) - break() - endif() - endforeach() - endif() - endif() - endif() - - # Set release candidate version information Must be a string in the format of "x.x.x-rcx" - if(DEFINED RELEASE_CANDIDATE) - string(REPLACE "-rc" "." _OBS_RELEASE_CANDIDATE ${RELEASE_CANDIDATE}) - string(REPLACE "." ";" _OBS_VERSION ${RELEASE_CANDIDATE}) - string(REPLACE "." ";" _OBS_RELEASE_CANDIDATE ${_OBS_RELEASE_CANDIDATE}) - list(GET _OBS_RELEASE_CANDIDATE 0 1 2 _OBS_VERSION_CANONICAL) - # Set beta version information Must be a string in the format of "x.x.x-betax" - elseif(DEFINED BETA) - string(REPLACE "-beta" "." _OBS_BETA ${BETA}) - string(REPLACE "." ";" _OBS_VERSION ${BETA}) - string(REPLACE "." ";" _OBS_BETA ${_OBS_BETA}) - list(GET _OBS_BETA 0 1 2 _OBS_VERSION_CANONICAL) - elseif(NOT DEFINED _OBS_VERSION) - set(_OBS_VERSION ${_OBS_DEFAULT_VERSION}) - set(_OBS_VERSION_CANONICAL ${_OBS_DEFAULT_VERSION}) - endif() -else() - string(REPLACE "." ";" _OBS_VERSION "${OBS_VERSION_OVERRIDE}") - string(REPLACE "-" ";" _OBS_VERSION_CANONICAL "${OBS_VERSION_OVERRIDE}") - list(GET _OBS_VERSION_CANONICAL 0 _OBS_VERSION_CANONICAL) - string(REPLACE "." ";" _OBS_VERSION_CANONICAL "${_OBS_VERSION_CANONICAL}") -endif() - -list(GET _OBS_VERSION_CANONICAL 0 OBS_VERSION_MAJOR) -list(GET _OBS_VERSION_CANONICAL 1 OBS_VERSION_MINOR) -list(GET _OBS_VERSION_CANONICAL 2 OBS_VERSION_PATCH) -list(GET _OBS_RELEASE_CANDIDATE 0 OBS_RELEASE_CANDIDATE_MAJOR) -list(GET _OBS_RELEASE_CANDIDATE 1 OBS_RELEASE_CANDIDATE_MINOR) -list(GET _OBS_RELEASE_CANDIDATE 2 OBS_RELEASE_CANDIDATE_PATCH) -list(GET _OBS_RELEASE_CANDIDATE 3 OBS_RELEASE_CANDIDATE) -list(GET _OBS_BETA 0 OBS_BETA_MAJOR) -list(GET _OBS_BETA 1 OBS_BETA_MINOR) -list(GET _OBS_BETA 2 OBS_BETA_PATCH) -list(GET _OBS_BETA 3 OBS_BETA) - -string(REPLACE ";" "." OBS_VERSION_CANONICAL "${_OBS_VERSION_CANONICAL}") -string(REPLACE ";" "." OBS_VERSION "${_OBS_VERSION}") - -if(OBS_RELEASE_CANDIDATE GREATER 0) - message( - AUTHOR_WARNING - "******************************************************************************\n" - " + OBS-Studio - Release candidate detected, OBS_VERSION is now: ${OBS_VERSION}\n" - "******************************************************************************") -elseif(OBS_BETA GREATER 0) - message( - AUTHOR_WARNING - "******************************************************************************\n" - " + OBS-Studio - Beta detected, OBS_VERSION is now: ${OBS_VERSION}\n" - "******************************************************************************") -endif() - -# Define build number cache file -set(BUILD_NUMBER_CACHE - ${CMAKE_SOURCE_DIR}/cmake/.CMakeBuildNumber - CACHE INTERNAL "OBS build number cache file") - -# Read build number from cache file or manual override -if(NOT DEFINED OBS_BUILD_NUMBER) - if(EXISTS ${BUILD_NUMBER_CACHE}) - file(READ ${BUILD_NUMBER_CACHE} OBS_BUILD_NUMBER) - math(EXPR OBS_BUILD_NUMBER "${OBS_BUILD_NUMBER}+1") - else() - set(OBS_BUILD_NUMBER "1") - endif() - file(WRITE ${BUILD_NUMBER_CACHE} "${OBS_BUILD_NUMBER}") -endif() - -message(STATUS "OBS: Application Version: ${OBS_VERSION} - Build Number: ${OBS_BUILD_NUMBER}") diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt deleted file mode 100644 index b99cb2cd2635e0..00000000000000 --- a/deps/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -if(OS_WINDOWS) - add_subdirectory(w32-pthreads) -endif() - -add_subdirectory(blake2) -add_subdirectory(glad) -add_subdirectory(libcaption) diff --git a/deps/glad/CMakeLists.txt b/deps/glad/CMakeLists.txt index 457de0a9d0bdeb..d62a7d85a14448 100644 --- a/deps/glad/CMakeLists.txt +++ b/deps/glad/CMakeLists.txt @@ -5,10 +5,6 @@ find_package(OpenGL REQUIRED) add_library(obsglad OBJECT) add_library(OBS::glad ALIAS obsglad) -if(OBS_CMAKE_VERSION VERSION_LESS 3.0.0) - add_library(OBS::obsglad ALIAS obsglad) -endif() - target_sources( obsglad PRIVATE diff --git a/deps/libcaption/cmake/legacy.cmake b/deps/libcaption/cmake/legacy.cmake deleted file mode 100644 index eff25bb9c4b802..00000000000000 --- a/deps/libcaption/cmake/legacy.cmake +++ /dev/null @@ -1,44 +0,0 @@ -project(libcaption) - -add_library(caption STATIC) -add_library(OBS::caption ALIAS caption) - -target_sources( - caption - PRIVATE src/caption.c - src/utf8.c - caption/utf8.h - src/srt.c - src/scc.c - caption/scc.h - src/mpeg.c - caption/mpeg.h - src/cea708.c - caption/cea708.h - src/xds.c - src/eia608.c - caption/eia608.h - src/eia608_from_utf8.c - src/eia608_charmap.c - caption/eia608_charmap.h - PUBLIC caption/caption.h) - -target_compile_definitions(caption PRIVATE __STDC_CONSTANT_MACROS $<$:_CRT_SECURE_NO_WARNINGS>) - -target_compile_options( - caption - PRIVATE - $<$,$,$>:-Wno-unused-but-set-parameter> -) - -target_include_directories( - caption - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/caption - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -set_target_properties( - caption - PROPERTIES FOLDER "deps" - VERSION "0" - SOVERSION "0" - POSITION_INDEPENDENT_CODE ON) diff --git a/deps/w32-pthreads/cmake/legacy.cmake b/deps/w32-pthreads/cmake/legacy.cmake deleted file mode 100644 index e08987cbaa6682..00000000000000 --- a/deps/w32-pthreads/cmake/legacy.cmake +++ /dev/null @@ -1,22 +0,0 @@ -if(POLICY CMP0090) - cmake_policy(SET CMP0090 NEW) -endif() - -project(w32-pthreads) - -add_library(w32-pthreads SHARED) -add_library(OBS::w32-pthreads ALIAS w32-pthreads) - -target_sources(w32-pthreads PRIVATE implement.h pthread.c pthread.h sched.h semaphore.h w32-pthreads.rc) - -set(MODULE_DESCRIPTION "POSIX Threads for Windows") -configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in w32-pthreads.rc) - -target_compile_definitions(w32-pthreads PRIVATE __CLEANUP_C PTW32_BUILD) - -target_include_directories(w32-pthreads PUBLIC "$") - -set_target_properties(w32-pthreads PROPERTIES FOLDER "deps" PUBLIC_HEADER "pthread.h;sched.h") - -setup_binary_target(w32-pthreads) -export_target(w32-pthreads) diff --git a/libobs-opengl/cmake/legacy.cmake b/libobs-opengl/cmake/legacy.cmake deleted file mode 100644 index f540876470c083..00000000000000 --- a/libobs-opengl/cmake/legacy.cmake +++ /dev/null @@ -1,71 +0,0 @@ -project(libobs-opengl) - -add_library(libobs-opengl SHARED) -add_library(OBS::libobs-opengl ALIAS libobs-opengl) - -target_sources( - libobs-opengl - PRIVATE gl-helpers.c - gl-helpers.h - gl-indexbuffer.c - gl-shader.c - gl-shaderparser.c - gl-shaderparser.h - gl-stagesurf.c - gl-subsystem.c - gl-subsystem.h - gl-texture2d.c - gl-texture3d.c - gl-texturecube.c - gl-vertexbuffer.c - gl-zstencil.c) - -target_link_libraries(libobs-opengl PRIVATE OBS::libobs OBS::obsglad) - -set_target_properties( - libobs-opengl - PROPERTIES FOLDER "core" - VERSION "${OBS_VERSION_MAJOR}" - SOVERSION "1") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Library OpenGL wrapper") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in libobs-opengl.rc) - - target_sources(libobs-opengl PRIVATE gl-windows.c libobs-opengl.rc) - -elseif(OS_MACOS) - find_library(COCOA Cocoa) - find_library(IOSURF IOSurface) - - target_sources(libobs-opengl PRIVATE gl-cocoa.m) - target_compile_definitions(libobs-opengl PRIVATE GL_SILENCE_DEPRECATION) - - target_link_libraries(libobs-opengl PRIVATE ${COCOA} ${IOSURF}) - set_target_properties(libobs-opengl PROPERTIES PREFIX "") - -elseif(OS_POSIX) - find_package(X11 REQUIRED) - find_package(XCB COMPONENTS XCB) - find_package(X11_XCB REQUIRED) - - target_sources(libobs-opengl PRIVATE gl-egl-common.c gl-nix.c gl-x11-egl.c) - - target_link_libraries(libobs-opengl PRIVATE XCB::XCB X11::X11_xcb) - - set_target_properties(libobs-opengl PROPERTIES PREFIX "") - - if(ENABLE_WAYLAND) - find_package( - OpenGL - COMPONENTS EGL - REQUIRED) - find_package(Wayland REQUIRED) - - target_sources(libobs-opengl PRIVATE gl-wayland-egl.c) - - target_link_libraries(libobs-opengl PRIVATE OpenGL::EGL Wayland::EGL) - endif() -endif() - -setup_binary_target(libobs-opengl) diff --git a/libobs-winrt/cmake/legacy.cmake b/libobs-winrt/cmake/legacy.cmake deleted file mode 100644 index 578be6fb4d30ab..00000000000000 --- a/libobs-winrt/cmake/legacy.cmake +++ /dev/null @@ -1,31 +0,0 @@ -project(libobs-winrt) - -add_library(libobs-winrt MODULE) -add_library(OBS::libobs-winrt ALIAS libobs-winrt) - -target_sources(libobs-winrt PRIVATE winrt-capture.cpp winrt-capture.h winrt-dispatch.cpp winrt-dispatch.h) - -target_precompile_headers( - libobs-winrt - PRIVATE - [["../libobs/util/windows/ComPtr.hpp"]] - - - - - - - - - ) - -target_link_libraries(libobs-winrt PRIVATE OBS::libobs Dwmapi windowsapp) - -target_compile_features(libobs-winrt PRIVATE cxx_std_17) - -set_target_properties( - libobs-winrt - PROPERTIES OUTPUT_NAME libobs-winrt FOLDER "core" PREFIX "" -) - -setup_binary_target(libobs-winrt) diff --git a/libobs/cmake/legacy.cmake b/libobs/cmake/legacy.cmake deleted file mode 100644 index 177554060ff288..00000000000000 --- a/libobs/cmake/legacy.cmake +++ /dev/null @@ -1,508 +0,0 @@ -if(POLICY CMP0090) - cmake_policy(SET CMP0090 NEW) -endif() - -project(libobs) - -add_library(libobs-version STATIC EXCLUDE_FROM_ALL) -add_library(OBS::libobs-version ALIAS libobs-version) -configure_file(obsversion.c.in obsversion.c @ONLY) -target_sources(libobs-version PRIVATE obsversion.c obsversion.h) -target_include_directories(libobs-version PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -set_property(TARGET libobs-version PROPERTY FOLDER core) - -find_package(Jansson 2.5 REQUIRED) -find_package(Threads REQUIRED) -find_package(FFmpeg REQUIRED COMPONENTS avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec) -find_package(ZLIB REQUIRED) -find_package(Uthash REQUIRED) - -add_library(libobs SHARED) -add_library(OBS::libobs ALIAS libobs) - -target_sources( - libobs - PRIVATE - obs.c - obs.h - obs.hpp - obs-audio.c - obs-audio-controls.c - obs-audio-controls.h - obs-av1.c - obs-av1.h - obs-avc.c - obs-avc.h - obs-data.c - obs-data.h - obs-defs.h - obs-display.c - obs-encoder.c - obs-encoder.h - obs-ffmpeg-compat.h - obs-hotkey.c - obs-hotkey.h - obs-hotkeys.h - obs-missing-files.c - obs-missing-files.h - obs-nal.c - obs-nal.h - obs-hotkey-name-map.c - obs-interaction.h - obs-internal.h - obs-module.c - obs-module.h - obs-output.c - obs-output.h - obs-output-delay.c - obs-properties.c - obs-properties.h - obs-service.c - obs-service.h - obs-scene.c - obs-scene.h - obs-source.c - obs-source.h - obs-source-deinterlace.c - obs-source-transition.c - obs-video.c - obs-video-gpu-encode.c - obs-view.c - obs-config.h -) - -target_sources( - libobs - PRIVATE - util/simde/check.h - util/simde/debug-trap.h - util/simde/hedley.h - util/simde/simde-align.h - util/simde/simde-arch.h - util/simde/simde-common.h - util/simde/simde-constify.h - util/simde/simde-detect-clang.h - util/simde/simde-diagnostic.h - util/simde/simde-features.h - util/simde/simde-math.h - util/simde/x86/mmx.h - util/simde/x86/sse2.h - util/simde/x86/sse.h -) - -target_sources( - libobs - PRIVATE - callback/calldata.c - callback/calldata.h - callback/decl.c - callback/decl.h - callback/signal.c - callback/signal.h - callback/proc.c - callback/proc.h -) - -target_sources( - libobs - PRIVATE - graphics/graphics.c - graphics/graphics.h - graphics/graphics-imports.c - graphics/graphics-internal.h - graphics/axisang.c - graphics/axisang.h - graphics/bounds.c - graphics/bounds.h - graphics/device-exports.h - graphics/effect.c - graphics/effect.h - graphics/effect-parser.c - graphics/effect-parser.h - graphics/half.h - graphics/image-file.c - graphics/image-file.h - graphics/math-extra.c - graphics/math-extra.h - graphics/matrix3.c - graphics/matrix3.h - graphics/matrix4.c - graphics/matrix4.h - graphics/plane.c - graphics/plane.h - graphics/quat.c - graphics/quat.h - graphics/shader-parser.c - graphics/shader-parser.h - graphics/srgb.h - graphics/texture-render.c - graphics/vec2.c - graphics/vec2.h - graphics/vec3.c - graphics/vec3.h - graphics/vec4.c - graphics/vec4.h - graphics/libnsgif/libnsgif.c - graphics/libnsgif/libnsgif.h - graphics/graphics-ffmpeg.c -) - -target_sources( - libobs - PRIVATE - media-io/audio-io.c - media-io/audio-io.h - media-io/audio-math.h - media-io/audio-resampler.h - media-io/audio-resampler-ffmpeg.c - media-io/format-conversion.c - media-io/format-conversion.h - media-io/frame-rate.h - media-io/media-remux.c - media-io/media-remux.h - media-io/video-fourcc.c - media-io/video-frame.c - media-io/video-frame.h - media-io/video-io.c - media-io/video-io.h - media-io/media-io-defs.h - media-io/video-matrices.c - media-io/video-scaler-ffmpeg.c - media-io/video-scaler.h -) - -target_sources( - libobs - PRIVATE util/array-serializer.c - util/array-serializer.h - util/base.c - util/base.h - util/bitstream.c - util/bitstream.h - util/bmem.c - util/bmem.h - util/buffered-file-serializer.c - util/buffered-file-serializer.h - util/c99defs.h - util/cf-lexer.c - util/cf-lexer.h - util/cf-parser.c - util/cf-parser.h - util/circlebuf.h - util/config-file.c - util/config-file.h - util/crc32.c - util/crc32.h - util/deque.h - util/dstr.c - util/dstr.h - util/file-serializer.c - util/file-serializer.h - util/lexer.c - util/lexer.h - util/platform.c - util/platform.h - util/profiler.c - util/profiler.h - util/profiler.hpp - util/pipe.c - util/pipe.h - util/serializer.h - util/sse-intrin.h - util/task.c - util/task.h - util/text-lookup.c - util/text-lookup.h - util/threading.h - util/utf8.c - util/utf8.h - util/uthash.h - util/util_uint64.h - util/util_uint128.h - util/curl/curl-helper.h - util/darray.h - util/util.hpp - util/source-profiler.c - util/source-profiler.h) - -if(ENABLE_HEVC) - target_sources(libobs PRIVATE obs-hevc.c obs-hevc.h) -endif() - -# Contents of "data" dir already automatically added to bundles on macOS -if(NOT OS_MACOS) - target_sources( - libobs - PRIVATE - data/area.effect - data/bicubic_scale.effect - data/bilinear_lowres_scale.effect - data/color.effect - data/default.effect - data/default_rect.effect - data/deinterlace_base.effect - data/deinterlace_blend.effect - data/deinterlace_blend_2x.effect - data/deinterlace_discard.effect - data/deinterlace_discard_2x.effect - data/deinterlace_linear.effect - data/deinterlace_linear_2x.effect - data/deinterlace_yadif.effect - data/deinterlace_yadif_2x.effect - data/format_conversion.effect - data/lanczos_scale.effect - data/opaque.effect - data/premultiplied_alpha.effect - data/repeat.effect - data/solid.effect - ) -endif() - -target_link_libraries( - libobs - PRIVATE - FFmpeg::avcodec - FFmpeg::avformat - FFmpeg::avutil - FFmpeg::swscale - FFmpeg::swresample - Jansson::Jansson - OBS::caption - OBS::libobs-version - Uthash::Uthash - ZLIB::ZLIB - PUBLIC Threads::Threads -) - -set_target_properties( - libobs - PROPERTIES OUTPUT_NAME obs FOLDER "core" VERSION "${OBS_VERSION_MAJOR}" SOVERSION "0" -) - -target_compile_definitions(libobs PUBLIC ${ARCH_SIMD_DEFINES} PRIVATE IS_LIBOBS) - -target_compile_features(libobs PRIVATE cxx_alias_templates) - -target_compile_options(libobs PUBLIC ${ARCH_SIMD_FLAGS}) - -target_include_directories( - libobs - PUBLIC $ $ -) - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Library") - set(UI_VERSION "${OBS_VERSION_CANONICAL}") - - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in libobs.rc) - - target_sources( - libobs - PRIVATE - obs-win-crash-handler.c - obs-windows.c - util/threading-windows.c - util/threading-windows.h - util/pipe-windows.c - util/platform-windows.c - util/windows/device-enum.c - util/windows/device-enum.h - util/windows/obfuscate.c - util/windows/obfuscate.h - util/windows/win-registry.h - util/windows/win-version.h - util/windows/window-helpers.c - util/windows/window-helpers.h - util/windows/ComPtr.hpp - util/windows/CoTaskMemPtr.hpp - util/windows/HRError.hpp - util/windows/WinHandle.hpp - libobs.rc - audio-monitoring/win32/wasapi-output.c - audio-monitoring/win32/wasapi-enum-devices.c - audio-monitoring/win32/wasapi-output.h - audio-monitoring/win32/wasapi-monitoring-available.c - ) - - target_compile_definitions(libobs PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS) - set_source_files_properties( - obs-win-crash-handler.c - PROPERTIES COMPILE_DEFINITIONS OBS_VERSION="${OBS_VERSION_CANONICAL}" - ) - target_link_libraries( - libobs - PRIVATE dxgi Avrt Dwmapi winmm Rpcrt4 - ) - - if(MSVC) - target_link_libraries(libobs PUBLIC OBS::w32-pthreads) - - target_compile_options(libobs PRIVATE "$<$:/EHc->" "$<$:/EHc->") - - target_link_options(libobs PRIVATE "LINKER:/IGNORE:4098" "LINKER:/SAFESEH:NO") - - add_library(obs-obfuscate INTERFACE) - add_library(OBS::obfuscate ALIAS obs-obfuscate) - target_sources(obs-obfuscate INTERFACE util/windows/obfuscate.c util/windows/obfuscate.h) - target_include_directories(obs-obfuscate INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - add_library(obs-comutils INTERFACE) - add_library(OBS::COMutils ALIAS obs-comutils) - target_sources(obs-comutils INTERFACE util/windows/ComPtr.hpp) - target_include_directories(obs-comutils INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - - add_library(obs-winhandle INTERFACE) - add_library(OBS::winhandle ALIAS obs-winhandle) - target_sources(obs-winhandle INTERFACE util/windows/WinHandle.hpp) - target_include_directories(obs-winhandle INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") - endif() -elseif(OS_MACOS) - find_library(COCOA Cocoa) - find_library(COREAUDIO CoreAudio) - find_library(AUDIOTOOLBOX AudioToolbox) - find_library(AUDIOUNIT AudioUnit) - find_library(APPKIT AppKit) - find_library(IOKIT IOKit) - find_library(CARBON Carbon) - - mark_as_advanced( - COCOA - COREAUDIO - AUDIOTOOLBOX - AUDIOUNIT - APPKIT - IOKIT - CARBON - ) - - target_link_libraries( - libobs - PRIVATE ${COCOA} ${COREAUDIO} ${AUDIOTOOLBOX} ${AUDIOUNIT} ${APPKIT} ${IOKIT} ${CARBON} - ) - - target_sources( - libobs - PRIVATE - obs-cocoa.m - util/pipe-posix.c - util/platform-cocoa.m - util/platform-nix.c - util/threading-posix.c - util/threading-posix.h - util/apple/cfstring-utils.h - audio-monitoring/osx/coreaudio-enum-devices.c - audio-monitoring/osx/coreaudio-output.c - audio-monitoring/osx/coreaudio-monitoring-available.c - audio-monitoring/osx/mac-helpers.h - ) - - set_source_files_properties(util/platform-cocoa.m obs-cocoa.m PROPERTIES COMPILE_FLAGS -fobjc-arc) - - set_target_properties(libobs PROPERTIES SOVERSION "1" BUILD_RPATH "$") -elseif(OS_POSIX) - if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - target_compile_definitions(libobs PRIVATE ENABLE_DARRAY_TYPE_TEST) - endif() - - find_package(LibUUID REQUIRED) - find_package(X11 REQUIRED) - find_package(XCB COMPONENTS XCB OPTIONAL_COMPONENTS XINPUT QUIET) - find_package(X11_XCB REQUIRED) - - target_sources( - libobs - PRIVATE - obs-nix.c - obs-nix-platform.c - obs-nix-platform.h - obs-nix-x11.c - util/threading-posix.c - util/threading-posix.h - util/pipe-posix.c - util/platform-nix.c - ) - - target_link_libraries(libobs PRIVATE X11::X11_xcb XCB::XCB LibUUID::LibUUID) - - if(USE_XDG) - target_compile_definitions(libobs PRIVATE USE_XDG) - endif() - - if(ENABLE_PULSEAUDIO) - find_package(PulseAudio REQUIRED) - obs_status(STATUS "-> PulseAudio found - audio monitoring enabled") - target_sources( - libobs - PRIVATE - audio-monitoring/pulse/pulseaudio-output.c - audio-monitoring/pulse/pulseaudio-enum-devices.c - audio-monitoring/pulse/pulseaudio-wrapper.c - audio-monitoring/pulse/pulseaudio-wrapper.h - audio-monitoring/pulse/pulseaudio-monitoring-available.c - ) - - target_link_libraries(libobs PRIVATE ${PULSEAUDIO_LIBRARY}) - else() - obs_status(WARNING "-> No audio backend found - audio monitoring disabled") - target_sources(libobs PRIVATE audio-monitoring/null/null-audio-monitoring.c) - endif() - - find_package(Gio) - if(TARGET GIO::GIO) - target_link_libraries(libobs PRIVATE GIO::GIO) - - target_sources(libobs PRIVATE util/platform-nix-dbus.c util/platform-nix-portal.c) - endif() - - if(TARGET XCB::XINPUT) - target_link_libraries(libobs PRIVATE XCB::XINPUT) - endif() - - if(ENABLE_WAYLAND) - find_package(Wayland COMPONENTS Client REQUIRED) - find_package(Xkbcommon REQUIRED) - - target_link_libraries(libobs PRIVATE Wayland::Client Xkbcommon::Xkbcommon) - - target_sources(libobs PRIVATE obs-nix-wayland.c) - endif() - - if(OS_LINUX) - target_link_libraries(obsglad PRIVATE ${CMAKE_DL_LIBS}) - endif() - - if(OS_FREEBSD) - find_package(Sysinfo REQUIRED) - target_link_libraries(libobs PRIVATE Sysinfo::Sysinfo) - endif() - - set_target_properties(libobs PROPERTIES BUILD_RPATH "$") -endif() - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/obsconfig.h.in ${CMAKE_BINARY_DIR}/config/obsconfig.h) - -target_compile_definitions( - libobs - PUBLIC HAVE_OBSCONFIG_H - PRIVATE "OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\"" "$<$:LINUX_PORTABLE>" -) - -if(ENABLE_FFMPEG_MUX_DEBUG) - target_compile_definitions(libobs PRIVATE SHOW_SUBPROCESSES) -endif() - -get_target_property(_OBS_SOURCES libobs SOURCES) -set(_OBS_HEADERS ${_OBS_SOURCES}) -set(_OBS_FILTERS ${_OBS_SOURCES}) -list(FILTER _OBS_HEADERS INCLUDE REGEX ".*\\.h(pp)?") -list(FILTER _OBS_SOURCES INCLUDE REGEX ".*\\.(m|c[cp]?p?)") -list(FILTER _OBS_FILTERS INCLUDE REGEX ".*\\.effect") - -source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${_OBS_SOURCES}) -source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Header Files" FILES ${_OBS_HEADERS}) -source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Effect Files" FILES ${_OBS_FILTERS}) - -setup_binary_target(libobs) -setup_target_resources(libobs libobs) -export_target(libobs) -install_headers(libobs) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 11f3b165c94df6..1769a02a95733f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,186 +2,89 @@ cmake_minimum_required(VERSION 3.22...3.25) option(ENABLE_PLUGINS "Enable building OBS plugins" ON) -if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0) - if(NOT ENABLE_PLUGINS) - set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_DISABLED "Plugin Support") - return() - endif() +if(NOT ENABLE_PLUGINS) + set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_DISABLED "Plugin Support") + return() +endif() - set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_ENABLED "Plugin Support") +set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_ENABLED "Plugin Support") - macro(check_obs_browser) - if((OS_WINDOWS AND CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") OR OS_MACOS OR OS_LINUX) - if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") - message(FATAL_ERROR "Required submodule 'obs-browser' not available.") - else() - add_subdirectory(obs-browser) - endif() +macro(check_obs_browser) + if((OS_WINDOWS AND CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") OR OS_MACOS OR OS_LINUX) + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") + message(FATAL_ERROR "Required submodule 'obs-browser' not available.") else() - add_custom_target(obs-browser) - target_disable(obs-browser) + add_subdirectory(obs-browser) endif() - endmacro() + else() + add_custom_target(obs-browser) + target_disable(obs-browser) + endif() +endmacro() - macro(check_obs_websocket) - if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-websocket/CMakeLists.txt") - message(FATAL_ERROR "Required submodule 'obs-websocket' not available.") - else() - add_subdirectory(obs-websocket) - endif() - endmacro() +macro(check_obs_websocket) + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-websocket/CMakeLists.txt") + message(FATAL_ERROR "Required submodule 'obs-websocket' not available.") + else() + add_subdirectory(obs-websocket) + endif() +endmacro() - # Add plugins in alphabetical order to retain order in IDE projects - add_obs_plugin( +# Add plugins in alphabetical order to retain order in IDE projects +add_obs_plugin( aja PLATFORMS WINDOWS MACOS LINUX WITH_MESSAGE - ) - add_obs_plugin(coreaudio-encoder PLATFORMS WINDOWS MACOS) - add_obs_plugin( +) +add_obs_plugin(coreaudio-encoder PLATFORMS WINDOWS MACOS) +add_obs_plugin( decklink PLATFORMS WINDOWS MACOS LINUX WITH_MESSAGE - ) - add_obs_plugin(image-source) - add_obs_plugin(linux-alsa PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(linux-capture PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(linux-jack PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(linux-pipewire PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(linux-pulseaudio PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(linux-v4l2 PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(mac-avcapture PLATFORMS MACOS) - add_obs_plugin(mac-capture PLATFORMS MACOS) - add_obs_plugin(mac-syphon PLATFORMS MACOS) - add_obs_plugin(mac-videotoolbox PLATFORMS MACOS) - add_obs_plugin(mac-virtualcam PLATFORMS MACOS) - add_obs_plugin(nv-filters PLATFORMS WINDOWS) - - check_obs_browser() - - add_obs_plugin(obs-ffmpeg) - add_obs_plugin(obs-filters) - add_obs_plugin(obs-libfdk) - add_obs_plugin(obs-nvenc PLATFORMS WINDOWS LINUX) - add_obs_plugin(obs-outputs) - add_obs_plugin( +) +add_obs_plugin(image-source) +add_obs_plugin(linux-alsa PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(linux-capture PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(linux-jack PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(linux-pipewire PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(linux-pulseaudio PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(linux-v4l2 PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(mac-avcapture PLATFORMS MACOS) +add_obs_plugin(mac-capture PLATFORMS MACOS) +add_obs_plugin(mac-syphon PLATFORMS MACOS) +add_obs_plugin(mac-videotoolbox PLATFORMS MACOS) +add_obs_plugin(mac-virtualcam PLATFORMS MACOS) +add_obs_plugin(nv-filters PLATFORMS WINDOWS) + +check_obs_browser() + +add_obs_plugin(obs-ffmpeg) +add_obs_plugin(obs-filters) +add_obs_plugin(obs-libfdk) +add_obs_plugin(obs-nvenc PLATFORMS WINDOWS LINUX) +add_obs_plugin(obs-outputs) +add_obs_plugin( obs-qsv11 PLATFORMS WINDOWS LINUX ARCHITECTURES x64 x86_64 - ) - add_obs_plugin(obs-text PLATFORMS WINDOWS) - add_obs_plugin(obs-transitions) - add_obs_plugin( +) +add_obs_plugin(obs-text PLATFORMS WINDOWS) +add_obs_plugin(obs-transitions) +add_obs_plugin( obs-vst PLATFORMS WINDOWS MACOS LINUX WITH_MESSAGE - ) - add_obs_plugin(obs-webrtc) - - check_obs_websocket() - - add_obs_plugin(obs-x264) - add_obs_plugin(oss-audio PLATFORMS FREEBSD OPENBSD) - add_obs_plugin(rtmp-services) - add_obs_plugin(sndio PLATFORMS LINUX FREEBSD OPENBSD) - add_obs_plugin(text-freetype2) - add_obs_plugin(vlc-video WITH_MESSAGE) - add_obs_plugin(win-capture PLATFORMS WINDOWS) - add_obs_plugin(win-dshow PLATFORMS WINDOWS) - add_obs_plugin(win-wasapi PLATFORMS WINDOWS) - - return() -endif() - -if(NOT ENABLE_PLUGINS) - obs_status(STATUS "Building with plugins disabled.") - return() -endif() - -function(check_obs_browser) - if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt) - add_subdirectory(obs-browser) - else() - obs_status(FATAL_ERROR "obs-browser submodule not available.") - endif() -endfunction() - -# APPLE/WIN32/UNIX are soft-deprecated: https://discourse.cmake.org/t/platform-id-vs-win32-vs-cmake-system-name/1226/2 -if(OS_WINDOWS) - add_subdirectory(coreaudio-encoder) - add_subdirectory(win-wasapi) - add_subdirectory(win-dshow) - add_subdirectory(win-capture) - add_subdirectory(decklink) - add_subdirectory(obs-qsv11) - add_subdirectory(obs-text) - add_subdirectory(vlc-video) - add_subdirectory(obs-vst) - add_subdirectory(nv-filters) - - check_obs_browser() -elseif(OS_MACOS) - add_subdirectory(coreaudio-encoder) - add_subdirectory(mac-avcapture) - add_subdirectory(mac-capture) - add_subdirectory(mac-videotoolbox) - add_subdirectory(mac-syphon) - add_subdirectory(mac-virtualcam) - add_subdirectory(decklink) - add_subdirectory(vlc-video) - add_subdirectory(linux-jack) - add_subdirectory(obs-vst) - - check_obs_browser() -elseif(OS_LINUX) - add_subdirectory(linux-capture) - add_subdirectory(linux-pulseaudio) - add_subdirectory(linux-v4l2) - add_subdirectory(linux-jack) - add_subdirectory(linux-alsa) - add_subdirectory(linux-pipewire) - add_subdirectory(decklink) - add_subdirectory(vlc-video) - add_subdirectory(sndio) - add_subdirectory(obs-vst) - add_subdirectory(obs-qsv11) - - check_obs_browser() -elseif(OS_FREEBSD) - add_subdirectory(linux-capture) - add_subdirectory(linux-pipewire) - add_subdirectory(linux-pulseaudio) - add_subdirectory(linux-v4l2) - add_subdirectory(linux-jack) - add_subdirectory(linux-alsa) - add_subdirectory(vlc-video) - add_subdirectory(oss-audio) - add_subdirectory(sndio) - - obs_status(STATUS "obs-browser plugin not available.") - obs_status(STATUS "obs-vst plugin not available.") -elseif(OS_OPENBSD) - add_subdirectory(linux-capture) - add_subdirectory(sndio) - - obs_status(STATUS "obs-browser plugin not available.") - obs_status(STATUS "obs-vst plugin not available.") -endif() - -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/obs-websocket/CMakeLists.txt) - add_subdirectory(obs-websocket) -else() - obs_status(FATAL_ERROR "obs-websocket submodule not available.") -endif() - -add_subdirectory(image-source) -add_subdirectory(obs-x264) -add_subdirectory(obs-libfdk) -add_subdirectory(obs-ffmpeg) -add_subdirectory(obs-outputs) -add_subdirectory(obs-filters) -add_subdirectory(obs-transitions) -add_subdirectory(rtmp-services) -add_subdirectory(text-freetype2) -add_subdirectory(aja) -add_subdirectory(obs-webrtc) +) +add_obs_plugin(obs-webrtc) + +check_obs_websocket() + +add_obs_plugin(obs-x264) +add_obs_plugin(oss-audio PLATFORMS FREEBSD OPENBSD) +add_obs_plugin(rtmp-services) +add_obs_plugin(sndio PLATFORMS LINUX FREEBSD OPENBSD) +add_obs_plugin(text-freetype2) +add_obs_plugin(vlc-video WITH_MESSAGE) +add_obs_plugin(win-capture PLATFORMS WINDOWS) +add_obs_plugin(win-dshow PLATFORMS WINDOWS) +add_obs_plugin(win-wasapi PLATFORMS WINDOWS) diff --git a/plugins/aja/cmake/legacy.cmake b/plugins/aja/cmake/legacy.cmake deleted file mode 100644 index 35d0f246bedd93..00000000000000 --- a/plugins/aja/cmake/legacy.cmake +++ /dev/null @@ -1,74 +0,0 @@ -project(aja) - -option(ENABLE_AJA "Build OBS with aja support" ON) -if(NOT ENABLE_AJA) - obs_status(DISABLED "aja") - return() -endif() - -if(NOT OS_MACOS AND NOT CMAKE_SIZEOF_VOID_P EQUAL 8) - obs_status(STATUS "aja support not enabled (32-bit not supported).") - set(ENABLE_AJA - OFF - CACHE BOOL "Build OBS with aja support" FORCE) - return() -endif() - -find_package(LibAJANTV2 REQUIRED) - -add_library(aja MODULE) -add_library(OBS::aja ALIAS aja) - -target_sources( - aja - PRIVATE main.cpp - aja-card-manager.cpp - aja-common.cpp - aja-common.hpp - aja-output.cpp - aja-enums.hpp - aja-output.hpp - aja-presets.cpp - aja-presets.hpp - aja-props.cpp - aja-props.hpp - aja-routing.cpp - aja-routing.hpp - aja-source.cpp - aja-source.hpp - aja-vpid-data.cpp - aja-vpid-data.hpp - aja-widget-io.cpp - aja-widget-io.hpp - aja-card-manager.hpp - aja-ui-props.hpp - audio-repack.c - audio-repack.h - audio-repack.hpp) - -target_link_libraries(aja PRIVATE OBS::libobs AJA::LibAJANTV2) - -if(OS_MACOS) - find_library(IOKIT IOKit) - find_library(COREFOUNDATION CoreFoundation) - find_library(APPKIT AppKit) - - target_link_libraries(aja PRIVATE ${IOKIT} ${COREFOUNDATION} ${APPKIT}) -elseif(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS AJA Windows module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in win-aja.rc) - - target_sources(aja PRIVATE win-aja.rc) - - target_compile_options(aja PRIVATE /wd4996) - target_link_libraries(aja PRIVATE ws2_32.lib setupapi.lib Winmm.lib netapi32.lib Shlwapi.lib) - target_link_options(aja PRIVATE "LINKER:/IGNORE:4099") -endif() - -if(NOT MSVC) - target_compile_options(aja PRIVATE -Wno-error=deprecated-declarations) -endif() - -set_target_properties(aja PROPERTIES FOLDER "plugins/aja" PREFIX "") - -setup_plugin_target(aja) diff --git a/plugins/coreaudio-encoder/cmake/legacy.cmake b/plugins/coreaudio-encoder/cmake/legacy.cmake deleted file mode 100644 index 4d45816324438c..00000000000000 --- a/plugins/coreaudio-encoder/cmake/legacy.cmake +++ /dev/null @@ -1,48 +0,0 @@ -project(coreaudio-encoder) - -if(OS_WINDOWS) - option(ENABLE_COREAUDIO_ENCODER "Enable building with CoreAudio encoder (Windows)" ON) - if(NOT ENABLE_COREAUDIO_ENCODER) - obs_status(DISABLED "coreaudio-encoder") - return() - endif() -endif() - -add_library(coreaudio-encoder MODULE) -add_library(OBS::coreaudio-encoder ALIAS coreaudio-encoder) - -target_sources(coreaudio-encoder PRIVATE encoder.cpp) - -set_target_properties( - coreaudio-encoder - PROPERTIES OUTPUT_NAME "coreaudio-encoder" - FOLDER "plugins" - PREFIX "") - -target_compile_features(coreaudio-encoder PRIVATE cxx_std_11) - -target_link_libraries(coreaudio-encoder PRIVATE OBS::libobs) - -if(OS_MACOS) - find_library(COREFOUNDATION CoreFoundation) - find_library(COREAUDIO CoreAudio) - find_library(AUDIOTOOLBOX AudioToolbox) - - mark_as_advanced(AUDIOTOOLBOX COREAUDIO COREFOUNDATION) - - target_link_libraries(coreaudio-encoder PRIVATE ${COREFOUNDATION} ${COREAUDIO} ${AUDIOTOOLBOX}) - -elseif(OS_WINDOWS) - if(MINGW) - set_source_files_properties(encoder.cpp PROPERTIES COMPILE_FLAGS -Wno-multichar) - endif() - - target_compile_definitions(coreaudio-encoder PRIVATE UNICODE _UNICODE) - - set(MODULE_DESCRIPTION "OBS CoreAudio encoder") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in coreaudio-encoder.rc) - - target_sources(coreaudio-encoder PRIVATE coreaudio-encoder.rc windows-imports.h) -endif() - -setup_plugin_target(coreaudio-encoder) diff --git a/plugins/decklink/cmake/legacy.cmake b/plugins/decklink/cmake/legacy.cmake deleted file mode 100644 index b30c3ae43e7a47..00000000000000 --- a/plugins/decklink/cmake/legacy.cmake +++ /dev/null @@ -1,104 +0,0 @@ -project(decklink) - -option(ENABLE_DECKLINK "Build OBS with Decklink support" ON) -if(NOT ENABLE_DECKLINK) - obs_status(DISABLED "decklink") - return() -endif() - -if(OS_WINDOWS) - include(IDLFileHelper) - add_idl_files(win-decklink-sdk_GENERATED_FILES win/decklink-sdk/DeckLinkAPI.idl) -endif() - -add_library(decklink MODULE) -add_library(OBS::decklink ALIAS decklink) - -add_library(decklink-sdk INTERFACE) -add_library(Decklink::SDK ALIAS decklink-sdk) - -target_sources( - decklink - PRIVATE OBSVideoFrame.cpp - OBSVideoFrame.h - audio-repack.c - audio-repack.h - audio-repack.hpp - const.h - decklink-device.cpp - decklink-device.hpp - decklink-devices.cpp - decklink-devices.hpp - decklink-device-discovery.cpp - decklink-device-discovery.hpp - decklink-device-instance.cpp - decklink-device-instance.hpp - decklink-device-mode.cpp - decklink-device-mode.hpp - decklink-output.cpp - decklink-source.cpp - DecklinkBase.cpp - DecklinkBase.h - DecklinkInput.cpp - DecklinkInput.hpp - DecklinkOutput.cpp - DecklinkOutput.hpp - platform.hpp - plugin-main.cpp - util.cpp - util.hpp) - -target_include_directories(decklink-sdk INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) - -target_link_libraries(decklink PRIVATE OBS::libobs OBS::caption Decklink::SDK) - -set_target_properties(decklink PROPERTIES FOLDER "plugins/decklink") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS DeckLink Windows module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in win-decklink.rc) - - target_compile_definitions(decklink PRIVATE NOMINMAX) - target_sources(decklink PRIVATE win/platform.cpp win-decklink.rc) - - target_sources(decklink-sdk INTERFACE win/decklink-sdk/DeckLinkAPIVersion.h ${win-decklink-sdk_GENERATED_FILES}) - -elseif(OS_MACOS) - find_library(COREFOUNDATION CoreFoundation) - mark_as_advanced(COREFOUNDATION) - - target_sources(decklink PRIVATE mac/platform.cpp) - - target_sources( - decklink-sdk - INTERFACE mac/decklink-sdk/DeckLinkAPIDispatch.cpp - mac/decklink-sdk/DeckLinkAPI.h - mac/decklink-sdk/DeckLinkAPIConfiguration.h - mac/decklink-sdk/DeckLinkAPIDeckControl.h - mac/decklink-sdk/DeckLinkAPIDiscovery.h - mac/decklink-sdk/DeckLinkAPIModes.h - mac/decklink-sdk/DeckLinkAPIStreaming.h - mac/decklink-sdk/DeckLinkAPITypes.h - mac/decklink-sdk/DeckLinkAPIVersion.h) - - target_link_libraries(decklink PRIVATE ${COREFOUNDATION}) - - target_compile_features(decklink PRIVATE cxx_auto_type) -elseif(OS_POSIX) - target_sources(decklink PRIVATE linux/platform.cpp) - - target_sources( - decklink-sdk - INTERFACE linux/decklink-sdk/DeckLinkAPIDispatch.cpp - linux/decklink-sdk/DeckLinkAPI.h - linux/decklink-sdk/DeckLinkAPIConfiguration.h - linux/decklink-sdk/DeckLinkAPIDeckControl.h - linux/decklink-sdk/DeckLinkAPIDiscovery.h - linux/decklink-sdk/DeckLinkAPIModes.h - linux/decklink-sdk/DeckLinkAPITypes.h - linux/decklink-sdk/DeckLinkAPIVersion.h - linux/decklink-sdk/LinuxCOM.h) -endif() - -setup_plugin_target(decklink) -setup_target_resources(decklink "obs-plugins/decklink") diff --git a/plugins/image-source/cmake/legacy.cmake b/plugins/image-source/cmake/legacy.cmake deleted file mode 100644 index 38582c06fb6eeb..00000000000000 --- a/plugins/image-source/cmake/legacy.cmake +++ /dev/null @@ -1,23 +0,0 @@ -project(image-source) - -add_library(image-source MODULE) -add_library(OBS::image-source ALIAS image-source) - -target_sources(image-source PRIVATE image-source.c color-source.c obs-slideshow.c obs-slideshow-mk2.c) - -target_link_libraries(image-source PRIVATE OBS::libobs) - -if(OS_WINDOWS) - if(MSVC) - target_link_libraries(image-source PRIVATE OBS::w32-pthreads) - endif() - - set(MODULE_DESCRIPTION "OBS image module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in image-source.rc) - - target_sources(image-source PRIVATE image-source.rc) -endif() - -set_target_properties(image-source PROPERTIES FOLDER "plugins" PREFIX "") - -setup_plugin_target(image-source) diff --git a/plugins/linux-alsa/cmake/legacy.cmake b/plugins/linux-alsa/cmake/legacy.cmake deleted file mode 100644 index a3ea221fb6ca91..00000000000000 --- a/plugins/linux-alsa/cmake/legacy.cmake +++ /dev/null @@ -1,20 +0,0 @@ -project(linux-alsa) - -option(ENABLE_ALSA "Build OBS with ALSA support" ON) -if(NOT ENABLE_ALSA) - obs_status(DISABLED "linux-alsa") - return() -endif() - -find_package(ALSA REQUIRED) - -add_library(linux-alsa MODULE) -add_library(OBS::alsa ALIAS linux-alsa) - -target_sources(linux-alsa PRIVATE linux-alsa.c alsa-input.c) - -target_link_libraries(linux-alsa PRIVATE OBS::libobs ALSA::ALSA) - -set_target_properties(linux-alsa PROPERTIES FOLDER "plugins") - -setup_plugin_target(linux-alsa) diff --git a/plugins/linux-capture/cmake/legacy.cmake b/plugins/linux-capture/cmake/legacy.cmake deleted file mode 100644 index feb8077e823b1d..00000000000000 --- a/plugins/linux-capture/cmake/legacy.cmake +++ /dev/null @@ -1,37 +0,0 @@ -project(linux-capture) - -find_package(X11 REQUIRED) -find_package(XCB COMPONENTS XCB XFIXES RANDR SHM XINERAMA COMPOSITE) -if(NOT TARGET XCB::COMPOSITE) - obs_status(FATAL_ERROR "xcb composite library not found") -endif() - -add_library(linux-capture MODULE) -add_library(OBS::capture ALIAS linux-capture) - -target_sources( - linux-capture - PRIVATE linux-capture.c - xcursor-xcb.c - xcursor-xcb.h - xhelpers.c - xhelpers.h - xshm-input.c - xcomposite-input.c - xcomposite-input.h) - -target_link_libraries( - linux-capture - PRIVATE OBS::libobs - OBS::obsglad - X11::X11 - XCB::XCB - XCB::XFIXES - XCB::RANDR - XCB::SHM - XCB::XINERAMA - XCB::COMPOSITE) - -set_target_properties(linux-capture PROPERTIES FOLDER "plugins") - -setup_plugin_target(linux-capture) diff --git a/plugins/linux-jack/cmake/legacy.cmake b/plugins/linux-jack/cmake/legacy.cmake deleted file mode 100644 index c78418d63ff0c3..00000000000000 --- a/plugins/linux-jack/cmake/legacy.cmake +++ /dev/null @@ -1,20 +0,0 @@ -project(linux-jack) - -option(ENABLE_JACK "Build OBS with JACK support" OFF) -if(NOT ENABLE_JACK) - obs_status(DISABLED "linux-jack") - return() -endif() - -find_package(Jack REQUIRED) - -add_library(linux-jack MODULE) -add_library(OBS::jack ALIAS linux-jack) - -target_sources(linux-jack PRIVATE linux-jack.c jack-wrapper.c jack-input.c) - -target_link_libraries(linux-jack PRIVATE OBS::libobs Jack::Jack) - -set_target_properties(linux-jack PROPERTIES FOLDER "plugins" PROJECT_LABEL "JACK Audio") - -setup_plugin_target(linux-jack) diff --git a/plugins/linux-pipewire/cmake/legacy.cmake b/plugins/linux-pipewire/cmake/legacy.cmake deleted file mode 100644 index 9f4b601bb326a8..00000000000000 --- a/plugins/linux-pipewire/cmake/legacy.cmake +++ /dev/null @@ -1,46 +0,0 @@ -project(linux-pipewire) - -option(ENABLE_PIPEWIRE "Enable PipeWire support" ON) -if(NOT ENABLE_PIPEWIRE) - obs_status(DISABLED "linux-pipewire") - return() -endif() - -find_package(PipeWire 0.3.33 REQUIRED) -find_package(Gio QUIET) -find_package(Libdrm QUIET) # we require libdrm/drm_fourcc.h to build - -if(NOT TARGET PipeWire::PipeWire) - obs_status(FATAL_ERROR "PipeWire library not found! Please install PipeWire or set ENABLE_PIPEWIRE=OFF.") -elseif(NOT TARGET GIO::GIO) - obs_status(FATAL_ERROR "Gio library not found! Please install GLib2 (or Gio) or set ENABLE_PIPEWIRE=OFF.") -elseif(NOT TARGET Libdrm::Libdrm) - obs_status(FATAL_ERROR "libdrm headers not found! Please install libdrm or set ENABLE_PIPEWIRE=OFF.") -endif() - -add_library(linux-pipewire MODULE) -add_library(OBS::pipewire ALIAS linux-pipewire) - -target_sources( - linux-pipewire - PRIVATE formats.c - formats.h - linux-pipewire.c - pipewire.c - pipewire.h - portal.c - portal.h - screencast-portal.c - screencast-portal.h) - -target_link_libraries(linux-pipewire PRIVATE OBS::libobs OBS::obsglad PipeWire::PipeWire GIO::GIO Libdrm::Libdrm) - -if(PIPEWIRE_VERSION VERSION_GREATER_EQUAL 0.3.60) - obs_status(STATUS "PipeWire 0.3.60+ found, enabling camera support") - - target_sources(linux-pipewire PRIVATE camera-portal.c camera-portal.h) -endif() - -set_target_properties(linux-pipewire PROPERTIES FOLDER "plugins") - -setup_plugin_target(linux-pipewire) diff --git a/plugins/linux-pulseaudio/cmake/legacy.cmake b/plugins/linux-pulseaudio/cmake/legacy.cmake deleted file mode 100644 index 59d0d5d97c32aa..00000000000000 --- a/plugins/linux-pulseaudio/cmake/legacy.cmake +++ /dev/null @@ -1,21 +0,0 @@ -project(linux-pulseaudio) - -if(NOT ENABLE_PULSEAUDIO) - obs_status(DISABLED "linux-pulseaudio") - return() -endif() - -find_package(PulseAudio REQUIRED) - -add_library(linux-pulseaudio MODULE) -add_library(OBS::pulseaudio ALIAS linux-pulseaudio) - -target_sources(linux-pulseaudio PRIVATE linux-pulseaudio.c pulse-wrapper.c pulse-input.c) - -target_include_directories(linux-pulseaudio PRIVATE ${PULSEAUDIO_INCLUDE_DIR}) - -target_link_libraries(linux-pulseaudio PRIVATE OBS::libobs ${PULSEAUDIO_LIBRARY}) - -set_target_properties(linux-pulseaudio PROPERTIES FOLDER "plugins") - -setup_plugin_target(linux-pulseaudio) diff --git a/plugins/linux-v4l2/cmake/legacy.cmake b/plugins/linux-v4l2/cmake/legacy.cmake deleted file mode 100644 index 90db5d46e0812b..00000000000000 --- a/plugins/linux-v4l2/cmake/legacy.cmake +++ /dev/null @@ -1,31 +0,0 @@ -project(linux-v4l2) - -option(ENABLE_V4L2 "Build OBS with v4l2 support" ON) -if(NOT ENABLE_V4L2) - obs_status(DISABLED "linux-v4l2") - return() -endif() - -option(ENABLE_UDEV "Build linux-v4l2 with UDEV support" ON) - -find_package(Libv4l2 REQUIRED) -find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) - -add_library(linux-v4l2 MODULE) -add_library(OBS::v4l2 ALIAS linux-v4l2) - -target_sources(linux-v4l2 PRIVATE linux-v4l2.c v4l2-controls.c v4l2-input.c v4l2-helpers.c v4l2-output.c v4l2-decoder.c) - -target_link_libraries(linux-v4l2 PRIVATE OBS::libobs LIB4L2::LIB4L2 FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil) - -set_target_properties(linux-v4l2 PROPERTIES FOLDER "plugins") - -if(ENABLE_UDEV) - find_package(Udev REQUIRED) - target_sources(linux-v4l2 PRIVATE v4l2-udev.c) - - target_link_libraries(linux-v4l2 PRIVATE Udev::Udev) - target_compile_definitions(linux-v4l2 PRIVATE HAVE_UDEV) -endif() - -setup_plugin_target(linux-v4l2) diff --git a/plugins/obs-ffmpeg/cmake/legacy.cmake b/plugins/obs-ffmpeg/cmake/legacy.cmake deleted file mode 100644 index 6bc1b6b2f090b7..00000000000000 --- a/plugins/obs-ffmpeg/cmake/legacy.cmake +++ /dev/null @@ -1,125 +0,0 @@ -project(obs-ffmpeg) - -option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF) -option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON) - -if(OS_LINUX OR OS_WINDOWS) - option(ENABLE_FFMPEG_NVENC "Enables legacy FFmpeg NVENC encoder" OFF) -endif() - -find_package( - FFmpeg REQUIRED - COMPONENTS avcodec - avfilter - avdevice - avutil - swscale - avformat - swresample) - -add_library(obs-ffmpeg MODULE) -add_library(OBS::ffmpeg ALIAS obs-ffmpeg) - -if(NOT TARGET OBS::media-playback) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/media-playback" "${CMAKE_BINARY_DIR}/shared/media-playback") -endif() - -if(NOT TARGET OBS::opts-parser) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") -endif() - -add_subdirectory(ffmpeg-mux) -if(ENABLE_NEW_MPEGTS_OUTPUT) - find_package(Librist QUIET) - find_package(Libsrt QUIET) - - if(NOT TARGET Librist::Librist AND NOT TARGET Libsrt::Libsrt) - obs_status( - FATAL_ERROR - "SRT and RIST libraries not found! Please install SRT and RIST libraries or set ENABLE_NEW_MPEGTS_OUTPUT=OFF.") - elseif(NOT TARGET Libsrt::Libsrt) - obs_status(FATAL_ERROR "SRT library not found! Please install SRT library or set ENABLE_NEW_MPEGTS_OUTPUT=OFF.") - elseif(NOT TARGET Librist::Librist) - obs_status(FATAL_ERROR "RIST library not found! Please install RIST library or set ENABLE_NEW_MPEGTS_OUTPUT=OFF.") - endif() -endif() - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/obs-ffmpeg-config.h.in ${CMAKE_BINARY_DIR}/config/obs-ffmpeg-config.h) - -target_sources( - obs-ffmpeg - PRIVATE obs-ffmpeg.c - obs-ffmpeg-video-encoders.c - obs-ffmpeg-audio-encoders.c - obs-ffmpeg-av1.c - obs-ffmpeg-output.c - obs-ffmpeg-output.h - obs-ffmpeg-mux.c - obs-ffmpeg-mux.h - obs-ffmpeg-hls-mux.c - obs-ffmpeg-source.c - obs-ffmpeg-compat.h - obs-ffmpeg-formats.h - ${CMAKE_BINARY_DIR}/config/obs-ffmpeg-config.h) - -target_include_directories(obs-ffmpeg PRIVATE ${CMAKE_BINARY_DIR}/config) - -target_link_libraries( - obs-ffmpeg - PRIVATE OBS::libobs - OBS::media-playback - OBS::opts-parser - FFmpeg::avcodec - FFmpeg::avfilter - FFmpeg::avformat - FFmpeg::avdevice - FFmpeg::avutil - FFmpeg::swscale - FFmpeg::swresample) - -if(ENABLE_NEW_MPEGTS_OUTPUT) - target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-mpegts.c obs-ffmpeg-srt.h obs-ffmpeg-rist.h obs-ffmpeg-url.h) - - target_link_libraries(obs-ffmpeg PRIVATE Librist::Librist Libsrt::Libsrt) - if(OS_WINDOWS) - target_link_libraries(obs-ffmpeg PRIVATE ws2_32.lib) - endif() - target_compile_definitions(obs-ffmpeg PRIVATE NEW_MPEGTS_OUTPUT) -endif() - -if(ENABLE_FFMPEG_NVENC) - target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-nvenc.c) - target_compile_definitions(obs-ffmpeg PRIVATE ENABLE_FFMPEG_NVENC) -endif() - -if(ENABLE_FFMPEG_LOGGING) - target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-logging.c) -endif() - -set_target_properties(obs-ffmpeg PROPERTIES FOLDER "plugins/obs-ffmpeg" PREFIX "") - -if(OS_WINDOWS) - find_package(AMF 1.4.29 REQUIRED) - find_package(FFnvcodec 12 REQUIRED) - - add_subdirectory(obs-amf-test) - - if(MSVC) - target_link_libraries(obs-ffmpeg PRIVATE OBS::w32-pthreads) - endif() - target_link_libraries(obs-ffmpeg PRIVATE AMF::AMF FFnvcodec::FFnvcodec) - - set(MODULE_DESCRIPTION "OBS FFmpeg module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-ffmpeg.rc) - - target_sources(obs-ffmpeg PRIVATE texture-amf.cpp texture-amf-opts.hpp obs-ffmpeg.rc) - -elseif(OS_POSIX AND NOT OS_MACOS) - find_package(Libva REQUIRED) - find_package(Libpci REQUIRED) - find_package(Libdrm REQUIRED) - target_sources(obs-ffmpeg PRIVATE obs-ffmpeg-vaapi.c vaapi-utils.c vaapi-utils.h) - target_link_libraries(obs-ffmpeg PRIVATE Libva::va Libva::drm LIBPCI::LIBPCI Libdrm::Libdrm) -endif() - -setup_plugin_target(obs-ffmpeg) diff --git a/plugins/obs-filters/cmake/legacy.cmake b/plugins/obs-filters/cmake/legacy.cmake deleted file mode 100644 index e7675586b5f7a3..00000000000000 --- a/plugins/obs-filters/cmake/legacy.cmake +++ /dev/null @@ -1,188 +0,0 @@ -project(obs-filters) - -option(ENABLE_SPEEXDSP "Enable building with SpeexDSP-based noise suppression filter" ON) -option(ENABLE_RNNOISE "Enable building with RNNoise noise supression filter" ON) - -if(OS_WINDOWS) - option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON) - option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON) -endif() - -add_library(obs-filters MODULE) -add_library(OBS::filters ALIAS obs-filters) - -set(HAS_NOISEREDUCTION OFF) - -if(NOT ENABLE_SPEEXDSP) - obs_status(DISABLED "SpeexDSP") -else() - find_package(Libspeexdsp REQUIRED) - - target_sources(obs-filters PRIVATE noise-suppress-filter.c) - - target_link_libraries(obs-filters PRIVATE LibspeexDSP::LibspeexDSP) - - target_compile_definitions(obs-filters PRIVATE LIBSPEEXDSP_ENABLED) - - if(OS_WINDOWS) - target_link_options(obs-filters PRIVATE "LINKER:/LTCG" "LINKER:/IGNORE:4098" "LINKER:/IGNORE:4099") - endif() -endif() - -if(NOT ENABLE_RNNOISE) - obs_status(DISABLED "RNNoise") -else() - find_package(Librnnoise QUIET) - - if(NOT TARGET Librnnoise::Librnnoise) - obs_status(STATUS "obs-filters -> using bundled RNNoise library") - add_library(obs-rnnoise INTERFACE) - add_library(Librnnoise::Librnnoise ALIAS obs-rnnoise) - - set(_RNNOISE_SOURCES - rnnoise/src/arch.h - rnnoise/src/celt_lpc.c - rnnoise/src/celt_lpc.h - rnnoise/src/common.h - rnnoise/src/denoise.c - rnnoise/src/kiss_fft.c - rnnoise/src/kiss_fft.h - rnnoise/src/opus_types.h - rnnoise/src/pitch.c - rnnoise/src/pitch.h - rnnoise/src/rnn_data.c - rnnoise/src/rnn_data.h - rnnoise/src/rnn_reader.c - rnnoise/src/rnn.c - rnnoise/src/rnn.h - rnnoise/src/tansig_table.h - rnnoise/src/_kiss_fft_guts.h - rnnoise/include/rnnoise.h) - - target_sources(obs-rnnoise INTERFACE ${_RNNOISE_SOURCES}) - - target_include_directories(obs-rnnoise INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/rnnoise/include") - - target_compile_definitions(obs-rnnoise INTERFACE COMPILE_OPUS) - - target_compile_options(obs-rnnoise INTERFACE "$<$:-Wno-null-dereference>") - - if(OS_LINUX) - set_property(SOURCE ${_RNNOISE_SOURCES} PROPERTY COMPILE_FLAGS -fvisibility=hidden) - endif() - - source_group("rnnoise" FILES ${_RNNOISE_SOURCES}) - endif() - - target_sources(obs-filters PRIVATE noise-suppress-filter.c) - - target_link_libraries(obs-filters PRIVATE Librnnoise::Librnnoise) - - target_compile_definitions(obs-filters PRIVATE LIBRNNOISE_ENABLED) -endif() - -if(NOT ENABLE_NVAFX) - obs_status(DISABLED "NVIDIA Audio FX support") - set(LIBNVAFX_FOUND OFF) -else() - obs_status(ENABLED "NVIDIA Audio FX support") - - target_compile_definitions(obs-filters PRIVATE LIBNVAFX_ENABLED) - - set(LIBNVAFX_FOUND ON) -endif() - -if(NOT ENABLE_NVVFX) - obs_status(DISABLED "NVIDIA Video FX support") - set(LIBNVVFX_FOUND OFF) -else() - obs_status(ENABLED "NVIDIA Video FX support") - set(LIBNVVFX_FOUND ON) - target_compile_definitions(obs-filters PRIVATE LIBNVVFX_ENABLED) -endif() - -if(TARGET Librnnoise::Librnnoise - OR TARGET LibspeexDSP::LibspeexDSP - OR LIBNVAFX_FOUND) - target_compile_definitions(obs-filters PRIVATE HAS_NOISEREDUCTION) -endif() - -target_sources( - obs-filters - PRIVATE obs-filters.c - color-correction-filter.c - async-delay-filter.c - gpu-delay.c - hdr-tonemap-filter.c - crop-filter.c - scale-filter.c - scroll-filter.c - chroma-key-filter.c - color-key-filter.c - color-grade-filter.c - sharpness-filter.c - eq-filter.c - gain-filter.c - noise-gate-filter.c - mask-filter.c - invert-audio-polarity.c - compressor-filter.c - limiter-filter.c - expander-filter.c - luma-key-filter.c) - -if(NOT OS_MACOS) - target_sources( - obs-filters - PRIVATE data/blend_add_filter.effect - data/blend_mul_filter.effect - data/blend_sub_filter.effect - data/chroma_key_filter.effect - data/chroma_key_filter_v2.effect - data/color.effect - data/color_correction_filter.effect - data/color_grade_filter.effect - data/color_key_filter.effect - data/color_key_filter_v2.effect - data/crop_filter.effect - data/hdr_tonemap_filter.effect - data/luma_key_filter.effect - data/luma_key_filter_v2.effect - data/mask_alpha_filter.effect - data/mask_color_filter.effect - data/sharpness.effect - data/rtx_greenscreen.effect) - - get_target_property(_SOURCES obs-filters SOURCES) - set(_FILTERS ${_SOURCES}) - list(FILTER _FILTERS INCLUDE REGEX ".*\\.effect") - - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Effect Files" - FILES ${_FILTERS}) -endif() - -if(LIBNVVFX_FOUND) - target_sources(obs-filters PRIVATE nvidia-greenscreen-filter.c) - obs_status(STATUS "NVIDIA Video FX support enabled; requires redist to be installed by end-user") -endif() - -target_include_directories(obs-filters PRIVATE $) - -target_link_libraries(obs-filters PRIVATE OBS::libobs) - -set_target_properties(obs-filters PROPERTIES FOLDER "plugins" PREFIX "") - -if(OS_WINDOWS) - if(MSVC) - target_link_libraries(obs-filters PRIVATE OBS::w32-pthreads) - endif() - - set(MODULE_DESCRIPTION "OBS A/V Filters") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-filters.rc) - - target_sources(obs-filters PRIVATE obs-filters.rc) -endif() - -setup_plugin_target(obs-filters) diff --git a/plugins/obs-libfdk/cmake/legacy.cmake b/plugins/obs-libfdk/cmake/legacy.cmake deleted file mode 100644 index efdb6908f650c4..00000000000000 --- a/plugins/obs-libfdk/cmake/legacy.cmake +++ /dev/null @@ -1,21 +0,0 @@ -project(obs-libfdk) - -option(ENABLE_LIBFDK "Enable FDK AAC support" OFF) - -if(NOT ENABLE_LIBFDK) - obs_status(DISABLED "obs-libfdk") - return() -endif() - -find_package(Libfdk REQUIRED) - -add_library(obs-libfdk MODULE) -add_library(OBS::libfdk ALIAS obs-libfdk) - -target_sources(obs-libfdk PRIVATE obs-libfdk.c) - -target_link_libraries(obs-libfdk PRIVATE OBS::libobs LibFDK::LibFDK) - -set_target_properties(obs-libfdk PROPERTIES FOLDER "plugins" PREFIX "") - -setup_plugin_target(obs-libfdk) diff --git a/plugins/obs-outputs/cmake/legacy.cmake b/plugins/obs-outputs/cmake/legacy.cmake deleted file mode 100644 index fd16b035b5567b..00000000000000 --- a/plugins/obs-outputs/cmake/legacy.cmake +++ /dev/null @@ -1,121 +0,0 @@ -project(obs-outputs) - -if(NOT DEFINED ENABLE_RTMPS) - set(ENABLE_RTMPS - AUTO - CACHE STRING "Enable RTMPS support with mbedTLS" FORCE) - set_property(CACHE ENABLE_RTMPS PROPERTY STRINGS AUTO ON OFF) -endif() - -option(ENABLE_STATIC_MBEDTLS "Enable statically linking mbedTLS into binary" OFF) -mark_as_advanced(ENABLE_STATIC_MBEDTLS) - -add_library(obs-outputs MODULE) -add_library(OBS::outputs ALIAS obs-outputs) - -if(NOT TARGET happy-eyeballs) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/happy-eyeballs" "${CMAKE_BINARY_DIR}/shared/happy-eyeballs") -endif() - -if(NOT TARGET OBS::opts-parser) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") -endif() - -target_sources( - obs-outputs - PRIVATE obs-outputs.c - obs-output-ver.h - flv-mux.c - flv-mux.h - flv-output.c - mp4-mux-internal.h - mp4-mux.c - mp4-mux.h - mp4-output.c - net-if.c - net-if.h - null-output.c - rtmp-helpers.h - rtmp-stream.c - rtmp-stream.h - rtmp-windows.c - rtmp-av1.c - rtmp-av1.h - utils.h - librtmp/amf.c - librtmp/amf.h - librtmp/bytes.h - librtmp/cencode.c - librtmp/cencode.h - librtmp/handshake.h - librtmp/hashswf.c - librtmp/http.h - librtmp/log.c - librtmp/log.h - librtmp/md5.c - librtmp/md5.h - librtmp/parseurl.c - librtmp/rtmp.c - librtmp/rtmp.h - librtmp/rtmp_sys.h) - -if(ENABLE_HEVC) - target_sources(obs-outputs PRIVATE rtmp-hevc.c rtmp-hevc.h) -endif() - -target_link_libraries(obs-outputs PRIVATE OBS::libobs OBS::happy-eyeballs OBS::opts-parser) - -set_target_properties(obs-outputs PROPERTIES FOLDER "plugins" PREFIX "") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS output module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-outputs.rc) - - target_sources(obs-outputs PRIVATE obs-outputs.rc) - - if(MSVC) - target_link_libraries(obs-outputs PRIVATE OBS::w32-pthreads) - target_link_options(obs-outputs PRIVATE "LINKER:/IGNORE:4098" "LINKER:/IGNORE:4099") - endif() - - target_link_libraries(obs-outputs PRIVATE ws2_32 winmm Iphlpapi) -endif() - -if(ENABLE_RTMPS STREQUAL "AUTO" OR ENABLE_RTMPS STREQUAL "ON") - find_package(MbedTLS) - find_package(ZLIB) - if(NOT MBEDTLS_FOUND OR NOT ZLIB_FOUND) - if(ENABLE_RTMPS STREQUAL "ON") - obs_status(FATAL_ERROR "mbedTLS or zlib not found, but required for RTMPS support.") - return() - else() - obs_status(WARNING "mbedTLS or zlib was not found, RTMPS will be automatically disabled.") - target_compile_definitions(obs-outputs PRIVATE NO_CRYPTO) - endif() - else() - target_compile_definitions(obs-outputs PRIVATE USE_MBEDTLS CRYPTO) - - target_link_libraries(obs-outputs PRIVATE Mbedtls::Mbedtls ZLIB::ZLIB) - - if(OS_WINDOWS) - target_link_libraries(obs-outputs PRIVATE crypt32) - - elseif(OS_MACOS) - find_library(FOUNDATION_FRAMEWORK Foundation) - find_library(SECURITY_FRAMEWORK Security) - mark_as_advanced(FOUNDATION_FRAMEWORK SECURITY_FRAMEWORK) - - target_link_libraries(obs-outputs PRIVATE ${FOUNDATION_FRAMEWORK} ${SECURITY_FRAMEWORK}) - set_target_properties(obs-outputs PROPERTIES CXX_VISIBILITY_PRESET hidden) - set_target_properties(obs-outputs PROPERTIES C_VISIBILITY_PRESET hidden) - - elseif(OS_POSIX) - set_target_properties(obs-outputs PROPERTIES CXX_VISIBILITY_PRESET hidden) - set_target_properties(obs-outputs PROPERTIES C_VISIBILITY_PRESET hidden) - endif() - endif() -else() - target_compile_definitions(obs-outputs PRIVATE NO_CRYPTO) -endif() - -setup_plugin_target(obs-outputs) diff --git a/plugins/obs-qsv11/cmake/legacy.cmake b/plugins/obs-qsv11/cmake/legacy.cmake deleted file mode 100644 index 34a445bde2433c..00000000000000 --- a/plugins/obs-qsv11/cmake/legacy.cmake +++ /dev/null @@ -1,60 +0,0 @@ -option(ENABLE_QSV11 "Build Intel QSV11 Hardware Encoder." TRUE) - -if(NOT ENABLE_QSV11) - obs_status(DISABLED "obs-qsv11") - return() -endif() - -project(obs-qsv11) - -add_library(obs-qsv11 MODULE) -add_library(OBS::qsv11 ALIAS obs-qsv11) - -find_package(VPL 2.6 REQUIRED) - -target_sources( - obs-qsv11 - PRIVATE obs-qsv11.c - obs-qsv11-plugin-main.c - common_utils.cpp - common_utils.h - QSV_Encoder.cpp - QSV_Encoder.h - QSV_Encoder_Internal.cpp - QSV_Encoder_Internal.h - bits/linux_defs.h - bits/windows_defs.h) - -target_link_libraries(obs-qsv11 PRIVATE OBS::libobs VPL::VPL) - -if(OS_WINDOWS) - add_subdirectory(obs-qsv-test) - - target_compile_definitions(obs-qsv11 PRIVATE DX11_D3D) - - set(MODULE_DESCRIPTION "OBS QSV encoder") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-qsv11.rc) - - target_sources(obs-qsv11 PRIVATE obs-qsv11.rc common_directx11.cpp common_directx11.h common_utils_windows.cpp) - - target_link_libraries(obs-qsv11 PRIVATE d3d11 dxgi dxguid) - target_link_options(obs-qsv11 PRIVATE /IGNORE:4099) - - target_compile_definitions(obs-qsv11 PRIVATE UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS) -elseif(OS_LINUX) - find_package(Libva REQUIRED) - - target_sources(obs-qsv11 PRIVATE common_utils_linux.cpp) - - target_link_libraries(obs-qsv11 PRIVATE Libva::va Libva::drm) -endif() - -set_target_properties(obs-qsv11 PROPERTIES FOLDER "plugins/obs-qsv11") - -file(GLOB _OBS_QSV11_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.c ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) -file(GLOB _OBS_QSV11_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) - -source_group("obs-qsv11\\Source Files" FILES ${_OBS_QSV11_SOURCE_FILES}) -source_group("obs-qsv11\\Header Files" FILES ${_OBS_QSV11_HEADER_FILES}) - -setup_plugin_target(obs-qsv11) diff --git a/plugins/obs-transitions/cmake/legacy.cmake b/plugins/obs-transitions/cmake/legacy.cmake deleted file mode 100644 index 6153ad784d90b6..00000000000000 --- a/plugins/obs-transitions/cmake/legacy.cmake +++ /dev/null @@ -1,45 +0,0 @@ -project(obs-transitions) - -add_library(obs-transitions MODULE) -add_library(OBS::transition ALIAS obs-transitions) - -target_sources( - obs-transitions - PRIVATE obs-transitions.c - transition-slide.c - transition-swipe.c - transition-fade.c - transition-cut.c - transition-fade-to-color.c - transition-luma-wipe.c - transition-stinger.c) - -if(NOT OS_MACOS) - target_sources( - obs-transitions - PRIVATE data/fade_to_color_transition.effect data/fade_transition.effect data/luma_wipe_transition.effect - data/slide_transition.effect data/stinger_matte_transition.effect data/swipe_transition.effect) - - get_target_property(_SOURCES obs-transitions SOURCES) - set(_FILTERS ${_SOURCES}) - list(FILTER _FILTERS INCLUDE REGEX ".*\\.effect") - - source_group( - TREE "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "Effect Files" - FILES ${_FILTERS}) -endif() - -target_link_libraries(obs-transitions PRIVATE OBS::libobs) - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Transitions module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-transitions.rc) - - target_sources(obs-transitions PRIVATE obs-transitions.rc) - -endif() - -set_target_properties(obs-transitions PROPERTIES FOLDER "plugins" PREFIX "") - -setup_plugin_target(obs-transitions) diff --git a/plugins/obs-vst/cmake/legacy.cmake b/plugins/obs-vst/cmake/legacy.cmake deleted file mode 100644 index a605664975b1d7..00000000000000 --- a/plugins/obs-vst/cmake/legacy.cmake +++ /dev/null @@ -1,79 +0,0 @@ -project(obs-vst) - -option(ENABLE_VST "Enable building OBS with VST plugin" ON) - -if(NOT ENABLE_VST) - message(STATUS "OBS: DISABLED obs-vst") - return() -endif() - -option(ENABLE_VST_BUNDLED_HEADERS "Build with Bundled Headers" ON) -mark_as_advanced(ENABLE_VST_BUNDLED_HEADERS) - -add_library(obs-vst MODULE) -add_library(OBS::vst ALIAS obs-vst) - -find_qt(COMPONENTS Widgets) - -set_target_properties( - obs-vst - PROPERTIES AUTOMOC ON - AUTOUIC ON - AUTORCC ON) - -if(OS_WINDOWS) - set_target_properties(obs-vst PROPERTIES AUTORCC_OPTIONS "--format-version;1") -endif() - -target_include_directories(obs-vst PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - -target_sources(obs-vst PRIVATE obs-vst.cpp VSTPlugin.cpp EditorWidget.cpp headers/vst-plugin-callbacks.hpp - headers/EditorWidget.h headers/VSTPlugin.h) - -target_link_libraries(obs-vst PRIVATE OBS::libobs Qt::Widgets) - -target_include_directories(obs-vst PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/headers) - -target_compile_features(obs-vst PRIVATE cxx_std_17) - -if(ENABLE_VST_BUNDLED_HEADERS) - message(STATUS "OBS: - obs-vst uses bundled VST headers") - - target_sources(obs-vst PRIVATE vst_header/aeffectx.h) - - target_include_directories(obs-vst PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vst_header) -else() - set(VST_INCLUDE_DIR - "" - CACHE PATH "Path to Steinburg headers (e.g. C:/VST3 SDK/pluginterfaces/vst2.x)" FORCE) - mark_as_advanced(VST_INCLUDE_DIR) - - message(WARNING "OBS: You should only use the Steinburg headers for debugging or local builds. " - "It is illegal to distribute the Steinburg headers with anything, and " - "possibly against the GPL to distribute the binaries from the resultant compile.") - - target_sources(obs-vst PRIVATE ${VST_INCLUDE_DIR}/aeffectx.h) -endif() - -if(OS_MACOS) - find_library(FOUNDATION Foundation) - find_library(COCOA Cocoa) - mark_as_advanced(COCOA FOUNDATION) - - target_sources(obs-vst PRIVATE mac/VSTPlugin-osx.mm mac/EditorWidget-osx.mm) - - target_link_libraries(obs-vst PRIVATE ${COCOA} ${FOUNDATION}) - -elseif(OS_WINDOWS) - target_sources(obs-vst PRIVATE win/VSTPlugin-win.cpp win/EditorWidget-win.cpp) - - target_compile_definitions(obs-vst PRIVATE UNICODE _UNICODE) - -elseif(OS_POSIX) - target_sources(obs-vst PRIVATE linux/VSTPlugin-linux.cpp linux/EditorWidget-linux.cpp) - -endif() - -set_target_properties(obs-vst PROPERTIES FOLDER "plugins" PREFIX "") - -setup_plugin_target(obs-vst) diff --git a/plugins/obs-webrtc/cmake/legacy.cmake b/plugins/obs-webrtc/cmake/legacy.cmake deleted file mode 100644 index 76d9d0903ad434..00000000000000 --- a/plugins/obs-webrtc/cmake/legacy.cmake +++ /dev/null @@ -1,22 +0,0 @@ -project(obs-webrtc) - -option(ENABLE_WEBRTC "Enable WebRTC Output support" ON) -if(NOT ENABLE_WEBRTC) - obs_status(DISABLED "obs-webrtc") - return() -endif() - -find_package(LibDataChannel 0.20 REQUIRED) -find_package(CURL REQUIRED) - -add_library(obs-webrtc MODULE) -add_library(OBS::webrtc ALIAS obs-webrtc) - -target_sources(obs-webrtc PRIVATE obs-webrtc.cpp whip-output.cpp whip-output.h whip-service.cpp whip-service.h - whip-utils.h) - -target_link_libraries(obs-webrtc PRIVATE OBS::libobs LibDataChannel::LibDataChannel CURL::libcurl) - -set_target_properties(obs-webrtc PROPERTIES FOLDER "plugins") - -setup_plugin_target(obs-webrtc) diff --git a/plugins/obs-x264/cmake/legacy.cmake b/plugins/obs-x264/cmake/legacy.cmake deleted file mode 100644 index c9d0fa21317c24..00000000000000 --- a/plugins/obs-x264/cmake/legacy.cmake +++ /dev/null @@ -1,34 +0,0 @@ -project(obs-x264) - -find_package(Libx264 REQUIRED) - -add_library(obs-x264 MODULE) -add_library(OBS::x264 ALIAS obs-x264) -add_executable(obs-x264-test) - -if(NOT TARGET OBS::opts-parser) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/opts-parser" "${CMAKE_BINARY_DIR}/shared/opts-parser") -endif() - -target_sources(obs-x264-test PRIVATE obs-x264-test.c) - -target_link_libraries(obs-x264-test PRIVATE OBS::opts-parser) - -target_sources(obs-x264 PRIVATE obs-x264.c obs-x264-plugin-main.c) - -target_link_libraries(obs-x264 PRIVATE LIBX264::LIBX264 OBS::opts-parser) - -set_target_properties(obs-x264 PROPERTIES FOLDER "plugins" PREFIX "") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS x264 encoder") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-x264.rc) - - target_sources(obs-x264 PRIVATE obs-x264.rc) - -endif() - -set_target_properties(obs-x264-test PROPERTIES FOLDER "plugins") -add_test(NAME obs-x264-test COMMAND obs-x264-test) - -setup_plugin_target(obs-x264) diff --git a/plugins/oss-audio/cmake/legacy.cmake b/plugins/oss-audio/cmake/legacy.cmake deleted file mode 100644 index b538cf4db21342..00000000000000 --- a/plugins/oss-audio/cmake/legacy.cmake +++ /dev/null @@ -1,27 +0,0 @@ -project(oss-audio) - -option(ENABLE_OSS "Enable building with OSS audio support" ON) - -if(NOT ENABLE_OSS) - obs_status(DISABLED "oss-audio") - return() -endif() - -find_package(OSS REQUIRED) - -add_library(oss-audio MODULE) -add_library(OBS::oss-audio ALIAS oss-audio) - -target_sources(oss-audio PRIVATE oss-audio.c oss-input.c) - -target_link_libraries(oss-audio PRIVATE OBS::libobs OSS::OSS) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/oss-platform.h.in oss-platform.h) - -target_include_directories(oss-audio PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -target_sources(oss-audio PRIVATE oss-platform.h) - -set_target_properties(oss-audio PROPERTIES FOLDER "plugins") - -setup_plugin_target(oss-audio) diff --git a/plugins/rtmp-services/cmake/legacy.cmake b/plugins/rtmp-services/cmake/legacy.cmake deleted file mode 100644 index 92a925d76c6b10..00000000000000 --- a/plugins/rtmp-services/cmake/legacy.cmake +++ /dev/null @@ -1,55 +0,0 @@ -project(rtmp-services) - -option(ENABLE_SERVICE_UPDATES "Checks for service updates" ON) - -set(RTMP_SERVICES_URL - "https://obsproject.com/obs2_update/rtmp-services" - CACHE STRING "Default services package URL" FORCE) - -mark_as_advanced(RTMP_SERVICES_URL) - -add_library(rtmp-services MODULE) -add_library(OBS::rtmp-services ALIAS rtmp-services) - -find_package(Jansson 2.5 REQUIRED) - -if(NOT TARGET OBS::file-updater) - add_subdirectory("${CMAKE_SOURCE_DIR}/shared/file-updater" "${CMAKE_BINARY_DIR}/shared/file-updater") -endif() - -target_sources( - rtmp-services - PRIVATE service-specific/twitch.c - service-specific/twitch.h - service-specific/nimotv.c - service-specific/nimotv.h - service-specific/showroom.c - service-specific/showroom.h - service-specific/dacast.c - service-specific/dacast.h - rtmp-common.c - rtmp-custom.c - rtmp-services-main.c - rtmp-format-ver.h) - -target_compile_definitions(rtmp-services PRIVATE SERVICES_URL="${RTMP_SERVICES_URL}" - $<$:ENABLE_SERVICE_UPDATES>) - -target_include_directories(rtmp-services PRIVATE ${CMAKE_BINARY_DIR}/config) - -target_link_libraries(rtmp-services PRIVATE OBS::libobs OBS::file-updater Jansson::Jansson) - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS RTMP Services") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in rtmp-services.rc) - - target_sources(rtmp-services PRIVATE rtmp-services.rc) - - if(MSVC) - target_link_options(rtmp-services PRIVATE "LINKER:/IGNORE:4098") - endif() -endif() - -set_target_properties(rtmp-services PROPERTIES FOLDER "plugins" PREFIX "") - -setup_plugin_target(rtmp-services) diff --git a/plugins/sndio/cmake/legacy.cmake b/plugins/sndio/cmake/legacy.cmake deleted file mode 100644 index 850f685e34c3b5..00000000000000 --- a/plugins/sndio/cmake/legacy.cmake +++ /dev/null @@ -1,20 +0,0 @@ -project(sndio) - -option(ENABLE_SNDIO "Build OBS with sndio support" OFF) -if(NOT ENABLE_SNDIO) - obs_status(DISABLED "sndio") - return() -endif() - -find_package(Sndio REQUIRED) - -add_library(sndio MODULE) -add_library(OBS::sndio ALIAS sndio) - -target_sources(sndio PRIVATE sndio.c sndio-input.c) - -target_link_libraries(sndio PRIVATE OBS::libobs Sndio::Sndio) - -set_target_properties(sndio PROPERTIES FOLDER "plugins") - -setup_plugin_target(sndio) diff --git a/plugins/text-freetype2/cmake/legacy.cmake b/plugins/text-freetype2/cmake/legacy.cmake deleted file mode 100644 index 598ef81a9affe4..00000000000000 --- a/plugins/text-freetype2/cmake/legacy.cmake +++ /dev/null @@ -1,46 +0,0 @@ -project(text-freetype2) - -option(ENABLE_FREETYPE "Enable FreeType text plugin" ON) - -if(NOT ENABLE_FREETYPE) - obs_status(DISABLED "text-freetype2") - return() -endif() - -find_package(Freetype REQUIRED) - -add_library(text-freetype2 MODULE) -add_library(OBS::text-freetype2 ALIAS text-freetype2) - -target_sources(text-freetype2 PRIVATE find-font.h obs-convenience.c text-functionality.c text-freetype2.c - obs-convenience.h text-freetype2.h) - -target_link_libraries(text-freetype2 PRIVATE OBS::libobs Freetype::Freetype) - -set_target_properties(text-freetype2 PROPERTIES FOLDER "plugins" PREFIX "") - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS FreeType text module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in text-freetype2.rc) - - target_sources(text-freetype2 PRIVATE find-font.c find-font-windows.c text-freetype2.rc) - target_link_options(text-freetype2 PRIVATE "LINKER:/IGNORE:4098" "LINKER:/IGNORE:4099") - -elseif(OS_MACOS) - find_package(Iconv REQUIRED) - find_library(COCOA Cocoa) - mark_as_advanced(COCOA) - - target_sources(text-freetype2 PRIVATE find-font.c find-font-cocoa.m find-font-iconv.c) - - target_link_libraries(text-freetype2 PRIVATE Iconv::Iconv ${COCOA}) - -elseif(OS_POSIX) - find_package(Fontconfig REQUIRED) - - target_sources(text-freetype2 PRIVATE find-font-unix.c) - - target_link_libraries(text-freetype2 PRIVATE Fontconfig::Fontconfig) -endif() - -setup_plugin_target(text-freetype2) diff --git a/plugins/vlc-video/cmake/legacy.cmake b/plugins/vlc-video/cmake/legacy.cmake deleted file mode 100644 index c7fd1bdc02ca28..00000000000000 --- a/plugins/vlc-video/cmake/legacy.cmake +++ /dev/null @@ -1,35 +0,0 @@ -project(vlc-video) - -option(ENABLE_VLC "Build OBS with VLC plugin support" ${OS_LINUX}) -if(NOT ENABLE_VLC) - obs_status(DISABLED "vlc-video") - obs_status( - WARNING - "VLC plugin supported is not enabled by default - please switch ENABLE_VLC to ON to enable this functionality.") - return() -endif() - -find_package(LibVLC REQUIRED) - -add_library(vlc-video MODULE) -add_library(OBS::vlc-video ALIAS vlc-video) - -target_sources(vlc-video PRIVATE vlc-video-plugin.c vlc-video-plugin.h vlc-video-source.c) - -target_link_libraries(vlc-video PRIVATE OBS::libobs VLC::LibVLC) - -if(OS_WINDOWS) - if(MSVC) - target_link_libraries(vlc-video PRIVATE OBS::w32-pthreads) - endif() - - set(MODULE_DESCRIPTION "OBS VLC Plugin") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in vlc-video.rc) - - target_sources(vlc-video PRIVATE vlc-video.rc) - -endif() - -set_target_properties(vlc-video PROPERTIES FOLDER "plugins" PREFIX "") - -setup_plugin_target(vlc-video) diff --git a/shared/media-playback/CMakeLists.txt b/shared/media-playback/CMakeLists.txt index 59321510ebf498..5b2d4ebba0619b 100644 --- a/shared/media-playback/CMakeLists.txt +++ b/shared/media-playback/CMakeLists.txt @@ -21,9 +21,4 @@ target_sources( target_include_directories(media-playback INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") -if(OBS_CMAKE_VERSION VERSION_LESS 3.0) - target_compile_options(media-playback INTERFACE ${ARCH_SIMD_FLAGS}) - target_compile_definitions(media-playback INTERFACE ${ARCH_SIMD_DEFINES}) -endif() - target_link_libraries(media-playback INTERFACE FFmpeg::avcodec FFmpeg::avdevice FFmpeg::avutil FFmpeg::avformat) diff --git a/shared/obs-scripting/cmake/legacy.cmake b/shared/obs-scripting/cmake/legacy.cmake deleted file mode 100644 index 75a7d72aa3e7ac..00000000000000 --- a/shared/obs-scripting/cmake/legacy.cmake +++ /dev/null @@ -1,181 +0,0 @@ -option(ENABLE_SCRIPTING_LUA "Enable Lua scripting support" ON) -option(ENABLE_SCRIPTING_PYTHON "Enable Python scripting support" ON) - -if(NOT ENABLE_SCRIPTING) - obs_status(DISABLED "obs-scripting") - return() -endif() - -project(obs-scripting) - -if(ENABLE_SCRIPTING_LUA) - add_subdirectory(obslua) - find_package(Luajit) - - if(NOT TARGET Luajit::Luajit) - obs_status(FATAL_ERROR "obs-scripting -> Luajit not found.") - return() - else() - obs_status(STATUS "obs-scripting -> Luajit found.") - endif() -else() - obs_status(DISABLED "Luajit support") -endif() - -if(ENABLE_SCRIPTING_PYTHON) - add_subdirectory(obspython) - if(OS_WINDOWS) - find_package(PythonWindows) - else() - find_package(Python COMPONENTS Interpreter Development) - endif() - - if(NOT TARGET Python::Python) - obs_status(FATAL_ERROR "obs-scripting -> Python not found.") - return() - else() - obs_status(STATUS "obs-scripting -> Python ${Python_VERSION} found.") - endif() -else() - obs_status(DISABLED "Python support") -endif() - -if(NOT TARGET Luajit::Luajit AND NOT TARGET Python::Python) - obs_status(WARNING "obs-scripting -> No supported scripting libraries found.") - return() -endif() - -if(OS_MACOS) - find_package(SWIG 4 REQUIRED) -elseif(OS_POSIX) - find_package(SWIG 3 REQUIRED) -elseif(OS_WINDOWS) - find_package(SwigWindows 3 REQUIRED) -endif() - -add_library(obs-scripting SHARED) -add_library(OBS::scripting ALIAS obs-scripting) - -target_sources( - obs-scripting - PUBLIC obs-scripting.h - PRIVATE obs-scripting.c cstrcache.cpp cstrcache.h obs-scripting-logging.c obs-scripting-callback.h) - -target_link_libraries(obs-scripting PRIVATE OBS::libobs) - -target_compile_features(obs-scripting PRIVATE cxx_auto_type) - -target_include_directories(obs-scripting PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -if(OS_WINDOWS) - set(MODULE_DESCRIPTION "OBS Studio scripting module") - configure_file(${CMAKE_SOURCE_DIR}/cmake/bundle/windows/obs-module.rc.in obs-scripting.rc) - - target_sources(obs-scripting PRIVATE obs-scripting.rc) - - target_link_libraries(obs-scripting PRIVATE OBS::w32-pthreads) - -elseif(OS_MACOS) - target_link_libraries(obs-scripting PRIVATE objc) -endif() - -set_target_properties( - obs-scripting - PROPERTIES FOLDER "scripting" - VERSION "${OBS_VERSION_MAJOR}" - SOVERSION "1") - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/swig) - -if(TARGET Luajit::Luajit) - add_custom_command( - OUTPUT swig/swigluarun.h - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E env "SWIG_LIB=${SWIG_DIR}" ${SWIG_EXECUTABLE} -lua -external-runtime swig/swigluarun.h - COMMENT "obs-scripting - generating Luajit SWIG interface headers") - - set_source_files_properties(swig/swigluarun.h PROPERTIES GENERATED ON) - - target_link_libraries(obs-scripting PRIVATE Luajit::Luajit) - - target_compile_definitions(obs-scripting PUBLIC LUAJIT_FOUND) - - target_sources(obs-scripting PRIVATE obs-scripting-lua.c obs-scripting-lua.h obs-scripting-lua-source.c - ${CMAKE_CURRENT_BINARY_DIR}/swig/swigluarun.h) - - target_include_directories(obs-scripting PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - - if(ENABLE_UI) - target_link_libraries(obs-scripting PRIVATE OBS::frontend-api) - - target_sources(obs-scripting PRIVATE obs-scripting-lua-frontend.c) - - target_compile_definitions(obs-scripting PRIVATE UI_ENABLED=ON) - endif() - -endif() - -if(TARGET Python::Python) - add_custom_command( - OUTPUT swig/swigpyrun.h - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - PRE_BUILD - COMMAND - ${CMAKE_COMMAND} -E env "SWIG_LIB=${SWIG_DIR}" ${SWIG_EXECUTABLE} -python - $,$>>,-py3,-py3-stable-abi> -external-runtime - swig/swigpyrun.h - COMMENT "obs-scripting - generating Python 3 SWIG interface headers") - - set_source_files_properties(swig/swigpyrun.h PROPERTIES GENERATED ON) - - target_sources(obs-scripting PRIVATE obs-scripting-python.c obs-scripting-python.h obs-scripting-python-import.h - ${CMAKE_CURRENT_BINARY_DIR}/swig/swigpyrun.h) - - target_compile_definitions( - obs-scripting - PRIVATE ENABLE_SCRIPTING PYTHON_LIB="$" - PUBLIC Python_FOUND) - - target_include_directories(obs-scripting PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - - get_filename_component(_PYTHON_PATH "${Python_LIBRARIES}" PATH) - get_filename_component(_PYTHON_FILE "${Python_LIBRARIES}" NAME) - - string(REGEX REPLACE "\\.[^.]*$" "" _PYTHON_FILE ${_PYTHON_FILE}) - - if(OS_WINDOWS) - string(REGEX REPLACE "_d" "" _PYTHON_FILE ${_PYTHON_FILE}) - endif() - set(OBS_SCRIPT_PYTHON_PATH "${_PYTHON_FILE}") - - unset(_PYTHON_FILE) - unset(_PYTHON_PATH) - - if(OS_WINDOWS OR OS_MACOS) - target_include_directories(obs-scripting PRIVATE ${Python_INCLUDE_DIRS}) - - target_sources(obs-scripting PRIVATE obs-scripting-python-import.c) - if(OS_MACOS) - target_link_options(obs-scripting PRIVATE -undefined dynamic_lookup) - endif() - else() - target_link_libraries(obs-scripting PRIVATE Python::Python) - endif() - - if(ENABLE_UI) - target_link_libraries(obs-scripting PRIVATE OBS::frontend-api) - - target_sources(obs-scripting PRIVATE obs-scripting-python-frontend.c) - - target_compile_definitions(obs-scripting PRIVATE UI_ENABLED=ON) - endif() -endif() - -target_compile_definitions(obs-scripting PRIVATE SCRIPT_DIR="${OBS_SCRIPT_PLUGIN_PATH}" - $<$:ENABLE_UI>) - -setup_binary_target(obs-scripting) - -# Dirty workaround: CMake 2.0 seems to fail without this file -file(TOUCH "${CMAKE_BINARY_DIR}/shared/cmake_install.cmake") diff --git a/shared/obs-scripting/obslua/cmake/legacy.cmake b/shared/obs-scripting/obslua/cmake/legacy.cmake deleted file mode 100644 index d032a47b18e719..00000000000000 --- a/shared/obs-scripting/obslua/cmake/legacy.cmake +++ /dev/null @@ -1,63 +0,0 @@ -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif() - -if(POLICY CMP0078) - cmake_policy(SET CMP0078 NEW) -endif() - -project(obslua) - -find_package(Luajit REQUIRED) - -if(OS_MACOS) - find_package(SWIG 4 REQUIRED) -elseif(OS_POSIX) - find_package(SWIG 3 REQUIRED) -elseif(OS_WINDOWS) - find_package(SwigWindows 3 REQUIRED) -endif() -include(UseSWIG) - -set_source_files_properties(obslua.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES TRUE) - -swig_add_library( - obslua - LANGUAGE lua - TYPE MODULE - SOURCES obslua.i ../cstrcache.cpp ../cstrcache.h) - -target_link_libraries(obslua PRIVATE OBS::scripting OBS::libobs Luajit::Luajit) - -list(APPEND _SWIG_DEFINITIONS "SWIG_TYPE_TABLE=obslua" "SWIG_LUA_INTERPRETER_NO_DEBUG") - -set_target_properties( - obslua - PROPERTIES FOLDER "scripting" - VERSION "${OBS_VERSION_MAJOR}" - SOVERSION "${OBS_VERSION_CANONICAL}") - -target_compile_definitions(obslua PRIVATE SWIG_TYPE_TABLE=obslua SWIG_LUA_INTERPRETER_NO_DEBUG) - -if(ENABLE_UI) - list(APPEND _SWIG_DEFINITIONS "ENABLE_UI") - target_link_libraries(obslua PRIVATE OBS::frontend-api) - - target_compile_definitions(obslua PRIVATE ENABLE_UI) -endif() - -set_target_properties(obslua PROPERTIES SWIG_COMPILE_DEFINITIONS "${_SWIG_DEFINITIONS}") - -if(OS_WINDOWS) - if(MSVC) - target_compile_options(obslua PRIVATE /wd4054 /wd4197 /wd4244 /wd4267) - endif() -elseif(OS_MACOS) - set_target_properties(obslua PROPERTIES MACHO_CURRENT_VERSION 0 MACHO_COMPATIBILITY_VERSION 0) -endif() - -if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND SWIG_VERSION VERSION_LESS "4.1") - target_compile_options(obslua PRIVATE -Wno-maybe-uninitialized) -endif() - -setup_script_plugin_target(obslua) diff --git a/shared/obs-scripting/obspython/cmake/legacy.cmake b/shared/obs-scripting/obspython/cmake/legacy.cmake deleted file mode 100644 index 572ec0546fe585..00000000000000 --- a/shared/obs-scripting/obspython/cmake/legacy.cmake +++ /dev/null @@ -1,107 +0,0 @@ -if(POLICY CMP0078) - cmake_policy(SET CMP0078 NEW) -endif() - -if(POLICY CMP0086) - cmake_policy(SET CMP0086 NEW) -endif() - -project(obspython) - -if(OS_MACOS) - find_package(Python REQUIRED COMPONENTS Interpreter Development) - find_package(SWIG 4 REQUIRED) -elseif(OS_POSIX) - find_package(Python REQUIRED COMPONENTS Interpreter Development) - find_package(SWIG 3 REQUIRED) -elseif(OS_WINDOWS) - find_package(PythonWindows REQUIRED) - find_package(SwigWindows 3 REQUIRED) -endif() -include(UseSWIG) - -set_source_files_properties( - obspython.i PROPERTIES USE_TARGET_INCLUDE_DIRECTORIES TRUE - SWIG_FLAGS "$,$>>,-py3,-py3-stable-abi>") - -swig_add_library( - obspython - LANGUAGE python - TYPE MODULE - SOURCES obspython.i ../cstrcache.cpp ../cstrcache.h) - -file( - GENERATE - OUTPUT $<$:$/>obspython.h - CONTENT "#pragma once\n\n#define PYTHON_LIB \"$\"") - -target_include_directories(obspython PRIVATE "$<$:${CMAKE_CURRENT_BINARY_DIR}/$>" - "${CMAKE_CURRENT_BINARY_DIR}") - -target_link_libraries(obspython PRIVATE OBS::scripting OBS::libobs) - -list(APPEND _SWIG_DEFINITIONS "SWIG_TYPE_TABLE=obspython" "Py_ENABLE_SHARED=1" "SWIG_PYTHON_INTERPRETER_NO_DEBUG") - -target_compile_features(obspython PRIVATE cxx_auto_type c_std_11) - -target_compile_definitions(obspython PRIVATE SWIG_TYPE_TABLE=obspython Py_ENABLE_SHARED=1 - SWIG_PYTHON_INTERPRETER_NO_DEBUG) - -if(ENABLE_UI) - list(APPEND _SWIG_DEFINITIONS "ENABLE_UI") - target_link_libraries(obspython PRIVATE OBS::frontend-api) - - target_compile_definitions(obspython PRIVATE ENABLE_UI) -endif() - -set_target_properties(obspython PROPERTIES SWIG_COMPILE_DEFINITIONS "${_SWIG_DEFINITIONS}") - -if(OS_WINDOWS) - set_target_properties(obspython PROPERTIES SWIG_COMPILE_DEFINITIONS "${_SWIG_DEFINITIONS};MS_NO_COREDLL") - - target_link_libraries(obspython PRIVATE Python::Python) - - target_compile_options(obspython PRIVATE /wd4100 /wd4197) - - if(MSVC) - add_custom_command( - TARGET obspython - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" - "${CMAKE_CURRENT_BINARY_DIR}/$/obspython.py" - VERBATIM) - endif() - -elseif(OS_MACOS) - get_target_property(_PYTHON_INCLUDE_DIRECTORY Python::Python INTERFACE_INCLUDE_DIRECTORIES) - - target_include_directories(obspython PRIVATE ${_PYTHON_INCLUDE_DIRECTORY}) - - target_link_options(obspython PRIVATE -undefined dynamic_lookup) - - target_compile_options(obspython PRIVATE -Wno-unused-parameter -Wno-error=macro-redefined) - - if(XCODE) - add_custom_command( - TARGET obspython - POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_BINARY_DIR}/obspython.py" - "${CMAKE_CURRENT_BINARY_DIR}/$/obspython.py" - VERBATIM) - endif() - - set_target_properties(obspython PROPERTIES MACHO_CURRENT_VERSION 0 MACHO_COMPATIBILITY_VERSION 0) -elseif(OS_POSIX) - target_link_libraries(obspython PRIVATE Python::Python) - - target_compile_options(obspython PRIVATE -Wno-unused-parameter) - -endif() - -set_target_properties( - obspython - PROPERTIES FOLDER "scripting" - VERSION "${OBS_VERSION_MAJOR}" - SOVERSION "${OBS_VERSION_CANONICAL}") - -setup_script_plugin_target(obspython) From cba6ed40d2e86d324a186528a220b951b08171f1 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Tue, 27 Aug 2024 18:19:44 -0400 Subject: [PATCH 0461/1073] win-dshow: Fix CMake preventing Virtual Camera from working The code prior to this change would never add virtualcam.c to the win-dshow target, which resulted in the virtualcam_output not being registered and thus Virtual Camera not working. Co-authored-by: PatTheMav --- plugins/win-dshow/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/win-dshow/CMakeLists.txt b/plugins/win-dshow/CMakeLists.txt index f98c39c6630def..c04c90fb074c6e 100644 --- a/plugins/win-dshow/CMakeLists.txt +++ b/plugins/win-dshow/CMakeLists.txt @@ -44,14 +44,14 @@ target_link_libraries( winmm ) +add_subdirectory(virtualcam-module) + if(TARGET OBS::virtualcam AND TARGET OBS::virtualcam-guid) target_sources(win-dshow PRIVATE virtualcam.c) - target_link_libraries(win-dshow PRIVATE OBS::virtualcam-interface OBS::virtualcam-guid) + target_link_libraries(win-dshow PRIVATE OBS::virtualcam-guid) target_compile_definitions(win-dshow PRIVATE VIRTUALCAM_AVAILABLE) add_dependencies(win-dshow obs-virtualcam-module) endif() set_target_properties_obs(win-dshow PROPERTIES FOLDER plugins/win-dshow PREFIX "") - -add_subdirectory(virtualcam-module) From f0d619521bf88279aba647c6ee6f1785c1230209 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 28 Aug 2024 16:18:59 -0400 Subject: [PATCH 0462/1073] libobs: Fix Windows x86 CMake when using reduced obs-deps package A recent obs-deps change removed all non-essential x86 deps. This caused the x86 subproject(s) on Windows to fail to configure due to being unable to find x86 dependencies that do not exist. Co-authored-by: PatTheMav --- libobs/CMakeLists.txt | 5 +++++ libobs/cmake/os-windows.cmake | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 5142078e9815b9..d6d91567f3f716 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -4,6 +4,11 @@ legacy_check() include(cmake/obs-version.cmake) +if(OS_WINDOWS AND NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + include(cmake/os-windows.cmake) + return() +endif() + find_package(Threads REQUIRED) find_package(FFmpeg 6.1 REQUIRED avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec) diff --git a/libobs/cmake/os-windows.cmake b/libobs/cmake/os-windows.cmake index 2f56e9fd823de5..59d6ec35bb77f2 100644 --- a/libobs/cmake/os-windows.cmake +++ b/libobs/cmake/os-windows.cmake @@ -30,6 +30,10 @@ if(NOT TARGET OBS::w32-pthreads) add_subdirectory("${CMAKE_SOURCE_DIR}/deps/w32-pthreads" "${CMAKE_BINARY_DIR}/deps/w32-pthreads") endif() +if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + return() +endif() + configure_file(cmake/windows/obs-module.rc.in libobs.rc) target_sources( From 1703361dbab9db2f92cf90beb486d5fb1f7ec1df Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 08:38:54 +0200 Subject: [PATCH 0463/1073] libobs: Remove obs_{duplicate,free}_encoder_packet Deprecated in 0.2.4(!) over 10 years(!!) ago. --- libobs/obs-encoder.c | 13 ------------- libobs/obs.h | 10 ---------- 2 files changed, 23 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index e8916c6cffae5f..1daf94e35124b0 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -1768,19 +1768,6 @@ void obs_encoder_packet_create_instance(struct encoder_packet *dst, memcpy(dst->data, src->data, src->size); } -/* OBS_DEPRECATED */ -void obs_duplicate_encoder_packet(struct encoder_packet *dst, - const struct encoder_packet *src) -{ - obs_encoder_packet_create_instance(dst, src); -} - -/* OBS_DEPRECATED */ -void obs_free_encoder_packet(struct encoder_packet *packet) -{ - obs_encoder_packet_release(packet); -} - void obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src) { diff --git a/libobs/obs.h b/libobs/obs.h index b1105a7f4c315d..249c9dfb04334a 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2596,16 +2596,6 @@ EXPORT const char *obs_encoder_get_id(const obs_encoder_t *encoder); EXPORT uint32_t obs_get_encoder_caps(const char *encoder_id); EXPORT uint32_t obs_encoder_get_caps(const obs_encoder_t *encoder); -#ifndef SWIG -/** Duplicates an encoder packet */ -OBS_DEPRECATED -EXPORT void obs_duplicate_encoder_packet(struct encoder_packet *dst, - const struct encoder_packet *src); - -OBS_DEPRECATED -EXPORT void obs_free_encoder_packet(struct encoder_packet *packet); -#endif - EXPORT void obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src); EXPORT void obs_encoder_packet_release(struct encoder_packet *packet); From 07aa98ab34596ff8f7a2e6add6e7997c9d61b3a9 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:31:52 +0200 Subject: [PATCH 0464/1073] libobs: Remove obs_get_default_rect_effect() Originally removed in 0.12.1 but "temporarily" brought back, then never removed. --- libobs/obs.c | 6 ------ libobs/obs.h | 6 ------ 2 files changed, 12 deletions(-) diff --git a/libobs/obs.c b/libobs/obs.c index fad764868995c4..b821ec7b71fa2e 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -2143,12 +2143,6 @@ gs_effect_t *obs_get_base_effect(enum obs_base_effect effect) return NULL; } -/* OBS_DEPRECATED */ -gs_effect_t *obs_get_default_rect_effect(void) -{ - return obs->video.default_rect_effect; -} - signal_handler_t *obs_get_signal_handler(void) { return obs->signals; diff --git a/libobs/obs.h b/libobs/obs.h index 249c9dfb04334a..771e63a6cc5fd5 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -746,12 +746,6 @@ enum obs_base_effect { /** Returns a commonly used base effect */ EXPORT gs_effect_t *obs_get_base_effect(enum obs_base_effect effect); -#ifndef SWIG -/* DEPRECATED: gets texture_rect default effect */ -OBS_DEPRECATED -EXPORT gs_effect_t *obs_get_default_rect_effect(void); -#endif - /** Returns the primary obs signal handler */ EXPORT signal_handler_t *obs_get_signal_handler(void); From bda463932e7058f3f63a7e82c58aa5de1b900411 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 08:45:19 +0200 Subject: [PATCH 0465/1073] docs,libobs: Remove obs_render_main_view() Deprecated since 21.0 --- docs/sphinx/reference-core.rst | 8 -------- libobs/obs.c | 6 ------ libobs/obs.h | 6 ------ 3 files changed, 20 deletions(-) diff --git a/docs/sphinx/reference-core.rst b/docs/sphinx/reference-core.rst index 576cd0351a2b92..c624c7bbf00dc0 100644 --- a/docs/sphinx/reference-core.rst +++ b/docs/sphinx/reference-core.rst @@ -501,14 +501,6 @@ Video, Audio, and Graphics --------------------- -.. function:: void obs_render_main_view(void) - - Renders the main view. - - Note: This function is deprecated. - ---------------------- - .. function:: void obs_render_main_texture(void) Renders the main output texture. Useful for rendering a preview pane diff --git a/libobs/obs.c b/libobs/obs.c index b821ec7b71fa2e..c0036729de9b45 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -2153,12 +2153,6 @@ proc_handler_t *obs_get_proc_handler(void) return obs->procs; } -/* OBS_DEPRECATED */ -void obs_render_main_view(void) -{ - obs_view_render(&obs->data.main_view); -} - static void obs_render_main_texture_internal(enum gs_blend_type src_c, enum gs_blend_type dest_c, enum gs_blend_type src_a, diff --git a/libobs/obs.h b/libobs/obs.h index 771e63a6cc5fd5..25b5eb13ff2fb0 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -752,12 +752,6 @@ EXPORT signal_handler_t *obs_get_signal_handler(void); /** Returns the primary obs procedure handler */ EXPORT proc_handler_t *obs_get_proc_handler(void); -#ifndef SWIG -/** Renders the main view */ -OBS_DEPRECATED -EXPORT void obs_render_main_view(void); -#endif - /** Renders the last main output texture */ EXPORT void obs_render_main_texture(void); From a4f5a33b141ff9e7de04da69b3db0ee2b58616f3 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:43:53 +0200 Subject: [PATCH 0466/1073] libobs: Remove obs_proprety_text_type() Typoed function name, deprecated in 21.1. --- libobs/obs-properties.c | 5 ----- libobs/obs-properties.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/libobs/obs-properties.c b/libobs/obs-properties.c index 9bb2fcb46b7f89..a489a4a9e4c222 100644 --- a/libobs/obs-properties.c +++ b/libobs/obs-properties.c @@ -1494,11 +1494,6 @@ obs_property_frame_rate_fps_range_max(obs_property_t *p, size_t idx) : (struct media_frames_per_second){0}; } -enum obs_text_type obs_proprety_text_type(obs_property_t *p) -{ - return obs_property_text_type(p); -} - enum obs_group_type obs_property_group_type(obs_property_t *p) { struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP); diff --git a/libobs/obs-properties.h b/libobs/obs-properties.h index 2b9b5a7d9abd3b..16339aad815b61 100644 --- a/libobs/obs-properties.h +++ b/libobs/obs-properties.h @@ -433,11 +433,6 @@ EXPORT obs_properties_t *obs_property_group_content(obs_property_t *p); EXPORT enum obs_button_type obs_property_button_type(obs_property_t *p); EXPORT const char *obs_property_button_url(obs_property_t *p); -#ifndef SWIG -OBS_DEPRECATED -EXPORT enum obs_text_type obs_proprety_text_type(obs_property_t *p); -#endif - #ifdef __cplusplus } #endif From 78bc3300c0498babb47a8e7a7fef80b18da9e51a Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:50:02 +0200 Subject: [PATCH 0467/1073] libobs: Remove obs_volmeter_{get,set}_update_interval() Deprecated in 27.2. --- libobs/obs-audio-controls.c | 23 ----------------------- libobs/obs-audio-controls.h | 30 ------------------------------ 2 files changed, 53 deletions(-) diff --git a/libobs/obs-audio-controls.c b/libobs/obs-audio-controls.c index e47322b3169098..7a9ff6182b062c 100644 --- a/libobs/obs-audio-controls.c +++ b/libobs/obs-audio-controls.c @@ -861,29 +861,6 @@ void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter, pthread_mutex_unlock(&volmeter->mutex); } -void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter, - const unsigned int ms) -{ - if (!volmeter || !ms) - return; - - pthread_mutex_lock(&volmeter->mutex); - volmeter->update_ms = ms; - pthread_mutex_unlock(&volmeter->mutex); -} - -unsigned int obs_volmeter_get_update_interval(obs_volmeter_t *volmeter) -{ - if (!volmeter) - return 0; - - pthread_mutex_lock(&volmeter->mutex); - const unsigned int interval = volmeter->update_ms; - pthread_mutex_unlock(&volmeter->mutex); - - return interval; -} - int obs_volmeter_get_nr_channels(obs_volmeter_t *volmeter) { int source_nr_audio_channels; diff --git a/libobs/obs-audio-controls.h b/libobs/obs-audio-controls.h index 3254712191dfcd..c9c6d769548829 100644 --- a/libobs/obs-audio-controls.h +++ b/libobs/obs-audio-controls.h @@ -231,36 +231,6 @@ EXPORT void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter, enum obs_peak_meter_type peak_meter_type); -/** - * @brief Set the update interval for the volume meter - * @param volmeter pointer to the volume meter object - * @param ms update interval in ms - * - * This sets the update interval in milliseconds that should be processed before - * the resulting values are emitted by the levels_updated signal. The resulting - * number of audio samples is rounded to an integer. - * - * Please note that due to way obs does receive audio data from the sources - * this is no hard guarantee for the timing of the signal itself. When the - * volume meter receives a chunk of data that is multiple the size of the sample - * interval, all data will be sampled and the values updated accordingly, but - * only the signal for the last segment is actually emitted. - * On the other hand data might be received in a way that will cause the signal - * to be emitted in shorter intervals than specified here under some - * circumstances. - */ -OBS_DEPRECATED -EXPORT void obs_volmeter_set_update_interval(obs_volmeter_t *volmeter, - const unsigned int ms); - -/** - * @brief Get the update interval currently used for the volume meter - * @param volmeter pointer to the volume meter object - * @return update interval in ms - */ -OBS_DEPRECATED -EXPORT unsigned int obs_volmeter_get_update_interval(obs_volmeter_t *volmeter); - /** * @brief Get the number of channels which are configured for this source. * @param volmeter pointer to the volume meter object From 71d49b0ef2e31a5e2afe8e44382d4b6ab33f0fbe Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 08:33:26 +0200 Subject: [PATCH 0468/1073] docs,libobs: Remove/internalize deprecated addref functions These have been deprecated for external users since 27.2 (early 2022) and only two are still in use internally. --- docs/sphinx/reference-encoders.rst | 9 --------- docs/sphinx/reference-outputs.rst | 9 --------- docs/sphinx/reference-scenes.rst | 9 --------- docs/sphinx/reference-services.rst | 9 --------- docs/sphinx/reference-sources.rst | 9 --------- libobs/obs-encoder.c | 8 -------- libobs/obs-internal.h | 1 + libobs/obs-output.c | 8 -------- libobs/obs-scene.c | 2 +- libobs/obs-service.c | 8 -------- libobs/obs.h | 5 ----- 11 files changed, 2 insertions(+), 75 deletions(-) diff --git a/docs/sphinx/reference-encoders.rst b/docs/sphinx/reference-encoders.rst index 5c267382e979d4..ba67a4017695cc 100644 --- a/docs/sphinx/reference-encoders.rst +++ b/docs/sphinx/reference-encoders.rst @@ -351,15 +351,6 @@ General Encoder Functions --------------------- -.. function:: void obs_encoder_addref(obs_encoder_t *encoder) - - Adds a reference to an encoder. - -.. deprecated:: 27.2.0 - Use :c:func:`obs_encoder_get_ref()` instead. - ---------------------- - .. function:: obs_encoder_t *obs_encoder_get_ref(obs_encoder_t *encoder) Returns an incremented reference if still valid, otherwise returns diff --git a/docs/sphinx/reference-outputs.rst b/docs/sphinx/reference-outputs.rst index 5433eb057c5ebd..327f7eda47d181 100644 --- a/docs/sphinx/reference-outputs.rst +++ b/docs/sphinx/reference-outputs.rst @@ -343,15 +343,6 @@ General Output Functions --------------------- -.. function:: void obs_output_addref(obs_output_t *output) - - Adds a reference to an output. - -.. deprecated:: 27.2.0 - Use :c:func:`obs_output_get_ref()` instead. - ---------------------- - .. function:: obs_output_t *obs_output_get_ref(obs_output_t *output) Returns an incremented reference if still valid, otherwise returns diff --git a/docs/sphinx/reference-scenes.rst b/docs/sphinx/reference-scenes.rst index b3c39ddbefa706..92d6ad04822361 100644 --- a/docs/sphinx/reference-scenes.rst +++ b/docs/sphinx/reference-scenes.rst @@ -197,15 +197,6 @@ General Scene Functions --------------------- -.. function:: void obs_scene_addref(obs_scene_t *scene) - - Adds a reference to a scene. - -.. deprecated:: 27.2.0 - Use :c:func:`obs_scene_get_ref()` instead. - ---------------------- - .. function:: obs_scene_t *obs_scene_get_ref(obs_scene_t *scene) Returns an incremented reference if still valid, otherwise returns diff --git a/docs/sphinx/reference-services.rst b/docs/sphinx/reference-services.rst index d35a919ec5f565..f0f2f4c4da35b7 100644 --- a/docs/sphinx/reference-services.rst +++ b/docs/sphinx/reference-services.rst @@ -241,15 +241,6 @@ General Service Functions --------------------- -.. function:: void obs_service_addref(obs_service_t *service) - - Adds a reference to a service. - -.. deprecated:: 27.2.0 - Use :c:func:`obs_service_get_ref()` instead. - ---------------------- - .. function:: obs_service_t *obs_service_get_ref(obs_service_t *service) Returns an incremented reference if still valid, otherwise returns diff --git a/docs/sphinx/reference-sources.rst b/docs/sphinx/reference-sources.rst index bac280dc6b9262..4729e055d8adee 100644 --- a/docs/sphinx/reference-sources.rst +++ b/docs/sphinx/reference-sources.rst @@ -915,15 +915,6 @@ General Source Functions --------------------- -.. function:: void obs_source_addref(obs_source_t *source) - - Adds a reference to a source. - -.. deprecated:: 27.2.0 - Use :c:func:`obs_source_get_ref()` instead. - ---------------------- - .. function:: obs_source_t *obs_source_get_ref(obs_source_t *source) Returns an incremented reference if still valid, otherwise returns diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 1daf94e35124b0..36a01d1384fa1a 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -1814,14 +1814,6 @@ obs_encoder_get_preferred_video_format(const obs_encoder_t *encoder) return encoder->preferred_format; } -void obs_encoder_addref(obs_encoder_t *encoder) -{ - if (!encoder) - return; - - obs_ref_addref(&encoder->context.control->ref); -} - void obs_encoder_release(obs_encoder_t *encoder) { if (!encoder) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index cc28381b3ebfe6..7a0938d2e79b4e 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -932,6 +932,7 @@ obs_source_create_set_last_ver(const char *id, const char *name, obs_data_t *hotkey_data, uint32_t last_obs_ver, bool is_private); extern void obs_source_destroy(struct obs_source *source); +extern void obs_source_addref(obs_source_t *source); enum view_type { MAIN_VIEW, diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 4266239b479aa6..03cc75b6cc6019 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -3022,14 +3022,6 @@ void obs_output_signal_stop(obs_output_t *output, int code) } } -void obs_output_addref(obs_output_t *output) -{ - if (!output) - return; - - obs_ref_addref(&output->context.control->ref); -} - void obs_output_release(obs_output_t *output) { if (!output) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 678e171cfa5a04..b7d138135624f8 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -2197,7 +2197,7 @@ obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, return new_scene; } -void obs_scene_addref(obs_scene_t *scene) +static inline void obs_scene_addref(obs_scene_t *scene) { if (scene) obs_source_addref(scene->source); diff --git a/libobs/obs-service.c b/libobs/obs-service.c index f4d358bf6993e2..770e6bd2ecb560 100644 --- a/libobs/obs-service.c +++ b/libobs/obs-service.c @@ -322,14 +322,6 @@ void obs_service_apply_encoder_settings(obs_service_t *service, audio_encoder_settings); } -void obs_service_addref(obs_service_t *service) -{ - if (!service) - return; - - obs_ref_addref(&service->context.control->ref); -} - void obs_service_release(obs_service_t *service) { if (!service) diff --git a/libobs/obs.h b/libobs/obs.h index 25b5eb13ff2fb0..fee858ef33ee93 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -1030,7 +1030,6 @@ EXPORT obs_source_t *obs_source_duplicate(obs_source_t *source, * Adds/releases a reference to a source. When the last reference is * released, the source is destroyed. */ -OBS_EXTERNAL_DEPRECATED EXPORT void obs_source_addref(obs_source_t *source); EXPORT void obs_source_release(obs_source_t *source); EXPORT void obs_weak_source_addref(obs_weak_source_t *weak); @@ -1735,7 +1734,6 @@ enum obs_scene_duplicate_type { EXPORT obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, enum obs_scene_duplicate_type type); -OBS_EXTERNAL_DEPRECATED EXPORT void obs_scene_addref(obs_scene_t *scene); EXPORT void obs_scene_release(obs_scene_t *scene); EXPORT obs_scene_t *obs_scene_get_ref(obs_scene_t *scene); @@ -2053,7 +2051,6 @@ EXPORT obs_output_t *obs_output_create(const char *id, const char *name, * Adds/releases a reference to an output. When the last reference is * released, the output is destroyed. */ -OBS_EXTERNAL_DEPRECATED EXPORT void obs_output_addref(obs_output_t *output); EXPORT void obs_output_release(obs_output_t *output); EXPORT void obs_weak_output_addref(obs_weak_output_t *weak); @@ -2410,7 +2407,6 @@ EXPORT obs_encoder_t *obs_audio_encoder_create(const char *id, const char *name, * Adds/releases a reference to an encoder. When the last reference is * released, the encoder is destroyed. */ -OBS_EXTERNAL_DEPRECATED EXPORT void obs_encoder_addref(obs_encoder_t *encoder); EXPORT void obs_encoder_release(obs_encoder_t *encoder); EXPORT void obs_weak_encoder_addref(obs_weak_encoder_t *weak); @@ -2628,7 +2624,6 @@ EXPORT obs_service_t *obs_service_create_private(const char *id, * Adds/releases a reference to a service. When the last reference is * released, the service is destroyed. */ -OBS_EXTERNAL_DEPRECATED EXPORT void obs_service_addref(obs_service_t *service); EXPORT void obs_service_release(obs_service_t *service); EXPORT void obs_weak_service_addref(obs_weak_service_t *weak); From 03fbe287175f19c8dd7f4d0b325a99edb04c7bf7 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:42:16 +0200 Subject: [PATCH 0469/1073] libobs: Remove base_set_allocator() Deprecated in 28.0. --- libobs/util/bmem.c | 5 ----- libobs/util/bmem.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/libobs/util/bmem.c b/libobs/util/bmem.c index 3f941ed0112a70..b1b1f178694c32 100644 --- a/libobs/util/bmem.c +++ b/libobs/util/bmem.c @@ -170,8 +170,3 @@ void *bmemdup(const void *ptr, size_t size) return out; } - -OBS_DEPRECATED void base_set_allocator(struct base_allocator *defs) -{ - UNUSED_PARAMETER(defs); -} diff --git a/libobs/util/bmem.h b/libobs/util/bmem.h index 046dba9405bc60..d1b206768a0bcd 100644 --- a/libobs/util/bmem.h +++ b/libobs/util/bmem.h @@ -31,8 +31,6 @@ struct base_allocator { void (*free)(void *); }; -OBS_DEPRECATED EXPORT void base_set_allocator(struct base_allocator *defs); - EXPORT void *bmalloc(size_t size); EXPORT void *brealloc(void *ptr, size_t size); EXPORT void bfree(void *ptr); From 84d462c31cc0f669a2fa203e8684f501fa2ec001 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:46:00 +0200 Subject: [PATCH 0470/1073] libobs: Remove obs_hotkey_enable_strict_modifiers() Deprecated in 28.0. --- libobs/obs-hotkey.c | 9 --------- libobs/obs-hotkey.h | 2 -- 2 files changed, 11 deletions(-) diff --git a/libobs/obs-hotkey.c b/libobs/obs-hotkey.c index 48f47e102c90bc..dc2af467b313c8 100644 --- a/libobs/obs-hotkey.c +++ b/libobs/obs-hotkey.c @@ -1188,15 +1188,6 @@ void obs_hotkey_enable_background_press(bool enable) unlock(); } -void obs_hotkey_enable_strict_modifiers(bool enable) -{ - if (!lock()) - return; - - obs->hotkeys.strict_modifiers = enable; - unlock(); -} - struct obs_query_hotkeys_helper { uint32_t modifiers; bool no_press; diff --git a/libobs/obs-hotkey.h b/libobs/obs-hotkey.h index 68721c628af1c4..4244a69df0faab 100644 --- a/libobs/obs-hotkey.h +++ b/libobs/obs-hotkey.h @@ -277,8 +277,6 @@ EXPORT void obs_hotkey_inject_event(obs_key_combination_t hotkey, bool pressed); EXPORT void obs_hotkey_enable_background_press(bool enable); -OBS_DEPRECATED EXPORT void obs_hotkey_enable_strict_modifiers(bool enable); - /* hotkey callback routing (trigger callbacks through e.g. a UI thread) */ typedef void (*obs_hotkey_callback_router_func)(void *data, obs_hotkey_id id, From 75cde08e8d6b47d08e91ff89d2ceada6fe33d1ef Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 10:30:13 +0200 Subject: [PATCH 0471/1073] libobs: Rename OBS_NIX_PLATFORM_X11_GLX to OBS_NIX_PLATFORM_INVALID Deprecated in 28.0. Renamed so that the values of non-deprecated members stay the same. --- libobs/obs-nix-platform.c | 5 +---- libobs/obs-nix-platform.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libobs/obs-nix-platform.c b/libobs/obs-nix-platform.c index 0db6dd0d47291d..8762eee0c1b0d5 100644 --- a/libobs/obs-nix-platform.c +++ b/libobs/obs-nix-platform.c @@ -25,10 +25,7 @@ static void *obs_nix_platform_display = NULL; void obs_set_nix_platform(enum obs_nix_platform_type platform) { - PRAGMA_WARN_PUSH - PRAGMA_WARN_DEPRECATION - assert(platform != OBS_NIX_PLATFORM_X11_GLX); - PRAGMA_WARN_POP + assert(platform != OBS_NIX_PLATFORM_INVALID); obs_nix_platform = platform; } diff --git a/libobs/obs-nix-platform.h b/libobs/obs-nix-platform.h index 502dbf2aa68207..a8583c02e9f8d0 100644 --- a/libobs/obs-nix-platform.h +++ b/libobs/obs-nix-platform.h @@ -24,7 +24,7 @@ extern "C" { #endif enum obs_nix_platform_type { - OBS_NIX_PLATFORM_X11_GLX OBS_DEPRECATED, + OBS_NIX_PLATFORM_INVALID, OBS_NIX_PLATFORM_X11_EGL, OBS_NIX_PLATFORM_WAYLAND, }; From 8729cebce79c6caaa4daf6eac181b3d981e466fb Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:34:44 +0200 Subject: [PATCH 0472/1073] docs,libobs: Remove deprecated scene item transition functions Deprecated in 28.0, documentation erroneously states 27.2. The following functions were erroneously not marked as deprecated in the header: - obs_sceneitem_set_show_transition() - obs_sceneitem_set_show_transition_duration() --- docs/sphinx/reference-scenes.rst | 38 ------------------ libobs/obs-scene.c | 66 -------------------------------- libobs/obs.h | 19 --------- 3 files changed, 123 deletions(-) diff --git a/docs/sphinx/reference-scenes.rst b/docs/sphinx/reference-scenes.rst index 92d6ad04822361..d8eee24e8a091a 100644 --- a/docs/sphinx/reference-scenes.rst +++ b/docs/sphinx/reference-scenes.rst @@ -615,16 +615,6 @@ Scene Item Functions --------------------- -.. function:: void obs_sceneitem_set_show_transition(obs_sceneitem_t *item, obs_source_t *transition) - void obs_sceneitem_set_hide_transition(obs_sceneitem_t *item, obs_source_t *transition) - - Sets a transition for showing or hiding a scene item. Set *NULL* to remove the transition. - -.. deprecated:: 27.2.4 - Use :c:func:`obs_sceneitem_set_transition()` instead. - ---------------------- - .. function:: obs_source_t *obs_sceneitem_get_transition(obs_sceneitem_t *item, bool show) :param item: The target scene item @@ -634,14 +624,6 @@ Scene Item Functions --------------------- -.. function:: obs_source_t *obs_sceneitem_get_show_transition(obs_sceneitem_t *item) - obs_source_t *obs_sceneitem_get_hide_transition(obs_sceneitem_t *item) - - :return: The transition for showing or hiding a scene item. *NULL* if no transition is set. - -.. deprecated:: 27.2.4 - Use :c:func:`obs_sceneitem_get_transition()` instead. - --------------------- .. function:: void obs_sceneitem_set_transition_duration(obs_sceneitem_t *item, bool show, uint32_t duration_ms) @@ -655,16 +637,6 @@ Scene Item Functions --------------------- -.. function:: void obs_sceneitem_set_show_transition_duration(obs_sceneitem_t *item, uint32_t duration_ms) - void obs_sceneitem_set_hide_transition_duration(obs_sceneitem_t *item, uint32_t duration_ms) - - Sets the transition duration for showing or hiding a scene item. - -.. deprecated:: 27.2.4 - Use :c:func:`obs_sceneitem_set_transition_duration()` instead. - ---------------------- - .. function:: uint32_t obs_sceneitem_get_transition_duration(obs_sceneitem_t *item, bool show) Gets the transition duration for showing or hiding a scene item. @@ -676,16 +648,6 @@ Scene Item Functions --------------------- -.. function:: uint32_t obs_sceneitem_get_show_transition_duration(obs_sceneitem_t *item) - uint32_t obs_sceneitem_get_hide_transition_duration(obs_sceneitem_t *item) - - :return: The transition duration in ms for showing or hiding a scene item. - -.. deprecated:: 27.2.4 - Use :c:func:`obs_sceneitem_get_transition_duration()` instead. - ---------------------- - .. function:: void obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible) Start the transition for showing or hiding a scene item. diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index b7d138135624f8..0af5fda9ad2e7d 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -4403,72 +4403,6 @@ void obs_sceneitem_force_update_transform(obs_sceneitem_t *item) update_item_transform(item, false); } -void obs_sceneitem_set_show_transition(obs_sceneitem_t *item, - obs_source_t *transition) -{ - if (!item) - return; - if (item->show_transition) - obs_source_release(item->show_transition); - - item->show_transition = obs_source_get_ref(transition); -} - -void obs_sceneitem_set_show_transition_duration(obs_sceneitem_t *item, - uint32_t duration_ms) -{ - if (!item) - return; - item->show_transition_duration = duration_ms; -} - -obs_source_t *obs_sceneitem_get_show_transition(obs_sceneitem_t *item) -{ - if (!item) - return NULL; - return item->show_transition; -} - -uint32_t obs_sceneitem_get_show_transition_duration(obs_sceneitem_t *item) -{ - if (!item) - return 0; - return item->show_transition_duration; -} - -void obs_sceneitem_set_hide_transition(obs_sceneitem_t *item, - obs_source_t *transition) -{ - if (!item) - return; - if (item->hide_transition) - obs_source_release(item->hide_transition); - - item->hide_transition = obs_source_get_ref(transition); -} - -void obs_sceneitem_set_hide_transition_duration(obs_sceneitem_t *item, - uint32_t duration_ms) -{ - if (!item) - return; - item->hide_transition_duration = duration_ms; -} - -obs_source_t *obs_sceneitem_get_hide_transition(obs_sceneitem_t *item) -{ - if (!item) - return NULL; - return item->hide_transition; -} - -uint32_t obs_sceneitem_get_hide_transition_duration(obs_sceneitem_t *item) -{ - if (!item) - return 0; - return item->hide_transition_duration; -} - void obs_sceneitem_set_transition(obs_sceneitem_t *item, bool show, obs_source_t *transition) { diff --git a/libobs/obs.h b/libobs/obs.h index fee858ef33ee93..c2f784f249799c 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -1997,25 +1997,6 @@ obs_group_or_scene_from_source(const obs_source_t *source) EXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item); EXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item); -EXPORT void obs_sceneitem_set_show_transition(obs_sceneitem_t *item, - obs_source_t *transition); -EXPORT void obs_sceneitem_set_show_transition_duration(obs_sceneitem_t *item, - uint32_t duration_ms); -OBS_DEPRECATED EXPORT obs_source_t * -obs_sceneitem_get_show_transition(obs_sceneitem_t *item); -OBS_DEPRECATED EXPORT uint32_t -obs_sceneitem_get_show_transition_duration(obs_sceneitem_t *item); -OBS_DEPRECATED EXPORT void -obs_sceneitem_set_hide_transition(obs_sceneitem_t *item, - obs_source_t *transition); -OBS_DEPRECATED EXPORT void -obs_sceneitem_set_hide_transition_duration(obs_sceneitem_t *item, - uint32_t duration_ms); -OBS_DEPRECATED EXPORT obs_source_t * -obs_sceneitem_get_hide_transition(obs_sceneitem_t *item); -OBS_DEPRECATED EXPORT uint32_t -obs_sceneitem_get_hide_transition_duration(obs_sceneitem_t *item); - EXPORT void obs_sceneitem_set_transition(obs_sceneitem_t *item, bool show, obs_source_t *transition); EXPORT obs_source_t *obs_sceneitem_get_transition(obs_sceneitem_t *item, From b7553b588338b0a64857cb296a6da4a74b211c31 Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 09:29:04 +0200 Subject: [PATCH 0473/1073] docs,libobs: Remove master volume functions Never implemented, deprecated in 29.0. --- docs/sphinx/reference-core.rst | 18 ------------------ libobs/obs.c | 10 ---------- libobs/obs.h | 6 ------ 3 files changed, 34 deletions(-) diff --git a/docs/sphinx/reference-core.rst b/docs/sphinx/reference-core.rst index c624c7bbf00dc0..d6be06d6d416ea 100644 --- a/docs/sphinx/reference-core.rst +++ b/docs/sphinx/reference-core.rst @@ -508,24 +508,6 @@ Video, Audio, and Graphics --------------------- -.. function:: void obs_set_master_volume(float volume) - - No-op, only exists to keep ABI compatibility. - - .. deprecated:: 29.0 - ---------------------- - -.. function:: float obs_get_master_volume(void) - - No-op, only exists to keep ABI compatibility. - - :return: Always returns 1 - - .. deprecated:: 29.0 - ---------------------- - .. function:: bool obs_audio_monitoring_available(void) :return: Whether audio monitoring is supported and available on the current platform diff --git a/libobs/obs.c b/libobs/obs.c index c0036729de9b45..4f000ac7a4abda 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -2229,16 +2229,6 @@ gs_texture_t *obs_get_main_texture(void) return video->render_texture; } -void obs_set_master_volume(float volume) -{ - UNUSED_PARAMETER(volume); -} - -float obs_get_master_volume(void) -{ - return 1.f; -} - static obs_source_t *obs_load_source_type(obs_data_t *source_data, bool is_private) { diff --git a/libobs/obs.h b/libobs/obs.h index c2f784f249799c..f1fe9c9b3207b5 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -762,12 +762,6 @@ EXPORT void obs_render_main_texture_src_color_only(void); * is unavailable. */ EXPORT gs_texture_t *obs_get_main_texture(void); -/** Sets the master user volume */ -OBS_DEPRECATED EXPORT void obs_set_master_volume(float volume); - -/** Gets the master user volume */ -OBS_DEPRECATED EXPORT float obs_get_master_volume(void); - /** Saves a source to settings data */ EXPORT obs_data_t *obs_save_source(obs_source_t *source); From 22fc29c5b4e13c89f547afd7713e47f84a3517cd Mon Sep 17 00:00:00 2001 From: derrod Date: Wed, 21 Aug 2024 08:40:57 +0200 Subject: [PATCH 0474/1073] docs,libobs: Remove deprecated service APIs Deprecated in 29.1 --- docs/sphinx/reference-services.rst | 36 ----------------------- libobs/obs-service.c | 46 ------------------------------ libobs/obs.h | 21 -------------- 3 files changed, 103 deletions(-) diff --git a/docs/sphinx/reference-services.rst b/docs/sphinx/reference-services.rst index f0f2f4c4da35b7..3ed58b5d985ab3 100644 --- a/docs/sphinx/reference-services.rst +++ b/docs/sphinx/reference-services.rst @@ -310,42 +310,6 @@ General Service Functions --------------------- -.. function:: const char *obs_service_get_url(const obs_service_t *service) - - :return: The URL currently used for this service - -.. deprecated:: 29.1.0 - Use :c:func:`obs_service_get_connect_info()` instead. - ---------------------- - -.. function:: const char *obs_service_get_key(const obs_service_t *service) - - :return: Stream key (if any) currently used for this service - -.. deprecated:: 29.1.0 - Use :c:func:`obs_service_get_connect_info()` instead. - ---------------------- - -.. function:: const char *obs_service_get_username(const obs_service_t *service) - - :return: User name (if any) currently used for this service - -.. deprecated:: 29.1.0 - Use :c:func:`obs_service_get_connect_info()` instead. - ---------------------- - -.. function:: const char *obs_service_get_password(const obs_service_t *service) - - :return: Password (if any) currently used for this service - -.. deprecated:: 29.1.0 - Use :c:func:`obs_service_get_connect_info()` instead. - ---------------------- - .. function:: void obs_service_apply_encoder_settings(obs_service_t *service, obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings) Applies service-specific video encoder settings. diff --git a/libobs/obs-service.c b/libobs/obs-service.c index 770e6bd2ecb560..3852c958630c67 100644 --- a/libobs/obs-service.c +++ b/libobs/obs-service.c @@ -209,46 +209,6 @@ proc_handler_t *obs_service_get_proc_handler(const obs_service_t *service) : NULL; } -const char *obs_service_get_url(const obs_service_t *service) -{ - if (!obs_service_valid(service, "obs_service_get_url")) - return NULL; - - if (!service->info.get_url) - return NULL; - return service->info.get_url(service->context.data); -} - -const char *obs_service_get_key(const obs_service_t *service) -{ - if (!obs_service_valid(service, "obs_service_get_key")) - return NULL; - - if (!service->info.get_key) - return NULL; - return service->info.get_key(service->context.data); -} - -const char *obs_service_get_username(const obs_service_t *service) -{ - if (!obs_service_valid(service, "obs_service_get_username")) - return NULL; - - if (!service->info.get_username) - return NULL; - return service->info.get_username(service->context.data); -} - -const char *obs_service_get_password(const obs_service_t *service) -{ - if (!obs_service_valid(service, "obs_service_get_password")) - return NULL; - - if (!service->info.get_password) - return NULL; - return service->info.get_password(service->context.data); -} - void obs_service_activate(struct obs_service *service) { if (!obs_service_valid(service, "obs_service_activate")) @@ -477,12 +437,6 @@ const char *obs_service_get_protocol(const obs_service_t *service) return service->info.get_protocol(service->context.data); } -/* OBS_DEPRECATED */ -const char *obs_service_get_output_type(const obs_service_t *service) -{ - return obs_service_get_preferred_output_type(service); -} - const char *obs_service_get_preferred_output_type(const obs_service_t *service) { if (!obs_service_valid(service, diff --git a/libobs/obs.h b/libobs/obs.h index f1fe9c9b3207b5..e62730f4ff0c92 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2634,22 +2634,6 @@ EXPORT void obs_service_update(obs_service_t *service, obs_data_t *settings); /** Returns the current settings for this service */ EXPORT obs_data_t *obs_service_get_settings(const obs_service_t *service); -/** Returns the URL for this service context */ -OBS_DEPRECATED EXPORT const char * -obs_service_get_url(const obs_service_t *service); - -/** Returns the stream key (if any) for this service context */ -OBS_DEPRECATED EXPORT const char * -obs_service_get_key(const obs_service_t *service); - -/** Returns the username (if any) for this service context */ -OBS_DEPRECATED EXPORT const char * -obs_service_get_username(const obs_service_t *service); - -/** Returns the password (if any) for this service context */ -OBS_DEPRECATED EXPORT const char * -obs_service_get_password(const obs_service_t *service); - /** * Applies service-specific video encoder settings. * @@ -2679,11 +2663,6 @@ obs_service_get_supported_video_codecs(const obs_service_t *service); EXPORT const char ** obs_service_get_supported_audio_codecs(const obs_service_t *service); -/* NOTE: This function is temporary and should be removed/replaced at a later - * date. */ -OBS_DEPRECATED EXPORT const char * -obs_service_get_output_type(const obs_service_t *service); - /** Returns the protocol for this service context */ EXPORT const char *obs_service_get_protocol(const obs_service_t *service); From 7840c53effcb72b92ad83d62fcfe50dc372cde61 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sat, 25 Feb 2023 15:08:45 +0100 Subject: [PATCH 0475/1073] shared/qt: Add IconLabel widget --- shared/qt/icon-label/CMakeLists.txt | 11 ++++++ shared/qt/icon-label/icon-label.hpp | 56 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 shared/qt/icon-label/CMakeLists.txt create mode 100644 shared/qt/icon-label/icon-label.hpp diff --git a/shared/qt/icon-label/CMakeLists.txt b/shared/qt/icon-label/CMakeLists.txt new file mode 100644 index 00000000000000..ff16e337653826 --- /dev/null +++ b/shared/qt/icon-label/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +find_package(Qt6 REQUIRED Core Widgets) + +add_library(qt-icon-label INTERFACE) +add_library(OBS::qt-icon-label ALIAS qt-icon-label) + +target_sources(qt-icon-label INTERFACE icon-label.hpp) +target_include_directories(qt-icon-label INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") + +target_link_libraries(qt-icon-label INTERFACE Qt::Core Qt::Widgets) diff --git a/shared/qt/icon-label/icon-label.hpp b/shared/qt/icon-label/icon-label.hpp new file mode 100644 index 00000000000000..582a7c2501dccf --- /dev/null +++ b/shared/qt/icon-label/icon-label.hpp @@ -0,0 +1,56 @@ +/****************************************************************************** + Copyright (C) 2024 by Sebastian Beckmann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +******************************************************************************/ + +#pragma once + +#include +#include + +/** + * Widget to be used if a label is to be supplied a QIcon instead of a QPixmap, + * specifically so that qproperty-icon QSS styling (and as a result the OBS + * "themeID" property) works on it without having to first convert the icon to + * a fixed size PNG and then setting qproperty-pixmap in addition to the + * qproperty-icon statements. + */ +class IconLabel : public QLabel { + Q_OBJECT + Q_PROPERTY(QIcon icon READ icon WRITE setIcon) + Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize) + +public: + inline IconLabel(QWidget *parent = nullptr) + : QLabel(parent), + m_icon(), + m_iconSize(16) + { + } + + inline QIcon icon() const { return m_icon; } + void setIcon(const QIcon &icon) + { + m_icon = icon; + QLabel::setPixmap(icon.pixmap(m_iconSize)); + } + + inline int iconSize() const { return m_iconSize; } + void setIconSize(int newSize) { m_iconSize = newSize; } + +private: + QIcon m_icon; + int m_iconSize; +}; From 270cebe5811f030913c803853f7827b90b6ffc57 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 18 Aug 2024 04:08:17 +0200 Subject: [PATCH 0476/1073] shared/properties-view: Use IconLabel for help icon Changes the icon rendering for the properties view "question mark" icon from Qt label HTML to use the IconLabel widget. This makes the label high DPI. Unfortunately the properties view code is a complete nightmare and in a way, this PR makes this worse by adding the "leftWidget" widget as a placeholder for what the "normal" label used to be, but you can't easily replace that label with the icon label (while retaining prior modifications from other nightmare code) so here we are. The entire thing needs to be burnt to the ground and be rebuilt from the ground up but that's a task for another day. --- shared/properties-view/CMakeLists.txt | 5 ++ shared/properties-view/properties-view.cpp | 89 ++++++++++++---------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/shared/properties-view/CMakeLists.txt b/shared/properties-view/CMakeLists.txt index af3fdba3a78b10..2841bbe95bff07 100644 --- a/shared/properties-view/CMakeLists.txt +++ b/shared/properties-view/CMakeLists.txt @@ -24,6 +24,10 @@ if(NOT TARGET OBS::qt-slider-ignorewheel) ) endif() +if(NOT TARGET OBS::qt-icon-label) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/qt/icon-label" "${CMAKE_BINARY_DIR}/shared/qt/icon-label") +endif() + add_library(properties-view INTERFACE) add_library(OBS::properties-view ALIAS properties-view) @@ -53,6 +57,7 @@ target_link_libraries( OBS::qt-plain-text-edit OBS::qt-vertical-scroll-area OBS::qt-slider-ignorewheel + OBS::qt-icon-label Qt::Core Qt::Widgets ) diff --git a/shared/properties-view/properties-view.cpp b/shared/properties-view/properties-view.cpp index 3f991aaafca235..a94ee26e640993 100644 --- a/shared/properties-view/properties-view.cpp +++ b/shared/properties-view/properties-view.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -277,15 +278,46 @@ QWidget *OBSPropertiesView::AddCheckbox(obs_property_t *prop) { const char *name = obs_property_name(prop); const char *desc = obs_property_description(prop); + const char *long_desc = obs_property_long_description(prop); bool val = obs_data_get_bool(settings, name); QCheckBox *checkbox = new QCheckBox(QT_UTF8(desc)); checkbox->setCheckState(val ? Qt::Checked : Qt::Unchecked); + #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - return NewWidget(prop, checkbox, &QCheckBox::checkStateChanged); + QWidget *widget = + NewWidget(prop, checkbox, &QCheckBox::checkStateChanged); #else - return NewWidget(prop, checkbox, &QCheckBox::stateChanged); + QWidget *widget = NewWidget(prop, checkbox, &QCheckBox::stateChanged); #endif + + if (!long_desc) { + return widget; + } + + QString file = !obs_frontend_is_theme_dark() + ? ":/res/images/help.svg" + : ":/res/images/help_light.svg"; + + IconLabel *help = new IconLabel(checkbox); + help->setIcon(QIcon(file)); + help->setToolTip(long_desc); + +#ifdef __APPLE__ + checkbox->setAttribute(Qt::WA_LayoutUsesWidgetRect); +#endif + + widget = new QWidget(); + QHBoxLayout *layout = new QHBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignLeft); + layout->setSpacing(0); + + layout->addWidget(checkbox); + layout->addWidget(help); + widget->setLayout(layout); + + return widget; } QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, @@ -1625,53 +1657,30 @@ void OBSPropertiesView::AddProperty(obs_property_t *property, if (!obs_property_enabled(property)) widget->setEnabled(false); - if (obs_property_long_description(property)) { + QWidget *leftWidget = label; + if (obs_property_long_description(property) && label) { QString file = !obs_frontend_is_theme_dark() ? ":/res/images/help.svg" : ":/res/images/help_light.svg"; - if (label) { - QString lStr = "%1 "; - - label->setText(lStr.arg(label->text(), file)); - label->setToolTip( - obs_property_long_description(property)); - } else if (type == OBS_PROPERTY_BOOL) { - - QString bStr = " "; - const char *desc = obs_property_description(property); + QWidget *newWidget = new QWidget(); + newWidget->setToolTip(obs_property_long_description(property)); - QWidget *newWidget = new QWidget(); + QHBoxLayout *boxLayout = new QHBoxLayout(newWidget); + boxLayout->setContentsMargins(0, 0, 0, 0); + boxLayout->setAlignment(Qt::AlignLeft); + boxLayout->setSpacing(0); - QHBoxLayout *boxLayout = new QHBoxLayout(newWidget); - boxLayout->setContentsMargins(0, 0, 0, 0); - boxLayout->setAlignment(Qt::AlignLeft); - boxLayout->setSpacing(0); + IconLabel *help = new IconLabel(newWidget); + help->setIcon(QIcon(file)); + help->setToolTip(obs_property_long_description(property)); - QCheckBox *check = qobject_cast(widget); - check->setText(desc); - check->setToolTip( - obs_property_long_description(property)); -#ifdef __APPLE__ - check->setAttribute(Qt::WA_LayoutUsesWidgetRect); -#endif - - QLabel *help = new QLabel(check); - help->setText(bStr.arg(file)); - help->setToolTip( - obs_property_long_description(property)); - - boxLayout->addWidget(check); - boxLayout->addWidget(help); - widget = newWidget; - } + boxLayout->addWidget(label); + boxLayout->addWidget(help); + leftWidget = newWidget; } - layout->addRow(label, widget); + layout->addRow(leftWidget, widget); if (!lastFocused.empty()) if (lastFocused.compare(name) == 0) From f1f89a4864a05e508408889035fb7f065b15942d Mon Sep 17 00:00:00 2001 From: gxalpha Date: Tue, 27 Aug 2024 15:23:18 +0200 Subject: [PATCH 0477/1073] shared/properties-view: Remove Qt 6.2 cmake code --- shared/properties-view/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shared/properties-view/CMakeLists.txt b/shared/properties-view/CMakeLists.txt index 2841bbe95bff07..9769baa79c2d16 100644 --- a/shared/properties-view/CMakeLists.txt +++ b/shared/properties-view/CMakeLists.txt @@ -44,10 +44,6 @@ target_sources( ) target_include_directories(properties-view INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") -if(OS_LINUX AND Qt6_VERSION VERSION_LESS 6.3) - target_compile_options(properties-view INTERFACE -Wno-error=enum-conversion) -endif() - target_link_libraries( properties-view INTERFACE From 9925345eb6f93ee5e801d4b8c01f0378cc51586e Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 28 Aug 2024 19:47:34 -0400 Subject: [PATCH 0478/1073] CI: Update flatpak-builder and flat-manager actions This should remove some warnings on CI about deprecated GitHub Actions versions. --- .github/workflows/build-project.yaml | 2 +- .github/workflows/publish.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-project.yaml b/.github/workflows/build-project.yaml index b01d28f22531a2..194883dc2a2dfe 100644 --- a/.github/workflows/build-project.yaml +++ b/.github/workflows/build-project.yaml @@ -292,7 +292,7 @@ jobs: path: build-aux/com.obsproject.Studio.json - name: Build Flatpak Manifest 🧾 - uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8 + uses: flathub-infra/flatpak-github-actions/flatpak-builder@e5fa5a8fe51d76f770ad9d240e4ad5898b1c6a53 with: build-bundle: ${{ fromJSON(needs.check-event.outputs.package) }} bundle: obs-studio-flatpak-${{ needs.check-event.outputs.commitHash }}.flatpak diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 42f91d39361c54..51a93659cf026f 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -107,7 +107,7 @@ jobs: path: build-aux/com.obsproject.Studio.json - name: Build Flatpak Manifest - uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8 + uses: flathub-infra/flatpak-github-actions/flatpak-builder@e5fa5a8fe51d76f770ad9d240e4ad5898b1c6a53 with: bundle: obs-studio-${{ steps.setup.outputs.commitHash }}.flatpak manifest-path: ${{ github.workspace }}/build-aux/com.obsproject.Studio.json @@ -142,7 +142,7 @@ jobs: path: repo - name: Publish to Flathub Beta - uses: flathub-infra/flatpak-github-actions/flat-manager@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8 + uses: flathub-infra/flatpak-github-actions/flat-manager@e5fa5a8fe51d76f770ad9d240e4ad5898b1c6a53 if: ${{ matrix.branch == 'beta' }} with: flat-manager-url: https://hub.flathub.org/ @@ -151,7 +151,7 @@ jobs: build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - name: Publish to Flathub - uses: flathub-infra/flatpak-github-actions/flat-manager@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8 + uses: flathub-infra/flatpak-github-actions/flat-manager@e5fa5a8fe51d76f770ad9d240e4ad5898b1c6a53 if: ${{ matrix.branch == 'stable' }} with: flat-manager-url: https://hub.flathub.org/ From 16011f7c297fed566ffa697ee3fd8e6318c08fa2 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 30 Aug 2024 18:29:57 -0400 Subject: [PATCH 0479/1073] obs-browser: Update version to 2.24.2 98d94a4 - Enable Qt message loop on Linux 8e2b31f - Set the right Ozone platform on Linux 6451941 - Wait on shutdown for docks to close on Linux 174e6a8 - Remove CMake legacy code path e4e523d - Update version to 2.24.2 --- plugins/obs-browser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-browser b/plugins/obs-browser index be9f1b646406d2..e4e523df775bd6 160000 --- a/plugins/obs-browser +++ b/plugins/obs-browser @@ -1 +1 @@ -Subproject commit be9f1b646406d2250b402581b043f1558042d7f0 +Subproject commit e4e523df775bd6fa216d40a7488b76f3b21c1733 From 3559fdad0fad5177733a5e63ac13d3b6b06d91aa Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 28 Aug 2024 18:35:22 -0400 Subject: [PATCH 0480/1073] CI: Update CEF from 103/5060 to 127/6533 --- buildspec.json | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/buildspec.json b/buildspec.json index 2da30bfd56fe40..ba3b66689bad60 100644 --- a/buildspec.json +++ b/buildspec.json @@ -23,22 +23,15 @@ } }, "cef": { - "version": "5060", + "version": "6533", "baseUrl": "https://cdn-fastly.obsproject.com/downloads", "label": "Chromium Embedded Framework", "hashes": { - "macos-x86_64": "140bfd517b4b9d12343ac000abba7bb16eecefe80ecbd7225993d03f0b9c0603", - "macos-arm64": "3970a781132b033bb9be6a9fe328a07e97fa7737fdf1575b1c5db24a3e130da1", - "ubuntu-x86_64": "1253a6a36c3b8ac5b5ece9bfdb6eae6ab75e49516375fc475e2e871795ad9bea", - "ubuntu-aarch64": "66ebcfce94a4527c8dd085a685691d0c43291adab9f2be4f8a0762f4a614083a", - "windows-x64": "7480e9ed5688e09919db67237d130eef9a4c24df32ba2a7b8a5587de45ff8e69" - }, - "revision": { - "macos-x86_64": 4, - "macos-arm64": 4, - "ubuntu-x86_64": 3, - "ubuntu-aarch64": 3, - "windows-x64": 3 + "macos-x86_64": "139c6664b6c9c446e0b56f303586fa6bd4b3587bae4742e13967c2f0f99c8740", + "macos-arm64": "8e833fce815b83114ab381c055122a18dc415e29af5c4db0a577caf4b817baa2", + "ubuntu-x86_64": "fab66dfc9cfd2e26fb87798f855aef30c2004edc8e19570d37af555644ae1655", + "ubuntu-aarch64": "ab09f04e534306d3f301ea997c03a6a9f7bd245042d50a434f17c1c98ac64b89", + "windows-x64": "87b1033ff0f8f2fb7262d8a236bc36b981cb50d24b401c20cdf9b31099a9a217" } } }, From b7dbaf4eee56f4f2fb5b921e9750dfda89cdd933 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 29 Aug 2024 11:28:19 +0200 Subject: [PATCH 0481/1073] build-aux: Update Flatpak CEF from 103/5060 to 127/6533 The CEF module is also modified to: - Use the pre-built wrapper included in the tarball - Preserve debug symbols inside its binaries - The copy done later by OBS Studio build-system will be split from its debug symbols --- build-aux/com.obsproject.Studio.json | 3 ++- build-aux/modules/99-cef.json | 18 +++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index 70129fbd9bafc8..2b8c10767b510b 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -98,7 +98,8 @@ ], "post-install": [ "install -d /app/plugins", - "install -d /app/extensions/Plugins" + "install -d /app/extensions/Plugins", + "rm -rf /app/cef # Skip stripping original CEF library" ], "sources": [ { diff --git a/build-aux/modules/99-cef.json b/build-aux/modules/99-cef.json index 23bf14cd64eed0..bfe235beea6c11 100644 --- a/build-aux/modules/99-cef.json +++ b/build-aux/modules/99-cef.json @@ -1,19 +1,15 @@ { "name": "cef", - "buildsystem": "cmake-ninja", - "config-opts": [ - "-DCMAKE_BUILD_TYPE=Release" - ], - "no-make-install": true, - "make-args": [ - "libcef_dll_wrapper" - ], + "build-options": { + "no-debuginfo": true + }, + "buildsystem": "simple", "build-commands": [ "mkdir -p /app/cef/libcef_dll_wrapper", "cp -R ./include /app/cef", "cp -R ./Release /app/cef", "cp -R ./Resources /app/cef", - "cp -R ./libcef_dll_wrapper/libcef_dll_wrapper.a /app/cef/libcef_dll_wrapper" + "cp -R ./build/libcef_dll_wrapper/libcef_dll_wrapper.a /app/cef/libcef_dll_wrapper" ], "cleanup": [ "*" @@ -21,8 +17,8 @@ "sources": [ { "type": "archive", - "url": "https://cdn-fastly.obsproject.com/downloads/cef_binary_5060_linux_x86_64_v3.tar.xz", - "sha256": "1253a6a36c3b8ac5b5ece9bfdb6eae6ab75e49516375fc475e2e871795ad9bea" + "url": "https://cdn-fastly.obsproject.com/downloads/cef_binary_6533_linux_x86_64.tar.xz", + "sha256": "fab66dfc9cfd2e26fb87798f855aef30c2004edc8e19570d37af555644ae1655" } ] } From 213e0f13964a5d9e738ad9db08fa49eb372dbb72 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Tue, 27 Aug 2024 12:39:47 +0200 Subject: [PATCH 0482/1073] shared/qt: Unlink slider-ignorewheel from libobs As the obs.hpp include is unused and can get removed, the entire linkage to libobs is unnecessary. --- shared/qt/slider-ignorewheel/CMakeLists.txt | 2 +- shared/qt/slider-ignorewheel/slider-ignorewheel.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/qt/slider-ignorewheel/CMakeLists.txt b/shared/qt/slider-ignorewheel/CMakeLists.txt index b60ae3702889f8..92dae97fb07d4f 100644 --- a/shared/qt/slider-ignorewheel/CMakeLists.txt +++ b/shared/qt/slider-ignorewheel/CMakeLists.txt @@ -8,4 +8,4 @@ add_library(OBS::qt-slider-ignorewheel ALIAS qt-slider-ignorewheel) target_sources(qt-slider-ignorewheel INTERFACE slider-ignorewheel.cpp slider-ignorewheel.hpp) target_include_directories(qt-slider-ignorewheel INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(qt-slider-ignorewheel INTERFACE Qt::Core Qt::Widgets OBS::libobs) +target_link_libraries(qt-slider-ignorewheel INTERFACE Qt::Core Qt::Widgets) diff --git a/shared/qt/slider-ignorewheel/slider-ignorewheel.hpp b/shared/qt/slider-ignorewheel/slider-ignorewheel.hpp index 5d8f355096cb05..32b52db648a981 100644 --- a/shared/qt/slider-ignorewheel/slider-ignorewheel.hpp +++ b/shared/qt/slider-ignorewheel/slider-ignorewheel.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include From efa291b14005bc7920eadc39499dac35d002c6fe Mon Sep 17 00:00:00 2001 From: gxalpha Date: Tue, 27 Aug 2024 12:40:50 +0200 Subject: [PATCH 0483/1073] shared/qt: Remove unused graphics include from qt-wrappers --- shared/qt/wrappers/qt-wrappers.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/shared/qt/wrappers/qt-wrappers.cpp b/shared/qt/wrappers/qt-wrappers.cpp index f7cc449a1d5a7c..71b73ef85b3d87 100644 --- a/shared/qt/wrappers/qt-wrappers.cpp +++ b/shared/qt/wrappers/qt-wrappers.cpp @@ -17,7 +17,6 @@ #include "moc_qt-wrappers.cpp" -#include #include #include #include From 296a2b863192f5b6ec519862c9decfb1fcd78e57 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Sun, 25 Aug 2024 22:46:14 +0200 Subject: [PATCH 0484/1073] cmake: Exclude macOS .DS_Store from resources --- cmake/macos/helpers.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/macos/helpers.cmake b/cmake/macos/helpers.cmake index cdfd88ae3ac870..eb2b96d7537920 100644 --- a/cmake/macos/helpers.cmake +++ b/cmake/macos/helpers.cmake @@ -353,6 +353,7 @@ function(target_install_resources target) message(DEBUG "Installing resources for target ${target}...") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data") file(GLOB_RECURSE data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*") + list(FILTER data_files EXCLUDE REGEX "\\.DS_Store$") foreach(data_file IN LISTS data_files) cmake_path( RELATIVE_PATH From 7b0e15424521d10146d3fdf858cd9b9aa8160e10 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Thu, 29 Aug 2024 19:52:23 +0200 Subject: [PATCH 0485/1073] UI: Replace remaining SIGNAL/SLOT macros --- UI/auth-oauth.cpp | 8 ++++---- UI/window-basic-main.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/UI/auth-oauth.cpp b/UI/auth-oauth.cpp index fd4e53845fb2bd..0053b7863d48d6 100644 --- a/UI/auth-oauth.cpp +++ b/UI/auth-oauth.cpp @@ -51,10 +51,10 @@ OAuthLogin::OAuthLogin(QWidget *parent, const std::string &url, bool token) return; } - connect(cefWidget, SIGNAL(titleChanged(const QString &)), this, - SLOT(setWindowTitle(const QString &))); - connect(cefWidget, SIGNAL(urlChanged(const QString &)), this, - SLOT(urlChanged(const QString &))); + connect(cefWidget, &QCefWidget::titleChanged, this, + &OAuthLogin::setWindowTitle); + connect(cefWidget, &QCefWidget::urlChanged, this, + &OAuthLogin::urlChanged); QPushButton *close = new QPushButton(QTStr("Cancel")); connect(close, &QAbstractButton::clicked, this, &QDialog::reject); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 5e03d5544c6edf..3e0d9b17e46639 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2836,8 +2836,8 @@ void OBSBasic::ShowWhatsNew(const QString &url) return; } - connect(cefWidget, SIGNAL(titleChanged(const QString &)), dlg, - SLOT(setWindowTitle(const QString &))); + connect(cefWidget, &QCefWidget::titleChanged, dlg, + &QDialog::setWindowTitle); QPushButton *close = new QPushButton(QTStr("Close")); connect(close, &QAbstractButton::clicked, dlg, &QDialog::accept); From 34735be09441101217c1efc625b1b8d32fccac65 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 29 Aug 2024 11:20:39 +0200 Subject: [PATCH 0486/1073] UI: Fix Qt UIC warning in settings UI --- UI/forms/OBSBasicSettings.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index e3a103515c49e1..9a1c6cebf3b7ba 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -3987,7 +3987,7 @@ 9 - + Basic.Settings.Output.Adv.FFmpeg.CustomModeWarning From c521b23619f9938bc00de38d9ca15a3fe0061685 Mon Sep 17 00:00:00 2001 From: derrod Date: Thu, 29 Aug 2024 00:39:33 +0200 Subject: [PATCH 0487/1073] libobs: Always set initial scene item pos to top-left corner In relative mode (0, 0) is the center of the screen, so in order to maintain previous behaviour we need to convert the value here. --- libobs/obs-scene.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 0af5fda9ad2e7d..c38eeb6321f252 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -2558,6 +2558,10 @@ static obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, matrix4_identity(&item->draw_transform); matrix4_identity(&item->box_transform); + /* Ensure initial position is still top-left corner in relative mode. */ + if (!item->absolute_coordinates) + pos_from_absolute(&item->pos, &item->pos, item); + if (source_has_audio(source)) { item->visible = false; da_push_back(item->audio_actions, &action); From c837d3b533b0267d6e3670fd95398a497a2f7fa7 Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 02:05:45 -0700 Subject: [PATCH 0488/1073] linux-capture: Set xcomposite capture retry interval to 2sec Half a second was a bit too often and was spamming the debug logging when a window wasn't found. --- plugins/linux-capture/xcomposite-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/linux-capture/xcomposite-input.c b/plugins/linux-capture/xcomposite-input.c index 3e9788b069b891..55804ac01be728 100644 --- a/plugins/linux-capture/xcomposite-input.c +++ b/plugins/linux-capture/xcomposite-input.c @@ -23,7 +23,7 @@ #include #define WIN_STRING_DIV "\r\n" -#define FIND_WINDOW_INTERVAL 0.5 +#define FIND_WINDOW_INTERVAL 2.0 static Display *disp = NULL; static xcb_connection_t *conn = NULL; From 4d7bc233d7ff9c1d57c4019f5559873c202e7b4a Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 10:00:46 -0700 Subject: [PATCH 0489/1073] linux-capture: Move xcomp window ID decoding to its own func Moves the window handle/name/class decoding code out of the xcb_find_window() function and into its own dedicated function so it can be used elsewhere. This s*** is cursed. --- plugins/linux-capture/xcomposite-input.c | 53 ++++++++++++++++-------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/plugins/linux-capture/xcomposite-input.c b/plugins/linux-capture/xcomposite-input.c index 55804ac01be728..a2021102581ea3 100644 --- a/plugins/linux-capture/xcomposite-input.c +++ b/plugins/linux-capture/xcomposite-input.c @@ -69,6 +69,8 @@ struct xcompcap { }; static void xcompcap_update(void *data, obs_data_t *settings); +static xcb_window_t convert_encoded_window_id(const char *str, char **p_wname, + char **p_wcls); xcb_atom_t get_atom(xcb_connection_t *conn, const char *name) { @@ -278,19 +280,9 @@ struct darray xcomp_top_level_windows(xcb_connection_t *conn) return res.da; } -xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, - const char *str) +static xcb_window_t convert_encoded_window_id(const char *str, char **p_wname, + char **p_wcls) { - xcb_window_t ret = 0; - DARRAY(xcb_window_t) tlw = {0}; - tlw.da = xcomp_top_level_windows(conn); - if (!str || strlen(str) == 0) { - if (tlw.num > 0) - ret = *(xcb_window_t *)darray_item(sizeof(xcb_window_t), - &tlw.da, 0); - goto cleanup1; - } - size_t markSize = strlen(WIN_STRING_DIV); const char *firstMark = strstr(str, WIN_STRING_DIV); @@ -302,19 +294,45 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, const char *thirdStr = secondMark + markSize; // wstr only consists of the window-id - if (!firstMark) + if (!firstMark) { + *p_wname = NULL; + *p_wcls = NULL; return (xcb_window_t)atol(str); + } // wstr also contains window-name and window-class char *wname = bzalloc(secondMark - secondStr + 1); char *wcls = bzalloc(strEnd - thirdStr + 1); memcpy(wname, secondStr, secondMark - secondStr); memcpy(wcls, thirdStr, strEnd - thirdStr); - ret = (xcb_window_t)strtol(str, NULL, 10); + xcb_window_t ret = (xcb_window_t)strtol(str, NULL, 10); + + *p_wname = wname; + *p_wcls = wcls; + return ret; +} + +xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, + const char *str) +{ + xcb_window_t ret = 0; + char *wname = NULL; + char *wcls = NULL; + DARRAY(xcb_window_t) tlw = {0}; + + tlw.da = xcomp_top_level_windows(conn); + if (!str || strlen(str) == 0) { + if (tlw.num > 0) + ret = *(xcb_window_t *)darray_item(sizeof(xcb_window_t), + &tlw.da, 0); + goto cleanup; + } + + ret = convert_encoded_window_id(str, &wname, &wcls); // first try to find a match by the window-id if (da_find(tlw, &ret, 0) != DARRAY_INVALID) { - goto cleanup2; + goto cleanup; } // then try to find a match by name & class @@ -331,7 +349,7 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, dstr_free(&cwcls); if (found) { ret = cwin; - goto cleanup2; + goto cleanup; } } @@ -339,10 +357,9 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, "Did not find new window id for Name '%s' or Class '%s'", wname, wcls); -cleanup2: +cleanup: bfree(wname); bfree(wcls); -cleanup1: da_free(tlw); return ret; } From 74e49272bae47280dcd8c2d8711263c417947323 Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 10:02:46 -0700 Subject: [PATCH 0490/1073] linux-capture: Fix potential null pointer dereference I'm not happy. --- plugins/linux-capture/xcomposite-input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/linux-capture/xcomposite-input.c b/plugins/linux-capture/xcomposite-input.c index a2021102581ea3..b06d591d234eb9 100644 --- a/plugins/linux-capture/xcomposite-input.c +++ b/plugins/linux-capture/xcomposite-input.c @@ -767,7 +767,9 @@ static int cmp_wi(const void *a, const void *b) { struct WindowInfo *awi = (struct WindowInfo *)a; struct WindowInfo *bwi = (struct WindowInfo *)b; - return strcmp(awi->name_lower.array, bwi->name_lower.array); + const char *a_name = awi->name_lower.array ? awi->name_lower.array : ""; + const char *b_name = bwi->name_lower.array ? bwi->name_lower.array : ""; + return strcmp(a_name, b_name); } static obs_properties_t *xcompcap_props(void *unused) From c36575774bca9fd44a142395cf46e0ad96d3a5a8 Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 10:04:24 -0700 Subject: [PATCH 0491/1073] linux-capture: Add xcomp func for comparing encoded window IDs This is cursed. Window ID storage for xcomposite capture is absolutely cursed. It should not be storing the window handle with this. I'm pretty sure that whoever wrote it at the time decided to store the god-forsaken window handle (which does not persist after the window closes) as part of the ID because they were afraid it might capture the wrong window if they close OBS and open it up again while the window still exists. Again, xcomposite capture is absolutely cursed. --- plugins/linux-capture/xcomposite-input.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/linux-capture/xcomposite-input.c b/plugins/linux-capture/xcomposite-input.c index b06d591d234eb9..ef886d081dec1c 100644 --- a/plugins/linux-capture/xcomposite-input.c +++ b/plugins/linux-capture/xcomposite-input.c @@ -772,6 +772,17 @@ static int cmp_wi(const void *a, const void *b) return strcmp(a_name, b_name); } +static inline bool compare_ids(const char *id1, const char *id2) +{ + if (!id1 || !id2) + return false; + + id1 = strstr(id1, "\r\n"); + id2 = strstr(id2, "\r\n"); + + return id1 && id2 && strcmp(id1, id2) == 0; +} + static obs_properties_t *xcompcap_props(void *unused) { UNUSED_PARAMETER(unused); From dd64fef0848dd31c1bd75dbb1e06960d7ed0dd93 Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 10:06:40 -0700 Subject: [PATCH 0492/1073] linux-capture: Fix xcomp capturing random windows There are two situations where xcomposite window capture will capture random windows: on first creation, and when going to the properties when the current window is invalid. The first happens because for whatever reason someone decided to just make it capture the first top-level window if there is no set value. The second happens because the properties widget cannot find the value it's looking for and defaults to the first one when the properties are opened, thus selecting and capturing the first window in the list (which is probably something we should fix in the properties view at some point but I don't want to dive into code that's even *more* cursed than xcomposite code right now) I think that this was a major oversight and that whoever wrote it however many countless years ago did not realize that this is something that maybe users don't want to have happen. So instead, this diff makes it so that on first creation, it creates a value that says "[Select a window to capture]" that keeps the capture inactive until a user actually chooses a window rather than the top-level window. It also makes it so that if the user has a window that is no longer valid, it will keep that window in the list and as the currently selected value, which prevents it from automatically selecting the first window in the list when properties are opened. (Have I mentioned xcomposite is cursed? Trying to debug xcomposite code in a debugger freezes my window compositor and forces me to do a hard restart of my entire computer, so I was forced to use printf debugging. Absolute nightmare-inducing code in here.) --- plugins/linux-capture/data/locale/en-US.ini | 2 + plugins/linux-capture/xcomposite-input.c | 83 +++++++++++++++++---- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/plugins/linux-capture/data/locale/en-US.ini b/plugins/linux-capture/data/locale/en-US.ini index e34490bd10a188..45184085eef156 100644 --- a/plugins/linux-capture/data/locale/en-US.ini +++ b/plugins/linux-capture/data/locale/en-US.ini @@ -11,3 +11,5 @@ CropRight="Crop Right" CropBottom="Crop Bottom" IncludeXBorder="Include X Border" ExcludeAlpha="Use alpha-less texture format (Mesa workaround)" +SelectAWindow="[Select a window to capture]" +UnknownWindow="[Unknown window]" diff --git a/plugins/linux-capture/xcomposite-input.c b/plugins/linux-capture/xcomposite-input.c index ef886d081dec1c..2722945322aca9 100644 --- a/plugins/linux-capture/xcomposite-input.c +++ b/plugins/linux-capture/xcomposite-input.c @@ -320,17 +320,14 @@ xcb_window_t xcomp_find_window(xcb_connection_t *conn, Display *disp, char *wcls = NULL; DARRAY(xcb_window_t) tlw = {0}; - tlw.da = xcomp_top_level_windows(conn); if (!str || strlen(str) == 0) { - if (tlw.num > 0) - ret = *(xcb_window_t *)darray_item(sizeof(xcb_window_t), - &tlw.da, 0); goto cleanup; } ret = convert_encoded_window_id(str, &wname, &wcls); // first try to find a match by the window-id + tlw.da = xcomp_top_level_windows(conn); if (da_find(tlw, &ret, 0) != DARRAY_INVALID) { goto cleanup; } @@ -783,9 +780,9 @@ static inline bool compare_ids(const char *id1, const char *id2) return id1 && id2 && strcmp(id1, id2) == 0; } -static obs_properties_t *xcompcap_props(void *unused) +static obs_properties_t *xcompcap_props(void *data) { - UNUSED_PARAMETER(unused); + struct xcompcap *s = (struct xcompcap *)data; obs_properties_t *props = obs_properties_create(); obs_property_t *prop; @@ -796,7 +793,47 @@ static obs_properties_t *xcompcap_props(void *unused) OBS_COMBO_FORMAT_STRING); DARRAY(struct WindowInfo) window_strings = {0}; + bool had_window_saved = false; + + if (s) { + had_window_saved = s->windowName && *s->windowName; + if (had_window_saved) { + char *wname; + char *wcls; + + convert_encoded_window_id(s->windowName, &wname, &wcls); + bfree(wcls); + + struct dstr name = {0}; + struct dstr desc = {0}; + struct dstr name_lower = {0}; + + dstr_copy(&name, + wname ? wname + : obs_module_text("UnknownWindow")); + bfree(wname); + + dstr_copy(&desc, s->windowName); + dstr_copy_dstr(&name_lower, &name); + dstr_to_lower(&name_lower); + + da_push_back( + window_strings, + (&(struct WindowInfo){name_lower, name, desc})); + } else { + struct dstr select_window_str; + dstr_init_copy(&select_window_str, + obs_module_text("SelectAWindow")); + da_push_back(window_strings, + (&(struct WindowInfo){{0}, + select_window_str, + {0}})); + } + } + struct darray windows = xcomp_top_level_windows(conn); + bool window_found = false; + for (size_t w = 0; w < windows.num; w++) { xcb_window_t win = *(xcb_window_t *)darray_item( sizeof(xcb_window_t), &windows, w); @@ -813,27 +850,41 @@ static obs_properties_t *xcompcap_props(void *unused) dstr_init_copy_dstr(&name_lower, &name); dstr_to_lower(&name_lower); - da_push_back(window_strings, - (&(struct WindowInfo){name_lower, name, desc})); + if (!had_window_saved || + (s && !compare_ids(desc.array, s->windowName))) + da_push_back( + window_strings, + (&(struct WindowInfo){name_lower, name, desc})); + else { + window_found = true; + dstr_free(&name); + dstr_free(&name_lower); + dstr_free(&desc); + } } darray_free(&windows); - qsort(window_strings.array, window_strings.num, - sizeof(struct WindowInfo), cmp_wi); + if (window_strings.num > 2) + qsort(window_strings.array + 1, window_strings.num - 1, + sizeof(struct WindowInfo), cmp_wi); for (size_t i = 0; i < window_strings.num; i++) { - struct WindowInfo *s = (struct WindowInfo *)darray_item( + struct WindowInfo *w = (struct WindowInfo *)darray_item( sizeof(struct WindowInfo), &window_strings.da, i); - obs_property_list_add_string(prop, s->name.array, - s->desc.array); + obs_property_list_add_string(prop, w->name.array, + w->desc.array); - dstr_free(&s->name_lower); - dstr_free(&s->name); - dstr_free(&s->desc); + dstr_free(&w->name_lower); + dstr_free(&w->name); + dstr_free(&w->desc); } da_free(window_strings); + if (!had_window_saved || !window_found) { + obs_property_list_item_disable(prop, 0, true); + } + prop = obs_properties_add_int(props, "cut_top", obs_module_text("CropTop"), 0, 4096, 1); obs_property_int_set_suffix(prop, " px"); From 9107b90fb3bf381797e0f0d73d34c7015df7f597 Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 13:07:35 -0700 Subject: [PATCH 0493/1073] linux-capture: Fix xshm capturing first display on creation Like xcomposite, this was programmed to select the first display by default. Change it to not capture any display unless explicitly selected by the user. --- plugins/linux-capture/data/locale/en-US.ini | 1 + plugins/linux-capture/linux-capture.c | 2 + plugins/linux-capture/xshm-input.c | 57 +++++++++++++++++++-- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/plugins/linux-capture/data/locale/en-US.ini b/plugins/linux-capture/data/locale/en-US.ini index 45184085eef156..ab8a00eff13582 100644 --- a/plugins/linux-capture/data/locale/en-US.ini +++ b/plugins/linux-capture/data/locale/en-US.ini @@ -12,4 +12,5 @@ CropBottom="Crop Bottom" IncludeXBorder="Include X Border" ExcludeAlpha="Use alpha-less texture format (Mesa workaround)" SelectAWindow="[Select a window to capture]" +SelectADisplay="[Select a display to capture]" UnknownWindow="[Unknown window]" diff --git a/plugins/linux-capture/linux-capture.c b/plugins/linux-capture/linux-capture.c index 60cf76a4a82698..b17880e3eb3280 100644 --- a/plugins/linux-capture/linux-capture.c +++ b/plugins/linux-capture/linux-capture.c @@ -26,6 +26,7 @@ MODULE_EXPORT const char *obs_module_description(void) } extern struct obs_source_info xshm_input; +extern struct obs_source_info xshm_input_v2; bool obs_module_load(void) { @@ -33,6 +34,7 @@ bool obs_module_load(void) if (platform == OBS_NIX_PLATFORM_X11_EGL) { obs_register_source(&xshm_input); + obs_register_source(&xshm_input_v2); xcomposite_load(); } diff --git a/plugins/linux-capture/xshm-input.c b/plugins/linux-capture/xshm-input.c index 252625a067fb25..cc11ebe17076f1 100644 --- a/plugins/linux-capture/xshm-input.c +++ b/plugins/linux-capture/xshm-input.c @@ -32,6 +32,8 @@ along with this program. If not, see . #define blog(level, msg, ...) blog(level, "xshm-input: " msg, ##__VA_ARGS__) +#define INVALID_DISPLAY (-1) + struct xshm_data { obs_source_t *source; @@ -41,7 +43,7 @@ struct xshm_data { xcb_xcursor_t *cursor; char *server; - uint_fast32_t screen_id; + int_fast32_t screen_id; int_fast32_t x_org; int_fast32_t y_org; int_fast32_t width; @@ -225,6 +227,16 @@ static void xshm_capture_start(struct xshm_data *data) const char *server = (data->advanced && *data->server) ? data->server : NULL; + if (data->screen_id == -1) { + if (data->texture) { + gs_texture_destroy(data->texture); + data->texture = NULL; + } + data->adj_width = 0; + data->adj_height = 0; + return; + } + data->xcb = xcb_connect(server, NULL); if (!data->xcb || xcb_connection_has_error(data->xcb)) { blog(LOG_ERROR, "Unable to open X display !"); @@ -288,9 +300,10 @@ static void xshm_update(void *vptr, obs_data_t *settings) /** * Get the default settings for the capture */ -static void xshm_defaults(obs_data_t *defaults) +static void xshm_defaults(obs_data_t *defaults, int ver) { - obs_data_set_default_int(defaults, "screen", 0); + obs_data_set_default_int(defaults, "screen", + ver == 1 ? 0 : INVALID_DISPLAY); obs_data_set_default_bool(defaults, "show_cursor", true); obs_data_set_default_bool(defaults, "advanced", false); obs_data_set_default_int(defaults, "cut_top", 0); @@ -299,6 +312,16 @@ static void xshm_defaults(obs_data_t *defaults) obs_data_set_default_int(defaults, "cut_bot", 0); } +static void xshm_defaults_v1(obs_data_t *defaults) +{ + xshm_defaults(defaults, 1); +} + +static void xshm_defaults_v2(obs_data_t *defaults) +{ + xshm_defaults(defaults, 2); +} + /** * Toggle visibility of advanced settings */ @@ -335,6 +358,13 @@ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, obs_property_list_clear(screens); + if (old_screen == INVALID_DISPLAY) { + obs_property_list_add_int(screens, + obs_module_text("SelectADisplay"), + INVALID_DISPLAY); + obs_property_list_item_disable(screens, 0, true); + } + xcb_connection_t *xcb = xcb_connect(server, NULL); if (!xcb || xcb_connection_has_error(xcb)) { obs_property_set_enabled(screens, false); @@ -578,13 +608,32 @@ static uint32_t xshm_getheight(void *vptr) struct obs_source_info xshm_input = { .id = "xshm_input", .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | + OBS_SOURCE_DO_NOT_DUPLICATE | OBS_SOURCE_SRGB | + OBS_SOURCE_CAP_OBSOLETE, + .get_name = xshm_getname, + .create = xshm_create, + .destroy = xshm_destroy, + .update = xshm_update, + .get_defaults = xshm_defaults_v1, + .get_properties = xshm_properties, + .video_tick = xshm_video_tick, + .video_render = xshm_video_render, + .get_width = xshm_getwidth, + .get_height = xshm_getheight, + .icon_type = OBS_ICON_TYPE_DESKTOP_CAPTURE, +}; + +struct obs_source_info xshm_input_v2 = { + .id = "xshm_input_v2", + .type = OBS_SOURCE_TYPE_INPUT, .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_DO_NOT_DUPLICATE | OBS_SOURCE_SRGB, .get_name = xshm_getname, .create = xshm_create, .destroy = xshm_destroy, .update = xshm_update, - .get_defaults = xshm_defaults, + .get_defaults = xshm_defaults_v2, .get_properties = xshm_properties, .video_tick = xshm_video_tick, .video_render = xshm_video_render, From e9c8e10729fbe804285398c161d206b7ea1eaf8e Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 12:21:24 -0700 Subject: [PATCH 0494/1073] win-capture: Fix window capture capturing random windows This is due to the property view widget not having an item to select, so it selects the first one, but we want to have specific text for this anyway, so changing it here is still appropriate. (I don't want to touch the properties view widget right now for the sake of my sanity) --- plugins/win-capture/data/locale/en-US.ini | 1 + plugins/win-capture/window-capture.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/plugins/win-capture/data/locale/en-US.ini b/plugins/win-capture/data/locale/en-US.ini index 237b4726c9f9b7..d52b98da1925a2 100644 --- a/plugins/win-capture/data/locale/en-US.ini +++ b/plugins/win-capture/data/locale/en-US.ini @@ -42,6 +42,7 @@ Mode="Mode" CaptureAudio="Capture Audio (BETA)" CaptureAudio.TT="When enabled, creates an \"Application Audio Capture\" source that automatically updates to the currently captured window/application.\nNote that if Desktop Audio is configured, this could result in doubled audio." AudioSuffix="Audio" +SelectAWindow="[Select a window to capture]" # Generic compatibility messages Compatibility.GameCapture.Admin="%name% may require OBS to be run as admin to use Game Capture." diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index eb540016a4191d..0c49e8b566ac99 100644 --- a/plugins/win-capture/window-capture.c +++ b/plugins/win-capture/window-capture.c @@ -567,6 +567,15 @@ static obs_properties_t *wc_properties(void *data) p = obs_properties_add_list(ppts, "window", TEXT_WINDOW, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + + /* Add "[Select a window]" on first use to prevent the properties list + * widget from selecting the first one in the list automatically */ + if (wc && (!wc->title || !wc->class || !wc->executable)) { + obs_property_list_add_string( + p, obs_module_text("SelectAWindow"), ""); + obs_property_list_item_disable(p, 0, true); + } + ms_fill_window_list(p, EXCLUDE_MINIMIZED, NULL); obs_property_set_modified_callback(p, wc_window_changed); From a1db1e848222bc1e8577db8c2915c2891851ca87 Mon Sep 17 00:00:00 2001 From: Lain Date: Mon, 2 Sep 2024 12:33:53 -0700 Subject: [PATCH 0495/1073] win-capture: Fix display capture capturing on create Prevents Display capture from capturing the first display on creation. This issue is due to the properties view combo box automatically selecting the first item in the list by default, but this needs explicit text anyway to indicate display, so this adds a "[Select a display]" item that will prevent that from happening and tell the user to select a display. --- plugins/win-capture/data/locale/en-US.ini | 1 + plugins/win-capture/duplicator-monitor-capture.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/win-capture/data/locale/en-US.ini b/plugins/win-capture/data/locale/en-US.ini index d52b98da1925a2..a41ff1c7ae1a9c 100644 --- a/plugins/win-capture/data/locale/en-US.ini +++ b/plugins/win-capture/data/locale/en-US.ini @@ -43,6 +43,7 @@ CaptureAudio="Capture Audio (BETA)" CaptureAudio.TT="When enabled, creates an \"Application Audio Capture\" source that automatically updates to the currently captured window/application.\nNote that if Desktop Audio is configured, this could result in doubled audio." AudioSuffix="Audio" SelectAWindow="[Select a window to capture]" +SelectADisplay="[Select a display to capture]" # Generic compatibility messages Compatibility.GameCapture.Admin="%name% may require OBS to be run as admin to use Game Capture." diff --git a/plugins/win-capture/duplicator-monitor-capture.c b/plugins/win-capture/duplicator-monitor-capture.c index c9abb00141529c..fce26c72210bb9 100644 --- a/plugins/win-capture/duplicator-monitor-capture.c +++ b/plugins/win-capture/duplicator-monitor-capture.c @@ -38,6 +38,8 @@ #define RESET_INTERVAL_SEC 3.0f +#define INVALID_DISPLAY "DUMMY" + typedef BOOL (*PFN_winrt_capture_supported)(); typedef BOOL (*PFN_winrt_capture_cursor_toggle_supported)(); typedef struct winrt_capture *(*PFN_winrt_capture_init_monitor)( @@ -416,7 +418,7 @@ static void duplicator_capture_destroy(void *data) static void duplicator_capture_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, "method", METHOD_AUTO); - obs_data_set_default_string(settings, "monitor_id", "DUMMY"); + obs_data_set_default_string(settings, "monitor_id", INVALID_DISPLAY); obs_data_set_default_int(settings, "monitor_wgc", 0); obs_data_set_default_bool(settings, "capture_cursor", true); obs_data_set_default_bool(settings, "force_sdr", false); @@ -905,6 +907,13 @@ static obs_properties_t *duplicator_capture_properties(void *data) props, "monitor_id", TEXT_MONITOR, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + if (capture && strcmp(capture->monitor_id, INVALID_DISPLAY) == 0) { + obs_property_list_add_string(monitors, + obs_module_text("SelectADisplay"), + INVALID_DISPLAY); + obs_property_list_item_disable(monitors, 0, true); + } + obs_properties_add_bool(props, "capture_cursor", TEXT_CAPTURE_CURSOR); obs_properties_add_bool(props, "force_sdr", TEXT_FORCE_SDR); From f5646c6aafb7e2738c7f193b06ed4465c1a7d6a3 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 5 Sep 2024 15:45:31 -0400 Subject: [PATCH 0496/1073] obs-websocket: Update version to 5.5.3 Changes: - Remove legacy CMake - Fix various build issues Changelog: https://github.com/obsproject/obs-websocket/commit/a73c92788d70f08f91b8c0477b74f99c999beb09 --- plugins/obs-websocket | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-websocket b/plugins/obs-websocket index 0548c7798a323f..a73c92788d70f0 160000 --- a/plugins/obs-websocket +++ b/plugins/obs-websocket @@ -1 +1 @@ -Subproject commit 0548c7798a323fe5296c150e13b898a5ee62fc1e +Subproject commit a73c92788d70f08f91b8c0477b74f99c999beb09 From 26b7b4511c6ceeefc0605ff75f30e922588fd433 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Mon, 18 Mar 2024 17:32:20 +0100 Subject: [PATCH 0497/1073] libobs/util: Add `os_nstime_to_timespec` --- libobs/util/platform.c | 57 ++++++++++++++++++++++++++++++++++++++++++ libobs/util/platform.h | 4 +++ 2 files changed, 61 insertions(+) diff --git a/libobs/util/platform.c b/libobs/util/platform.c index 068b6d60e8e1b4..1b9d894aa99d16 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -27,6 +27,7 @@ #include "utf8.h" #include "dstr.h" #include "obs.h" +#include "threading.h" FILE *os_wfopen(const wchar_t *path, const char *mode) { @@ -807,3 +808,59 @@ char *os_generate_formatted_filename(const char *extension, bool space, return sf.array; } + +static struct { + struct timespec ts; + bool ts_valid; + uint64_t timestamp; +} timespec_offset = {0}; + +static void init_timespec_offset(void) +{ + timespec_offset.ts_valid = + timespec_get(×pec_offset.ts, TIME_UTC) == TIME_UTC; + timespec_offset.timestamp = os_gettime_ns(); +} + +struct timespec *os_nstime_to_timespec(uint64_t timestamp, + struct timespec *storage) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, init_timespec_offset); + + if (!storage || !timespec_offset.ts_valid) { + return NULL; + } + + *storage = timespec_offset.ts; + + static const int64_t nsecs_per_sec = 1000000000; + + int64_t nsecs = 0; + int64_t secs = 0; + if (timestamp >= timespec_offset.timestamp) { + uint64_t offset = timestamp - timespec_offset.timestamp; + nsecs = storage->tv_nsec + offset % nsecs_per_sec; + secs = storage->tv_sec + offset / nsecs_per_sec; + } else { + uint64_t offset = timespec_offset.timestamp - timestamp; + int64_t nsec_offset = offset % nsecs_per_sec; + int64_t sec_offset = offset / nsecs_per_sec; + int64_t tv_nsec = storage->tv_nsec; + if (nsec_offset > tv_nsec) { + storage->tv_sec -= 1; + tv_nsec += nsecs_per_sec; + } + nsecs = tv_nsec - nsec_offset; + secs = storage->tv_sec - sec_offset; + } + + if (nsecs > nsecs_per_sec) { + nsecs -= nsecs_per_sec; + secs += 1; + } + storage->tv_nsec = (long)nsecs; + storage->tv_sec = (time_t)secs; + + return storage; +} diff --git a/libobs/util/platform.h b/libobs/util/platform.h index a3a2b74c4369af..d66394f8289f4f 100644 --- a/libobs/util/platform.h +++ b/libobs/util/platform.h @@ -205,6 +205,10 @@ EXPORT uint64_t os_get_proc_virtual_size(void); EXPORT char *os_generate_uuid(void); +EXPORT +struct timespec *os_nstime_to_timespec(uint64_t timestamp, + struct timespec *storage); + /* clang-format off */ #ifdef __APPLE__ # define ARCH_BITS 64 From 6a53b8928f2c9942d8f7fdae1ceedfe7d133bd00 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Tue, 9 Jul 2024 13:23:21 -0400 Subject: [PATCH 0498/1073] libobs: Add encoder packet timing support Introduce support for the `encoder_packet_time` struct to capture timing information for each frame, starting from the composition of each frame, through the encoder, to the queueing of the frame data to each output_t. Timestamps for each of the following events are based on `os_gettime_ns()`: CTS: Composition time stamp (in the encoder render threads) FER: Frame encode request FERC: Frame encoder request completely PIR: Packet interleave request (`send_interleaved()`) Frame times are forwarded through encoder callbacks in the context that runs on the relevant encoder thread, ensuring no race conditions with accessing per encoder array happen. All per-output processing happens on data that is owned by the output. Co-authored-by: Ruwen Hahn --- libobs/obs-encoder.c | 101 ++++++++++++++++++++++++++-------- libobs/obs-encoder.h | 50 +++++++++++++++++ libobs/obs-internal.h | 26 +++++---- libobs/obs-output-delay.c | 17 ++++-- libobs/obs-output.c | 93 ++++++++++++++++++++++++++++--- libobs/obs-output.h | 1 + libobs/obs-video-gpu-encode.c | 27 +++++++++ 7 files changed, 268 insertions(+), 47 deletions(-) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 36a01d1384fa1a..70b5a98de1adbb 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -417,6 +417,7 @@ void obs_encoder_destroy(obs_encoder_t *encoder) encoder->info.destroy(encoder->context.data); da_free(encoder->callbacks); da_free(encoder->roi); + da_free(encoder->encoder_packet_times); pthread_mutex_destroy(&encoder->init_mutex); pthread_mutex_destroy(&encoder->callbacks_mutex); pthread_mutex_destroy(&encoder->outputs_mutex); @@ -713,10 +714,9 @@ void obs_encoder_shutdown(obs_encoder_t *encoder) pthread_mutex_unlock(&encoder->init_mutex); } -static inline size_t -get_callback_idx(const struct obs_encoder *encoder, - void (*new_packet)(void *param, struct encoder_packet *packet), - void *param) +static inline size_t get_callback_idx(const struct obs_encoder *encoder, + encoded_callback_t new_packet, + void *param) { for (size_t i = 0; i < encoder->callbacks.num; i++) { struct encoder_callback *cb = encoder->callbacks.array + i; @@ -738,10 +738,9 @@ void pause_reset(struct pause_data *pause) pthread_mutex_unlock(&pause->mutex); } -static inline void obs_encoder_start_internal( - obs_encoder_t *encoder, - void (*new_packet)(void *param, struct encoder_packet *packet), - void *param) +static inline void obs_encoder_start_internal(obs_encoder_t *encoder, + encoded_callback_t new_packet, + void *param) { struct encoder_callback cb = {false, new_packet, param}; bool first = false; @@ -768,9 +767,7 @@ static inline void obs_encoder_start_internal( } } -void obs_encoder_start(obs_encoder_t *encoder, - void (*new_packet)(void *param, - struct encoder_packet *packet), +void obs_encoder_start(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param) { if (!obs_encoder_valid(encoder, "obs_encoder_start")) @@ -783,9 +780,7 @@ void obs_encoder_start(obs_encoder_t *encoder, pthread_mutex_unlock(&encoder->init_mutex); } -void obs_encoder_stop(obs_encoder_t *encoder, - void (*new_packet)(void *param, - struct encoder_packet *packet), +void obs_encoder_stop(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param) { bool last = false; @@ -807,6 +802,8 @@ void obs_encoder_stop(obs_encoder_t *encoder, pthread_mutex_unlock(&encoder->callbacks_mutex); + encoder->encoder_packet_times.num = 0; + if (last) { remove_connection(encoder, true); pthread_mutex_unlock(&encoder->init_mutex); @@ -1263,7 +1260,8 @@ static inline bool get_sei(const struct obs_encoder *encoder, uint8_t **sei, static void send_first_video_packet(struct obs_encoder *encoder, struct encoder_callback *cb, - struct encoder_packet *packet) + struct encoder_packet *packet, + struct encoder_packet_time *packet_time) { struct encoder_packet first_packet; DARRAY(uint8_t) data; @@ -1277,7 +1275,7 @@ static void send_first_video_packet(struct obs_encoder *encoder, da_init(data); if (!get_sei(encoder, &sei, &size) || !sei || !size) { - cb->new_packet(cb->param, packet); + cb->new_packet(cb->param, packet, packet_time); cb->sent_first_packet = true; return; } @@ -1289,7 +1287,7 @@ static void send_first_video_packet(struct obs_encoder *encoder, first_packet.data = data.array; first_packet.size = data.num; - cb->new_packet(cb->param, &first_packet); + cb->new_packet(cb->param, &first_packet, packet_time); cb->sent_first_packet = true; da_free(data); @@ -1298,14 +1296,15 @@ static void send_first_video_packet(struct obs_encoder *encoder, static const char *send_packet_name = "send_packet"; static inline void send_packet(struct obs_encoder *encoder, struct encoder_callback *cb, - struct encoder_packet *packet) + struct encoder_packet *packet, + struct encoder_packet_time *packet_time) { profile_start(send_packet_name); /* include SEI in first video packet */ if (encoder->info.type == OBS_ENCODER_VIDEO && !cb->sent_first_packet) - send_first_video_packet(encoder, cb, packet); + send_first_video_packet(encoder, cb, packet, packet_time); else - cb->new_packet(cb->param, packet); + cb->new_packet(cb->param, packet, packet_time); profile_end(send_packet_name); } @@ -1357,12 +1356,40 @@ void send_off_encoder_packet(obs_encoder_t *encoder, bool success, pkt->sys_dts_usec += encoder->pause.ts_offset / 1000; pthread_mutex_unlock(&encoder->pause.mutex); + /* Find the encoder packet timing entry in the encoder + * timing array with the corresponding PTS value, then remove + * the entry from the array to ensure it doesn't continuously fill. + */ + struct encoder_packet_time ept_local; + struct encoder_packet_time *ept = NULL; + bool found_ept = false; + if (pkt->type == OBS_ENCODER_VIDEO) { + for (size_t i = encoder->encoder_packet_times.num; + i > 0; i--) { + ept = &encoder->encoder_packet_times + .array[i - 1]; + if (ept->pts == pkt->pts) { + ept_local = *ept; + da_erase(encoder->encoder_packet_times, + i - 1); + found_ept = true; + break; + } + } + if (!found_ept) + blog(LOG_DEBUG, + "%s: Encoder packet timing for PTS %" PRId64 + " not found", + __FUNCTION__, pkt->pts); + } + pthread_mutex_lock(&encoder->callbacks_mutex); for (size_t i = encoder->callbacks.num; i > 0; i--) { struct encoder_callback *cb; cb = encoder->callbacks.array + (i - 1); - send_packet(encoder, cb, pkt); + send_packet(encoder, cb, pkt, + found_ept ? &ept_local : NULL); } pthread_mutex_unlock(&encoder->callbacks_mutex); @@ -1370,7 +1397,8 @@ void send_off_encoder_packet(obs_encoder_t *encoder, bool success, } static const char *do_encode_name = "do_encode"; -bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame) +bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame, + const uint64_t *frame_cts) { profile_start(do_encode_name); if (!encoder->profile_encoder_encode_name) @@ -1381,6 +1409,7 @@ bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame) struct encoder_packet pkt = {0}; bool received = false; bool success; + uint64_t fer_ts = 0; if (encoder->reconfigure_requested) { encoder->reconfigure_requested = false; @@ -1392,10 +1421,34 @@ bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame) pkt.timebase_den = encoder->timebase_den; pkt.encoder = encoder; + /* Get the frame encode request timestamp. This + * needs to be read just before the encode request. + */ + fer_ts = os_gettime_ns(); + profile_start(encoder->profile_encoder_encode_name); success = encoder->info.encode(encoder->context.data, frame, &pkt, &received); profile_end(encoder->profile_encoder_encode_name); + + /* Generate and enqueue the frame timing metrics, namely + * the CTS (composition time), FER (frame encode request), FERC + * (frame encode request complete) and current PTS. PTS is used to + * associate the frame timing data with the encode packet. */ + if (frame_cts) { + struct encoder_packet_time *ept = + da_push_back_new(encoder->encoder_packet_times); + // Get the frame encode request complete timestamp + if (success) { + ept->ferc = os_gettime_ns(); + } else { + // Encode had error, set ferc to 0 + ept->ferc = 0; + } + ept->pts = frame->pts; + ept->cts = *frame_cts; + ept->fer = fer_ts; + } send_off_encoder_packet(encoder, success, received, &pkt); profile_end(do_encode_name); @@ -1484,7 +1537,7 @@ static void receive_video(void *param, struct video_data *frame) enc_frame.frames = 1; enc_frame.pts = encoder->cur_pts; - if (do_encode(encoder, &enc_frame)) + if (do_encode(encoder, &enc_frame, &frame->timestamp)) encoder->cur_pts += encoder->timebase_num * encoder->frame_rate_divisor; @@ -1622,7 +1675,7 @@ static bool send_audio_data(struct obs_encoder *encoder) enc_frame.frames = (uint32_t)encoder->framesize; enc_frame.pts = encoder->cur_pts; - if (!do_encode(encoder, &enc_frame)) + if (!do_encode(encoder, &enc_frame, NULL)) return false; encoder->cur_pts += encoder->framesize; diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h index 5c7859b8e4c0b8..780e0fe0e9bab5 100644 --- a/libobs/obs-encoder.h +++ b/libobs/obs-encoder.h @@ -45,6 +45,56 @@ enum obs_encoder_type { OBS_ENCODER_VIDEO /**< The encoder provides a video codec */ }; +/* encoder_packet_time is used for timestamping events associated + * with each video frame. This is useful for deriving absolute + * timestamps (i.e. wall-clock based formats) and measuring latency. + * + * For each frame, there are four events of interest, described in + * the encoder_packet_time struct, namely cts, fer, ferc, and pir. + * The timebase of these four events is os_gettime_ns(), which provides + * very high resolution timestamping, and the ability to convert the + * timing to any other time format. + * + * Each frame follows a timeline in the following temporal order: + * CTS, FER, FERC, PIR + * + * PTS is the integer-based monotonically increasing value that is used + * to associate an encoder_packet_time entry with a specific encoder_packet. + */ +struct encoder_packet_time { + /* PTS used to associate uncompressed frames with encoded packets. */ + int64_t pts; + + /* Composition timestamp is when the frame was rendered, + * captured via os_gettime_ns(). + */ + uint64_t cts; + + /* FERC (Frame Encode Request) is when the frame was + * submitted to the encoder for encoding via the encode + * callback (e.g. encode_texture2()), captured via os_gettime_ns(). + */ + uint64_t fer; + + /* FERC (Frame Encode Request Complete) is when + * the associated FER event completed. If the encode + * is synchronous with the call, this means FERC - FEC + * measures the actual encode time, otherwise if the + * encode is asynchronous, it measures the pipeline + * delay between encode request and encode complete. + * FERC is also captured via os_gettime_ns(). + */ + uint64_t ferc; + + /* PIR (Packet Interleave Request) is when the encoded packet + * is interleaved with the stream. PIR is captured via + * os_gettime_ns(). The difference between PIR and CTS gives + * the total latency between frame rendering + * and packet interleaving. + */ + uint64_t pir; +}; + /** Encoder output packet */ struct encoder_packet { uint8_t *data; /**< Packet data */ diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 7a0938d2e79b4e..9de549f294e943 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -26,6 +26,7 @@ #include "util/profiler.h" #include "util/task.h" #include "util/uthash.h" +#include "util/array-serializer.h" #include "callback/signal.h" #include "callback/proc.h" @@ -1066,9 +1067,12 @@ struct delay_data { enum delay_msg msg; uint64_t ts; struct encoder_packet packet; + bool packet_time_valid; + struct encoder_packet_time packet_time; }; -typedef void (*encoded_callback_t)(void *data, struct encoder_packet *packet); +typedef void (*encoded_callback_t)(void *data, struct encoder_packet *packet, + struct encoder_packet_time *frame_time); struct obs_weak_output { struct obs_weak_ref ref; @@ -1186,6 +1190,8 @@ struct obs_output { // captions are output per track struct caption_track_data *caption_tracks[MAX_OUTPUT_VIDEO_ENCODERS]; + DARRAY(struct encoder_packet_time) encoder_packet_times[MAX_OUTPUT_VIDEO_ENCODERS]; + bool valid; uint64_t active_delay_ns; @@ -1213,7 +1219,8 @@ static inline void do_output_signal(struct obs_output *output, calldata_free(¶ms); } -extern void process_delay(void *data, struct encoder_packet *packet); +extern void process_delay(void *data, struct encoder_packet *packet, + struct encoder_packet_time *packet_time); extern void obs_output_cleanup_delay(obs_output_t *output); extern bool obs_output_delay_start(obs_output_t *output); extern void obs_output_delay_stop(obs_output_t *output); @@ -1241,7 +1248,7 @@ struct obs_weak_encoder { struct encoder_callback { bool sent_first_packet; - void (*new_packet)(void *param, struct encoder_packet *packet); + encoded_callback_t new_packet; void *param; }; @@ -1333,6 +1340,8 @@ struct obs_encoder { pthread_mutex_t callbacks_mutex; DARRAY(struct encoder_callback) callbacks; + DARRAY(struct encoder_packet_time) encoder_packet_times; + struct pause_data pause; const char *profile_encoder_encode_name; @@ -1348,13 +1357,9 @@ extern bool obs_encoder_initialize(obs_encoder_t *encoder); extern void obs_encoder_shutdown(obs_encoder_t *encoder); extern void obs_encoder_start(obs_encoder_t *encoder, - void (*new_packet)(void *param, - struct encoder_packet *packet), - void *param); + encoded_callback_t new_packet, void *param); extern void obs_encoder_stop(obs_encoder_t *encoder, - void (*new_packet)(void *param, - struct encoder_packet *packet), - void *param); + encoded_callback_t new_packet, void *param); extern void obs_encoder_add_output(struct obs_encoder *encoder, struct obs_output *output); @@ -1364,7 +1369,8 @@ extern void obs_encoder_remove_output(struct obs_encoder *encoder, extern bool start_gpu_encode(obs_encoder_t *encoder); extern void stop_gpu_encode(obs_encoder_t *encoder); -extern bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame); +extern bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame, + const uint64_t *frame_cts); extern void send_off_encoder_packet(obs_encoder_t *encoder, bool success, bool received, struct encoder_packet *pkt); diff --git a/libobs/obs-output-delay.c b/libobs/obs-output-delay.c index 82812e9ddb0889..d0b386be4259af 100644 --- a/libobs/obs-output-delay.c +++ b/libobs/obs-output-delay.c @@ -45,12 +45,17 @@ static inline bool log_flag_encoded(const struct obs_output *output, } static inline void push_packet(struct obs_output *output, - struct encoder_packet *packet, uint64_t t) + struct encoder_packet *packet, + struct encoder_packet_time *packet_time, + uint64_t t) { struct delay_data dd; dd.msg = DELAY_MSG_PACKET; dd.ts = t; + dd.packet_time_valid = packet_time != NULL; + if (packet_time != NULL) + dd.packet_time = *packet_time; obs_encoder_packet_create_instance(&dd.packet, packet); pthread_mutex_lock(&output->delay_mutex); @@ -66,7 +71,10 @@ static inline void process_delay_data(struct obs_output *output, if (!delay_active(output) || !delay_capturing(output)) obs_encoder_packet_release(&dd->packet); else - output->delay_callback(output, &dd->packet); + output->delay_callback(output, &dd->packet, + dd->packet_time_valid + ? &dd->packet_time + : NULL); break; case DELAY_MSG_START: obs_output_actual_start(output); @@ -128,11 +136,12 @@ static inline bool pop_packet(struct obs_output *output, uint64_t t) return popped; } -void process_delay(void *data, struct encoder_packet *packet) +void process_delay(void *data, struct encoder_packet *packet, + struct encoder_packet_time *packet_time) { struct obs_output *output = data; uint64_t t = os_gettime_ns(); - push_packet(output, packet, t); + push_packet(output, packet, packet_time, t); while (pop_packet(output, t)) ; } diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 03cc75b6cc6019..da1c81d8b7af41 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -18,6 +18,7 @@ #include #include "util/platform.h" #include "util/util_uint64.h" +#include "util/array-serializer.h" #include "graphics/math-extra.h" #include "obs.h" #include "obs-internal.h" @@ -308,6 +309,9 @@ void obs_output_destroy(obs_output_t *output) da_free(output->keyframe_group_tracking); + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) + da_free(output->encoder_packet_times[i]); + clear_raw_audio_buffers(output); os_event_destroy(output->stopping_event); @@ -365,6 +369,7 @@ bool obs_output_actual_start(obs_output_t *output) deque_init(&ctrack->caption_data); pthread_mutex_unlock(&ctrack->caption_mutex); } + return success; } @@ -1476,8 +1481,10 @@ static inline void check_received(struct obs_output *output, } } -static inline void apply_interleaved_packet_offset(struct obs_output *output, - struct encoder_packet *out) +static inline void +apply_interleaved_packet_offset(struct obs_output *output, + struct encoder_packet *out, + struct encoder_packet_time *packet_time) { int64_t offset; @@ -1491,6 +1498,8 @@ static inline void apply_interleaved_packet_offset(struct obs_output *output, out->dts -= offset; out->pts -= offset; + if (packet_time) + packet_time->pts -= offset; /* convert the newly adjusted dts to relative dts time to ensure proper * interleaving. if we're using an audio encoder that's already been @@ -1526,7 +1535,7 @@ static size_t extract_itut_t35_buffer_from_sei(sei_t *sei, uint8_t **data_out) if (!sei || !sei->head) { return 0; } - /* We should only need to get one payload, because the SEI that was + /* We should only need to get one payload, because the SEI that was * generated should only have one message, so no need to iterate. If * we did iterate, we would need to generate multiple OBUs. */ sei_message_t *msg = sei_message_head(sei); @@ -1712,8 +1721,8 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) uint8_t *obu_buffer = NULL; size_t obu_buffer_size = 0; size = extract_itut_t35_buffer_from_sei(&sei, &data); - metadata_obu_itu_t35(data, size, &obu_buffer, - &obu_buffer_size); + metadata_obu_itut_t35(data, size, &obu_buffer, + &obu_buffer_size); if (obu_buffer) { da_push_back_array(out_data, obu_buffer, obu_buffer_size); @@ -1736,6 +1745,8 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) static inline void send_interleaved(struct obs_output *output) { struct encoder_packet out = output->interleaved_packets.array[0]; + struct encoder_packet_time ept_local = {0}; + bool found_ept = false; /* do not send an interleaved packet if there's no packet of the * opposing type of a higher timestamp in the interleave buffer. @@ -1779,6 +1790,39 @@ static inline void send_interleaved(struct obs_output *output) } } pthread_mutex_unlock(&ctrack->caption_mutex); + + /* Iterate the array of encoder packet times to + * find a matching PTS entry, and drain the array. + * Packet timing currently applies to video only. + */ + struct encoder_packet_time *ept = NULL; + size_t num_ept = + output->encoder_packet_times[out.track_idx].num; + if (num_ept) { + for (size_t i = 0; i < num_ept; i++) { + ept = &output->encoder_packet_times[out.track_idx] + .array[i]; + if (ept->pts == out.pts) { + ept_local = *ept; + da_erase(output->encoder_packet_times + [out.track_idx], + i); + found_ept = true; + break; + } + } + if (found_ept == false) { + blog(LOG_DEBUG, + "%s: Track %lu encoder packet timing for PTS%" PRId64 + " not found.", + __FUNCTION__, out.track_idx, out.pts); + } + } else { + // encoder_packet_times should not be empty; log if so. + blog(LOG_DEBUG, + "%s: Track %lu encoder packet timing array empty.", + __FUNCTION__, out.track_idx); + } } output->info.encoded_packet(output->context.data, &out); @@ -1908,6 +1952,10 @@ static void discard_to_idx(struct obs_output *output, size_t idx) for (size_t i = 0; i < idx; i++) { struct encoder_packet *packet = &output->interleaved_packets.array[i]; + if (packet->type == OBS_ENCODER_VIDEO) { + da_pop_front( + output->encoder_packet_times[packet->track_idx]); + } obs_encoder_packet_release(packet); } @@ -2095,7 +2143,7 @@ static bool initialize_interleaved_packets(struct obs_output *output) for (size_t i = 0; i < output->interleaved_packets.num; i++) { struct encoder_packet *packet = &output->interleaved_packets.array[i]; - apply_interleaved_packet_offset(output, packet); + apply_interleaved_packet_offset(output, packet, NULL); } return true; @@ -2242,12 +2290,25 @@ check_encoder_group_keyframe_alignment(obs_output_t *output, da_insert(output->keyframe_group_tracking, idx, &insert_data); } -static void interleave_packets(void *data, struct encoder_packet *packet) +static void apply_ept_offsets(struct obs_output *output) +{ + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + for (size_t j = 0; j < output->encoder_packet_times[i].num; + j++) { + output->encoder_packet_times[i].array[j].pts -= + output->video_offsets[i]; + } + } +} + +static void interleave_packets(void *data, struct encoder_packet *packet, + struct encoder_packet_time *packet_time) { struct obs_output *output = data; struct encoder_packet out; bool was_started; bool received_video; + struct encoder_packet_time *output_packet_time = NULL; if (!active(output)) return; @@ -2283,8 +2344,15 @@ static void interleave_packets(void *data, struct encoder_packet *packet) else obs_encoder_packet_create_instance(&out, packet); + if (packet_time) { + output_packet_time = da_push_back_new( + output->encoder_packet_times[packet->track_idx]); + *output_packet_time = *packet_time; + } + if (was_started) - apply_interleaved_packet_offset(output, &out); + apply_interleaved_packet_offset(output, &out, + output_packet_time); else check_received(output, packet); @@ -2304,6 +2372,7 @@ static void interleave_packets(void *data, struct encoder_packet *packet) if (prune_interleaved_packets(output)) { if (initialize_interleaved_packets(output)) { resort_interleaved_packets(output); + apply_ept_offsets(output); send_interleaved(output); } } @@ -2317,8 +2386,10 @@ static void interleave_packets(void *data, struct encoder_packet *packet) pthread_mutex_unlock(&output->interleaved_mutex); } -static void default_encoded_callback(void *param, struct encoder_packet *packet) +static void default_encoded_callback(void *param, struct encoder_packet *packet, + struct encoder_packet_time *packet_time) { + UNUSED_PARAMETER(packet_time); struct obs_output *output = param; if (data_active(output)) { @@ -2490,6 +2561,10 @@ static void reset_packet_data(obs_output_t *output) output->received_audio = false; output->highest_audio_ts = 0; + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + output->encoder_packet_times[i].num = 0; + } + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { output->received_video[i] = false; output->video_offsets[i] = 0; diff --git a/libobs/obs-output.h b/libobs/obs-output.h index 1f981fa679a3b2..1c3a26fc1ee6af 100644 --- a/libobs/obs-output.h +++ b/libobs/obs-output.h @@ -21,6 +21,7 @@ extern "C" { #endif +/* obs_output_info.flags definitions */ #define OBS_OUTPUT_VIDEO (1 << 0) #define OBS_OUTPUT_AUDIO (1 << 1) #define OBS_OUTPUT_AV (OBS_OUTPUT_VIDEO | OBS_OUTPUT_AUDIO) diff --git a/libobs/obs-video-gpu-encode.c b/libobs/obs-video-gpu-encode.c index bb42be80cdf871..3762fc826bdf31 100644 --- a/libobs/obs-video-gpu-encode.c +++ b/libobs/obs-video-gpu-encode.c @@ -40,6 +40,7 @@ static void *gpu_encode_thread(void *data) uint64_t lock_key; uint64_t next_key; size_t lock_count = 0; + uint64_t fer_ts = 0; if (os_atomic_load_bool(&video->gpu_encode_stop)) break; @@ -153,6 +154,11 @@ static void *gpu_encode_thread(void *data) else next_key++; + /* Get the frame encode request timestamp. This + * needs to be read just before the encode request. + */ + fer_ts = os_gettime_ns(); + profile_start(gpu_encode_frame_name); if (encoder->info.encode_texture2) { struct encoder_texture tex = {0}; @@ -173,6 +179,27 @@ static void *gpu_encode_thread(void *data) } profile_end(gpu_encode_frame_name); + /* Generate and enqueue the frame timing metrics, namely + * the CTS (composition time), FER (frame encode request), FERC + * (frame encode request complete) and current PTS. PTS is used to + * associate the frame timing data with the encode packet. */ + if (tf.timestamp) { + struct encoder_packet_time *ept = + da_push_back_new( + encoder->encoder_packet_times); + // Get the frame encode request complete timestamp + if (success) { + ept->ferc = os_gettime_ns(); + } else { + // Encode had error, set ferc to 0 + ept->ferc = 0; + } + + ept->pts = encoder->cur_pts; + ept->cts = tf.timestamp; + ept->fer = fer_ts; + } + send_off_encoder_packet(encoder, success, received, &pkt); From 0a36ed1164e86fed7cb254756f02349ab6e45688 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Wed, 21 Aug 2024 10:32:58 -0400 Subject: [PATCH 0499/1073] libobs: Add a packet callback mechanism Packet callbacks are invoked in `send_interleaved()` and are useful for any plugin to extend functionality at the packet processing level without needing to modify code in libobs. Closed caption support is one candidate that is suitable for migration to a packet callback. The packet callback also supports the new encoder packet timing feature. This means a registered callback will have access to both the compressed encoder packet and the associated encoder packet timing information. --- docs/sphinx/reference-outputs.rst | 38 +++++++++++++++++++++++++ libobs/obs-internal.h | 13 ++++++++- libobs/obs-output.c | 47 +++++++++++++++++++++++++++++++ libobs/obs.h | 18 ++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/reference-outputs.rst b/docs/sphinx/reference-outputs.rst index 327f7eda47d181..3466ccb7fd7922 100644 --- a/docs/sphinx/reference-outputs.rst +++ b/docs/sphinx/reference-outputs.rst @@ -734,6 +734,44 @@ General Output Functions --------------------- +.. function:: void obs_output_add_packet_callback(obs_output_t *output, void (*packet_cb)(obs_output_t *output, + struct encoder_packet *pkt, struct encoder_packet_time *pkt_time, void *param), void *param) + + Register a packet callback function for the output. The callback is invoked for each compressed + packet just before sending to the service. This packet callback mechanism is the preferred method + for all packet-level processing that is not required to be implemented in libobs. Any reallocation + of the packet buffer, if necessary, must be done with functions in `libobs\util\bmem.h`, otherwise + a memory leak may occur. Never use `memset()` to clear the packet buffer, as the buffer data is + needed for subsequent callback processing. + + :param output: The output to register the packet_cb() function against + :param packet_cb: Function pointer to the callback function + :param param: Data passed to the callback + :return: When the callback is added + + packet_cb() arguments: + :param output: The output associated with the invoked callback function + :param pkt: Compressed data packet (audio or video) + :param pkt_time: encoder_packet_time structure associated with the data packet + :param param: Data passed to the callback + + .. versionadded:: 31.0 + +--------------------- + +.. function:: void obs_output_remove_packet_callback(obs_output_t *output, void (*packet_cb)(obs_output_t *output, + struct encoder_packet *pkt, struct encoder_packet_time *pkt_time, void *param), void *param) + + Remove a packet callback function for the output, that had been previously registered with + `obs_output_add_packet_callback()`. + + :param output: The output to remove the packet_cb() function against + :param packet_cb: Function pointer to the callback function + :param param: Data passed to the callback + :return: When the callback is removed + + .. versionadded:: 31.0 + Functions used by outputs ------------------------- diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 9de549f294e943..a13c8a91556699 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -74,6 +74,12 @@ struct rendered_callback { void *param; }; +struct packet_callback { + void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param); + void *param; +}; + /* ------------------------------------------------------------------------- */ /* validity checks */ @@ -1190,7 +1196,12 @@ struct obs_output { // captions are output per track struct caption_track_data *caption_tracks[MAX_OUTPUT_VIDEO_ENCODERS]; - DARRAY(struct encoder_packet_time) encoder_packet_times[MAX_OUTPUT_VIDEO_ENCODERS]; + DARRAY(struct encoder_packet_time) + encoder_packet_times[MAX_OUTPUT_VIDEO_ENCODERS]; + + /* Packet callbacks */ + pthread_mutex_t pkt_callbacks_mutex; + DARRAY(struct packet_callback) pkt_callbacks; bool valid; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index da1c81d8b7af41..9c81b0aa4f1509 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -178,6 +178,7 @@ obs_output_t *obs_output_create(const char *id, const char *name, pthread_mutex_init_value(&output->interleaved_mutex); pthread_mutex_init_value(&output->delay_mutex); pthread_mutex_init_value(&output->pause.mutex); + pthread_mutex_init_value(&output->pkt_callbacks_mutex); if (pthread_mutex_init(&output->interleaved_mutex, NULL) != 0) goto fail; @@ -185,6 +186,8 @@ obs_output_t *obs_output_create(const char *id, const char *name, goto fail; if (pthread_mutex_init(&output->pause.mutex, NULL) != 0) goto fail; + if (pthread_mutex_init(&output->pkt_callbacks_mutex, NULL) != 0) + goto fail; if (os_event_init(&output->stopping_event, OS_EVENT_TYPE_MANUAL) != 0) goto fail; if (!init_output_handlers(output, name, settings, hotkey_data)) @@ -312,12 +315,15 @@ void obs_output_destroy(obs_output_t *output) for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) da_free(output->encoder_packet_times[i]); + da_free(output->pkt_callbacks); + clear_raw_audio_buffers(output); os_event_destroy(output->stopping_event); pthread_mutex_destroy(&output->pause.mutex); pthread_mutex_destroy(&output->interleaved_mutex); pthread_mutex_destroy(&output->delay_mutex); + pthread_mutex_destroy(&output->pkt_callbacks_mutex); os_event_destroy(output->reconnect_stop_event); obs_context_data_free(&output->context); deque_free(&output->delay_data); @@ -1825,6 +1831,21 @@ static inline void send_interleaved(struct obs_output *output) } } + /* Iterate the registered packet callback(s) and invoke + * each one. The caption track logic further above should + * eventually migrate to the packet callback mechanism. + */ + pthread_mutex_lock(&output->pkt_callbacks_mutex); + for (size_t i = 0; i < output->pkt_callbacks.num; ++i) { + struct packet_callback *const callback = + &output->pkt_callbacks.array[i]; + // Packet interleave request timestamp + ept_local.pir = os_gettime_ns(); + callback->packet_cb(output, &out, found_ept ? &ept_local : NULL, + callback->param); + } + pthread_mutex_unlock(&output->pkt_callbacks_mutex); + output->info.encoded_packet(output->context.data, &out); obs_encoder_packet_release(&out); } @@ -3384,3 +3405,29 @@ const char *obs_get_output_supported_audio_codecs(const char *id) const struct obs_output_info *info = find_output(id); return info ? info->encoded_audio_codecs : NULL; } + +void obs_output_add_packet_callback( + obs_output_t *output, + void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param), + void *param) +{ + struct packet_callback data = {packet_cb, param}; + + pthread_mutex_lock(&output->pkt_callbacks_mutex); + da_insert(output->pkt_callbacks, 0, &data); + pthread_mutex_unlock(&output->pkt_callbacks_mutex); +} + +void obs_output_remove_packet_callback( + obs_output_t *output, + void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param), + void *param) +{ + struct packet_callback data = {packet_cb, param}; + + pthread_mutex_lock(&output->pkt_callbacks_mutex); + da_erase_item(output->pkt_callbacks, &data); + pthread_mutex_unlock(&output->pkt_callbacks_mutex); +} diff --git a/libobs/obs.h b/libobs/obs.h index e62730f4ff0c92..fe8bbd419a31de 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2299,6 +2299,24 @@ EXPORT const char *obs_get_output_supported_video_codecs(const char *id); EXPORT const char *obs_get_output_supported_audio_codecs(const char *id); +/* Add/remove packet-processing callbacks that are invoked in + * send_interleaved(), before forwarding packets to the output service. + * This provides a mechanism to perform packet processing outside of + * libobs, however any callback function registering with this API should keep + * keep code to a minimum and understand it is running synchronously with the + * calling thread. + */ +EXPORT void obs_output_add_packet_callback( + obs_output_t *output, + void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param), + void *param); +EXPORT void obs_output_remove_packet_callback( + obs_output_t *output, + void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param), + void *param); + /* ------------------------------------------------------------------------- */ /* Functions used by outputs */ From 07d504e5c787d7d487e881480e81ec05a02ea8a3 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Wed, 21 Aug 2024 10:51:15 -0400 Subject: [PATCH 0500/1073] shared/bpm: Add BPM (Broadcast Performance Metrics) Introduce support for delivering BPM (Broadcast Performance Metrics) over SEI (for AVC/H.264 and HEVC/H.265) and OBU (for AV1) unregistered messages. Metrics being sent are the session frame counters, per-rendition frame counters, and RFC3339-based timestamping information to support end-to-end latency measurement. SEI/OBU messages are generated and sent with each IDR frame, and the frame counters are diff-based, meaning the counts reflect the diff between IDRs, not the running totals. BPM documentation is available at [1]. BPM relies on the recently introduced encoder packet timing support and the packet callback mechanism. BPM injection is enabled for an output by registering the `bpm_inject()` callback via `obs_output_add_packet_callback()` function. The callback must be unregistered using `obs_output_remove_packet_callback()` and `bpm_destroy()` must be used by the caller to release the BPM structures. It is important to measure the number of frames successfully encoded by the obs_encoder_t instances, particularly for renditions where the encoded frame rate differs from the canvas frame rate. The encoded_frames counter and `obs_encoder_get_encoded_frames()` API is introduced to measure and report this in the encoded rendition metrics message. [1] https://d50yg09cghihd.cloudfront.net/other/20240718-MultitrackVideoIntegrationGuide.pdf --- UI/CMakeLists.txt | 14 +- libobs/obs-av1.c | 28 +- libobs/obs-av1.h | 12 + libobs/obs-encoder.c | 11 + libobs/obs-internal.h | 3 + libobs/obs-output.c | 8 +- libobs/obs.h | 3 + shared/bpm/CMakeLists.txt | 12 + shared/bpm/bpm-internal.h | 110 +++++++ shared/bpm/bpm.c | 658 ++++++++++++++++++++++++++++++++++++++ shared/bpm/bpm.h | 26 ++ 11 files changed, 870 insertions(+), 15 deletions(-) create mode 100644 shared/bpm/CMakeLists.txt create mode 100644 shared/bpm/bpm-internal.h create mode 100644 shared/bpm/bpm.c create mode 100644 shared/bpm/bpm.h diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index ba640c62b67407..01f4ed3913c90d 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -20,12 +20,24 @@ if(NOT TARGET OBS::json11) add_subdirectory("${CMAKE_SOURCE_DIR}/deps/json11" "${CMAKE_BINARY_DIR}/deps/json11") endif() +if(NOT TARGET OBS::bpm) + add_subdirectory("${CMAKE_SOURCE_DIR}/shared/bpm" "${CMAKE_BINARY_DIR}/shared/bpm") +endif() + add_executable(obs-studio) add_executable(OBS::studio ALIAS obs-studio) target_link_libraries( obs-studio - PRIVATE CURL::libcurl FFmpeg::avcodec FFmpeg::avutil FFmpeg::avformat OBS::libobs OBS::frontend-api OBS::json11 + PRIVATE + CURL::libcurl + FFmpeg::avcodec + FFmpeg::avutil + FFmpeg::avformat + OBS::bpm + OBS::libobs + OBS::frontend-api + OBS::json11 ) include(cmake/ui-qt.cmake) diff --git a/libobs/obs-av1.c b/libobs/obs-av1.c index 9a8f5fa49c9ca3..9b4e55c8315fe1 100644 --- a/libobs/obs-av1.c +++ b/libobs/obs-av1.c @@ -81,12 +81,20 @@ static inline void encode_uleb128(uint64_t val, uint8_t *out_buf, *len_out = num_bytes; } -static const uint8_t METADATA_TYPE_ITUT_T35 = 4; -static const uint8_t OBU_METADATA = 5; - -// Create a metadata OBU to carry caption information. +/* metadata_obu_itu_t35() is a public symbol. Maintain the function + * and make it call the more general metadata_obu() function. + */ void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, uint8_t **out_buffer, size_t *outbuf_size) +{ + metadata_obu(itut_t35_buffer, itut_bufsize, out_buffer, outbuf_size, + METADATA_TYPE_ITUT_T35); +} + +// Create an OBU to carry AV1 metadata types, including captions and user private data +void metadata_obu(const uint8_t *source_buffer, size_t source_bufsize, + uint8_t **out_buffer, size_t *outbuf_size, + uint8_t metadata_type) { /* From the AV1 spec: 5.3.2 OBU Header Syntax * ------------- @@ -99,7 +107,7 @@ void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, * // skip, because we aren't setting this */ - uint8_t obu_header_byte = (OBU_METADATA << 3) | (1 << 1); + uint8_t obu_header_byte = (OBS_OBU_METADATA << 3) | (1 << 1); /* From the AV1 spec: 5.3.1 General OBU Syntax * if (obu_has_size_field) @@ -123,22 +131,22 @@ void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, * trailing_bits( obu_size * 8 - payloadBits ) */ - int64_t size_field = 1 + itut_bufsize + 1; + int64_t size_field = 1 + source_bufsize + 1; uint8_t size_buf[10]; size_t size_buf_size = 0; encode_uleb128(size_field, size_buf, &size_buf_size); // header + obu_size + metadata_type + metadata_payload + trailing_bits - *outbuf_size = 1 + size_buf_size + 1 + itut_bufsize + 1; + *outbuf_size = 1 + size_buf_size + 1 + source_bufsize + 1; *out_buffer = bzalloc(*outbuf_size); size_t offset = 0; (*out_buffer)[0] = obu_header_byte; ++offset; memcpy((*out_buffer) + offset, size_buf, size_buf_size); offset += size_buf_size; - (*out_buffer)[offset] = METADATA_TYPE_ITUT_T35; + (*out_buffer)[offset] = metadata_type; ++offset; - memcpy((*out_buffer) + offset, itut_t35_buffer, itut_bufsize); - offset += itut_bufsize; + memcpy((*out_buffer) + offset, source_buffer, source_bufsize); + offset += source_bufsize; /* From AV1 spec: 6.2.1 General OBU semantics * ... Trailing bits are always present, unless the OBU consists of only diff --git a/libobs/obs-av1.h b/libobs/obs-av1.h index a71453f8bad0e0..f325b4798c90ed 100644 --- a/libobs/obs-av1.h +++ b/libobs/obs-av1.h @@ -22,6 +22,15 @@ enum { OBS_OBU_PADDING = 15, }; +enum av1_obu_metadata_type { + METADATA_TYPE_HDR_CLL = 1, + METADATA_TYPE_HDR_MDCV, + METADATA_TYPE_SCALABILITY, + METADATA_TYPE_ITUT_T35, + METADATA_TYPE_TIMECODE, + METADATA_TYPE_USER_PRIVATE_6 +}; + /* Helpers for parsing AV1 OB units. */ EXPORT bool obs_av1_keyframe(const uint8_t *data, size_t size); @@ -33,6 +42,9 @@ EXPORT void obs_extract_av1_headers(const uint8_t *packet, size_t size, EXPORT void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, uint8_t **out_buffer, size_t *outbuf_size); +EXPORT void metadata_obu(const uint8_t *source_buffer, size_t source_bufsize, + uint8_t **out_buffer, size_t *outbuf_size, + uint8_t metadata_type); #ifdef __cplusplus } diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index 70b5a98de1adbb..d5086915cca63e 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -857,6 +857,13 @@ enum obs_encoder_type obs_get_encoder_type(const char *id) return info ? info->type : OBS_ENCODER_AUDIO; } +uint32_t obs_encoder_get_encoded_frames(const obs_encoder_t *encoder) +{ + return obs_encoder_valid(encoder, "obs_output_get_encoded_frames") + ? encoder->encoded_frames + : 0; +} + void obs_encoder_set_scaled_size(obs_encoder_t *encoder, uint32_t width, uint32_t height) { @@ -1393,6 +1400,10 @@ void send_off_encoder_packet(obs_encoder_t *encoder, bool success, } pthread_mutex_unlock(&encoder->callbacks_mutex); + + // Count number of video frames successfully encoded + if (pkt->type == OBS_ENCODER_VIDEO) + encoder->encoded_frames++; } } diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index a13c8a91556699..348e290f720564 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -1320,6 +1320,9 @@ struct obs_encoder { uint32_t frame_rate_divisor_counter; // only used for GPU encoders video_t *fps_override; + // Number of frames successfully encoded + uint32_t encoded_frames; + /* Regions of interest to prioritize during encoding */ pthread_mutex_t roi_mutex; DARRAY(struct obs_encoder_roi) roi; diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 9c81b0aa4f1509..5b341bb1599244 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -1536,7 +1536,7 @@ static inline bool has_higher_opposing_ts(struct obs_output *output, output->highest_audio_ts > packet->dts_usec); } -static size_t extract_itut_t35_buffer_from_sei(sei_t *sei, uint8_t **data_out) +static size_t extract_buffer_from_sei(sei_t *sei, uint8_t **data_out) { if (!sei || !sei->head) { return 0; @@ -1726,9 +1726,9 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out) } else if (av1) { uint8_t *obu_buffer = NULL; size_t obu_buffer_size = 0; - size = extract_itut_t35_buffer_from_sei(&sei, &data); - metadata_obu_itut_t35(data, size, &obu_buffer, - &obu_buffer_size); + size = extract_buffer_from_sei(&sei, &data); + metadata_obu(data, size, &obu_buffer, &obu_buffer_size, + METADATA_TYPE_ITUT_T35); if (obu_buffer) { da_push_back_array(out_data, obu_buffer, obu_buffer_size); diff --git a/libobs/obs.h b/libobs/obs.h index fe8bbd419a31de..b2c400c464da1d 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2493,6 +2493,9 @@ EXPORT enum obs_scale_type obs_encoder_get_scale_type(obs_encoder_t *encoder); /** For video encoders, returns the frame rate divisor (default is 1) */ EXPORT uint32_t obs_encoder_get_frame_rate_divisor(const obs_encoder_t *encoder); +/** For video encoders, returns the number of frames encoded */ +EXPORT uint32_t obs_encoder_get_encoded_frames(const obs_encoder_t *encoder); + /** For audio encoders, returns the sample rate of the audio */ EXPORT uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder); diff --git a/shared/bpm/CMakeLists.txt b/shared/bpm/CMakeLists.txt new file mode 100644 index 00000000000000..052736c7a15651 --- /dev/null +++ b/shared/bpm/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.22...3.25) + +add_library(bpm OBJECT) +add_library(OBS::bpm ALIAS bpm) + +target_sources(bpm PRIVATE bpm.c bpm-internal.h PUBLIC bpm.h) + +target_include_directories(bpm PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") + +target_link_libraries(bpm PUBLIC OBS::libobs OBS::caption) + +set_target_properties(bpm PROPERTIES FOLDER deps POSITION_INDEPENDENT_CODE TRUE) diff --git a/shared/bpm/bpm-internal.h b/shared/bpm/bpm-internal.h new file mode 100644 index 00000000000000..89d091d4b768c4 --- /dev/null +++ b/shared/bpm/bpm-internal.h @@ -0,0 +1,110 @@ +#ifndef BPM_INTERNAL_H +#define BPM_INTERNAL_H +#ifdef __cplusplus +extern "C" { +#endif +#include "bpm.h" +#include "caption/mpeg.h" +#include "obs-av1.h" +#include "util/array-serializer.h" +#include "util/platform.h" +#include "util/threading.h" + +struct counter_data { + uint32_t diff; + uint32_t ref; + uint32_t curr; +}; + +#define RFC3339_MAX_LENGTH (64) +struct metrics_time { + struct timespec tspec; + char rfc3339_str[RFC3339_MAX_LENGTH]; + bool valid; +}; + +// Broadcast Performance Metrics SEI types +enum bpm_sei_types { + BPM_TS_SEI = 0, // BPM Timestamp SEI + BPM_SM_SEI, // BPM Session Metrics SEI + BPM_ERM_SEI, // BPM Encoded Rendition Metrics SEI + BPM_MAX_SEI +}; + +#define SEI_UUID_SIZE 16 +static const uint8_t bpm_ts_uuid[SEI_UUID_SIZE] = {0x0a, 0xec, 0xff, 0xe7, + 0x52, 0x72, 0x4e, 0x2f, + 0xa6, 0x2f, 0xd1, 0x9c, + 0xd6, 0x1a, 0x93, 0xb5}; +static const uint8_t bpm_sm_uuid[SEI_UUID_SIZE] = {0xca, 0x60, 0xe7, 0x1c, + 0x6a, 0x8b, 0x43, 0x88, + 0xa3, 0x77, 0x15, 0x1d, + 0xf7, 0xbf, 0x8a, 0xc2}; +static const uint8_t bpm_erm_uuid[SEI_UUID_SIZE] = {0xf1, 0xfb, 0xc1, 0xd5, + 0x10, 0x1e, 0x4f, 0xb5, + 0xa6, 0x1e, 0xb8, 0xce, + 0x3c, 0x07, 0xb8, 0xc0}; + +// Broadcast Performance Metrics timestamp types +enum bpm_ts_type { + BPM_TS_RFC3339 = 1, // RFC3339 timestamp string + BPM_TS_DURATION, // Duration since epoch in milliseconds (64-bit) + BPM_TS_DELTA // Delta timestamp in nanoseconds (64-bit) +}; + +// Broadcast Performance Metrics timestamp event tags +enum bpm_ts_event_tag { + BPM_TS_EVENT_CTS = 1, // Composition Time Event + BPM_TS_EVENT_FER, // Frame Encode Request Event + BPM_TS_EVENT_FERC, // Frame Encode Request Complete Event + BPM_TS_EVENT_PIR // Packet Interleave Request Event +}; + +// Broadcast Performance Session Metrics types +enum bpm_sm_type { + BPM_SM_FRAMES_RENDERED = 1, // Frames rendered by compositor + BPM_SM_FRAMES_LAGGED, // Frames lagged by compositor + BPM_SM_FRAMES_DROPPED, // Frames dropped due to network congestion + BPM_SM_FRAMES_OUTPUT // Total frames output (sum of all video encoder rendition sinks) +}; + +// Broadcast Performance Encoded Rendition Metrics types +enum bpm_erm_type { + BPM_ERM_FRAMES_INPUT = 1, // Frames input to the encoder rendition + BPM_ERM_FRAMES_SKIPPED, // Frames skippped by the encoder rendition + BPM_ERM_FRAMES_OUTPUT // Frames output (encoded) by the encoder rendition +}; + +struct metrics_data { + pthread_mutex_t metrics_mutex; + struct counter_data rendition_frames_input; + struct counter_data rendition_frames_output; + struct counter_data rendition_frames_skipped; + struct counter_data session_frames_rendered; + struct counter_data session_frames_output; + struct counter_data session_frames_dropped; + struct counter_data session_frames_lagged; + struct array_output_data sei_payload[BPM_MAX_SEI]; + bool sei_rendered[BPM_MAX_SEI]; + struct metrics_time + cts; // Composition timestamp (i.e. when the frame was created) + struct metrics_time ferts; // Frame encode request timestamp + struct metrics_time fercts; // Frame encode request complete timestamp + struct metrics_time pirts; // Packet Interleave Request timestamp +}; + +struct output_metrics_link { + obs_output_t *output; + struct metrics_data *metrics_tracks[MAX_OUTPUT_VIDEO_ENCODERS]; +}; + +static pthread_mutex_t bpm_metrics_mutex; +/* This DARRAY is used for creating an association between the output_t + * and the BPM metrics track data for each output that requires BPM injection. + */ +static DARRAY(struct output_metrics_link) bpm_metrics; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/shared/bpm/bpm.c b/shared/bpm/bpm.c new file mode 100644 index 00000000000000..a95d19b0089aab --- /dev/null +++ b/shared/bpm/bpm.c @@ -0,0 +1,658 @@ +#include "obs.h" +#include "bpm-internal.h" + +static void render_metrics_time(struct metrics_time *m_time) +{ + /* Generate the RFC3339 time string from the timespec struct, for example: + * + * "2024-05-31T12:26:03.591Z" + */ + memset(&m_time->rfc3339_str, 0, sizeof(m_time->rfc3339_str)); + strftime(m_time->rfc3339_str, sizeof(m_time->rfc3339_str), + "%Y-%m-%dT%T", gmtime(&m_time->tspec.tv_sec)); + sprintf(m_time->rfc3339_str + strlen(m_time->rfc3339_str), ".%03ldZ", + m_time->tspec.tv_nsec / 1000000); + m_time->valid = true; +} + +static bool update_metrics(obs_output_t *output, + const struct encoder_packet *pkt, + const struct encoder_packet_time *ept, + struct metrics_data *m_track) +{ + if (!output || !pkt || !ept || !m_track) { + blog(LOG_DEBUG, "%s: Null arguments for track %lu", + __FUNCTION__, pkt->track_idx); + return false; + } + + // Perform reads on all the counters as close together as possible + m_track->session_frames_output.curr = + obs_output_get_total_frames(output); + m_track->session_frames_dropped.curr = + obs_output_get_frames_dropped(output); + m_track->session_frames_rendered.curr = obs_get_total_frames(); + m_track->session_frames_lagged.curr = obs_get_lagged_frames(); + + const video_t *video = obs_encoder_video(pkt->encoder); + if (video) { + /* video_output_get_total_frames() returns the number of frames + * before the framerate decimator. For example, if the OBS session + * is rendering at 60fps, and the rendition is set for 30 fps, + * the counter will increment by 60 per second, not 30 per second. + * For metrics we will consider this value to be the number of + * frames input to the obs_encoder_t instance. + */ + m_track->rendition_frames_input.curr = + video_output_get_total_frames(video); + m_track->rendition_frames_skipped.curr = + video_output_get_skipped_frames(video); + /* obs_encoder_get_encoded_frames() returns the number of frames + * successfully encoded by the obs_encoder_t instance. + */ + m_track->rendition_frames_output.curr = + obs_encoder_get_encoded_frames(pkt->encoder); + } else { + m_track->rendition_frames_input.curr = 0; + m_track->rendition_frames_skipped.curr = 0; + m_track->rendition_frames_output.curr = 0; + blog(LOG_ERROR, "update_metrics(): *video_t==null"); + } + + // Set the diff values to 0 if PTS is 0 + if (pkt->pts == 0) { + m_track->session_frames_output.diff = 0; + m_track->session_frames_dropped.diff = 0; + m_track->session_frames_rendered.diff = 0; + m_track->session_frames_lagged.diff = 0; + m_track->rendition_frames_input.diff = 0; + m_track->rendition_frames_skipped.diff = 0; + m_track->rendition_frames_output.diff = 0; + blog(LOG_DEBUG, "update_metrics(): Setting diffs to 0"); + } else { + // Calculate diff's + m_track->session_frames_output.diff = + m_track->session_frames_output.curr - + m_track->session_frames_output.ref; + m_track->session_frames_dropped.diff = + m_track->session_frames_dropped.curr - + m_track->session_frames_dropped.ref; + m_track->session_frames_rendered.diff = + m_track->session_frames_rendered.curr - + m_track->session_frames_rendered.ref; + m_track->session_frames_lagged.diff = + m_track->session_frames_lagged.curr - + m_track->session_frames_lagged.ref; + m_track->rendition_frames_input.diff = + m_track->rendition_frames_input.curr - + m_track->rendition_frames_input.ref; + m_track->rendition_frames_skipped.diff = + m_track->rendition_frames_skipped.curr - + m_track->rendition_frames_skipped.ref; + m_track->rendition_frames_output.diff = + m_track->rendition_frames_output.curr - + m_track->rendition_frames_output.ref; + } + + // Update the reference values + m_track->session_frames_output.ref = + m_track->session_frames_output.curr; + m_track->session_frames_dropped.ref = + m_track->session_frames_dropped.curr; + m_track->session_frames_rendered.ref = + m_track->session_frames_rendered.curr; + m_track->session_frames_lagged.ref = + m_track->session_frames_lagged.curr; + m_track->rendition_frames_input.ref = + m_track->rendition_frames_input.curr; + m_track->rendition_frames_skipped.ref = + m_track->rendition_frames_skipped.curr; + m_track->rendition_frames_output.ref = + m_track->rendition_frames_output.curr; + + /* BPM Timestamp Message */ + m_track->cts.valid = false; + m_track->ferts.valid = false; + m_track->fercts.valid = false; + + /* Generate the timestamp representations for CTS, FER, and FERC. + * Check if each is non-zero and that temporal consistency is correct: + * FEC > FERC > CTS + * FEC and FERC depends on CTS, and FERC depends on FER, so ensure + * we only signal an integral set of timestamps. + */ + os_nstime_to_timespec(ept->cts, &m_track->cts.tspec); + render_metrics_time(&m_track->cts); + if (ept->fer && (ept->fer > ept->cts)) { + os_nstime_to_timespec(ept->fer, &m_track->ferts.tspec); + render_metrics_time(&m_track->ferts); + if (ept->ferc && (ept->ferc > ept->fer)) { + os_nstime_to_timespec(ept->ferc, + &m_track->fercts.tspec); + render_metrics_time(&m_track->fercts); + } + } + + // Always generate the timestamp representation for PIR + m_track->pirts.valid = false; + os_nstime_to_timespec(ept->pir, &m_track->pirts.tspec); + render_metrics_time(&m_track->pirts); + + /* Log the BPM timestamp and frame counter information. This + * provides visibility into the metrics when OBS is started + * with "--verbose" and "--unfiltered_log". + */ + blog(LOG_DEBUG, + "BPM: %s, trk %lu: [CTS|FER-CTS|FERC-FER|PIR-CTS]:[%" PRIu64 + " ms|%" PRIu64 " ms|%" PRIu64 " us|%" PRIu64 + " ms], [dts|pts]:[%" PRId64 "|%" PRId64 + "], S[R:O:D:L],R[I:S:O]:%d:%d:%d:%d:%d:%d:%d", + obs_encoder_get_name(pkt->encoder), pkt->track_idx, + ept->cts / 1000000, (ept->fer - ept->cts) / 1000000, + (ept->ferc - ept->fer) / 1000, (ept->pir - ept->cts) / 1000000, + pkt->dts, pkt->pts, m_track->session_frames_rendered.diff, + m_track->session_frames_output.diff, + m_track->session_frames_dropped.diff, + m_track->session_frames_lagged.diff, + m_track->rendition_frames_input.diff, + m_track->rendition_frames_skipped.diff, + m_track->rendition_frames_output.diff); + + return true; +} +void bpm_ts_sei_render(struct metrics_data *m_track) +{ + uint8_t num_timestamps = 0; + struct serializer s; + + m_track->sei_rendered[BPM_TS_SEI] = false; + + // Initialize the output array here; caller is responsible to free it + array_output_serializer_init(&s, &m_track->sei_payload[BPM_TS_SEI]); + + // Write the UUID for this SEI message + s_write(&s, bpm_ts_uuid, sizeof(bpm_ts_uuid)); + + // Determine how many timestamps are valid + if (m_track->cts.valid) + num_timestamps++; + if (m_track->ferts.valid) + num_timestamps++; + if (m_track->fercts.valid) + num_timestamps++; + if (m_track->pirts.valid) + num_timestamps++; + + /* Encode number of timestamps for this SEI. Upper 4 bits are + * set to b0000 (reserved); lower 4-bits num_timestamps - 1. + */ + s_w8(&s, (num_timestamps - 1) & 0x0F); + + if (m_track->cts.valid) { + // Timestamp type + s_w8(&s, BPM_TS_RFC3339); + // Write the timestamp event tag (Composition Time Event) + s_w8(&s, BPM_TS_EVENT_CTS); + // Write the RFC3339-formatted string, including the null terminator + s_write(&s, m_track->cts.rfc3339_str, + strlen(m_track->cts.rfc3339_str) + 1); + } + + if (m_track->ferts.valid) { + // Timestamp type + s_w8(&s, BPM_TS_RFC3339); + // Write the timestamp event tag (Frame Encode Request Event) + s_w8(&s, BPM_TS_EVENT_FER); + // Write the RFC3339-formatted string, including the null terminator + s_write(&s, m_track->ferts.rfc3339_str, + strlen(m_track->ferts.rfc3339_str) + 1); + } + + if (m_track->fercts.valid) { + // Timestamp type + s_w8(&s, BPM_TS_RFC3339); + // Write the timestamp event tag (Frame Encode Request Complete Event) + s_w8(&s, BPM_TS_EVENT_FERC); + // Write the RFC3339-formatted string, including the null terminator + s_write(&s, m_track->fercts.rfc3339_str, + strlen(m_track->fercts.rfc3339_str) + 1); + } + + if (m_track->pirts.valid) { + // Timestamp type + s_w8(&s, BPM_TS_RFC3339); + // Write the timestamp event tag (Packet Interleave Request Event) + s_w8(&s, BPM_TS_EVENT_PIR); + // Write the RFC3339-formatted string, including the null terminator + s_write(&s, m_track->pirts.rfc3339_str, + strlen(m_track->pirts.rfc3339_str) + 1); + } + m_track->sei_rendered[BPM_TS_SEI] = true; +} + +void bpm_sm_sei_render(struct metrics_data *m_track) +{ + uint8_t num_timestamps = 0; + uint8_t num_counters = 0; + struct serializer s; + + m_track->sei_rendered[BPM_SM_SEI] = false; + + // Initialize the output array here; caller is responsible to free it + array_output_serializer_init(&s, &m_track->sei_payload[BPM_SM_SEI]); + + // Write the UUID for this SEI message + s_write(&s, bpm_sm_uuid, sizeof(bpm_sm_uuid)); + + // Encode number of timestamps for this SEI + num_timestamps = 1; + // Upper 4 bits are set to b0000 (reserved); lower 4-bits num_timestamps - 1 + s_w8(&s, (num_timestamps - 1) & 0x0F); + // Timestamp type + s_w8(&s, BPM_TS_RFC3339); + + /* Write the timestamp event tag (Packet Interleave Request Event). + * Use the PIR_TS timestamp because the data was all collected at that time. + */ + s_w8(&s, BPM_TS_EVENT_PIR); + // Write the RFC3339-formatted string, including the null terminator + s_write(&s, m_track->pirts.rfc3339_str, + strlen(m_track->pirts.rfc3339_str) + 1); + + // Session metrics has 4 counters + num_counters = 4; + /* Send all the counters with a tag(8-bit):value(32-bit) configuration. + * Upper 4 bits are set to b0000 (reserved); lower 4-bits num_counters - 1. + */ + s_w8(&s, (num_counters - 1) & 0x0F); + s_w8(&s, BPM_SM_FRAMES_RENDERED); + s_wb32(&s, m_track->session_frames_rendered.diff); + s_w8(&s, BPM_SM_FRAMES_LAGGED); + s_wb32(&s, m_track->session_frames_lagged.diff); + s_w8(&s, BPM_SM_FRAMES_DROPPED); + s_wb32(&s, m_track->session_frames_dropped.diff); + s_w8(&s, BPM_SM_FRAMES_OUTPUT); + s_wb32(&s, m_track->session_frames_output.diff); + + m_track->sei_rendered[BPM_SM_SEI] = true; +} + +void bpm_erm_sei_render(struct metrics_data *m_track) +{ + uint8_t num_timestamps = 0; + uint8_t num_counters = 0; + struct serializer s; + + m_track->sei_rendered[BPM_ERM_SEI] = false; + + // Initialize the output array here; caller is responsible to free it + array_output_serializer_init(&s, &m_track->sei_payload[BPM_ERM_SEI]); + + // Write the UUID for this SEI message + s_write(&s, bpm_erm_uuid, sizeof(bpm_erm_uuid)); + + // Encode number of timestamps for this SEI + num_timestamps = 1; + // Upper 4 bits are set to b0000 (reserved); lower 4-bits num_timestamps - 1 + s_w8(&s, (num_timestamps - 1) & 0x0F); + // Timestamp type + s_w8(&s, BPM_TS_RFC3339); + + /* Write the timestamp event tag (Packet Interleave Request Event). + * Use the PIRTS timestamp because the data was all collected at that time. + */ + s_w8(&s, BPM_TS_EVENT_PIR); + // Write the RFC3339-formatted string, including the null terminator + s_write(&s, m_track->pirts.rfc3339_str, + strlen(m_track->pirts.rfc3339_str) + 1); + + // Encoder rendition metrics has 3 counters + num_counters = 3; + /* Send all the counters with a tag(8-bit):value(32-bit) configuration. + * Upper 4 bits are set to b0000 (reserved); lower 4-bits num_counters - 1. + */ + s_w8(&s, (num_counters - 1) & 0x0F); + s_w8(&s, BPM_ERM_FRAMES_INPUT); + s_wb32(&s, m_track->rendition_frames_input.diff); + s_w8(&s, BPM_ERM_FRAMES_SKIPPED); + s_wb32(&s, m_track->rendition_frames_skipped.diff); + s_w8(&s, BPM_ERM_FRAMES_OUTPUT); + s_wb32(&s, m_track->rendition_frames_output.diff); + + m_track->sei_rendered[BPM_ERM_SEI] = true; +} + +/* Note : extract_buffer_from_sei() and nal_start are also defined + * in obs-output.c, however they are not public APIs. When the caption + * library is re-worked, this code should be refactored into that. + */ +static size_t extract_buffer_from_sei(sei_t *sei, uint8_t **data_out) +{ + if (!sei || !sei->head) { + return 0; + } + /* We should only need to get one payload, because the SEI that was + * generated should only have one message, so no need to iterate. If + * we did iterate, we would need to generate multiple OBUs. */ + sei_message_t *msg = sei_message_head(sei); + int payload_size = (int)sei_message_size(msg); + uint8_t *payload_data = sei_message_data(msg); + *data_out = bmalloc(payload_size); + memcpy(*data_out, payload_data, payload_size); + return payload_size; +} +static const uint8_t nal_start[4] = {0, 0, 0, 1}; + +/* process_metrics() will update and insert unregistered + * SEI (AVC/HEVC) or OBU (AV1) messages into the encoded + * video bitstream. +*/ +static bool process_metrics(obs_output_t *output, struct encoder_packet *out, + struct encoder_packet_time *ept, + struct metrics_data *m_track) +{ + struct encoder_packet backup = *out; + sei_t sei; + uint8_t *data = NULL; + size_t size; + long ref = 1; + bool avc = false; + bool hevc = false; + bool av1 = false; + + if (!m_track) { + blog(LOG_DEBUG, + "Metrics track for index: %lu had not be initialized", + out->track_idx); + return false; + } + + // Update the metrics for this track + if (!update_metrics(output, out, ept, m_track)) { + // Something went wrong; log it and return + blog(LOG_DEBUG, "update_metrics() for track index: %lu failed", + out->track_idx); + return false; + } + + if (strcmp(obs_encoder_get_codec(out->encoder), "h264") == 0) { + avc = true; + } else if (strcmp(obs_encoder_get_codec(out->encoder), "av1") == 0) { + av1 = true; +#ifdef ENABLE_HEVC + } else if (strcmp(obs_encoder_get_codec(out->encoder), "hevc") == 0) { + hevc = true; +#endif + } + +#ifdef ENABLE_HEVC + uint8_t hevc_nal_header[2]; + if (hevc) { + size_t nal_header_index_start = 4; + // Skip past the annex-b start code + if (memcmp(out->data, nal_start + 1, 3) == 0) { + nal_header_index_start = 3; + } else if (memcmp(out->data, nal_start, 4) == 0) { + nal_header_index_start = 4; + + } else { + /* We shouldn't ever see this unless we start getting + * packets without annex-b start codes. */ + blog(LOG_DEBUG, + "Annex-B start code not found, we may not " + "generate a valid hevc nal unit header " + "for our caption"); + return false; + } + /* We will use the same 2 byte NAL unit header for the SEI, + * but swap the NAL types out. */ + hevc_nal_header[0] = out->data[nal_header_index_start]; + hevc_nal_header[1] = out->data[nal_header_index_start + 1]; + } +#endif + // Create array for the original packet data + the SEI appended data + DARRAY(uint8_t) out_data; + da_init(out_data); + + // Copy the original packet + da_push_back_array(out_data, (uint8_t *)&ref, sizeof(ref)); + da_push_back_array(out_data, out->data, out->size); + + // Build the SEI metrics message payload + bpm_ts_sei_render(m_track); + bpm_sm_sei_render(m_track); + bpm_erm_sei_render(m_track); + + // Iterate over all the BPM SEI types + for (uint8_t i = 0; i < BPM_MAX_SEI; ++i) { + // Create and inject the syntax specific SEI messages in the bitstream if the rendering was successful + if (m_track->sei_rendered[i]) { + // Send one SEI message per NALU or OBU + sei_init(&sei, 0.0); + + // Generate the formatted SEI message + sei_message_t *msg = sei_message_new( + sei_type_user_data_unregistered, + m_track->sei_payload[i].bytes.array, + m_track->sei_payload[i].bytes.num); + sei_message_append(&sei, msg); + + // Free the SEI payload buffer in the metrics track + array_output_serializer_free(&m_track->sei_payload[i]); + + // Update for any codec specific syntax and add to the output bitstream + if (avc || hevc || av1) { + if (avc || hevc) { + data = bmalloc(sei_render_size(&sei)); + size = sei_render(&sei, data); + } + /* In each of these specs there is an identical structure that + * carries user private metadata. We have an AVC SEI wrapped + * version of that here. We will strip it out and repackage + * it slightly to fit the different codec carrying mechanisms. + * A slightly modified SEI for HEVC and a metadata OBU for AV1. + */ + if (avc) { + /* TODO: SEI should come after AUD/SPS/PPS, + * but before any VCL */ + da_push_back_array(out_data, nal_start, + 4); + da_push_back_array(out_data, data, + size); +#ifdef ENABLE_HEVC + } else if (hevc) { + /* Only first NAL (VPS/PPS/SPS) should use the 4 byte + * start code. SEIs use 3 byte version */ + da_push_back_array(out_data, + nal_start + 1, 3); + /* nal_unit_header( ) { + * forbidden_zero_bit f(1) + * nal_unit_type u(6) + * nuh_layer_id u(6) + * nuh_temporal_id_plus1 u(3) + * } + */ + const uint8_t prefix_sei_nal_type = 39; + /* The first bit is always 0, so we just need to + * save the last bit off the original header and + * add the SEI NAL type. */ + uint8_t first_byte = + (prefix_sei_nal_type << 1) | + (0x01 & hevc_nal_header[0]); + hevc_nal_header[0] = first_byte; + /* The HEVC NAL unit header is 2 byte instead of + * one, otherwise everything else is the + * same. */ + da_push_back_array(out_data, + hevc_nal_header, 2); + da_push_back_array(out_data, &data[1], + size - 1); +#endif + } else if (av1) { + uint8_t *obu_buffer = NULL; + size_t obu_buffer_size = 0; + size = extract_buffer_from_sei(&sei, + &data); + metadata_obu( + data, size, &obu_buffer, + &obu_buffer_size, + METADATA_TYPE_USER_PRIVATE_6); + if (obu_buffer) { + da_push_back_array( + out_data, obu_buffer, + obu_buffer_size); + bfree(obu_buffer); + } + } + if (data) { + bfree(data); + } + } + sei_free(&sei); + } + } + obs_encoder_packet_release(out); + + *out = backup; + out->data = (uint8_t *)out_data.array + sizeof(ref); + out->size = out_data.num - sizeof(ref); + + if (avc || hevc || av1) { + return true; + } + return false; +} + +static struct metrics_data *bpm_create_metrics_track(void) +{ + struct metrics_data *rval = bzalloc(sizeof(struct metrics_data)); + pthread_mutex_init_value(&rval->metrics_mutex); + + if (pthread_mutex_init(&rval->metrics_mutex, NULL) != 0) { + bfree(rval); + rval = NULL; + } + return rval; +} + +static bool bpm_get_track(obs_output_t *output, size_t track, + struct metrics_data **m_track) +{ + bool found = false; + // Walk the DARRAY looking for the output pointer + pthread_mutex_lock(&bpm_metrics_mutex); + for (size_t i = bpm_metrics.num; i > 0; i--) { + if (output == bpm_metrics.array[i - 1].output) { + *m_track = + bpm_metrics.array[i - 1].metrics_tracks[track]; + found = true; + break; + } + } + + if (!found) { + // Create the new BPM metrics entries + struct output_metrics_link *oml = da_push_back_new(bpm_metrics); + + oml->output = output; + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; ++i) { + oml->metrics_tracks[i] = bpm_create_metrics_track(); + } + *m_track = oml->metrics_tracks[track]; + found = true; + } + pthread_mutex_unlock(&bpm_metrics_mutex); + return found; +} + +void bpm_destroy(obs_output_t *output) +{ + int64_t idx = -1; + + pthread_mutex_lock(&bpm_metrics_mutex); + + // Walk the DARRAY looking for the index that matches the output + for (size_t i = bpm_metrics.num; i > 0; i--) { + if (output == bpm_metrics.array[i - 1].output) { + idx = i - 1; + break; + } + } + if (idx >= 0) { + struct output_metrics_link *oml = &bpm_metrics.array[idx]; + for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) { + if (oml->metrics_tracks[i]) { + struct metrics_data *m_track = + oml->metrics_tracks[i]; + for (uint8_t j = 0; j < BPM_MAX_SEI; ++j) { + array_output_serializer_free( + &m_track->sei_payload[j]); + } + pthread_mutex_destroy(&m_track->metrics_mutex); + bfree(m_track); + m_track = NULL; + } + } + da_erase(bpm_metrics, idx); + if (bpm_metrics.num == 0) + da_free(bpm_metrics); + } + pthread_mutex_unlock(&bpm_metrics_mutex); +} + +static void bpm_init(void) +{ + pthread_mutex_init_value(&bpm_metrics_mutex); + da_init(bpm_metrics); +} + +/* bpm_inject() is the callback function that needs to be registered + * with each output needing Broadcast Performance Metrics injected + * into the video bitstream, using SEI (AVC/HEVC) and OBU (AV1) syntax. + */ +void bpm_inject(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param) +{ + UNUSED_PARAMETER(param); + + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, bpm_init); + + if (!output || !pkt) { + blog(LOG_DEBUG, + "%s: Null pointer arguments supplied, returning", + __FUNCTION__); + return; + } + + /* Insert BPM on video frames and only when a keyframe + * is detected. + */ + if (pkt->type == OBS_ENCODER_VIDEO && pkt->keyframe) { + /* Video packet must have pkt_timing supplied for BPM */ + if (!pkt_time) { + blog(LOG_DEBUG, + "%s: Packet timing missing for track %ld, PTS %" PRId64, + __FUNCTION__, pkt->track_idx, pkt->pts); + return; + } + + /* Get the metrics track associated with the output. + * Allocate BPM metrics structures for the output if needed. + */ + struct metrics_data *m_track = NULL; + if (!bpm_get_track(output, pkt->track_idx, &m_track)) { + blog(LOG_DEBUG, "%s: BPM metrics track not found!", + __FUNCTION__); + return; + } + + pthread_mutex_lock(&m_track->metrics_mutex); + /* Update the metrics and generate BPM messages. */ + if (!process_metrics(output, pkt, pkt_time, m_track)) { + blog(LOG_DEBUG, "%s: BPM injection processing failed", + __FUNCTION__); + } + pthread_mutex_unlock(&m_track->metrics_mutex); + } +} diff --git a/shared/bpm/bpm.h b/shared/bpm/bpm.h new file mode 100644 index 00000000000000..33d6466dff4061 --- /dev/null +++ b/shared/bpm/bpm.h @@ -0,0 +1,26 @@ +#ifndef BPM_H +#define BPM_H +#ifdef __cplusplus +extern "C" { +#endif + +/* BPM callback. Allocation of BPM metrics data happens automatically + * with the first invokation of the callback associated with the output. + * Deallocation must be done explicitly with bpm_destroy(), after the + * callback is removed. + * + * BPM is designed to operate at the packet level. The bpm_inject() + * callback function must be registered and unregistered with + * obs_output_add_packet_callback() and obs_output_remove_packet_callback(), + * respectively. + */ +void bpm_inject(obs_output_t *output, struct encoder_packet *pkt, + struct encoder_packet_time *pkt_time, void *param); + +/* BPM function to destroy all allocations for a given output. */ +void bpm_destroy(obs_output_t *output); + +#ifdef __cplusplus +} +#endif +#endif From ea9e033d26cf47a9f5c55302d6475f5a8b00b2f0 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Wed, 21 Aug 2024 11:21:21 -0400 Subject: [PATCH 0501/1073] UI: Enable BPM for multitrack video --- UI/multitrack-video-output.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/UI/multitrack-video-output.cpp b/UI/multitrack-video-output.cpp index 3a1f3430bb85ab..a865b1b97ba4ce 100644 --- a/UI/multitrack-video-output.cpp +++ b/UI/multitrack-video-output.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -487,6 +488,9 @@ void MultitrackVideoOutput::PrepareStreaming( obs_output_set_service(output, multitrack_video_service); + // Register the BPM (Broadcast Performance Metrics) callback + obs_output_add_packet_callback(output, bpm_inject, NULL); + OBSSignal start_streaming; OBSSignal stop_streaming; OBSSignal deactivate_stream; @@ -908,7 +912,6 @@ SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name, const char *audio_encoder_id, size_t main_audio_mixer, std::optional vod_track_mixer) { - auto output = create_output(); OBSOutputAutoRelease recording_output; if (dump_stream_to_file_config) @@ -1013,6 +1016,15 @@ void StreamDeactivateHandler(void *arg, calldata_t *params) calldata_ptr(params, "output")))) return; + /* Unregister the BPM (Broadcast Performance Metrics) callback + * and destroy the allocated metrics data. + */ + obs_output_remove_packet_callback( + static_cast(calldata_ptr(params, "output")), + bpm_inject, NULL); + bpm_destroy( + static_cast(calldata_ptr(params, "output"))); + MultitrackVideoOutput::ReleaseOnMainThread(self->take_current()); } From 6bd2fdd717a350c3d097796fdbbda5f752b8ed76 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Thu, 5 Sep 2024 14:25:05 -0400 Subject: [PATCH 0502/1073] CI: Update deps to 2024-09-05 release Notable changes: * deps.windows: Disable x86 build slice for dependencies * deps.qt: Disable x86 build slice for Qt6 * CI: Remove Windows x86 slices for Qt6 and FFmpeg builds * deps.ffmpeg: Disable Windows x86 build slice for FFmpeg dependencies * deps.ffmpeg: Update libvpx to v1.14.1 * deps.ffmpeg: Update SVT-AV1 to 2.2.1 * deps.ffmpeg: Update aom to 3.9.1 * deps.ffmpeg: Update AMF to 1.4.34 * deps.ffmpeg: Update FFmpeg to 7.0.2 * deps.macos: Update LuaJIT to 2.1 f725e44cda * deps.macos: Update FreeType to 2.13.3 * deps.macos: Update Asio to 1.31.0 * deps.macos: Update qrcodegen-cmake to v1.8.0-cmake3 * deps.macos: Update Sparkle to 2.6.4 * deps.qt: Update Qt to 6.7.2 for macOS * deps.qt: Update Qt to 6.7.2 for Windows * deps.windows: Update FreeType to 2.13.3 * deps.windows: Update curl to 8.9.1 * deps.windows: Update LuaJIT to 2.1 f725e44cda * deps.windows: Update Asio to 1.31.0 * deps.windows: Update qrcodegen-cmake to v1.8.0-cmake3 * deps.windows: Update WIL to v1.0.240803.1 * deps.ffmpeg: Remove MbedTLS CMake files from macOS FFmpeg package Also update Sparkle to match the obs-deps version. --- buildspec.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/buildspec.json b/buildspec.json index ba3b66689bad60..78ca49cb6d7cdc 100644 --- a/buildspec.json +++ b/buildspec.json @@ -1,25 +1,25 @@ { "dependencies": { "prebuilt": { - "version": "2024-08-08", + "version": "2024-09-05", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "label": "Pre-Built obs-deps", "hashes": { - "macos-universal": "6faaa5737fc80087e13bd5cdff4df820359fff7bef7141a59e7170fdab8ce36f", - "windows-x64": "2b14cbf46064939d56a57f952d22b80d18b2a982c8bd6a2d30044452b8e99b1a", - "windows-x86": "334d08d4d59704372773c981d4f426661798bb2f4d3c5432a6f61d6368235b45" + "macos-universal": "d637141e6082106b6c8d67b8bf64175739bddb1f31a324c77e8fbf8488b7c33c", + "windows-x64": "85a45cacca0853fef9444b6e99c529e81ce896ce3e3318edf458fd7ad78eed89", + "windows-x86": "db3feabfeaf9e09d92f6b6eed026cd8d292406286f968c4036906aa96ded44cd" } }, "qt6": { - "version": "2024-08-08", + "version": "2024-09-05", "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", "label": "Pre-Built Qt6", "hashes": { - "macos-universal": "aca8e9888be610926abcf601718d9ec9a70ca7e5c7df102174de6572c56cee83", - "windows-x64": "169cf5f8218fc0be51dbe7d900e8c4f95b96e7c72b915e3de0b223a0560b94f7" + "macos-universal": "12b6f45f5d44715b1e434c1fba00d21680d4d0b529a2e900e206d96ed4bb66ae", + "windows-x64": "0344d771dd36d42b2f13e76268c2d7c03152f6fd822e90c8124e27785a966658" }, "debugSymbols": { - "windows-x64": "9dbc41ddb9fe50954b76a606d27003c51cec3a19584d43883fc01770e7c7be90" + "windows-x64": "12b6f45f5d44715b1e434c1fba00d21680d4d0b529a2e900e206d96ed4bb66ae" } }, "cef": { @@ -37,10 +37,10 @@ }, "tools": { "sparkle": { - "version": "2.6.2", + "version": "2.6.4", "baseUrl": "https://github.com/sparkle-project/Sparkle/releases/download", "label": "Sparkle 2", - "hash": "2300a7dc2545a4968e54621b7f351d388ddf1a5cb49e79f6c99e9a09d826f5e8" + "hash": "50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b" }, "ccache-win": { "version": "4.8.1", From 8963d25dd23d5a6c1ced8bae1dc4faf4456a3efc Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 5 Sep 2024 20:42:48 +0200 Subject: [PATCH 0503/1073] build-aux: Update Flatpak modules * Update SVT-AV1 to 2.2.1 * Update FFmpeg to 7.0.2 * Update LuaJIT to 2.1 f725e44cda * Update Asio to 1.31.0 * Update qrcodegen-cmake to v1.8.0-cmake3 --- build-aux/modules/20-svt-av1.json | 4 ++-- build-aux/modules/30-ffmpeg.json | 8 ++++---- build-aux/modules/40-luajit.json | 2 +- build-aux/modules/50-libqrcodegencpp.json | 4 ++-- build-aux/modules/90-asio.json | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build-aux/modules/20-svt-av1.json b/build-aux/modules/20-svt-av1.json index 993e1649e5be91..e5024c12b67784 100644 --- a/build-aux/modules/20-svt-av1.json +++ b/build-aux/modules/20-svt-av1.json @@ -18,8 +18,8 @@ { "type": "git", "url": "https://gitlab.com/AOMediaCodec/SVT-AV1.git", - "tag": "v2.0.0", - "commit": "2aeeb4f1a1d495b84bf5c21dbb60ae10e991fada" + "tag": "v2.2.1", + "commit": "55a01def732bb9e7016d23cc512384f7a88d6e86" } ] } diff --git a/build-aux/modules/30-ffmpeg.json b/build-aux/modules/30-ffmpeg.json index de8f2cdc7daf97..9bac5fbc21ec23 100644 --- a/build-aux/modules/30-ffmpeg.json +++ b/build-aux/modules/30-ffmpeg.json @@ -25,16 +25,16 @@ { "type": "git", "url": "https://github.com/FFmpeg/FFmpeg.git", - "tag": "n7.0", - "commit": "083443d67cb159ce469e5d902346b8d0c2cd1c93", + "tag": "n7.0.2", + "commit": "e3a61e91030696348b56361bdf80ea358aef4a19", "disable-shallow-clone": true }, { "type": "git", "dest": "obs-deps", "url": "https://github.com/obsproject/obs-deps.git", - "tag": "2024-05-08", - "commit": "85173b23c575ea09cdf33bada01b1d38dd1251ea" + "tag": "2024-09-05", + "commit": "45b7d2cfac0e2ac256d458c7466a925b0a94de35" }, { "type": "shell", diff --git a/build-aux/modules/40-luajit.json b/build-aux/modules/40-luajit.json index 378b5c4bae401b..fd65de63db2d1d 100644 --- a/build-aux/modules/40-luajit.json +++ b/build-aux/modules/40-luajit.json @@ -11,7 +11,7 @@ { "type": "git", "url": "https://luajit.org/git/luajit-2.0.git", - "commit": "5790d253972c9d78a0c2aece527eda5b134bbbf7", + "commit": "f725e44cda8f359869bf8f92ce71787ddca45618", "disable-shallow-clone": true }, { diff --git a/build-aux/modules/50-libqrcodegencpp.json b/build-aux/modules/50-libqrcodegencpp.json index 968e98742715f7..32a8041984e3da 100644 --- a/build-aux/modules/50-libqrcodegencpp.json +++ b/build-aux/modules/50-libqrcodegencpp.json @@ -20,8 +20,8 @@ }, { "type": "archive", - "url": "https://github.com/EasyCoding/qrcodegen-cmake/archive/refs/tags/v1.8.0-cmake2.tar.gz", - "sha256": "04e0dc2d7b19457928172e338b5b22216ad23a9dee8d26be4fb05fc39ea59904" + "url": "https://github.com/EasyCoding/qrcodegen-cmake/archive/refs/tags/v1.8.0-cmake3.tar.gz", + "sha256": "2037b0596e5651ce020f4d36e958d24672dbee75631bdb09ae8a45659121f4d0" } ] } diff --git a/build-aux/modules/90-asio.json b/build-aux/modules/90-asio.json index 3ab57ebd94a92f..169e010ea2ddbe 100644 --- a/build-aux/modules/90-asio.json +++ b/build-aux/modules/90-asio.json @@ -10,8 +10,8 @@ { "type": "git", "url": "https://github.com/chriskohlhoff/asio.git", - "tag": "asio-1-30-2", - "commit": "12e0ce9e0500bf0f247dbd1ae894272656456079" + "tag": "asio-1-31-0", + "commit": "1f534288b4be0be2dd664aab43882a0aa3106a1d" } ] } From b029ebfdc248c3d6e26772812b8c4e2a6be073cd Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 18 Jan 2024 13:32:32 +0100 Subject: [PATCH 0504/1073] rtmp-services: Add more granular Amazon IVS service servers --- plugins/rtmp-services/data/package.json | 4 +- plugins/rtmp-services/data/services.json | 616 ++++++++++++++++++++++- 2 files changed, 614 insertions(+), 6 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 406d84707bfea7..4dc438f0c6c0c2 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 261, + "version": 262, "files": [ { "name": "services.json", - "version": 261 + "version": 262 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index b32abc2be72bc9..ebc02b4f9eaa1a 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -2832,12 +2832,620 @@ "supported video codecs": ["h264"], "servers": [ { - "name": "Global (RTMPS, recommended)", - "url": "rtmps://ingest.global-contribute.live-video.net:443/app" + "name": "Asia: China, Hong Kong (6) (RTMPS)", + "url": "rtmps://hkg06.contribute.live-video.net/app" }, { - "name": "Global (RTMP)", - "url": "rtmp://ingest.global-contribute.live-video.net/app" + "name": "Asia: India, Bangalore (1) (RTMPS)", + "url": "rtmps://blr01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Chennai (RTMPS)", + "url": "rtmps://maa01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Hyderabad (1) (RTMPS)", + "url": "rtmps://hyd01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Mumbai (RTMPS)", + "url": "rtmps://bom01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, New Delhi (RTMPS)", + "url": "rtmps://del01.contribute.live-video.net/app" + }, + { + "name": "Asia: Indonesia, Cikarang Barat (1) (RTMPS)", + "url": "rtmps://jkt01.contribute.live-video.net/app" + }, + { + "name": "Asia: Indonesia, Jakarta (2) (RTMPS)", + "url": "rtmps://jkt02.contribute.live-video.net/app" + }, + { + "name": "Asia: Japan, Osaka (1) (RTMPS)", + "url": "rtmps://osa01.contribute.live-video.net/app" + }, + { + "name": "Asia: Japan, Tokyo (3) (RTMPS)", + "url": "rtmps://tyo03.contribute.live-video.net/app" + }, + { + "name": "Asia: Japan, Tokyo (5) (RTMPS)", + "url": "rtmps://tyo05.contribute.live-video.net/app" + }, + { + "name": "Asia: Manila, Philippines (1) (RTMPS)", + "url": "rtmps://mnl01.contribute.live-video.net/app" + }, + { + "name": "Asia: Singapore (1) (RTMPS)", + "url": "rtmps://sin01.contribute.live-video.net/app" + }, + { + "name": "Asia: Singapore (4) (RTMPS)", + "url": "rtmps://sin04.contribute.live-video.net/app" + }, + { + "name": "Asia: South Korea, Seoul (3) (RTMPS)", + "url": "rtmps://sel03.contribute.live-video.net/app" + }, + { + "name": "Asia: South Korea, Seoul (4) (RTMPS)", + "url": "rtmps://sel04.contribute.live-video.net/app" + }, + { + "name": "Asia: Taiwan, Taipei (1) (RTMPS)", + "url": "rtmps://tpe01.contribute.live-video.net/app" + }, + { + "name": "Asia: Taiwan, Taipei (3) (RTMPS)", + "url": "rtmps://tpe03.contribute.live-video.net/app" + }, + { + "name": "Asia: Thailand, Bangkok (2) (RTMPS)", + "url": "rtmps://bkk02.contribute.live-video.net/app" + }, + { + "name": "Europe: Austria, Vienna (2) (RTMPS)", + "url": "rtmps://vie02.contribute.live-video.net/app" + }, + { + "name": "Europe: Czech Republic, Prague (RTMPS)", + "url": "rtmps://prg03.contribute.live-video.net/app" + }, + { + "name": "Europe: Denmark, Copenhagen (RTMPS)", + "url": "rtmps://cph.contribute.live-video.net/app" + }, + { + "name": "Europe: Finland, Helsinki (3) (RTMPS)", + "url": "rtmps://hel03.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Marseille (RTMPS)", + "url": "rtmps://mrs.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Marseille (2) (RTMPS)", + "url": "rtmps://mrs02.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Paris (10) (RTMPS)", + "url": "rtmps://cdg10.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Paris (2) (RTMPS)", + "url": "rtmps://cdg02.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Berlin (RTMPS)", + "url": "rtmps://ber.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Dusseldorf (1) (RTMPS)", + "url": "rtmps://dus01.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Frankfurt (2) (RTMPS)", + "url": "rtmps://fra02.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Frankfurt (5) (RTMPS)", + "url": "rtmps://fra05.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Frankfurt (6) (RTMPS)", + "url": "rtmps://fra06.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Munich (1) (RTMPS)", + "url": "rtmps://muc01.contribute.live-video.net/app" + }, + { + "name": "Europe: Italy, Milan (2) (RTMPS)", + "url": "rtmps://mil02.contribute.live-video.net/app" + }, + { + "name": "Europe: Netherlands, Amsterdam (2) (RTMPS)", + "url": "rtmps://ams02.contribute.live-video.net/app" + }, + { + "name": "Europe: Netherlands, Amsterdam (3) (RTMPS)", + "url": "rtmps://ams03.contribute.live-video.net/app" + }, + { + "name": "Europe: Norway, Oslo (RTMPS)", + "url": "rtmps://osl.contribute.live-video.net/app" + }, + { + "name": "Europe: Poland, Warsaw (2) (RTMPS)", + "url": "rtmps://waw02.contribute.live-video.net/app" + }, + { + "name": "Europe: Spain, Madrid (1) (RTMPS)", + "url": "rtmps://mad01.contribute.live-video.net/app" + }, + { + "name": "Europe: Spain, Madrid (2) (RTMPS)", + "url": "rtmps://mad02.contribute.live-video.net/app" + }, + { + "name": "Europe: Sweden, Stockholm (3) (RTMPS)", + "url": "rtmps://arn03.contribute.live-video.net/app" + }, + { + "name": "Europe: Sweden, Stockholm (4) (RTMPS)", + "url": "rtmps://arn04.contribute.live-video.net/app" + }, + { + "name": "Europe: UK, London (3) (RTMPS)", + "url": "rtmps://lhr03.contribute.live-video.net/app" + }, + { + "name": "Europe: UK, London (4) (RTMPS)", + "url": "rtmps://lhr04.contribute.live-video.net/app" + }, + { + "name": "Europe: UK, London (8) (RTMPS)", + "url": "rtmps://lhr08.contribute.live-video.net/app" + }, + { + "name": "NA: Canada, Quebec (RTMPS)", + "url": "rtmps://ymq03.contribute.live-video.net/app" + }, + { + "name": "NA: Canada, Toronto (RTMPS)", + "url": "rtmps://yto.contribute.live-video.net/app" + }, + { + "name": "NA: Mexico, Queretaro (3) (RTMPS)", + "url": "rtmps://qro03.contribute.live-video.net/app" + }, + { + "name": "NA: Mexico, Queretaro (4) (RTMPS)", + "url": "rtmps://qro04.contribute.live-video.net/app" + }, + { + "name": "Oceania: Australia, Sydney (2) (RTMPS)", + "url": "rtmps://syd02.contribute.live-video.net/app" + }, + { + "name": "Oceania: Australia, Sydney (3) (RTMPS)", + "url": "rtmps://syd03.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Fortaleza (1) (RTMPS)", + "url": "rtmps://for01.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Rio de Janeiro (3) (RTMPS)", + "url": "rtmps://rio03.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Rio de Janeiro (4) (RTMPS)", + "url": "rtmps://rio04.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Sao Paulo (RTMPS)", + "url": "rtmps://sao03.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Sao Paulo (5) (RTMPS)", + "url": "rtmps://sao05.contribute.live-video.net/app" + }, + { + "name": "South America: Buenos Aires, Argentina (1) (RTMPS)", + "url": "rtmps://bue01.contribute.live-video.net/app" + }, + { + "name": "South America: Colombia, Bogota (1) (RTMPS)", + "url": "rtmps://bog01.contribute.live-video.net/app" + }, + { + "name": "US Central: Dallas, TX (RTMPS)", + "url": "rtmps://dfw.contribute.live-video.net/app" + }, + { + "name": "US Central: Dallas, TX (2) (RTMPS)", + "url": "rtmps://dfw02.contribute.live-video.net/app" + }, + { + "name": "US Central: Denver, CO (52) (RTMPS)", + "url": "rtmps://den52.contribute.live-video.net/app" + }, + { + "name": "US Central: Garland, TX (56) (RTMPS)", + "url": "rtmps://dfw56.contribute.live-video.net/app" + }, + { + "name": "US Central: Houston, TX (50) (RTMPS)", + "url": "rtmps://iah50.contribute.live-video.net/app" + }, + { + "name": "US East: Ashburn, VA (5) (RTMPS)", + "url": "rtmps://iad05.contribute.live-video.net/app" + }, + { + "name": "US East: Atlanta, GA (RTMPS)", + "url": "rtmps://atl.contribute.live-video.net/app" + }, + { + "name": "US East: Chicago, IL (3) (RTMPS)", + "url": "rtmps://ord03.contribute.live-video.net/app" + }, + { + "name": "US East: Chicago, IL (56) (RTMPS)", + "url": "rtmps://ord56.contribute.live-video.net/app" + }, + { + "name": "US East: McAllen, TX (1) (RTMPS)", + "url": "rtmps://mfe01.contribute.live-video.net/app" + }, + { + "name": "US East: Miami, FL (5) (RTMPS)", + "url": "rtmps://mia05.contribute.live-video.net/app" + }, + { + "name": "US East: New York, NY (RTMPS)", + "url": "rtmps://jfk.contribute.live-video.net/app" + }, + { + "name": "US East: New York, NY (50) (RTMPS)", + "url": "rtmps://jfk50.contribute.live-video.net/app" + }, + { + "name": "US West: Los Angeles, CA (RTMPS)", + "url": "rtmps://lax.contribute.live-video.net/app" + }, + { + "name": "US West: Salt Lake City, UT (RTMPS)", + "url": "rtmps://slc.contribute.live-video.net/app" + }, + { + "name": "US West: San Francisco, CA (RTMPS)", + "url": "rtmps://sfo.contribute.live-video.net/app" + }, + { + "name": "US West: San Jose, California (6) (RTMPS)", + "url": "rtmps://sjc06.contribute.live-video.net/app" + }, + { + "name": "US West: Seattle, WA (RTMPS)", + "url": "rtmps://sea.contribute.live-video.net/app" + }, + { + "name": "US West: Seattle, WA (2) (RTMPS)", + "url": "rtmps://sea02.contribute.live-video.net/app" + }, + { + "name": "Asia: China, Hong Kong (6) (RTMP)", + "url": "rtmp://hkg06.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Bangalore (1) (RTMP)", + "url": "rtmp://blr01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Chennai (RTMP)", + "url": "rtmp://maa01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Hyderabad (1) (RTMP)", + "url": "rtmp://hyd01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, Mumbai (RTMP)", + "url": "rtmp://bom01.contribute.live-video.net/app" + }, + { + "name": "Asia: India, New Delhi (RTMP)", + "url": "rtmp://del01.contribute.live-video.net/app" + }, + { + "name": "Asia: Indonesia, Cikarang Barat (1) (RTMP)", + "url": "rtmp://jkt01.contribute.live-video.net/app" + }, + { + "name": "Asia: Indonesia, Jakarta (2) (RTMP)", + "url": "rtmp://jkt02.contribute.live-video.net/app" + }, + { + "name": "Asia: Japan, Osaka (1) (RTMP)", + "url": "rtmp://osa01.contribute.live-video.net/app" + }, + { + "name": "Asia: Japan, Tokyo (3) (RTMP)", + "url": "rtmp://tyo03.contribute.live-video.net/app" + }, + { + "name": "Asia: Japan, Tokyo (5) (RTMP)", + "url": "rtmp://tyo05.contribute.live-video.net/app" + }, + { + "name": "Asia: Manila, Philippines (1) (RTMP)", + "url": "rtmp://mnl01.contribute.live-video.net/app" + }, + { + "name": "Asia: Singapore (1) (RTMP)", + "url": "rtmp://sin01.contribute.live-video.net/app" + }, + { + "name": "Asia: Singapore (4) (RTMP)", + "url": "rtmp://sin04.contribute.live-video.net/app" + }, + { + "name": "Asia: South Korea, Seoul (3) (RTMP)", + "url": "rtmp://sel03.contribute.live-video.net/app" + }, + { + "name": "Asia: South Korea, Seoul (4) (RTMP)", + "url": "rtmp://sel04.contribute.live-video.net/app" + }, + { + "name": "Asia: Taiwan, Taipei (1) (RTMP)", + "url": "rtmp://tpe01.contribute.live-video.net/app" + }, + { + "name": "Asia: Taiwan, Taipei (3) (RTMP)", + "url": "rtmp://tpe03.contribute.live-video.net/app" + }, + { + "name": "Asia: Thailand, Bangkok (2) (RTMP)", + "url": "rtmp://bkk02.contribute.live-video.net/app" + }, + { + "name": "Europe: Austria, Vienna (2) (RTMP)", + "url": "rtmp://vie02.contribute.live-video.net/app" + }, + { + "name": "Europe: Czech Republic, Prague (RTMP)", + "url": "rtmp://prg03.contribute.live-video.net/app" + }, + { + "name": "Europe: Denmark, Copenhagen (RTMP)", + "url": "rtmp://cph.contribute.live-video.net/app" + }, + { + "name": "Europe: Finland, Helsinki (3) (RTMP)", + "url": "rtmp://hel03.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Marseille (RTMP)", + "url": "rtmp://mrs.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Marseille (2) (RTMP)", + "url": "rtmp://mrs02.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Paris (10) (RTMP)", + "url": "rtmp://cdg10.contribute.live-video.net/app" + }, + { + "name": "Europe: France, Paris (2) (RTMP)", + "url": "rtmp://cdg02.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Berlin (RTMP)", + "url": "rtmp://ber.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Dusseldorf (1) (RTMP)", + "url": "rtmp://dus01.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Frankfurt (2) (RTMP)", + "url": "rtmp://fra02.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Frankfurt (5) (RTMP)", + "url": "rtmp://fra05.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Frankfurt (6) (RTMP)", + "url": "rtmp://fra06.contribute.live-video.net/app" + }, + { + "name": "Europe: Germany, Munich (1) (RTMP)", + "url": "rtmp://muc01.contribute.live-video.net/app" + }, + { + "name": "Europe: Italy, Milan (2) (RTMP)", + "url": "rtmp://mil02.contribute.live-video.net/app" + }, + { + "name": "Europe: Netherlands, Amsterdam (2) (RTMP)", + "url": "rtmp://ams02.contribute.live-video.net/app" + }, + { + "name": "Europe: Netherlands, Amsterdam (3) (RTMP)", + "url": "rtmp://ams03.contribute.live-video.net/app" + }, + { + "name": "Europe: Norway, Oslo (RTMP)", + "url": "rtmp://osl.contribute.live-video.net/app" + }, + { + "name": "Europe: Poland, Warsaw (2) (RTMP)", + "url": "rtmp://waw02.contribute.live-video.net/app" + }, + { + "name": "Europe: Spain, Madrid (1) (RTMP)", + "url": "rtmp://mad01.contribute.live-video.net/app" + }, + { + "name": "Europe: Spain, Madrid (2) (RTMP)", + "url": "rtmp://mad02.contribute.live-video.net/app" + }, + { + "name": "Europe: Sweden, Stockholm (3) (RTMP)", + "url": "rtmp://arn03.contribute.live-video.net/app" + }, + { + "name": "Europe: Sweden, Stockholm (4) (RTMP)", + "url": "rtmp://arn04.contribute.live-video.net/app" + }, + { + "name": "Europe: UK, London (3) (RTMP)", + "url": "rtmp://lhr03.contribute.live-video.net/app" + }, + { + "name": "Europe: UK, London (4) (RTMP)", + "url": "rtmp://lhr04.contribute.live-video.net/app" + }, + { + "name": "Europe: UK, London (8) (RTMP)", + "url": "rtmp://lhr08.contribute.live-video.net/app" + }, + { + "name": "NA: Canada, Quebec (RTMP)", + "url": "rtmp://ymq03.contribute.live-video.net/app" + }, + { + "name": "NA: Canada, Toronto (RTMP)", + "url": "rtmp://yto.contribute.live-video.net/app" + }, + { + "name": "NA: Mexico, Queretaro (3) (RTMP)", + "url": "rtmp://qro03.contribute.live-video.net/app" + }, + { + "name": "NA: Mexico, Queretaro (4) (RTMP)", + "url": "rtmp://qro04.contribute.live-video.net/app" + }, + { + "name": "Oceania: Australia, Sydney (2) (RTMP)", + "url": "rtmp://syd02.contribute.live-video.net/app" + }, + { + "name": "Oceania: Australia, Sydney (3) (RTMP)", + "url": "rtmp://syd03.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Fortaleza (1) (RTMP)", + "url": "rtmp://for01.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Rio de Janeiro (3) (RTMP)", + "url": "rtmp://rio03.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Rio de Janeiro (4) (RTMP)", + "url": "rtmp://rio04.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Sao Paulo (RTMP)", + "url": "rtmp://sao03.contribute.live-video.net/app" + }, + { + "name": "South America: Brazil, Sao Paulo (5) (RTMP)", + "url": "rtmp://sao05.contribute.live-video.net/app" + }, + { + "name": "South America: Buenos Aires, Argentina (1) (RTMP)", + "url": "rtmp://bue01.contribute.live-video.net/app" + }, + { + "name": "South America: Colombia, Bogota (1) (RTMP)", + "url": "rtmp://bog01.contribute.live-video.net/app" + }, + { + "name": "US Central: Dallas, TX (RTMP)", + "url": "rtmp://dfw.contribute.live-video.net/app" + }, + { + "name": "US Central: Dallas, TX (2) (RTMP)", + "url": "rtmp://dfw02.contribute.live-video.net/app" + }, + { + "name": "US Central: Denver, CO (52) (RTMP)", + "url": "rtmp://den52.contribute.live-video.net/app" + }, + { + "name": "US Central: Garland, TX (56) (RTMP)", + "url": "rtmp://dfw56.contribute.live-video.net/app" + }, + { + "name": "US Central: Houston, TX (50) (RTMP)", + "url": "rtmp://iah50.contribute.live-video.net/app" + }, + { + "name": "US East: Ashburn, VA (5) (RTMP)", + "url": "rtmp://iad05.contribute.live-video.net/app" + }, + { + "name": "US East: Atlanta, GA (RTMP)", + "url": "rtmp://atl.contribute.live-video.net/app" + }, + { + "name": "US East: Chicago, IL (3) (RTMP)", + "url": "rtmp://ord03.contribute.live-video.net/app" + }, + { + "name": "US East: Chicago, IL (56) (RTMP)", + "url": "rtmp://ord56.contribute.live-video.net/app" + }, + { + "name": "US East: McAllen, TX (1) (RTMP)", + "url": "rtmp://mfe01.contribute.live-video.net/app" + }, + { + "name": "US East: Miami, FL (5) (RTMP)", + "url": "rtmp://mia05.contribute.live-video.net/app" + }, + { + "name": "US East: New York, NY (RTMP)", + "url": "rtmp://jfk.contribute.live-video.net/app" + }, + { + "name": "US East: New York, NY (50) (RTMP)", + "url": "rtmp://jfk50.contribute.live-video.net/app" + }, + { + "name": "US West: Los Angeles, CA (RTMP)", + "url": "rtmp://lax.contribute.live-video.net/app" + }, + { + "name": "US West: Salt Lake City, UT (RTMP)", + "url": "rtmp://slc.contribute.live-video.net/app" + }, + { + "name": "US West: San Francisco, CA (RTMP)", + "url": "rtmp://sfo.contribute.live-video.net/app" + }, + { + "name": "US West: San Jose, California (6) (RTMP)", + "url": "rtmp://sjc06.contribute.live-video.net/app" + }, + { + "name": "US West: Seattle, WA (RTMP)", + "url": "rtmp://sea.contribute.live-video.net/app" + }, + { + "name": "US West: Seattle, WA (2) (RTMP)", + "url": "rtmp://sea02.contribute.live-video.net/app" } ], "multitrack_video_configuration_url": "https://ingest.contribute.live-video.net/api/v3/GetClientConfiguration", From 717161f246c904348256104de75b3ef7d41d97d8 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 10 Apr 2024 15:11:30 +0200 Subject: [PATCH 0505/1073] rtmp-services: Extract Twitch specific info from ingest update code --- .../rtmp-services/service-specific/twitch.c | 160 +++++++++++------- 1 file changed, 100 insertions(+), 60 deletions(-) diff --git a/plugins/rtmp-services/service-specific/twitch.c b/plugins/rtmp-services/service-specific/twitch.c index d4fe7bd17e9a5a..1833c0174e3cca 100644 --- a/plugins/rtmp-services/service-specific/twitch.c +++ b/plugins/rtmp-services/service-specific/twitch.c @@ -7,31 +7,45 @@ #include "twitch.h" -static update_info_t *twitch_update_info = NULL; -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static bool ingests_refreshed = false; -static bool ingests_refreshing = false; -static bool ingests_loaded = false; - struct ingest { char *name; char *url; }; -static DARRAY(struct ingest) cur_ingests; +struct service_ingests { + update_info_t *update_info; + pthread_mutex_t mutex; + bool ingests_refreshed; + bool ingests_refreshing; + bool ingests_loaded; + DARRAY(struct ingest) cur_ingests; + const char *cache_old_filename; + const char *cache_new_filename; +}; -static void free_ingests(void) +static struct service_ingests twitch = { + .update_info = NULL, + .mutex = PTHREAD_MUTEX_INITIALIZER, + .ingests_refreshed = false, + .ingests_refreshing = false, + .ingests_loaded = false, + .cur_ingests = {0}, + .cache_old_filename = "twitch_ingests.json", + .cache_new_filename = "twitch_ingests.new.json"}; + +static void free_ingests(struct service_ingests *si) { - for (size_t i = 0; i < cur_ingests.num; i++) { - struct ingest *ingest = cur_ingests.array + i; + for (size_t i = 0; i < si->cur_ingests.num; i++) { + struct ingest *ingest = si->cur_ingests.array + i; bfree(ingest->name); bfree(ingest->url); } - da_free(cur_ingests); + da_free(si->cur_ingests); } -static bool load_ingests(const char *json, bool write_file) +static bool load_ingests(struct service_ingests *si, const char *json, + bool write_file) { json_t *root; json_t *ingests; @@ -49,10 +63,10 @@ static bool load_ingests(const char *json, bool write_file) goto finish; count = json_array_size(ingests); - if (count <= 1 && cur_ingests.num) + if (count <= 1 && si->cur_ingests.num) goto finish; - free_ingests(); + free_ingests(si); for (size_t i = 0; i < count; i++) { json_t *item = json_array_get(ingests, i); @@ -79,10 +93,10 @@ static bool load_ingests(const char *json, bool write_file) ingest.name = bstrdup(name_str); ingest.url = url.array; - da_push_back(cur_ingests, &ingest); + da_push_back(si->cur_ingests, &ingest); } - if (!cur_ingests.num) + if (!si->cur_ingests.num) goto finish; success = true; @@ -90,8 +104,8 @@ static bool load_ingests(const char *json, bool write_file) if (!write_file) goto finish; - cache_old = obs_module_config_path("twitch_ingests.json"); - cache_new = obs_module_config_path("twitch_ingests.new.json"); + cache_old = obs_module_config_path(si->cache_old_filename); + cache_new = obs_module_config_path(si->cache_new_filename); os_quick_write_utf8_file(cache_new, json, strlen(json), false); os_safe_replace(cache_old, cache_new, NULL); @@ -105,78 +119,88 @@ static bool load_ingests(const char *json, bool write_file) return success; } -static bool twitch_ingest_update(void *param, struct file_download_data *data) +static bool ingest_update(void *param, struct file_download_data *data) { + struct service_ingests *service = param; bool success; - pthread_mutex_lock(&mutex); - success = load_ingests((const char *)data->buffer.array, true); - pthread_mutex_unlock(&mutex); + pthread_mutex_lock(&service->mutex); + success = load_ingests(service, (const char *)data->buffer.array, true); + pthread_mutex_unlock(&service->mutex); if (success) { - os_atomic_set_bool(&ingests_refreshed, true); - os_atomic_set_bool(&ingests_loaded, true); + os_atomic_set_bool(&service->ingests_refreshed, true); + os_atomic_set_bool(&service->ingests_loaded, true); } - UNUSED_PARAMETER(param); return true; } void twitch_ingests_lock(void) { - pthread_mutex_lock(&mutex); + pthread_mutex_lock(&twitch.mutex); } void twitch_ingests_unlock(void) { - pthread_mutex_unlock(&mutex); + pthread_mutex_unlock(&twitch.mutex); } size_t twitch_ingest_count(void) { - return cur_ingests.num; + return twitch.cur_ingests.num; } -struct twitch_ingest twitch_ingest(size_t idx) +struct twitch_ingest get_ingest(struct service_ingests *si, size_t idx) { struct twitch_ingest ingest; - if (cur_ingests.num <= idx) { + if (si->cur_ingests.num <= idx) { ingest.name = NULL; ingest.url = NULL; } else { - ingest = *(struct twitch_ingest *)(cur_ingests.array + idx); + ingest = *(struct twitch_ingest *)(si->cur_ingests.array + idx); } return ingest; } +struct twitch_ingest twitch_ingest(size_t idx) +{ + return get_ingest(&twitch, idx); +} + +void init_service_data(struct service_ingests *si) +{ + da_init(si->cur_ingests); + pthread_mutex_init(&si->mutex, NULL); +} + void init_twitch_data(void) { - da_init(cur_ingests); - pthread_mutex_init(&mutex, NULL); + init_service_data(&twitch); } extern const char *get_module_name(void); -void twitch_ingests_refresh(int seconds) +void service_ingests_refresh(struct service_ingests *si, int seconds, + const char *log_prefix, const char *file_url) { - if (os_atomic_load_bool(&ingests_refreshed)) + if (os_atomic_load_bool(&si->ingests_refreshed)) return; - if (!os_atomic_load_bool(&ingests_refreshing)) { - os_atomic_set_bool(&ingests_refreshing, true); + if (!os_atomic_load_bool(&si->ingests_refreshing)) { + os_atomic_set_bool(&si->ingests_refreshing, true); - twitch_update_info = update_info_create_single( - "[twitch ingest update] ", get_module_name(), - "https://ingest.twitch.tv/ingests", - twitch_ingest_update, NULL); + si->update_info = + update_info_create_single(log_prefix, get_module_name(), + file_url, ingest_update, si); } /* wait five seconds max when loading ingests for the first time */ - if (!os_atomic_load_bool(&ingests_loaded)) { + if (!os_atomic_load_bool(&si->ingests_loaded)) { for (int i = 0; i < seconds * 100; i++) { - if (os_atomic_load_bool(&ingests_refreshed)) { + if (os_atomic_load_bool(&si->ingests_refreshed)) { break; } os_sleep_ms(10); @@ -184,38 +208,54 @@ void twitch_ingests_refresh(int seconds) } } -void load_twitch_data(void) +void twitch_ingests_refresh(int seconds) { - char *twitch_cache = obs_module_config_path("twitch_ingests.json"); + service_ingests_refresh(&twitch, seconds, "[twitch ingest update] ", + "https://ingest.twitch.tv/ingests"); +} - struct ingest def = {.name = bstrdup("Default"), - .url = bstrdup("rtmp://live.twitch.tv/app")}; +void load_service_data(struct service_ingests *si, const char *cache_filename, + struct ingest *def) +{ + char *service_cache = obs_module_config_path(cache_filename); - pthread_mutex_lock(&mutex); - da_push_back(cur_ingests, &def); - pthread_mutex_unlock(&mutex); + pthread_mutex_lock(&si->mutex); + da_push_back(si->cur_ingests, def); + pthread_mutex_unlock(&si->mutex); - if (os_file_exists(twitch_cache)) { - char *data = os_quick_read_utf8_file(twitch_cache); + if (os_file_exists(service_cache)) { + char *data = os_quick_read_utf8_file(service_cache); bool success; - pthread_mutex_lock(&mutex); - success = load_ingests(data, false); - pthread_mutex_unlock(&mutex); + pthread_mutex_lock(&si->mutex); + success = load_ingests(si, data, false); + pthread_mutex_unlock(&si->mutex); if (success) { - os_atomic_set_bool(&ingests_loaded, true); + os_atomic_set_bool(&si->ingests_loaded, true); } bfree(data); } - bfree(twitch_cache); + bfree(service_cache); +} + +void load_twitch_data(void) +{ + struct ingest def = {.name = bstrdup("Default"), + .url = bstrdup("rtmp://live.twitch.tv/app")}; + load_service_data(&twitch, "twitch_ingests.json", &def); +} + +void unload_service_data(struct service_ingests *si) +{ + update_info_destroy(si->update_info); + free_ingests(si); + pthread_mutex_destroy(&si->mutex); } void unload_twitch_data(void) { - update_info_destroy(twitch_update_info); - free_ingests(); - pthread_mutex_destroy(&mutex); + unload_service_data(&twitch); } From 6440a60d1de6f576b197b31e85286cdb7c5e4edc Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Wed, 10 Apr 2024 16:39:17 +0200 Subject: [PATCH 0506/1073] rtmp-services: Add Amazon IVS ingest refresh infrastructure --- plugins/rtmp-services/rtmp-services-main.c | 23 ++++++++ .../rtmp-services/service-specific/twitch.c | 56 +++++++++++++++++++ .../rtmp-services/service-specific/twitch.h | 5 ++ 3 files changed, 84 insertions(+) diff --git a/plugins/rtmp-services/rtmp-services-main.c b/plugins/rtmp-services/rtmp-services-main.c index 93d595bfcebcd0..815beae6a7e1d7 100644 --- a/plugins/rtmp-services/rtmp-services-main.c +++ b/plugins/rtmp-services/rtmp-services-main.c @@ -59,6 +59,11 @@ extern void load_twitch_data(void); extern void unload_twitch_data(void); extern void twitch_ingests_refresh(int seconds); +extern void init_amazon_ivs_data(void); +extern void load_amazon_ivs_data(void); +extern void unload_amazon_ivs_data(void); +extern void amazon_ivs_ingests_refresh(int seconds); + static void refresh_callback(void *unused, calldata_t *cd) { int seconds = (int)calldata_int(cd, "seconds"); @@ -72,10 +77,24 @@ static void refresh_callback(void *unused, calldata_t *cd) UNUSED_PARAMETER(unused); } +static void amazon_ivs_refresh_callback(void *unused, calldata_t *cd) +{ + int seconds = (int)calldata_int(cd, "seconds"); + if (seconds <= 0) + seconds = 3; + if (seconds > 10) + seconds = 10; + + amazon_ivs_ingests_refresh(seconds); + + UNUSED_PARAMETER(unused); +} + bool obs_module_load(void) { init_twitch_data(); init_dacast_data(); + init_amazon_ivs_data(); dstr_copy(&module_name, "rtmp-services plugin (libobs "); dstr_cat(&module_name, obs_get_version_string()); @@ -84,6 +103,8 @@ bool obs_module_load(void) proc_handler_t *ph = obs_get_proc_handler(); proc_handler_add(ph, "void twitch_ingests_refresh(int seconds)", refresh_callback, NULL); + proc_handler_add(ph, "void amazon_ivs_ingests_refresh(int seconds)", + amazon_ivs_refresh_callback, NULL); #if defined(ENABLE_SERVICE_UPDATES) char *local_dir = obs_module_file(""); @@ -100,6 +121,7 @@ bool obs_module_load(void) } load_twitch_data(); + load_amazon_ivs_data(); bfree(local_dir); bfree(cache_dir); @@ -116,5 +138,6 @@ void obs_module_unload(void) unload_twitch_data(); free_showroom_data(); unload_dacast_data(); + unload_amazon_ivs_data(); dstr_free(&module_name); } diff --git a/plugins/rtmp-services/service-specific/twitch.c b/plugins/rtmp-services/service-specific/twitch.c index 1833c0174e3cca..7b850a380323cd 100644 --- a/plugins/rtmp-services/service-specific/twitch.c +++ b/plugins/rtmp-services/service-specific/twitch.c @@ -33,6 +33,16 @@ static struct service_ingests twitch = { .cache_old_filename = "twitch_ingests.json", .cache_new_filename = "twitch_ingests.new.json"}; +static struct service_ingests amazon_ivs = { + .update_info = NULL, + .mutex = PTHREAD_MUTEX_INITIALIZER, + .ingests_refreshed = false, + .ingests_refreshing = false, + .ingests_loaded = false, + .cur_ingests = {0}, + .cache_old_filename = "amazon_ivs_ingests.json", + .cache_new_filename = "amazon_ivs_ingests.new.json"}; + static void free_ingests(struct service_ingests *si) { for (size_t i = 0; i < si->cur_ingests.num; i++) { @@ -259,3 +269,49 @@ void unload_twitch_data(void) { unload_service_data(&twitch); } + +void init_amazon_ivs_data(void) +{ + init_service_data(&amazon_ivs); +} + +void load_amazon_ivs_data(void) +{ + struct ingest def = { + .name = bstrdup("Default"), + .url = bstrdup( + "rtmps://ingest.global-contribute.live-video.net:443/app/")}; + load_service_data(&amazon_ivs, "amazon_ivs_ingests.json", &def); +} + +void unload_amazon_ivs_data(void) +{ + unload_service_data(&amazon_ivs); +} + +void amazon_ivs_ingests_refresh(int seconds) +{ + service_ingests_refresh( + &amazon_ivs, seconds, "[amazon ivs ingest update] ", + "https://ingest.contribute.live-video.net/ingests"); +} + +void amazon_ivs_ingests_lock(void) +{ + pthread_mutex_lock(&amazon_ivs.mutex); +} + +void amazon_ivs_ingests_unlock(void) +{ + pthread_mutex_unlock(&amazon_ivs.mutex); +} + +size_t amazon_ivs_ingest_count(void) +{ + return amazon_ivs.cur_ingests.num; +} + +struct twitch_ingest amazon_ivs_ingest(size_t idx) +{ + return get_ingest(&amazon_ivs, idx); +} diff --git a/plugins/rtmp-services/service-specific/twitch.h b/plugins/rtmp-services/service-specific/twitch.h index 6caf0490b56b59..ed1adb599cb516 100644 --- a/plugins/rtmp-services/service-specific/twitch.h +++ b/plugins/rtmp-services/service-specific/twitch.h @@ -9,3 +9,8 @@ extern void twitch_ingests_lock(void); extern void twitch_ingests_unlock(void); extern size_t twitch_ingest_count(void); extern struct twitch_ingest twitch_ingest(size_t idx); + +extern void amazon_ivs_ingests_lock(void); +extern void amazon_ivs_ingests_unlock(void); +extern size_t amazon_ivs_ingest_count(void); +extern struct twitch_ingest amazon_ivs_ingest(size_t idx); From 6fbb06d1c2fa072d2b33d8b2439563d793f58936 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 11 Apr 2024 12:37:15 +0200 Subject: [PATCH 0507/1073] rtmp-services: Load Twitch/Amazon IVS rtmps URLs when refreshing --- plugins/rtmp-services/service-specific/twitch.c | 11 +++++++++++ plugins/rtmp-services/service-specific/twitch.h | 1 + 2 files changed, 12 insertions(+) diff --git a/plugins/rtmp-services/service-specific/twitch.c b/plugins/rtmp-services/service-specific/twitch.c index 7b850a380323cd..57bfa46f3fc203 100644 --- a/plugins/rtmp-services/service-specific/twitch.c +++ b/plugins/rtmp-services/service-specific/twitch.c @@ -10,6 +10,7 @@ struct ingest { char *name; char *url; + char *rtmps_url; }; struct service_ingests { @@ -49,6 +50,7 @@ static void free_ingests(struct service_ingests *si) struct ingest *ingest = si->cur_ingests.array + i; bfree(ingest->name); bfree(ingest->url); + bfree(ingest->rtmps_url); } da_free(si->cur_ingests); @@ -82,13 +84,17 @@ static bool load_ingests(struct service_ingests *si, const char *json, json_t *item = json_array_get(ingests, i); json_t *item_name = json_object_get(item, "name"); json_t *item_url = json_object_get(item, "url_template"); + json_t *item_rtmps_url = + json_object_get(item, "url_template_secure"); struct ingest ingest = {0}; struct dstr url = {0}; + struct dstr rtmps_url = {0}; if (!item_name || !item_url) continue; const char *url_str = json_string_value(item_url); + const char *rtmps_url_str = json_string_value(item_rtmps_url); const char *name_str = json_string_value(item_name); /* At the moment they currently mis-spell "deprecated", @@ -100,8 +106,12 @@ static bool load_ingests(struct service_ingests *si, const char *json, dstr_copy(&url, url_str); dstr_replace(&url, "/{stream_key}", ""); + dstr_copy(&rtmps_url, rtmps_url_str); + dstr_replace(&rtmps_url, "/{stream_key}", ""); + ingest.name = bstrdup(name_str); ingest.url = url.array; + ingest.rtmps_url = rtmps_url.array; da_push_back(si->cur_ingests, &ingest); } @@ -168,6 +178,7 @@ struct twitch_ingest get_ingest(struct service_ingests *si, size_t idx) if (si->cur_ingests.num <= idx) { ingest.name = NULL; ingest.url = NULL; + ingest.rtmps_url = NULL; } else { ingest = *(struct twitch_ingest *)(si->cur_ingests.array + idx); } diff --git a/plugins/rtmp-services/service-specific/twitch.h b/plugins/rtmp-services/service-specific/twitch.h index ed1adb599cb516..0ff8b7a37cba73 100644 --- a/plugins/rtmp-services/service-specific/twitch.h +++ b/plugins/rtmp-services/service-specific/twitch.h @@ -3,6 +3,7 @@ struct twitch_ingest { const char *name; const char *url; + const char *rtmps_url; }; extern void twitch_ingests_lock(void); From fc60ca63dc3a789b4625dfd71a2aefb9f31e2bc5 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 11 Apr 2024 12:31:32 +0200 Subject: [PATCH 0508/1073] rtmp-services: Add Amazon IVS auto server entries --- plugins/rtmp-services/data/locale/en-US.ini | 2 + plugins/rtmp-services/rtmp-common.c | 71 +++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/plugins/rtmp-services/data/locale/en-US.ini b/plugins/rtmp-services/data/locale/en-US.ini index f5f2147522f618..bd5dd2301e4be0 100644 --- a/plugins/rtmp-services/data/locale/en-US.ini +++ b/plugins/rtmp-services/data/locale/en-US.ini @@ -3,6 +3,8 @@ CustomStreamingServer="Custom Streaming Server" Service="Service" Server="Server" Server.Auto="Auto (Recommended)" +Server.AutoRTMPS="Auto (RTMPS, Recommended)" +Server.AutoRTMP="Auto (RTMP)" StreamKey="Stream key" UseAuth="Use authentication" Username="Username" diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index c409bca6e97b7c..e00a1b50a35d8c 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -40,6 +40,7 @@ static inline const char *get_string_val(json_t *service, const char *key); static inline int get_int_val(json_t *service, const char *key); extern void twitch_ingests_refresh(int seconds); +extern void amazon_ivs_ingests_refresh(int seconds); static void ensure_valid_url(struct rtmp_common *service, json_t *json, obs_data_t *settings) @@ -446,6 +447,55 @@ static inline bool fill_twitch_servers(obs_property_t *servers_prop) return success; } +static bool fill_amazon_ivs_servers_locked(obs_property_t *servers_prop) +{ + struct dstr name_buffer = {0}; + size_t count = amazon_ivs_ingest_count(); + bool rtmps_available = obs_is_output_protocol_registered("RTMPS"); + + if (rtmps_available) { + obs_property_list_add_string( + servers_prop, obs_module_text("Server.AutoRTMPS"), + "auto-rtmps"); + } + obs_property_list_add_string( + servers_prop, obs_module_text("Server.AutoRTMP"), "auto-rtmp"); + + if (count <= 1) + return false; + + if (rtmps_available) { + for (size_t i = 0; i < count; i++) { + struct twitch_ingest ing = amazon_ivs_ingest(i); + dstr_printf(&name_buffer, "%s (RTMPS)", ing.name); + obs_property_list_add_string( + servers_prop, name_buffer.array, ing.rtmps_url); + } + } + + for (size_t i = 0; i < count; i++) { + struct twitch_ingest ing = amazon_ivs_ingest(i); + dstr_printf(&name_buffer, "%s (RTMP)", ing.name); + obs_property_list_add_string(servers_prop, name_buffer.array, + ing.url); + } + + dstr_free(&name_buffer); + + return true; +} + +static inline bool fill_amazon_ivs_servers(obs_property_t *servers_prop) +{ + bool success; + + amazon_ivs_ingests_lock(); + success = fill_amazon_ivs_servers_locked(servers_prop); + amazon_ivs_ingests_unlock(); + + return success; +} + static void fill_servers(obs_property_t *servers_prop, json_t *service, const char *name) { @@ -476,6 +526,11 @@ static void fill_servers(obs_property_t *servers_prop, json_t *service, servers_prop, obs_module_text("Server.Auto"), "auto"); } + if (strcmp(name, "Amazon IVS") == 0) { + if (fill_amazon_ivs_servers(servers_prop)) + return; + } + json_array_foreach (servers, index, server) { const char *server_name = get_string_val(server, "name"); const char *url = get_string_val(server, "url"); @@ -825,6 +880,22 @@ static const char *rtmp_common_url(void *data) } } + if (service->service && strcmp(service->service, "Amazon IVS") == 0) { + if (service->server && + strncmp(service->server, "auto", 4) == 0) { + struct twitch_ingest ing; + bool rtmp = strcmp(service->server, "auto-rtmp") == 0; + + amazon_ivs_ingests_refresh(3); + + amazon_ivs_ingests_lock(); + ing = amazon_ivs_ingest(0); + amazon_ivs_ingests_unlock(); + + return rtmp ? ing.url : ing.rtmps_url; + } + } + if (service->service && strcmp(service->service, "Nimo TV") == 0) { if (service->server && strcmp(service->server, "auto") == 0) { return nimotv_get_ingest(service->key); From adcaf539c9fc6dfca094211946de18b5b5a3b582 Mon Sep 17 00:00:00 2001 From: Ruwen Hahn Date: Thu, 11 Apr 2024 14:27:44 +0200 Subject: [PATCH 0509/1073] UI: Add Amazon IVS auto config QoL changes This doesn't generally make the speedtest work for Amazon IVS, since most Amazon IVS channels will be disconnected if the bitrate being sent exceeds the upper limit for that channel. --- UI/window-basic-auto-config-test.cpp | 9 ++++++--- UI/window-basic-auto-config.cpp | 23 ++++++++++++++++++++++- UI/window-basic-auto-config.hpp | 2 ++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index b242e1d94ba97c..b1962d455e3204 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -221,7 +221,8 @@ void AutoConfigTestPage::TestBandwidthThread() OBSDataAutoRelease output_settings = obs_data_create(); std::string key = wiz->key; - if (wiz->service == AutoConfig::Service::Twitch) { + if (wiz->service == AutoConfig::Service::Twitch || + wiz->service == AutoConfig::Service::AmazonIVS) { string_depad_key(key); key += "?bandwidthtest"; } else if (wiz->serviceName == "Restream.io" || @@ -266,8 +267,10 @@ void AutoConfigTestPage::TestBandwidthThread() wiz->serviceName == "Nimo TV") { servers.resize(1); - } else if (wiz->service == AutoConfig::Service::Twitch && - wiz->twitchAuto) { + } else if ((wiz->service == AutoConfig::Service::Twitch && + wiz->twitchAuto) || + (wiz->service == AutoConfig::Service::AmazonIVS && + wiz->amazonIVSAuto)) { /* if using Twitch and "Auto" is available, test 3 closest * server */ servers.erase(servers.begin() + 1); diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 01c356702ca8e3..20f2705ef143fe 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -411,6 +411,8 @@ bool AutoConfigStreamPage::validatePage() else if (IsYouTubeService(wiz->serviceName)) wiz->service = AutoConfig::Service::YouTube; #endif + else if (wiz->serviceName == "Amazon IVS") + wiz->service = AutoConfig::Service::AmazonIVS; else wiz->service = AutoConfig::Service::Other; } else { @@ -498,6 +500,7 @@ bool AutoConfigStreamPage::validatePage() if (wiz->service != AutoConfig::Service::Twitch && wiz->service != AutoConfig::Service::YouTube && + wiz->service != AutoConfig::Service::AmazonIVS && wiz->bandwidthTest) { QMessageBox::StandardButton button; #define WARNING_TEXT(x) QTStr("Basic.AutoConfig.StreamPage.StreamWarning." x) @@ -774,7 +777,8 @@ void AutoConfigStreamPage::ServiceChanged() reset_service_ui_fields(service); /* Test three closest servers if "Auto" is available for Twitch */ - if (service == "Twitch" && wiz->twitchAuto) + if ((service == "Twitch" && wiz->twitchAuto) || + (service == "Amazon IVS" && wiz->amazonIVSAuto)) regionBased = false; ui->streamkeyPageLayout->removeWidget(ui->serverLabel); @@ -1025,6 +1029,7 @@ AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent) proc_handler_t *ph = obs_get_proc_handler(); proc_handler_call(ph, "twitch_ingests_refresh", &cd); + proc_handler_call(ph, "amazon_ivs_ingests_refresh", &cd); calldata_free(&cd); OBSBasic *main = reinterpret_cast(parent); @@ -1068,6 +1073,22 @@ AutoConfig::AutoConfig(QWidget *parent) : QWizard(parent) obs_properties_destroy(props); + /* ----------------------------------------- */ + /* check to see if Amazon IVS "auto" entries are available */ + + OBSDataAutoRelease amazonIVSSettings = obs_data_create(); + + obs_data_set_string(amazonIVSSettings, "service", "Amazon IVS"); + + props = obs_get_service_properties("rtmp_common"); + obs_properties_apply_settings(props, amazonIVSSettings); + + p = obs_properties_get(props, "server"); + first = obs_property_list_item_string(p, 0); + amazonIVSAuto = strncmp(first, "auto", 4) == 0; + + obs_properties_destroy(props); + /* ----------------------------------------- */ /* load service/servers */ diff --git a/UI/window-basic-auto-config.hpp b/UI/window-basic-auto-config.hpp index 2f93c540b25a14..d715c15631082b 100644 --- a/UI/window-basic-auto-config.hpp +++ b/UI/window-basic-auto-config.hpp @@ -40,6 +40,7 @@ class AutoConfig : public QWizard { enum class Service { Twitch, YouTube, + AmazonIVS, Other, }; @@ -110,6 +111,7 @@ class AutoConfig : public QWizard { bool testMultitrackVideo = false; bool testRegions = true; bool twitchAuto = false; + bool amazonIVSAuto = false; bool regionUS = true; bool regionEU = true; bool regionAsia = true; From e11d206da8e171a4ad55c49ad3b257dba8707884 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Wed, 28 Aug 2024 09:55:27 -0400 Subject: [PATCH 0510/1073] rtmp-services: Refactor Twitch/Amazon IVS support Separate the commonly used functions into service-ingest.c/h. Split the Amazon IVS support out of the Twitch specific files and into the new amazon-ivs.c/h files. This allows for clean usage of `struct ingest` between the two services. --- plugins/rtmp-services/CMakeLists.txt | 4 + plugins/rtmp-services/rtmp-common.c | 35 ++- .../service-specific/amazon-ivs.c | 58 ++++ .../service-specific/amazon-ivs.h | 8 + .../service-specific/service-ingest.c | 199 +++++++++++++ .../service-specific/service-ingest.h | 32 ++ .../rtmp-services/service-specific/twitch.c | 277 +----------------- .../rtmp-services/service-specific/twitch.h | 13 +- 8 files changed, 325 insertions(+), 301 deletions(-) create mode 100644 plugins/rtmp-services/service-specific/amazon-ivs.c create mode 100644 plugins/rtmp-services/service-specific/amazon-ivs.h create mode 100644 plugins/rtmp-services/service-specific/service-ingest.c create mode 100644 plugins/rtmp-services/service-specific/service-ingest.h diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index 3b4ff65a7818b2..1f6374ce456599 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -29,10 +29,14 @@ target_sources( rtmp-custom.c rtmp-format-ver.h rtmp-services-main.c + service-specific/amazon-ivs.c + service-specific/amazon-ivs.h service-specific/dacast.c service-specific/dacast.h service-specific/nimotv.c service-specific/nimotv.h + service-specific/service-ingest.c + service-specific/service-ingest.h service-specific/showroom.c service-specific/showroom.h service-specific/twitch.c diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index e00a1b50a35d8c..363ed33a498256 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -9,6 +9,7 @@ #include "service-specific/nimotv.h" #include "service-specific/showroom.h" #include "service-specific/dacast.h" +#include "service-specific/amazon-ivs.h" struct rtmp_common { char *service; @@ -429,8 +430,9 @@ static bool fill_twitch_servers_locked(obs_property_t *servers_prop) return false; for (size_t i = 0; i < count; i++) { - struct twitch_ingest ing = twitch_ingest(i); - obs_property_list_add_string(servers_prop, ing.name, ing.url); + struct ingest twitch_ing = twitch_ingest(i); + obs_property_list_add_string(servers_prop, twitch_ing.name, + twitch_ing.url); } return true; @@ -466,18 +468,20 @@ static bool fill_amazon_ivs_servers_locked(obs_property_t *servers_prop) if (rtmps_available) { for (size_t i = 0; i < count; i++) { - struct twitch_ingest ing = amazon_ivs_ingest(i); - dstr_printf(&name_buffer, "%s (RTMPS)", ing.name); - obs_property_list_add_string( - servers_prop, name_buffer.array, ing.rtmps_url); + struct ingest amazon_ivs_ing = amazon_ivs_ingest(i); + dstr_printf(&name_buffer, "%s (RTMPS)", + amazon_ivs_ing.name); + obs_property_list_add_string(servers_prop, + name_buffer.array, + amazon_ivs_ing.rtmps_url); } } for (size_t i = 0; i < count; i++) { - struct twitch_ingest ing = amazon_ivs_ingest(i); - dstr_printf(&name_buffer, "%s (RTMP)", ing.name); + struct ingest amazon_ivs_ing = amazon_ivs_ingest(i); + dstr_printf(&name_buffer, "%s (RTMP)", amazon_ivs_ing.name); obs_property_list_add_string(servers_prop, name_buffer.array, - ing.url); + amazon_ivs_ing.url); } dstr_free(&name_buffer); @@ -868,31 +872,32 @@ static const char *rtmp_common_url(void *data) if (service->service && strcmp(service->service, "Twitch") == 0) { if (service->server && strcmp(service->server, "auto") == 0) { - struct twitch_ingest ing; + struct ingest twitch_ing; twitch_ingests_refresh(3); twitch_ingests_lock(); - ing = twitch_ingest(0); + twitch_ing = twitch_ingest(0); twitch_ingests_unlock(); - return ing.url; + return twitch_ing.url; } } if (service->service && strcmp(service->service, "Amazon IVS") == 0) { if (service->server && strncmp(service->server, "auto", 4) == 0) { - struct twitch_ingest ing; + struct ingest amazon_ivs_ing; bool rtmp = strcmp(service->server, "auto-rtmp") == 0; amazon_ivs_ingests_refresh(3); amazon_ivs_ingests_lock(); - ing = amazon_ivs_ingest(0); + amazon_ivs_ing = amazon_ivs_ingest(0); amazon_ivs_ingests_unlock(); - return rtmp ? ing.url : ing.rtmps_url; + return rtmp ? amazon_ivs_ing.url + : amazon_ivs_ing.rtmps_url; } } diff --git a/plugins/rtmp-services/service-specific/amazon-ivs.c b/plugins/rtmp-services/service-specific/amazon-ivs.c new file mode 100644 index 00000000000000..5a021cbf42c092 --- /dev/null +++ b/plugins/rtmp-services/service-specific/amazon-ivs.c @@ -0,0 +1,58 @@ +#include "service-ingest.h" +#include "amazon-ivs.h" + +static struct service_ingests amazon_ivs = { + .update_info = NULL, + .mutex = PTHREAD_MUTEX_INITIALIZER, + .ingests_refreshed = false, + .ingests_refreshing = false, + .ingests_loaded = false, + .cur_ingests = {0}, + .cache_old_filename = "amazon_ivs_ingests.json", + .cache_new_filename = "amazon_ivs_ingests.new.json"}; + +void init_amazon_ivs_data(void) +{ + init_service_data(&amazon_ivs); +} + +void load_amazon_ivs_data(void) +{ + struct ingest def = { + .name = bstrdup("Default"), + .url = bstrdup( + "rtmps://ingest.global-contribute.live-video.net:443/app/")}; + load_service_data(&amazon_ivs, "amazon_ivs_ingests.json", &def); +} + +void unload_amazon_ivs_data(void) +{ + unload_service_data(&amazon_ivs); +} + +void amazon_ivs_ingests_refresh(int seconds) +{ + service_ingests_refresh( + &amazon_ivs, seconds, "[amazon ivs ingest update] ", + "https://ingest.contribute.live-video.net/ingests"); +} + +void amazon_ivs_ingests_lock(void) +{ + pthread_mutex_lock(&amazon_ivs.mutex); +} + +void amazon_ivs_ingests_unlock(void) +{ + pthread_mutex_unlock(&amazon_ivs.mutex); +} + +size_t amazon_ivs_ingest_count(void) +{ + return amazon_ivs.cur_ingests.num; +} + +struct ingest amazon_ivs_ingest(size_t idx) +{ + return get_ingest(&amazon_ivs, idx); +} diff --git a/plugins/rtmp-services/service-specific/amazon-ivs.h b/plugins/rtmp-services/service-specific/amazon-ivs.h new file mode 100644 index 00000000000000..18ceb587e3636e --- /dev/null +++ b/plugins/rtmp-services/service-specific/amazon-ivs.h @@ -0,0 +1,8 @@ +#pragma once + +#include "service-ingest.h" + +extern void amazon_ivs_ingests_lock(void); +extern void amazon_ivs_ingests_unlock(void); +extern size_t amazon_ivs_ingest_count(void); +extern struct ingest amazon_ivs_ingest(size_t idx); diff --git a/plugins/rtmp-services/service-specific/service-ingest.c b/plugins/rtmp-services/service-specific/service-ingest.c new file mode 100644 index 00000000000000..b3d2bad2a25571 --- /dev/null +++ b/plugins/rtmp-services/service-specific/service-ingest.c @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include "service-ingest.h" + +extern const char *get_module_name(void); + +void init_service_data(struct service_ingests *si) +{ + da_init(si->cur_ingests); + pthread_mutex_init(&si->mutex, NULL); +} + +static void free_ingests(struct service_ingests *si) +{ + for (size_t i = 0; i < si->cur_ingests.num; i++) { + struct ingest *ingest = si->cur_ingests.array + i; + bfree(ingest->name); + bfree(ingest->url); + bfree(ingest->rtmps_url); + } + + da_free(si->cur_ingests); +} + +static bool load_ingests(struct service_ingests *si, const char *json, + bool write_file) +{ + json_t *root; + json_t *ingests; + bool success = false; + char *cache_old; + char *cache_new; + size_t count; + + root = json_loads(json, 0, NULL); + if (!root) + goto finish; + + ingests = json_object_get(root, "ingests"); + if (!ingests) + goto finish; + + count = json_array_size(ingests); + if (count <= 1 && si->cur_ingests.num) + goto finish; + + free_ingests(si); + + for (size_t i = 0; i < count; i++) { + json_t *item = json_array_get(ingests, i); + json_t *item_name = json_object_get(item, "name"); + json_t *item_url = json_object_get(item, "url_template"); + json_t *item_rtmps_url = + json_object_get(item, "url_template_secure"); + struct ingest ingest = {0}; + struct dstr url = {0}; + struct dstr rtmps_url = {0}; + + if (!item_name || !item_url) + continue; + + const char *url_str = json_string_value(item_url); + const char *rtmps_url_str = json_string_value(item_rtmps_url); + const char *name_str = json_string_value(item_name); + + /* At the moment they currently mis-spell "deprecated", + * but that may change in the future, so blacklist both */ + if (strstr(name_str, "deprecated") != NULL || + strstr(name_str, "depracated") != NULL) + continue; + + dstr_copy(&url, url_str); + dstr_replace(&url, "/{stream_key}", ""); + + dstr_copy(&rtmps_url, rtmps_url_str); + dstr_replace(&rtmps_url, "/{stream_key}", ""); + + ingest.name = bstrdup(name_str); + ingest.url = url.array; + ingest.rtmps_url = rtmps_url.array; + + da_push_back(si->cur_ingests, &ingest); + } + + if (!si->cur_ingests.num) + goto finish; + + success = true; + + if (!write_file) + goto finish; + + cache_old = obs_module_config_path(si->cache_old_filename); + cache_new = obs_module_config_path(si->cache_new_filename); + + os_quick_write_utf8_file(cache_new, json, strlen(json), false); + os_safe_replace(cache_old, cache_new, NULL); + + bfree(cache_old); + bfree(cache_new); + +finish: + if (root) + json_decref(root); + return success; +} + +static bool ingest_update(void *param, struct file_download_data *data) +{ + struct service_ingests *service = param; + bool success; + + pthread_mutex_lock(&service->mutex); + success = load_ingests(service, (const char *)data->buffer.array, true); + pthread_mutex_unlock(&service->mutex); + + if (success) { + os_atomic_set_bool(&service->ingests_refreshed, true); + os_atomic_set_bool(&service->ingests_loaded, true); + } + + return true; +} + +void service_ingests_refresh(struct service_ingests *si, int seconds, + const char *log_prefix, const char *file_url) +{ + if (os_atomic_load_bool(&si->ingests_refreshed)) + return; + + if (!os_atomic_load_bool(&si->ingests_refreshing)) { + os_atomic_set_bool(&si->ingests_refreshing, true); + + si->update_info = + update_info_create_single(log_prefix, get_module_name(), + file_url, ingest_update, si); + } + + /* wait five seconds max when loading ingests for the first time */ + if (!os_atomic_load_bool(&si->ingests_loaded)) { + for (int i = 0; i < seconds * 100; i++) { + if (os_atomic_load_bool(&si->ingests_refreshed)) { + break; + } + os_sleep_ms(10); + } + } +} + +void load_service_data(struct service_ingests *si, const char *cache_filename, + struct ingest *def) +{ + char *service_cache = obs_module_config_path(cache_filename); + + pthread_mutex_lock(&si->mutex); + da_push_back(si->cur_ingests, def); + pthread_mutex_unlock(&si->mutex); + + if (os_file_exists(service_cache)) { + char *data = os_quick_read_utf8_file(service_cache); + bool success; + + pthread_mutex_lock(&si->mutex); + success = load_ingests(si, data, false); + pthread_mutex_unlock(&si->mutex); + + if (success) { + os_atomic_set_bool(&si->ingests_loaded, true); + } + + bfree(data); + } + + bfree(service_cache); +} + +void unload_service_data(struct service_ingests *si) +{ + update_info_destroy(si->update_info); + free_ingests(si); + pthread_mutex_destroy(&si->mutex); +} + +struct ingest get_ingest(struct service_ingests *si, size_t idx) +{ + struct ingest ingest; + + if (si->cur_ingests.num <= idx) { + ingest.name = NULL; + ingest.url = NULL; + ingest.rtmps_url = NULL; + } else { + ingest = *(struct ingest *)(si->cur_ingests.array + idx); + } + + return ingest; +} diff --git a/plugins/rtmp-services/service-specific/service-ingest.h b/plugins/rtmp-services/service-specific/service-ingest.h new file mode 100644 index 00000000000000..95d47bc4a5c135 --- /dev/null +++ b/plugins/rtmp-services/service-specific/service-ingest.h @@ -0,0 +1,32 @@ +#pragma once + +/* service-ingest.h is common between the Amazon IVS service + * and the Twitch streaming service. + */ +#include +#include + +struct ingest { + char *name; + char *url; + char *rtmps_url; +}; + +struct service_ingests { + update_info_t *update_info; + pthread_mutex_t mutex; + bool ingests_refreshed; + bool ingests_refreshing; + bool ingests_loaded; + DARRAY(struct ingest) cur_ingests; + const char *cache_old_filename; + const char *cache_new_filename; +}; + +void init_service_data(struct service_ingests *si); +void service_ingests_refresh(struct service_ingests *si, int seconds, + const char *log_prefix, const char *file_url); +void load_service_data(struct service_ingests *si, const char *cache_filename, + struct ingest *def); +void unload_service_data(struct service_ingests *si); +struct ingest get_ingest(struct service_ingests *si, size_t idx); diff --git a/plugins/rtmp-services/service-specific/twitch.c b/plugins/rtmp-services/service-specific/twitch.c index 57bfa46f3fc203..6c11b112b9383b 100644 --- a/plugins/rtmp-services/service-specific/twitch.c +++ b/plugins/rtmp-services/service-specific/twitch.c @@ -1,29 +1,6 @@ -#include -#include -#include -#include -#include -#include - +#include "service-ingest.h" #include "twitch.h" -struct ingest { - char *name; - char *url; - char *rtmps_url; -}; - -struct service_ingests { - update_info_t *update_info; - pthread_mutex_t mutex; - bool ingests_refreshed; - bool ingests_refreshing; - bool ingests_loaded; - DARRAY(struct ingest) cur_ingests; - const char *cache_old_filename; - const char *cache_new_filename; -}; - static struct service_ingests twitch = { .update_info = NULL, .mutex = PTHREAD_MUTEX_INITIALIZER, @@ -34,128 +11,6 @@ static struct service_ingests twitch = { .cache_old_filename = "twitch_ingests.json", .cache_new_filename = "twitch_ingests.new.json"}; -static struct service_ingests amazon_ivs = { - .update_info = NULL, - .mutex = PTHREAD_MUTEX_INITIALIZER, - .ingests_refreshed = false, - .ingests_refreshing = false, - .ingests_loaded = false, - .cur_ingests = {0}, - .cache_old_filename = "amazon_ivs_ingests.json", - .cache_new_filename = "amazon_ivs_ingests.new.json"}; - -static void free_ingests(struct service_ingests *si) -{ - for (size_t i = 0; i < si->cur_ingests.num; i++) { - struct ingest *ingest = si->cur_ingests.array + i; - bfree(ingest->name); - bfree(ingest->url); - bfree(ingest->rtmps_url); - } - - da_free(si->cur_ingests); -} - -static bool load_ingests(struct service_ingests *si, const char *json, - bool write_file) -{ - json_t *root; - json_t *ingests; - bool success = false; - char *cache_old; - char *cache_new; - size_t count; - - root = json_loads(json, 0, NULL); - if (!root) - goto finish; - - ingests = json_object_get(root, "ingests"); - if (!ingests) - goto finish; - - count = json_array_size(ingests); - if (count <= 1 && si->cur_ingests.num) - goto finish; - - free_ingests(si); - - for (size_t i = 0; i < count; i++) { - json_t *item = json_array_get(ingests, i); - json_t *item_name = json_object_get(item, "name"); - json_t *item_url = json_object_get(item, "url_template"); - json_t *item_rtmps_url = - json_object_get(item, "url_template_secure"); - struct ingest ingest = {0}; - struct dstr url = {0}; - struct dstr rtmps_url = {0}; - - if (!item_name || !item_url) - continue; - - const char *url_str = json_string_value(item_url); - const char *rtmps_url_str = json_string_value(item_rtmps_url); - const char *name_str = json_string_value(item_name); - - /* At the moment they currently mis-spell "deprecated", - * but that may change in the future, so blacklist both */ - if (strstr(name_str, "deprecated") != NULL || - strstr(name_str, "depracated") != NULL) - continue; - - dstr_copy(&url, url_str); - dstr_replace(&url, "/{stream_key}", ""); - - dstr_copy(&rtmps_url, rtmps_url_str); - dstr_replace(&rtmps_url, "/{stream_key}", ""); - - ingest.name = bstrdup(name_str); - ingest.url = url.array; - ingest.rtmps_url = rtmps_url.array; - - da_push_back(si->cur_ingests, &ingest); - } - - if (!si->cur_ingests.num) - goto finish; - - success = true; - - if (!write_file) - goto finish; - - cache_old = obs_module_config_path(si->cache_old_filename); - cache_new = obs_module_config_path(si->cache_new_filename); - - os_quick_write_utf8_file(cache_new, json, strlen(json), false); - os_safe_replace(cache_old, cache_new, NULL); - - bfree(cache_old); - bfree(cache_new); - -finish: - if (root) - json_decref(root); - return success; -} - -static bool ingest_update(void *param, struct file_download_data *data) -{ - struct service_ingests *service = param; - bool success; - - pthread_mutex_lock(&service->mutex); - success = load_ingests(service, (const char *)data->buffer.array, true); - pthread_mutex_unlock(&service->mutex); - - if (success) { - os_atomic_set_bool(&service->ingests_refreshed, true); - os_atomic_set_bool(&service->ingests_loaded, true); - } - - return true; -} - void twitch_ingests_lock(void) { pthread_mutex_lock(&twitch.mutex); @@ -171,97 +26,22 @@ size_t twitch_ingest_count(void) return twitch.cur_ingests.num; } -struct twitch_ingest get_ingest(struct service_ingests *si, size_t idx) -{ - struct twitch_ingest ingest; - - if (si->cur_ingests.num <= idx) { - ingest.name = NULL; - ingest.url = NULL; - ingest.rtmps_url = NULL; - } else { - ingest = *(struct twitch_ingest *)(si->cur_ingests.array + idx); - } - - return ingest; -} - -struct twitch_ingest twitch_ingest(size_t idx) +struct ingest twitch_ingest(size_t idx) { return get_ingest(&twitch, idx); } -void init_service_data(struct service_ingests *si) -{ - da_init(si->cur_ingests); - pthread_mutex_init(&si->mutex, NULL); -} - void init_twitch_data(void) { init_service_data(&twitch); } -extern const char *get_module_name(void); - -void service_ingests_refresh(struct service_ingests *si, int seconds, - const char *log_prefix, const char *file_url) -{ - if (os_atomic_load_bool(&si->ingests_refreshed)) - return; - - if (!os_atomic_load_bool(&si->ingests_refreshing)) { - os_atomic_set_bool(&si->ingests_refreshing, true); - - si->update_info = - update_info_create_single(log_prefix, get_module_name(), - file_url, ingest_update, si); - } - - /* wait five seconds max when loading ingests for the first time */ - if (!os_atomic_load_bool(&si->ingests_loaded)) { - for (int i = 0; i < seconds * 100; i++) { - if (os_atomic_load_bool(&si->ingests_refreshed)) { - break; - } - os_sleep_ms(10); - } - } -} - void twitch_ingests_refresh(int seconds) { service_ingests_refresh(&twitch, seconds, "[twitch ingest update] ", "https://ingest.twitch.tv/ingests"); } -void load_service_data(struct service_ingests *si, const char *cache_filename, - struct ingest *def) -{ - char *service_cache = obs_module_config_path(cache_filename); - - pthread_mutex_lock(&si->mutex); - da_push_back(si->cur_ingests, def); - pthread_mutex_unlock(&si->mutex); - - if (os_file_exists(service_cache)) { - char *data = os_quick_read_utf8_file(service_cache); - bool success; - - pthread_mutex_lock(&si->mutex); - success = load_ingests(si, data, false); - pthread_mutex_unlock(&si->mutex); - - if (success) { - os_atomic_set_bool(&si->ingests_loaded, true); - } - - bfree(data); - } - - bfree(service_cache); -} - void load_twitch_data(void) { struct ingest def = {.name = bstrdup("Default"), @@ -269,60 +49,7 @@ void load_twitch_data(void) load_service_data(&twitch, "twitch_ingests.json", &def); } -void unload_service_data(struct service_ingests *si) -{ - update_info_destroy(si->update_info); - free_ingests(si); - pthread_mutex_destroy(&si->mutex); -} - void unload_twitch_data(void) { unload_service_data(&twitch); } - -void init_amazon_ivs_data(void) -{ - init_service_data(&amazon_ivs); -} - -void load_amazon_ivs_data(void) -{ - struct ingest def = { - .name = bstrdup("Default"), - .url = bstrdup( - "rtmps://ingest.global-contribute.live-video.net:443/app/")}; - load_service_data(&amazon_ivs, "amazon_ivs_ingests.json", &def); -} - -void unload_amazon_ivs_data(void) -{ - unload_service_data(&amazon_ivs); -} - -void amazon_ivs_ingests_refresh(int seconds) -{ - service_ingests_refresh( - &amazon_ivs, seconds, "[amazon ivs ingest update] ", - "https://ingest.contribute.live-video.net/ingests"); -} - -void amazon_ivs_ingests_lock(void) -{ - pthread_mutex_lock(&amazon_ivs.mutex); -} - -void amazon_ivs_ingests_unlock(void) -{ - pthread_mutex_unlock(&amazon_ivs.mutex); -} - -size_t amazon_ivs_ingest_count(void) -{ - return amazon_ivs.cur_ingests.num; -} - -struct twitch_ingest amazon_ivs_ingest(size_t idx) -{ - return get_ingest(&amazon_ivs, idx); -} diff --git a/plugins/rtmp-services/service-specific/twitch.h b/plugins/rtmp-services/service-specific/twitch.h index 0ff8b7a37cba73..189b5f931e87e5 100644 --- a/plugins/rtmp-services/service-specific/twitch.h +++ b/plugins/rtmp-services/service-specific/twitch.h @@ -1,17 +1,8 @@ #pragma once -struct twitch_ingest { - const char *name; - const char *url; - const char *rtmps_url; -}; +#include "service-ingest.h" extern void twitch_ingests_lock(void); extern void twitch_ingests_unlock(void); extern size_t twitch_ingest_count(void); -extern struct twitch_ingest twitch_ingest(size_t idx); - -extern void amazon_ivs_ingests_lock(void); -extern void amazon_ivs_ingests_unlock(void); -extern size_t amazon_ivs_ingest_count(void); -extern struct twitch_ingest amazon_ivs_ingest(size_t idx); +extern struct ingest twitch_ingest(size_t idx); From df742ed0323feffa903381c16d81477db332b9b3 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 6 Sep 2024 14:49:07 -0400 Subject: [PATCH 0511/1073] CI: Pin actions/upload-artifact to v4.3.5 for windows-patches Work around a bug with too many open files in versions before v4.3.5 and also in v4.3.6 due to a revert. The relevant error message is: Error: EMFILE: too many open files We applied the same workaround to release/30.2. --- .github/actions/windows-patches/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/windows-patches/action.yaml b/.github/actions/windows-patches/action.yaml index 95d5688bef92a6..09090a958596e7 100644 --- a/.github/actions/windows-patches/action.yaml +++ b/.github/actions/windows-patches/action.yaml @@ -109,7 +109,7 @@ runs: Invoke-External "${{ github.workspace }}\bouf\bin\bouf.exe" @boufArgs - name: Upload Outputs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v4.3.5 with: name: windows-updater-files compression-level: 0 From dfe61514650dfc84f8854fef50002d20cf6e8d52 Mon Sep 17 00:00:00 2001 From: Scott Cooper Date: Tue, 3 Sep 2024 12:44:18 +1000 Subject: [PATCH 0512/1073] win-capture: Fix segfault when calling data.free() data.free should be checked before calling. --- plugins/win-capture/graphics-hook/dxgi-capture.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/win-capture/graphics-hook/dxgi-capture.cpp b/plugins/win-capture/graphics-hook/dxgi-capture.cpp index 7892dbf6389bd0..d175aa5652afed 100644 --- a/plugins/win-capture/graphics-hook/dxgi-capture.cpp +++ b/plugins/win-capture/graphics-hook/dxgi-capture.cpp @@ -48,7 +48,8 @@ static void STDMETHODCALLTYPE SwapChainDestructed(void *pData) dxgi_possible_swap_queue_count = 0; dxgi_present_attempted = false; - data.free(); + if (data.free) + data.free(); data.free = nullptr; } } @@ -187,7 +188,8 @@ static void update_mismatch_count(bool match) dxgi_possible_swap_queue_count = 0; dxgi_present_attempted = false; - data.free(); + if (data.free) + data.free(); data.free = nullptr; swap_chain_mismatch_count = 0; From 129d4f2f3fdbcb2c643a453012ad90e1e7b895d5 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 24 Aug 2024 13:35:57 +0200 Subject: [PATCH 0513/1073] cmake: Avoid breaking ABI through major version bump on Linux Freeze SOVERSION to 30, this number is to be incremented when a clean break is wanted. --- cmake/linux/helpers.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/linux/helpers.cmake b/cmake/linux/helpers.cmake index e390405a640a33..b954fb4a76a2b3 100644 --- a/cmake/linux/helpers.cmake +++ b/cmake/linux/helpers.cmake @@ -59,8 +59,8 @@ function(set_target_properties_obs target) set_target_properties( ${target} PROPERTIES - VERSION ${OBS_VERSION_CANONICAL} - SOVERSION ${OBS_VERSION_MAJOR} + VERSION 30 + SOVERSION 30 BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" INSTALL_RPATH "${OBS_LIBRARY_RPATH}" ) @@ -107,13 +107,13 @@ function(set_target_properties_obs target) endif() elseif(target_type STREQUAL MODULE_LIBRARY) if(target STREQUAL obs-browser) - set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_VERSION_MAJOR}) + set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION 30) else() set_target_properties( ${target} PROPERTIES VERSION 0 - SOVERSION ${OBS_VERSION_MAJOR} + SOVERSION 30 BUILD_RPATH "${OBS_OUTPUT_DIR}/$/${OBS_LIBRARY_DESTINATION}" INSTALL_RPATH "${OBS_MODULE_RPATH}" ) From 9f4f2e975473e4ad3cf725196d8f9b1013b67aec Mon Sep 17 00:00:00 2001 From: "Gale, Thy-Lan" Date: Tue, 11 Jun 2024 15:30:39 -0700 Subject: [PATCH 0514/1073] obs-qsv11: Fix CBR Spike --- plugins/obs-qsv11/QSV_Encoder_Internal.cpp | 50 ++++++++++++++++++++-- plugins/obs-qsv11/QSV_Encoder_Internal.h | 1 + 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp index 695d84ef533882..a90af811fd6e54 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp @@ -177,6 +177,26 @@ mfxStatus QSV_Encoder_Internal::Open(qsv_param_t *pParams, enum qsv_codec codec) return sts; } +PRAGMA_WARN_PUSH +PRAGMA_WARN_DEPRECATION +static inline bool HasOptimizedBRCSupport(const mfxPlatform &platform, + const mfxVersion &version, + mfxU16 rateControl) +{ +#if (MFX_VERSION_MAJOR >= 2 && MFX_VERSION_MINOR >= 12) || MFX_VERSION_MAJOR > 2 + if ((version.Major >= 2 && version.Minor >= 12) || version.Major > 2) + if (rateControl == MFX_RATECONTROL_CBR && + (platform.CodeName >= MFX_PLATFORM_LUNARLAKE && + platform.CodeName != MFX_PLATFORM_ALDERLAKE_N)) + return true; +#endif + UNUSED_PARAMETER(platform); + UNUSED_PARAMETER(version); + UNUSED_PARAMETER(rateControl); + return false; +} +PRAGMA_WARN_POP + mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, enum qsv_codec codec) { @@ -233,10 +253,18 @@ mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, switch (pParams->nRateControl) { case MFX_RATECONTROL_CBR: m_mfxEncParams.mfx.TargetKbps = pParams->nTargetBitRate; - m_mfxEncParams.mfx.BufferSizeInKB = - (pParams->nTargetBitRate / 8) * 2; + + if (HasOptimizedBRCSupport(platform, m_ver, + pParams->nRateControl)) { + m_mfxEncParams.mfx.BufferSizeInKB = + (pParams->nTargetBitRate / 8) * 1; + } else { + m_mfxEncParams.mfx.BufferSizeInKB = + (pParams->nTargetBitRate / 8) * 2; + } + m_mfxEncParams.mfx.InitialDelayInKB = - (pParams->nTargetBitRate / 8) * 1; + m_mfxEncParams.mfx.BufferSizeInKB / 2; break; case MFX_RATECONTROL_VBR: m_mfxEncParams.mfx.TargetKbps = pParams->nTargetBitRate; @@ -304,6 +332,22 @@ mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, extendedBuffers.push_back((mfxExtBuffer *)&m_co2); + if (HasOptimizedBRCSupport(platform, m_ver, pParams->nRateControl)) { + memset(&m_co3, 0, sizeof(mfxExtCodingOption3)); + m_co3.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3; + m_co3.Header.BufferSz = sizeof(m_co3); + m_co3.WinBRCSize = pParams->nFpsNum / pParams->nFpsDen; + + if (codec == QSV_CODEC_AVC || codec == QSV_CODEC_HEVC) { + m_co3.WinBRCMaxAvgKbps = + mfxU16(1.3 * pParams->nTargetBitRate); + } else if (codec == QSV_CODEC_AV1) { + m_co3.WinBRCMaxAvgKbps = + mfxU16(1.2 * pParams->nTargetBitRate); + } + extendedBuffers.push_back((mfxExtBuffer *)&m_co3); + } + if (codec == QSV_CODEC_HEVC) { if ((pParams->nWidth & 15) || (pParams->nHeight & 15)) { memset(&m_ExtHEVCParam, 0, sizeof(m_ExtHEVCParam)); diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.h b/plugins/obs-qsv11/QSV_Encoder_Internal.h index 59c078ca9b78e5..478f0b87acaf4c 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.h +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.h @@ -116,6 +116,7 @@ class QSV_Encoder_Internal { mfxU16 m_nPPSBufferSize; mfxVideoParam m_parameter; std::vector extendedBuffers; + mfxExtCodingOption3 m_co3; mfxExtCodingOption2 m_co2; mfxExtCodingOption m_co; mfxExtHEVCParam m_ExtHEVCParam{}; From 14aaaa325bf02e0078c197e84e90c40c6548efc3 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 26 Aug 2024 16:25:09 +0900 Subject: [PATCH 0515/1073] cmake/linux: Move color diagnostics setting to preset for ubuntu-ci This also removes color diagnostics setting for cmake < 3.24. --- CMakePresets.json | 3 ++- cmake/linux/compilerconfig.cmake | 8 +------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index bce62abc9a83fc..70fc423ccb5238 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -82,7 +82,8 @@ "inherits": ["ubuntu"], "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo", - "CMAKE_COMPILE_WARNING_AS_ERROR": true + "CMAKE_COMPILE_WARNING_AS_ERROR": true, + "CMAKE_COLOR_DIAGNOSTICS": true } }, { diff --git a/cmake/linux/compilerconfig.cmake b/cmake/linux/compilerconfig.cmake index c88857fdea24bc..92ab95735c1a11 100644 --- a/cmake/linux/compilerconfig.cmake +++ b/cmake/linux/compilerconfig.cmake @@ -51,17 +51,11 @@ add_compile_options( "$<$:${_obs_clang_cxx_options}>" ) -# Add support for color diagnostics and CMake switch for warnings as errors to CMake < 3.24 +# CMake switch for warnings as errors to CMake < 3.24 if(CMAKE_VERSION VERSION_LESS 3.24.0) - add_compile_options( - $<$:-fcolor-diagnostics> - $<$:-fcolor-diagnostics> - ) if(CMAKE_COMPILE_WARNING_AS_ERROR) add_compile_options(-Werror) endif() -else() - set(CMAKE_COLOR_DIAGNOSTICS ON) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) From e3265fd3e661e41c8b8527074be1773c7b6e6a8d Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Fri, 6 Sep 2024 01:27:03 +0900 Subject: [PATCH 0516/1073] CI: Enable diagnostics color on CI build on Ubuntu --- .github/scripts/.build.zsh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/scripts/.build.zsh b/.github/scripts/.build.zsh index 91c93d8db8bbd4..514fcb29576ed5 100755 --- a/.github/scripts/.build.zsh +++ b/.github/scripts/.build.zsh @@ -221,6 +221,8 @@ build() { cmake_build_args+=(build_${target%%-*} --config ${config} --parallel) cmake_install_args+=(build_${target%%-*} --prefix ${project_root}/build_${target%%-*}/install/${config}) + export CLICOLOR_FORCE=1 + log_group "Configuring ${product_name}..." ${cmake_bin} -S ${project_root} ${cmake_args} From 7fdcb166b4fcf84320b874ee382d0edd58783a55 Mon Sep 17 00:00:00 2001 From: pkv Date: Tue, 3 Sep 2024 22:00:52 +0200 Subject: [PATCH 0517/1073] obs-filters: Fix building of noise reduction If speex and rnnoise are disabled but nvafx is enabled, the noise reduction filter still needs to be built. This fixes the issue. Co-authored-by: Ryan Foster Signed-off-by: pkv --- plugins/obs-filters/CMakeLists.txt | 5 +---- plugins/obs-filters/cmake/nvidia.cmake | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 plugins/obs-filters/cmake/nvidia.cmake diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 3deb2b57b50233..af07700b5bf4c5 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -5,10 +5,6 @@ legacy_check() add_library(obs-filters MODULE) add_library(OBS::filters ALIAS obs-filters) -if(OS_WINDOWS) - target_enable_feature(obs-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) -endif() - target_sources( obs-filters PRIVATE @@ -41,6 +37,7 @@ include(cmake/speexdsp.cmake) include(cmake/rnnoise.cmake) if(OS_WINDOWS) + include(cmake/nvidia.cmake) configure_file(cmake/windows/obs-module.rc.in obs-filters.rc) target_sources(obs-filters PRIVATE obs-filters.rc) endif() diff --git a/plugins/obs-filters/cmake/nvidia.cmake b/plugins/obs-filters/cmake/nvidia.cmake new file mode 100644 index 00000000000000..e6b1e9f0ba87a1 --- /dev/null +++ b/plugins/obs-filters/cmake/nvidia.cmake @@ -0,0 +1,4 @@ +if(ENABLE_NVAFX) + target_sources(obs-filters PRIVATE noise-suppress-filter.c) + target_compile_definitions(obs-filters PRIVATE LIBNVAFX_ENABLED HAS_NOISEREDUCTION) +endif() From de2e42b231a4f1f3533380250243d6b8c801e872 Mon Sep 17 00:00:00 2001 From: pkv Date: Tue, 3 Sep 2024 23:12:39 +0200 Subject: [PATCH 0518/1073] nv-filters: Remove unused flag in cmake This removes the HAS_NOISEREDUCTION flag which is not used in this plugin. Signed-off-by: pkv --- plugins/nv-filters/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/nv-filters/CMakeLists.txt b/plugins/nv-filters/CMakeLists.txt index 85ac94635cd55e..9047e3ceef5800 100644 --- a/plugins/nv-filters/CMakeLists.txt +++ b/plugins/nv-filters/CMakeLists.txt @@ -7,7 +7,7 @@ if(OS_WINDOWS) option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON) if(ENABLE_NVAFX) - target_enable_feature(nv-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION) + target_enable_feature(nv-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED) target_sources(nv-filters PRIVATE nvidia-audiofx-filter.c) else() target_disable_feature(nv-filters "NVIDIA Audio FX support") From 97d9826352eff37256f4bfa984179d9ac1681177 Mon Sep 17 00:00:00 2001 From: Alex Luccisano Date: Fri, 23 Aug 2024 17:20:31 -0400 Subject: [PATCH 0519/1073] UI: Fix multitrack video autoconfig option When using the Auto-Configuration Wizard with the Twitch service, testing for Enhanced Broadcasting would always run, even if deselected. Add a conditional check to only run the test if selected. --- UI/window-basic-auto-config.cpp | 142 +++++++++++++++++--------------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 20f2705ef143fe..82907841d25de0 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -422,79 +422,91 @@ bool AutoConfigStreamPage::validatePage() if (wiz->service == AutoConfig::Service::Twitch) { wiz->testMultitrackVideo = ui->useMultitrackVideo->isChecked(); - auto postData = - constructGoLivePost(QString::fromStdString(wiz->key), - std::nullopt, std::nullopt, false); - - OBSDataAutoRelease service_settings = - obs_service_get_settings(service); - auto multitrack_video_name = - QTStr("Basic.Settings.Stream.MultitrackVideoLabel"); - if (obs_data_has_user_value(service_settings, - "multitrack_video_name")) { - multitrack_video_name = obs_data_get_string( - service_settings, "multitrack_video_name"); - } - - try { - auto config = DownloadGoLiveConfig( - this, MultitrackVideoAutoConfigURL(service), - postData, multitrack_video_name); - - for (const auto &endpoint : config.ingest_endpoints) { - if (qstrnicmp("RTMP", endpoint.protocol.c_str(), - 4) != 0) - continue; - - std::string address = endpoint.url_template; - auto pos = address.find("/{stream_key}"); - if (pos != address.npos) - address.erase(pos); - - wiz->serviceConfigServers.push_back( - {address, address}); + if (wiz->testMultitrackVideo) { + auto postData = constructGoLivePost( + QString::fromStdString(wiz->key), std::nullopt, + std::nullopt, false); + + OBSDataAutoRelease service_settings = + obs_service_get_settings(service); + auto multitrack_video_name = QTStr( + "Basic.Settings.Stream.MultitrackVideoLabel"); + if (obs_data_has_user_value(service_settings, + "multitrack_video_name")) { + multitrack_video_name = obs_data_get_string( + service_settings, + "multitrack_video_name"); } - int multitrackVideoBitrate = 0; - for (auto &encoder_config : - config.encoder_configurations) { - auto it = - encoder_config.settings.find("bitrate"); - if (it == encoder_config.settings.end()) - continue; + try { + auto config = DownloadGoLiveConfig( + this, + MultitrackVideoAutoConfigURL(service), + postData, multitrack_video_name); + + for (const auto &endpoint : + config.ingest_endpoints) { + if (qstrnicmp("RTMP", + endpoint.protocol.c_str(), + 4) != 0) + continue; + + std::string address = + endpoint.url_template; + auto pos = + address.find("/{stream_key}"); + if (pos != address.npos) + address.erase(pos); + + wiz->serviceConfigServers.push_back( + {address, address}); + } - if (!it->is_number_integer()) - continue; + int multitrackVideoBitrate = 0; + for (auto &encoder_config : + config.encoder_configurations) { + auto it = encoder_config.settings.find( + "bitrate"); + if (it == encoder_config.settings.end()) + continue; - int bitrate = 0; - it->get_to(bitrate); - multitrackVideoBitrate += bitrate; - } + if (!it->is_number_integer()) + continue; - // grab a streamkey from the go live config if we can - for (auto &endpoint : config.ingest_endpoints) { - const char *p = endpoint.protocol.c_str(); - const char *auth = - endpoint.authentication - ? endpoint.authentication - ->c_str() - : nullptr; - if (qstrnicmp("RTMP", p, 4) == 0 && auth && - *auth) { - wiz->key = auth; - break; + int bitrate = 0; + it->get_to(bitrate); + multitrackVideoBitrate += bitrate; } - } - if (multitrackVideoBitrate > 0) { - wiz->startingBitrate = multitrackVideoBitrate; - wiz->idealBitrate = multitrackVideoBitrate; - wiz->multitrackVideo.targetBitrate = - multitrackVideoBitrate; - wiz->multitrackVideo.testSuccessful = true; + // grab a streamkey from the go live config if we can + for (auto &endpoint : config.ingest_endpoints) { + const char *p = + endpoint.protocol.c_str(); + const char *auth = + endpoint.authentication + ? endpoint.authentication + ->c_str() + : nullptr; + if (qstrnicmp("RTMP", p, 4) == 0 && + auth && *auth) { + wiz->key = auth; + break; + } + } + + if (multitrackVideoBitrate > 0) { + wiz->startingBitrate = + multitrackVideoBitrate; + wiz->idealBitrate = + multitrackVideoBitrate; + wiz->multitrackVideo.targetBitrate = + multitrackVideoBitrate; + wiz->multitrackVideo.testSuccessful = + true; + } + } catch (const MultitrackVideoError & /*err*/) { + // FIXME: do something sensible } - } catch (const MultitrackVideoError & /*err*/) { - // FIXME: do something sensible } } From 4b0777a303c1391dba70bf1cda989890345f6726 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 10 Sep 2024 18:54:55 +0200 Subject: [PATCH 0520/1073] obs-nvenc: Support new error code for too many sessions At some point NVIDIA started using NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY instead of NV_ENC_ERR_OUT_OF_MEMORY to signal that the session limit has been exceeded. --- plugins/obs-nvenc/nvenc-helpers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/obs-nvenc/nvenc-helpers.c b/plugins/obs-nvenc/nvenc-helpers.c index 455831e7dbf041..9bd89b03c6abb5 100644 --- a/plugins/obs-nvenc/nvenc-helpers.c +++ b/plugins/obs-nvenc/nvenc-helpers.c @@ -64,6 +64,7 @@ bool nv_failed2(obs_encoder_t *encoder, void *session, NVENCSTATUS err, switch (err) { case NV_ENC_ERR_OUT_OF_MEMORY: + case NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY: obs_encoder_set_last_error(encoder, obs_module_text("TooManySessions")); break; From e25a8b3a4f845d78bb08cdfd62a7809e0f34cf38 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 10 Sep 2024 23:22:00 +0200 Subject: [PATCH 0521/1073] obs-nvenc: Check if device index in settings object is actually set --- plugins/obs-nvenc/nvenc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/obs-nvenc/nvenc.c b/plugins/obs-nvenc/nvenc.c index d183198f6d0987..bf6d1647232054 100644 --- a/plugins/obs-nvenc/nvenc.c +++ b/plugins/obs-nvenc/nvenc.c @@ -888,13 +888,14 @@ static void *nvenc_create_base(enum codec_type codec, obs_data_t *settings, * option as it may cause issues for people. */ const int gpu = (int)obs_data_get_int(settings, "device"); + const bool gpu_set = obs_data_has_user_value(settings, "device"); #ifndef _WIN32 const bool force_tex = obs_data_get_bool(settings, "force_cuda_tex"); #else const bool force_tex = false; #endif - if (gpu != -1 && texture && !force_tex) { + if (gpu_set && gpu != -1 && texture && !force_tex) { blog(LOG_INFO, "[obs-nvenc] different GPU selected by user, falling back " "to non-texture encoder"); From 7385947780ae80a13b81111739fded121cb8919b Mon Sep 17 00:00:00 2001 From: Vainock Date: Sun, 8 Sep 2024 22:31:47 +0200 Subject: [PATCH 0522/1073] CI: Fix condition for changed files in upload locales action --- .github/workflows/scheduled.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml index d961fa92861e95..f72382378b434a 100644 --- a/.github/workflows/scheduled.yaml +++ b/.github/workflows/scheduled.yaml @@ -132,7 +132,7 @@ jobs: checkGlob: '**/en-US.ini' - name: Upload US English Language Files 🇺🇸 - if: fromJSON(steps.nightly-checks.outputs.passed) && steps.checks.outputs.passed == 'true' + if: fromJSON(steps.checks.outputs.hasChangedFiles) uses: obsproject/obs-crowdin-sync/upload@30b5446e3b5eb19595aa68a81ddf896a857302cf env: CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }} From e87593b3d426dae1c21aa3f5a8420cffaf2181e0 Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Wed, 11 Sep 2024 21:55:52 +0900 Subject: [PATCH 0523/1073] obs-nvenc: Fix reading uninitialized variable --- plugins/obs-nvenc/nvenc-helpers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/obs-nvenc/nvenc-helpers.c b/plugins/obs-nvenc/nvenc-helpers.c index 9bd89b03c6abb5..024fc7fe9a4365 100644 --- a/plugins/obs-nvenc/nvenc-helpers.c +++ b/plugins/obs-nvenc/nvenc-helpers.c @@ -310,6 +310,7 @@ static bool nvenc_check(void) os_process_args_t *args; struct dstr caps_str = {0}; config_t *config = NULL; + bool success = false; args = os_process_args_create(test_exe); @@ -344,7 +345,7 @@ static bool nvenc_check(void) goto fail; } - bool success = config_get_bool(config, "general", "nvenc_supported"); + success = config_get_bool(config, "general", "nvenc_supported"); if (!success) { const char *error = config_get_string(config, "general", "reason"); From 31385006d5442a33c3d4cf8307a3f921a13773d6 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Fri, 6 Sep 2024 07:54:57 +0200 Subject: [PATCH 0524/1073] Remove legacy_check CMake macro The macro was not removed in 72428ccd97ae032825fb3b390c0b2b50768655bf waiting for submodules to be be updated to a commit where their legacy code path is removed. --- UI/CMakeLists.txt | 2 -- UI/frontend-plugins/aja-output-ui/CMakeLists.txt | 2 -- UI/frontend-plugins/decklink-captions/CMakeLists.txt | 2 -- UI/frontend-plugins/decklink-output-ui/CMakeLists.txt | 2 -- UI/frontend-plugins/frontend-tools/CMakeLists.txt | 2 -- UI/obs-frontend-api/CMakeLists.txt | 2 -- UI/win-update/updater/CMakeLists.txt | 2 -- cmake/common/helpers_common.cmake | 7 ------- cmake/windows/architecture.cmake | 4 ---- deps/w32-pthreads/CMakeLists.txt | 2 -- libobs-opengl/CMakeLists.txt | 2 -- libobs-winrt/CMakeLists.txt | 2 -- libobs/CMakeLists.txt | 2 -- plugins/aja/CMakeLists.txt | 2 -- plugins/coreaudio-encoder/CMakeLists.txt | 2 -- plugins/decklink/CMakeLists.txt | 2 -- plugins/image-source/CMakeLists.txt | 2 -- plugins/linux-alsa/CMakeLists.txt | 2 -- plugins/linux-capture/CMakeLists.txt | 2 -- plugins/linux-jack/CMakeLists.txt | 2 -- plugins/linux-pipewire/CMakeLists.txt | 2 -- plugins/linux-pulseaudio/CMakeLists.txt | 2 -- plugins/linux-v4l2/CMakeLists.txt | 2 -- plugins/obs-ffmpeg/CMakeLists.txt | 2 -- plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt | 2 -- plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt | 2 -- plugins/obs-filters/CMakeLists.txt | 2 -- plugins/obs-libfdk/CMakeLists.txt | 2 -- plugins/obs-outputs/CMakeLists.txt | 2 -- plugins/obs-qsv11/CMakeLists.txt | 2 -- plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt | 2 -- plugins/obs-transitions/CMakeLists.txt | 2 -- plugins/obs-vst/CMakeLists.txt | 2 -- plugins/obs-webrtc/CMakeLists.txt | 2 -- plugins/obs-x264/CMakeLists.txt | 2 -- plugins/oss-audio/CMakeLists.txt | 2 -- plugins/rtmp-services/CMakeLists.txt | 2 -- plugins/sndio/CMakeLists.txt | 2 -- plugins/text-freetype2/CMakeLists.txt | 2 -- plugins/vlc-video/CMakeLists.txt | 2 -- shared/obs-scripting/CMakeLists.txt | 2 -- shared/obs-scripting/obslua/CMakeLists.txt | 2 -- shared/obs-scripting/obspython/CMakeLists.txt | 2 -- test/test-input/CMakeLists.txt | 2 -- 44 files changed, 95 deletions(-) diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 01f4ed3913c90d..d793644a084e35 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - add_subdirectory(obs-frontend-api) option(ENABLE_UI "Enable building with UI (requires Qt)" ON) diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index 3d7b2e5902705a..da8db3cada24d1 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(NOT ENABLE_AJA) target_disable(aja-output-ui) return() diff --git a/UI/frontend-plugins/decklink-captions/CMakeLists.txt b/UI/frontend-plugins/decklink-captions/CMakeLists.txt index 89a94f4a2d5eb7..5e84b2a800d7af 100644 --- a/UI/frontend-plugins/decklink-captions/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-captions/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(NOT ENABLE_DECKLINK) target_disable(decklink-captions) return() diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index a6d379185ea529..9df137b2d6b048 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(NOT ENABLE_DECKLINK) target_disable(decklink-output-ui) return() diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 444da23f4a771d..9f01a3d5721a78 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - find_package(Qt6 REQUIRED Widgets) if(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD) diff --git a/UI/obs-frontend-api/CMakeLists.txt b/UI/obs-frontend-api/CMakeLists.txt index 6a160f937bfc23..cd019c8d26a7be 100644 --- a/UI/obs-frontend-api/CMakeLists.txt +++ b/UI/obs-frontend-api/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - add_library(obs-frontend-api SHARED) add_library(OBS::frontend-api ALIAS obs-frontend-api) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index c8809809e2e383..2cba74e05d5028 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - find_package(zstd) find_package(nlohmann_json 3 REQUIRED) diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index 2fd493d34428c4..a8ff5067c21a81 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -435,13 +435,6 @@ function(check_uuid uuid_string return_value) set(${return_value} ${valid_uuid} PARENT_SCOPE) endfunction() -# legacy_check: Check if new CMake framework was not enabled and load legacy rules instead -macro(legacy_check) - if(OBS_CMAKE_VERSION VERSION_LESS 3.0.0) - message(FATAL_ERROR "CMake version changed between CMakeLists.txt.") - endif() -endmacro() - # add_obs_plugin: Add plugin subdirectory if host platform is in specified list of supported platforms and architectures function(add_obs_plugin target) set(options WITH_MESSAGE) diff --git a/cmake/windows/architecture.cmake b/cmake/windows/architecture.cmake index 941e6697e3c422..4b8a34f03a7b32 100644 --- a/cmake/windows/architecture.cmake +++ b/cmake/windows/architecture.cmake @@ -25,10 +25,6 @@ if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) ) endif() else() - # legacy_check: Stub macro for child architecture builds - macro(legacy_check) - endmacro() - # target_disable_feature: Stub macro for child architecture builds macro(target_disable_feature) endmacro() diff --git a/deps/w32-pthreads/CMakeLists.txt b/deps/w32-pthreads/CMakeLists.txt index 67dba8040220e1..40c47c2298bf17 100644 --- a/deps/w32-pthreads/CMakeLists.txt +++ b/deps/w32-pthreads/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - add_library(w32-pthreads SHARED EXCLUDE_FROM_ALL) add_library(OBS::w32-pthreads ALIAS w32-pthreads) diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt index d91495ef8b1eaf..61205f6478e344 100644 --- a/libobs-opengl/CMakeLists.txt +++ b/libobs-opengl/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - add_library(libobs-opengl SHARED) add_library(OBS::libobs-opengl ALIAS libobs-opengl) diff --git a/libobs-winrt/CMakeLists.txt b/libobs-winrt/CMakeLists.txt index 09daed3a6d994f..1926b5a56901c9 100644 --- a/libobs-winrt/CMakeLists.txt +++ b/libobs-winrt/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - add_library(libobs-winrt-headers INTERFACE) add_library(OBS::winrt-headers ALIAS libobs-winrt-headers) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index d6d91567f3f716..828a576ead6348 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - include(cmake/obs-version.cmake) if(OS_WINDOWS AND NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) diff --git a/plugins/aja/CMakeLists.txt b/plugins/aja/CMakeLists.txt index ea9e8c6320b80a..fdb96f76f5651f 100644 --- a/plugins/aja/CMakeLists.txt +++ b/plugins/aja/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_AJA "Build OBS with aja support" ON) if(NOT ENABLE_AJA) diff --git a/plugins/coreaudio-encoder/CMakeLists.txt b/plugins/coreaudio-encoder/CMakeLists.txt index 230035eb0f505f..5eac410b297e86 100644 --- a/plugins/coreaudio-encoder/CMakeLists.txt +++ b/plugins/coreaudio-encoder/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - if(OS_WINDOWS) option(ENABLE_COREAUDIO_ENCODER "Enable building with CoreAudio encoder (Windows)" ON) if(NOT ENABLE_COREAUDIO_ENCODER) diff --git a/plugins/decklink/CMakeLists.txt b/plugins/decklink/CMakeLists.txt index 130f3e90b43010..43e5078c60cbf6 100644 --- a/plugins/decklink/CMakeLists.txt +++ b/plugins/decklink/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_DECKLINK "Build OBS with Decklink support" ON) if(NOT ENABLE_DECKLINK) target_disable(decklink) diff --git a/plugins/image-source/CMakeLists.txt b/plugins/image-source/CMakeLists.txt index 07fa2ad68dc2a2..7a6edc07a90ee0 100644 --- a/plugins/image-source/CMakeLists.txt +++ b/plugins/image-source/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - add_library(image-source MODULE) add_library(OBS::image-source ALIAS image-source) diff --git a/plugins/linux-alsa/CMakeLists.txt b/plugins/linux-alsa/CMakeLists.txt index caafd18b48b1ae..b90147da4acb64 100644 --- a/plugins/linux-alsa/CMakeLists.txt +++ b/plugins/linux-alsa/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_ALSA "Build OBS with ALSA support" ON) if(NOT ENABLE_ALSA) diff --git a/plugins/linux-capture/CMakeLists.txt b/plugins/linux-capture/CMakeLists.txt index b5c7f0b9d49738..55415b2724b149 100644 --- a/plugins/linux-capture/CMakeLists.txt +++ b/plugins/linux-capture/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - find_package(X11 REQUIRED) find_package( diff --git a/plugins/linux-jack/CMakeLists.txt b/plugins/linux-jack/CMakeLists.txt index a3a101b6f29f07..65aed52e49a2e3 100644 --- a/plugins/linux-jack/CMakeLists.txt +++ b/plugins/linux-jack/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_JACK "Build OBS with JACK support" OFF) if(NOT ENABLE_JACK) target_disable(linux-jack) diff --git a/plugins/linux-pipewire/CMakeLists.txt b/plugins/linux-pipewire/CMakeLists.txt index 75e772f3fb5a18..5f93636d524c0f 100644 --- a/plugins/linux-pipewire/CMakeLists.txt +++ b/plugins/linux-pipewire/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_PIPEWIRE "Enable PipeWire support" ON) if(NOT ENABLE_PIPEWIRE) target_disable(linux-pipewire) diff --git a/plugins/linux-pulseaudio/CMakeLists.txt b/plugins/linux-pulseaudio/CMakeLists.txt index 43676c0254aadb..6e11f8b984a306 100644 --- a/plugins/linux-pulseaudio/CMakeLists.txt +++ b/plugins/linux-pulseaudio/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(NOT ENABLE_PULSEAUDIO) target_disable(linux-pulseaudio) return() diff --git a/plugins/linux-v4l2/CMakeLists.txt b/plugins/linux-v4l2/CMakeLists.txt index 9ae7b8a4bfe938..7a36555f43805a 100644 --- a/plugins/linux-v4l2/CMakeLists.txt +++ b/plugins/linux-v4l2/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_V4L2 "Build OBS with v4l2 support" ON) option(ENABLE_UDEV "Build linux-v4l2 with UDEV support" ON) diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 766f04052682ec..818b64b28fa90f 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF) option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON) diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt index 43e10c513b1889..4d275ab70bfe41 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt +++ b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_FFMPEG_MUX_DEBUG "Enable FFmpeg-mux debugging" OFF) find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat) diff --git a/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt b/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt index 991f421b68ac0d..a08b6c0d8af58b 100644 --- a/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt +++ b/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - find_package(AMF 1.4.29 REQUIRED) add_executable(obs-amf-test) diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index af07700b5bf4c5..269f9f512d9f1a 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - add_library(obs-filters MODULE) add_library(OBS::filters ALIAS obs-filters) diff --git a/plugins/obs-libfdk/CMakeLists.txt b/plugins/obs-libfdk/CMakeLists.txt index 36a69257b88dc4..f171c6c7485303 100644 --- a/plugins/obs-libfdk/CMakeLists.txt +++ b/plugins/obs-libfdk/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_LIBFDK "Enable FDK AAC support" OFF) if(NOT ENABLE_LIBFDK) target_disable(obs-libfdk) diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index 7046214362c6ae..e8b83d2fc13e87 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - find_package(MbedTLS REQUIRED) find_package(ZLIB REQUIRED) diff --git a/plugins/obs-qsv11/CMakeLists.txt b/plugins/obs-qsv11/CMakeLists.txt index d5ffd41ae725c8..7aa42585c1fd51 100644 --- a/plugins/obs-qsv11/CMakeLists.txt +++ b/plugins/obs-qsv11/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_QSV11 "Build Intel QSV11 Hardware Encoder." TRUE) if(NOT ENABLE_QSV11) target_disable_feature(obs-qsv11 "Intel QSV11 Hardware Encoder") diff --git a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt index ca77731bfa334b..668bc9cffe8ab3 100644 --- a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt +++ b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.24...3.25) -legacy_check() - add_executable(obs-qsv-test) find_package(VPL 2.6 REQUIRED) diff --git a/plugins/obs-transitions/CMakeLists.txt b/plugins/obs-transitions/CMakeLists.txt index c63b46d4164d88..6af13a2e61ffa6 100644 --- a/plugins/obs-transitions/CMakeLists.txt +++ b/plugins/obs-transitions/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - add_library(obs-transitions MODULE) add_library(OBS::transition ALIAS obs-transitions) diff --git a/plugins/obs-vst/CMakeLists.txt b/plugins/obs-vst/CMakeLists.txt index 62c9ed9e790c11..683be4e77ddaff 100644 --- a/plugins/obs-vst/CMakeLists.txt +++ b/plugins/obs-vst/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_VST "Enable building OBS with VST plugin" ON) if(NOT ENABLE_VST) diff --git a/plugins/obs-webrtc/CMakeLists.txt b/plugins/obs-webrtc/CMakeLists.txt index f6150003822b73..5a8a325ce65087 100644 --- a/plugins/obs-webrtc/CMakeLists.txt +++ b/plugins/obs-webrtc/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_WEBRTC "Enable WebRTC Output support" ON) if(NOT ENABLE_WEBRTC) target_disable(obs-webrtc) diff --git a/plugins/obs-x264/CMakeLists.txt b/plugins/obs-x264/CMakeLists.txt index e6479b4d6e19a7..010f031b5d429d 100644 --- a/plugins/obs-x264/CMakeLists.txt +++ b/plugins/obs-x264/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - find_package(Libx264 REQUIRED) if(NOT TARGET OBS::opts-parser) diff --git a/plugins/oss-audio/CMakeLists.txt b/plugins/oss-audio/CMakeLists.txt index 05b315c3c4f58f..d753466a91ae45 100644 --- a/plugins/oss-audio/CMakeLists.txt +++ b/plugins/oss-audio/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_OSS "Enable building with OSS audio support" ON) if(NOT ENABLE_OSS) diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index 1f6374ce456599..78571eb06ea489 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_SERVICE_UPDATES "Checks for service updates" ON) set( diff --git a/plugins/sndio/CMakeLists.txt b/plugins/sndio/CMakeLists.txt index c60c2a1c64f82c..0d7448a90e995c 100644 --- a/plugins/sndio/CMakeLists.txt +++ b/plugins/sndio/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_SNDIO "Build OBS with sndio support" OFF) if(NOT ENABLE_SNDIO) target_disable(sndio) diff --git a/plugins/text-freetype2/CMakeLists.txt b/plugins/text-freetype2/CMakeLists.txt index a158dd4021247e..da768e7fd2c0f4 100644 --- a/plugins/text-freetype2/CMakeLists.txt +++ b/plugins/text-freetype2/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_FREETYPE "Enable FreeType text plugin" ON) if(NOT ENABLE_FREETYPE) diff --git a/plugins/vlc-video/CMakeLists.txt b/plugins/vlc-video/CMakeLists.txt index 03a40e0b5d5834..4cce9b0275d6b1 100644 --- a/plugins/vlc-video/CMakeLists.txt +++ b/plugins/vlc-video/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - macro(check_vlc_path) find_package(PkgConfig QUIET) if(PKG_CONFIG_FOUND) diff --git a/shared/obs-scripting/CMakeLists.txt b/shared/obs-scripting/CMakeLists.txt index 305780ddeb8200..a6892194c19cbe 100644 --- a/shared/obs-scripting/CMakeLists.txt +++ b/shared/obs-scripting/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(NOT ENABLE_SCRIPTING) target_disable_feature(obs-scripting "Scripting support") return() diff --git a/shared/obs-scripting/obslua/CMakeLists.txt b/shared/obs-scripting/obslua/CMakeLists.txt index be4888db9ad5fb..d579c00e95b471 100644 --- a/shared/obs-scripting/obslua/CMakeLists.txt +++ b/shared/obs-scripting/obslua/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(POLICY CMP0078) cmake_policy(SET CMP0078 NEW) endif() diff --git a/shared/obs-scripting/obspython/CMakeLists.txt b/shared/obs-scripting/obspython/CMakeLists.txt index 2841ac9ccfe98b..072dea9209e821 100644 --- a/shared/obs-scripting/obspython/CMakeLists.txt +++ b/shared/obs-scripting/obspython/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - if(POLICY CMP0078) cmake_policy(SET CMP0078 NEW) endif() diff --git a/test/test-input/CMakeLists.txt b/test/test-input/CMakeLists.txt index 547fba00f20d9d..2e0a7f00c8d845 100644 --- a/test/test-input/CMakeLists.txt +++ b/test/test-input/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.22...3.25) -legacy_check() - option(ENABLE_TEST_INPUT "Build test sources" OFF) if(NOT ENABLE_TEST_INPUT) From 62c4a908091efecd79406136f7c6b3ea4b1ef946 Mon Sep 17 00:00:00 2001 From: Paul Gregoire Date: Fri, 9 Aug 2024 08:56:43 -0700 Subject: [PATCH 0525/1073] obs-webrtc: Add STUN support to Link header parse --- plugins/obs-webrtc/whip-output.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/obs-webrtc/whip-output.cpp b/plugins/obs-webrtc/whip-output.cpp index 8786fc16959589..8a92a3bd1afb8e 100644 --- a/plugins/obs-webrtc/whip-output.cpp +++ b/plugins/obs-webrtc/whip-output.cpp @@ -324,7 +324,8 @@ void WHIPOutput::ParseLinkHeader(std::string val, token = val.substr(0, pos); } - if (token.find(" Date: Thu, 12 Sep 2024 13:57:25 +0200 Subject: [PATCH 0527/1073] CI: Remove run-cmake-format action cmake-format was replaced with gersemi in 19d3e30a3a1a4eeecbe41bed8768e15c71e9d4ce and this action is now unused (and wouldn't work anymore anyways due to the removal of the ./build-aux/run-cmake-format script). This commit includes build-aux README fix-ups where the now nonexistent run-cmake-format script was mentioned and the run-gersemi script was misspelled. --- .github/actions/run-cmake-format/action.yaml | 59 -------------------- build-aux/README.md | 4 +- 2 files changed, 2 insertions(+), 61 deletions(-) delete mode 100644 .github/actions/run-cmake-format/action.yaml diff --git a/.github/actions/run-cmake-format/action.yaml b/.github/actions/run-cmake-format/action.yaml deleted file mode 100644 index 1a2bed3ffb1112..00000000000000 --- a/.github/actions/run-cmake-format/action.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: Run cmake-format -description: Runs cmake-format and checks for any changes introduced by it -inputs: - failCondition: - description: Controls whether failed checks also fail the workflow run - required: false - default: never - workingDirectory: - description: Working directory for checks - required: false - default: ${{ github.workspace }} -runs: - using: composite - steps: - - name: Check Runner Operating System 🏃‍♂️ - if: runner.os == 'Windows' - shell: bash - run: | - : Check Runner Operating System 🏃‍♂️ - echo "::notice::run-cmake-format action requires a macOS-based or Linux-based runner." - exit 2 - - - name: Check for Changed Files ✅ - uses: ./.github/actions/check-changes - id: checks - with: - checkGlob: "'*.cmake' '*CMakeLists.txt'" - diffFilter: 'ACM' - - - name: Install Dependencies 🛍️ - if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles) - shell: bash - run: | - : Install Dependencies 🛍️ - echo ::group::Install Dependencies - eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" - echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH - brew install --quiet zsh - echo ::endgroup:: - - - name: Run cmake-format 🎛️ - if: fromJSON(steps.checks.outputs.hasChangedFiles) - id: result - shell: zsh --no-rcs --errexit --pipefail {0} - working-directory: ${{ github.workspace }} - env: - CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }} - run: | - : Run cmake-format 🎛️ - if (( ${+RUNNER_DEBUG} )) setopt XTRACE - - print ::group::Install cmakelang - pip3 install cmakelang - print ::endgroup:: - - print ::group::Run cmake-format - local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/}) - ./build-aux/run-cmake-format --fail-${{ inputs.failCondition }} --check ${changes} - print ::endgroup:: diff --git a/build-aux/README.md b/build-aux/README.md index 490b7144ad3d0f..0b5b133f2b8ac4 100644 --- a/build-aux/README.md +++ b/build-aux/README.md @@ -3,7 +3,7 @@ This folder contains: - Various formatting scripts: - `run-clang-format` which formats C/C++/ObjC/ObjC++ files - - `run-cmake-format` which formats CMake files + - `run-gersemi` which formats CMake files - `run-swift-format` which formats Swift files - `format-manifest.py` which formats Flatpak manifest JSON files - The Flatpak manifest used to build OBS Studio @@ -23,7 +23,7 @@ Example of use: ./build-aux/run-clang-format ``` -### `run-gersemi-format` +### `run-gersemi` This script allows to check the formatting and/or format of the CMake files and requires ZSH and `gersemi` Python package. From 8b31c487bab7af8f32f3931f6bb4e87cccf4b201 Mon Sep 17 00:00:00 2001 From: Fabien Lavocat <4154532+FabienLavocat@users.noreply.github.com> Date: Thu, 12 Sep 2024 09:08:45 +0200 Subject: [PATCH 0528/1073] rtmp-services: Remove outdated Dolby Millicast locations --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 4dc438f0c6c0c2..814c56fd95750d 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 262, + "version": 263, "files": [ { "name": "services.json", - "version": 262 + "version": 263 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index ebc02b4f9eaa1a..46c73ae89b7f51 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -3468,14 +3468,6 @@ "name": "Global (RTMP)", "url": "rtmp://rtmp-auto.millicast.com:1935/v2/pub" }, - { - "name": "Amsterdam, Netherlands (RTMPS)", - "url": "rtmps://rtmp-ams-1.millicast.com:443/v2/pub" - }, - { - "name": "Amsterdam, Netherlands (RTMP)", - "url": "rtmp://rtmp-ams-1.millicast.com:1935/v2/pub" - }, { "name": "Bangalore, India (RTMPS)", "url": "rtmps://rtmp-blr-1.millicast.com:443/v2/pub" From 26d275223ae7b39baf08011992e836ee0b0678eb Mon Sep 17 00:00:00 2001 From: tytan652 Date: Thu, 29 Aug 2024 14:57:10 +0200 Subject: [PATCH 0529/1073] libobs-opengl: Relax texture format copy check SRGB and non-SRGB formats are compatible for copy. --- libobs-opengl/gl-subsystem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libobs-opengl/gl-subsystem.c b/libobs-opengl/gl-subsystem.c index d0b05193dea190..53dc8f2b4f01bc 100644 --- a/libobs-opengl/gl-subsystem.c +++ b/libobs-opengl/gl-subsystem.c @@ -978,7 +978,8 @@ void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst, goto fail; } - if (dst->format != src->format) { + if (gs_generalize_format(dst->format) != + gs_generalize_format(src->format)) { blog(LOG_ERROR, "Source and destination formats do not match"); goto fail; } From 2635cf3a2a62cd43b8a577b662d0f4ad1fee1bc4 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 3 Sep 2024 16:28:36 +0200 Subject: [PATCH 0530/1073] UI: Split global config into app and user config This introduces a split of the current single ConfigFile instance for all configuration into two separate instances. The app config instance contains system-wide settings that mainly concern the working of the app itself, whereas the user config instance contains settings actually exposed to the user for the configuration. --- UI/adv-audio-control.cpp | 2 +- UI/api-interface.cpp | 546 ++++++++++-------- UI/auth-twitch.cpp | 2 +- .../frontend-tools/scripts.cpp | 19 +- UI/log-viewer.cpp | 14 +- UI/media-controls.cpp | 4 +- UI/obs-app-theming.cpp | 44 +- UI/obs-app.cpp | 458 +++++++++------ UI/obs-app.hpp | 35 +- UI/obs-frontend-api/obs-frontend-api.cpp | 16 +- UI/obs-frontend-api/obs-frontend-api.h | 4 +- UI/obs-frontend-api/obs-frontend-internal.hpp | 6 +- UI/platform-windows.cpp | 2 +- UI/update/mac-update.cpp | 8 +- UI/update/shared-update.cpp | 8 +- UI/update/win-update.cpp | 20 +- UI/volume-control.cpp | 37 +- UI/window-basic-adv-audio.cpp | 6 +- UI/window-basic-interaction.cpp | 12 +- UI/window-basic-main-outputs.cpp | 10 +- UI/window-basic-main-transitions.cpp | 9 +- UI/window-basic-main.cpp | 337 ++++++----- UI/window-basic-main.hpp | 2 +- UI/window-basic-preview.cpp | 14 +- UI/window-basic-properties.cpp | 8 +- UI/window-basic-settings-a11y.cpp | 6 +- UI/window-basic-settings-appearance.cpp | 2 +- UI/window-basic-settings-stream.cpp | 5 +- UI/window-basic-settings.cpp | 235 ++++---- UI/window-dock-youtube-app.cpp | 8 +- UI/window-dock.cpp | 7 +- UI/window-extra-browsers.cpp | 4 +- UI/window-importer.cpp | 10 +- UI/window-permissions.cpp | 2 +- UI/window-projector.cpp | 19 +- 35 files changed, 1072 insertions(+), 849 deletions(-) diff --git a/UI/adv-audio-control.cpp b/UI/adv-audio-control.cpp index 2131419da3b75f..ce677cb83bf108 100644 --- a/UI/adv-audio-control.cpp +++ b/UI/adv-audio-control.cpp @@ -125,7 +125,7 @@ OBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) stackedWidget->addWidget(percent); VolumeType volType = (VolumeType)config_get_int( - GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType"); + App()->GetUserConfig(), "BasicWindow", "AdvAudioVolumeType"); SetVolumeWidget(volType); diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 691f507c424933..b41945f425062c 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -511,310 +511,352 @@ struct OBSStudioAPI : obs_frontend_callbacks { config_t *obs_frontend_get_profile_config(void) override { return main->basicConfig; - } + config_t *obs_frontend_get_global_config(void) override + { + blog(LOG_WARNING, + "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead."); + return App()->GetAppConfig(); + } - config_t *obs_frontend_get_global_config(void) override - { - return App()->GlobalConfig(); - } + config_t *obs_frontend_get_app_config(void) override + { + return App()->GetAppConfig(); + } - void obs_frontend_open_projector(const char *type, int monitor, - const char *geometry, - const char *name) override - { - SavedProjectorInfo proj = { - ProjectorType::Preview, - monitor, - geometry ? geometry : "", - name ? name : "", - }; - if (type) { - if (astrcmpi(type, "Source") == 0) - proj.type = ProjectorType::Source; - else if (astrcmpi(type, "Scene") == 0) - proj.type = ProjectorType::Scene; - else if (astrcmpi(type, "StudioProgram") == 0) - proj.type = ProjectorType::StudioProgram; - else if (astrcmpi(type, "Multiview") == 0) - proj.type = ProjectorType::Multiview; - } - QMetaObject::invokeMethod(main, "OpenSavedProjector", - WaitConnection(), - Q_ARG(SavedProjectorInfo *, &proj)); - } + config_t *obs_frontend_get_user_config(void) override + { + return App()->GetUserConfig(); + } - void obs_frontend_save(void) override { main->SaveProject(); } + void obs_frontend_open_projector(const char *type, int monitor, + const char *geometry, + const char *name) override + { + SavedProjectorInfo proj = { + ProjectorType::Preview, + monitor, + geometry ? geometry : "", + name ? name : "", + }; + if (type) { + if (astrcmpi(type, "Source") == 0) + proj.type = ProjectorType::Source; + else if (astrcmpi(type, "Scene") == 0) + proj.type = ProjectorType::Scene; + else if (astrcmpi(type, "StudioProgram") == 0) + proj.type = + ProjectorType::StudioProgram; + else if (astrcmpi(type, "Multiview") == 0) + proj.type = ProjectorType::Multiview; + } + QMetaObject::invokeMethod( + main, "OpenSavedProjector", WaitConnection(), + Q_ARG(SavedProjectorInfo *, &proj)); + } - void obs_frontend_defer_save_begin(void) override - { - QMetaObject::invokeMethod(main, "DeferSaveBegin"); - } + void obs_frontend_save(void) override + { + main->SaveProject(); + } - void obs_frontend_defer_save_end(void) override - { - QMetaObject::invokeMethod(main, "DeferSaveEnd"); - } + void obs_frontend_defer_save_begin(void) override + { + QMetaObject::invokeMethod(main, "DeferSaveBegin"); + } - void obs_frontend_add_save_callback(obs_frontend_save_cb callback, - void *private_data) override - { - size_t idx = - GetCallbackIdx(saveCallbacks, callback, private_data); - if (idx == (size_t)-1) - saveCallbacks.emplace_back(callback, private_data); - } + void obs_frontend_defer_save_end(void) override + { + QMetaObject::invokeMethod(main, "DeferSaveEnd"); + } - void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, - void *private_data) override - { - size_t idx = - GetCallbackIdx(saveCallbacks, callback, private_data); - if (idx == (size_t)-1) - return; + void obs_frontend_add_save_callback( + obs_frontend_save_cb callback, void *private_data) + override + { + size_t idx = GetCallbackIdx(saveCallbacks, callback, + private_data); + if (idx == (size_t)-1) + saveCallbacks.emplace_back(callback, + private_data); + } - saveCallbacks.erase(saveCallbacks.begin() + idx); - } + void obs_frontend_remove_save_callback( + obs_frontend_save_cb callback, void *private_data) + override + { + size_t idx = GetCallbackIdx(saveCallbacks, callback, + private_data); + if (idx == (size_t)-1) + return; - void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, - void *private_data) override - { - size_t idx = GetCallbackIdx(preloadCallbacks, callback, - private_data); - if (idx == (size_t)-1) - preloadCallbacks.emplace_back(callback, private_data); - } + saveCallbacks.erase(saveCallbacks.begin() + idx); + } - void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, - void *private_data) override - { - size_t idx = GetCallbackIdx(preloadCallbacks, callback, - private_data); - if (idx == (size_t)-1) - return; + void obs_frontend_add_preload_callback( + obs_frontend_save_cb callback, void *private_data) + override + { + size_t idx = GetCallbackIdx(preloadCallbacks, callback, + private_data); + if (idx == (size_t)-1) + preloadCallbacks.emplace_back(callback, + private_data); + } - preloadCallbacks.erase(preloadCallbacks.begin() + idx); - } + void obs_frontend_remove_preload_callback( + obs_frontend_save_cb callback, void *private_data) + override + { + size_t idx = GetCallbackIdx(preloadCallbacks, callback, + private_data); + if (idx == (size_t)-1) + return; - void obs_frontend_push_ui_translation( - obs_frontend_translate_ui_cb translate) override - { - App()->PushUITranslation(translate); - } + preloadCallbacks.erase(preloadCallbacks.begin() + idx); + } - void obs_frontend_pop_ui_translation(void) override - { - App()->PopUITranslation(); - } + void obs_frontend_push_ui_translation( + obs_frontend_translate_ui_cb translate) override + { + App()->PushUITranslation(translate); + } - void obs_frontend_set_streaming_service(obs_service_t *service) override - { - main->SetService(service); - } + void obs_frontend_pop_ui_translation(void) override + { + App()->PopUITranslation(); + } - obs_service_t *obs_frontend_get_streaming_service(void) override - { - return main->GetService(); - } + void obs_frontend_set_streaming_service(obs_service_t * service) + override + { + main->SetService(service); + } - void obs_frontend_save_streaming_service(void) override - { - main->SaveService(); - } + obs_service_t *obs_frontend_get_streaming_service(void) override + { + return main->GetService(); + } - bool obs_frontend_preview_program_mode_active(void) override - { - return main->IsPreviewProgramMode(); - } + void obs_frontend_save_streaming_service(void) override + { + main->SaveService(); + } - void obs_frontend_set_preview_program_mode(bool enable) override - { - main->SetPreviewProgramMode(enable); - } + bool obs_frontend_preview_program_mode_active(void) override + { + return main->IsPreviewProgramMode(); + } - void obs_frontend_preview_program_trigger_transition(void) override - { - QMetaObject::invokeMethod(main, "TransitionClicked"); - } + void obs_frontend_set_preview_program_mode(bool enable) override + { + main->SetPreviewProgramMode(enable); + } - bool obs_frontend_preview_enabled(void) override - { - return main->previewEnabled; - } + void obs_frontend_preview_program_trigger_transition(void) + override + { + QMetaObject::invokeMethod(main, "TransitionClicked"); + } - void obs_frontend_set_preview_enabled(bool enable) override - { - if (main->previewEnabled != enable) - main->EnablePreviewDisplay(enable); - } + bool obs_frontend_preview_enabled(void) override + { + return main->previewEnabled; + } - obs_source_t *obs_frontend_get_current_preview_scene(void) override - { - if (main->IsPreviewProgramMode()) { - OBSSource source = main->GetCurrentSceneSource(); - return obs_source_get_ref(source); + void obs_frontend_set_preview_enabled(bool enable) override + { + if (main->previewEnabled != enable) + main->EnablePreviewDisplay(enable); } - return nullptr; - } + obs_source_t *obs_frontend_get_current_preview_scene(void) + override + { + if (main->IsPreviewProgramMode()) { + OBSSource source = + main->GetCurrentSceneSource(); + return obs_source_get_ref(source); + } - void - obs_frontend_set_current_preview_scene(obs_source_t *scene) override - { - if (main->IsPreviewProgramMode()) { - QMetaObject::invokeMethod(main, "SetCurrentScene", - Q_ARG(OBSSource, - OBSSource(scene)), - Q_ARG(bool, false)); + return nullptr; } - } - void obs_frontend_take_screenshot(void) override - { - QMetaObject::invokeMethod(main, "Screenshot"); - } + void obs_frontend_set_current_preview_scene(obs_source_t * + scene) override + { + if (main->IsPreviewProgramMode()) { + QMetaObject::invokeMethod( + main, "SetCurrentScene", + Q_ARG(OBSSource, OBSSource(scene)), + Q_ARG(bool, false)); + } + } - void obs_frontend_take_source_screenshot(obs_source_t *source) override - { - QMetaObject::invokeMethod(main, "Screenshot", - Q_ARG(OBSSource, OBSSource(source))); - } + void obs_frontend_take_screenshot(void) override + { + QMetaObject::invokeMethod(main, "Screenshot"); + } - obs_output_t *obs_frontend_get_virtualcam_output(void) override - { - OBSOutput output = main->outputHandler->virtualCam.Get(); - return obs_output_get_ref(output); - } + void obs_frontend_take_source_screenshot(obs_source_t * source) + override + { + QMetaObject::invokeMethod(main, "Screenshot", + Q_ARG(OBSSource, + OBSSource(source))); + } - void obs_frontend_start_virtualcam(void) override - { - QMetaObject::invokeMethod(main, "StartVirtualCam"); - } + obs_output_t *obs_frontend_get_virtualcam_output(void) override + { + OBSOutput output = + main->outputHandler->virtualCam.Get(); + return obs_output_get_ref(output); + } - void obs_frontend_stop_virtualcam(void) override - { - QMetaObject::invokeMethod(main, "StopVirtualCam"); - } + void obs_frontend_start_virtualcam(void) override + { + QMetaObject::invokeMethod(main, "StartVirtualCam"); + } - bool obs_frontend_virtualcam_active(void) override - { - return os_atomic_load_bool(&virtualcam_active); - } + void obs_frontend_stop_virtualcam(void) override + { + QMetaObject::invokeMethod(main, "StopVirtualCam"); + } - void obs_frontend_reset_video(void) override { main->ResetVideo(); } + bool obs_frontend_virtualcam_active(void) override + { + return os_atomic_load_bool(&virtualcam_active); + } - void obs_frontend_open_source_properties(obs_source_t *source) override - { - QMetaObject::invokeMethod(main, "OpenProperties", - Q_ARG(OBSSource, OBSSource(source))); - } + void obs_frontend_reset_video(void) override + { + main->ResetVideo(); + } - void obs_frontend_open_source_filters(obs_source_t *source) override - { - QMetaObject::invokeMethod(main, "OpenFilters", - Q_ARG(OBSSource, OBSSource(source))); - } + void obs_frontend_open_source_properties(obs_source_t * source) + override + { + QMetaObject::invokeMethod(main, "OpenProperties", + Q_ARG(OBSSource, + OBSSource(source))); + } - void obs_frontend_open_source_interaction(obs_source_t *source) override - { - QMetaObject::invokeMethod(main, "OpenInteraction", - Q_ARG(OBSSource, OBSSource(source))); - } + void obs_frontend_open_source_filters(obs_source_t * source) + override + { + QMetaObject::invokeMethod(main, "OpenFilters", + Q_ARG(OBSSource, + OBSSource(source))); + } - void obs_frontend_open_sceneitem_edit_transform( - obs_sceneitem_t *item) override - { - QMetaObject::invokeMethod(main, "OpenEditTransform", - Q_ARG(OBSSceneItem, - OBSSceneItem(item))); - } + void obs_frontend_open_source_interaction(obs_source_t * source) + override + { + QMetaObject::invokeMethod(main, "OpenInteraction", + Q_ARG(OBSSource, + OBSSource(source))); + } - char *obs_frontend_get_current_record_output_path(void) override - { - const char *recordOutputPath = main->GetCurrentOutputPath(); + void obs_frontend_open_sceneitem_edit_transform( + obs_sceneitem_t * item) override + { + QMetaObject::invokeMethod(main, "OpenEditTransform", + Q_ARG(OBSSceneItem, + OBSSceneItem(item))); + } - return bstrdup(recordOutputPath); - } + char *obs_frontend_get_current_record_output_path(void) override + { + const char *recordOutputPath = + main->GetCurrentOutputPath(); - const char *obs_frontend_get_locale_string(const char *string) override - { - return Str(string); - } + return bstrdup(recordOutputPath); + } - bool obs_frontend_is_theme_dark(void) override - { - return App()->IsThemeDark(); - } + const char *obs_frontend_get_locale_string(const char *string) + override + { + return Str(string); + } - char *obs_frontend_get_last_recording(void) override - { - return bstrdup(main->outputHandler->lastRecordingPath.c_str()); - } + bool obs_frontend_is_theme_dark(void) override + { + return App()->IsThemeDark(); + } - char *obs_frontend_get_last_screenshot(void) override - { - return bstrdup(main->lastScreenshot.c_str()); - } + char *obs_frontend_get_last_recording(void) override + { + return bstrdup( + main->outputHandler->lastRecordingPath.c_str()); + } - char *obs_frontend_get_last_replay(void) override - { - return bstrdup(main->lastReplay.c_str()); - } + char *obs_frontend_get_last_screenshot(void) override + { + return bstrdup(main->lastScreenshot.c_str()); + } - void obs_frontend_add_undo_redo_action(const char *name, - const undo_redo_cb undo, - const undo_redo_cb redo, - const char *undo_data, - const char *redo_data, - bool repeatable) override - { - main->undo_s.add_action( - name, - [undo](const std::string &data) { undo(data.c_str()); }, - [redo](const std::string &data) { redo(data.c_str()); }, - undo_data, redo_data, repeatable); - } + char *obs_frontend_get_last_replay(void) override + { + return bstrdup(main->lastReplay.c_str()); + } - void on_load(obs_data_t *settings) override - { - for (size_t i = saveCallbacks.size(); i > 0; i--) { - auto cb = saveCallbacks[i - 1]; - cb.callback(settings, false, cb.private_data); + void obs_frontend_add_undo_redo_action( + const char *name, const undo_redo_cb undo, + const undo_redo_cb redo, const char *undo_data, + const char *redo_data, bool repeatable) override + { + main->undo_s.add_action( + name, + [undo](const std::string &data) { + undo(data.c_str()); + }, + [redo](const std::string &data) { + redo(data.c_str()); + }, + undo_data, redo_data, repeatable); } - } - void on_preload(obs_data_t *settings) override - { - for (size_t i = preloadCallbacks.size(); i > 0; i--) { - auto cb = preloadCallbacks[i - 1]; - cb.callback(settings, false, cb.private_data); + void on_load(obs_data_t * settings) override + { + for (size_t i = saveCallbacks.size(); i > 0; i--) { + auto cb = saveCallbacks[i - 1]; + cb.callback(settings, false, cb.private_data); + } } - } - void on_save(obs_data_t *settings) override - { - for (size_t i = saveCallbacks.size(); i > 0; i--) { - auto cb = saveCallbacks[i - 1]; - cb.callback(settings, true, cb.private_data); + void on_preload(obs_data_t * settings) override + { + for (size_t i = preloadCallbacks.size(); i > 0; i--) { + auto cb = preloadCallbacks[i - 1]; + cb.callback(settings, false, cb.private_data); + } } - } - void on_event(enum obs_frontend_event event) override - { - if (main->disableSaving && - event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP && - event != OBS_FRONTEND_EVENT_EXIT) - return; + void on_save(obs_data_t * settings) override + { + for (size_t i = saveCallbacks.size(); i > 0; i--) { + auto cb = saveCallbacks[i - 1]; + cb.callback(settings, true, cb.private_data); + } + } - for (size_t i = callbacks.size(); i > 0; i--) { - auto cb = callbacks[i - 1]; - cb.callback(event, cb.private_data); + void on_event(enum obs_frontend_event event) override + { + if (main->disableSaving && + event != + OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP && + event != OBS_FRONTEND_EVENT_EXIT) + return; + + for (size_t i = callbacks.size(); i > 0; i--) { + auto cb = callbacks[i - 1]; + cb.callback(event, cb.private_data); + } } - } -}; + }; -obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main) -{ - obs_frontend_callbacks *api = new OBSStudioAPI(main); - obs_frontend_set_callbacks_internal(api); - return api; -} + obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main) + { + obs_frontend_callbacks *api = new OBSStudioAPI(main); + obs_frontend_set_callbacks_internal(api); + return api; + } diff --git a/UI/auth-twitch.cpp b/UI/auth-twitch.cpp index cc391bc292d29b..7ec2bdd753646e 100644 --- a/UI/auth-twitch.cpp +++ b/UI/auth-twitch.cpp @@ -410,7 +410,7 @@ void TwitchAuth::LoadSecondaryUIPanes() stats->setVisible(false); feed->setVisible(false); } else { - uint32_t lastVersion = config_get_int(App()->GlobalConfig(), + uint32_t lastVersion = config_get_int(App()->GetAppConfig(), "General", "LastVersion"); if (lastVersion <= MAKE_SEMANTIC_VERSION(23, 0, 2)) { diff --git a/UI/frontend-plugins/frontend-tools/scripts.cpp b/UI/frontend-plugins/frontend-tools/scripts.cpp index 2906f7e7a50d55..3e2c433f161fd6 100644 --- a/UI/frontend-plugins/frontend-tools/scripts.cpp +++ b/UI/frontend-plugins/frontend-tools/scripts.cpp @@ -113,7 +113,7 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr) resize(600, 400); - config_t *global_config = obs_frontend_get_global_config(); + config_t *global_config = obs_frontend_get_user_config(); const char *geom = config_get_string(global_config, "ScriptLogWindow", "geometry"); if (geom != nullptr) { @@ -129,7 +129,7 @@ ScriptLogWindow::ScriptLogWindow() : QDialog(nullptr) ScriptLogWindow::~ScriptLogWindow() { - config_t *global_config = obs_frontend_get_global_config(); + config_t *global_config = obs_frontend_get_user_config(); config_set_string(global_config, "ScriptLogWindow", "geometry", saveGeometry().toBase64().constData()); } @@ -189,7 +189,7 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool) RefreshLists(); #if PYTHON_UI - config_t *config = obs_frontend_get_global_config(); + config_t *config = obs_frontend_get_user_config(); const char *path = config_get_string(config, "Python", "Path" ARCH_NAME); ui->pythonPath->setText(path); @@ -207,16 +207,15 @@ ScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool) QSizePolicy::Expanding); ui->propertiesLayout->addWidget(propertiesView); - config_t *global_config = obs_frontend_get_global_config(); - int row = - config_get_int(global_config, "scripts-tool", "prevScriptRow"); + config_t *user_config = obs_frontend_get_user_config(); + int row = config_get_int(user_config, "scripts-tool", "prevScriptRow"); ui->scripts->setCurrentRow(row); } ScriptsTool::~ScriptsTool() { - config_t *global_config = obs_frontend_get_global_config(); - config_set_int(global_config, "scripts-tool", "prevScriptRow", + config_t *user_config = obs_frontend_get_user_config(); + config_set_int(user_config, "scripts-tool", "prevScriptRow", ui->scripts->currentRow()); } @@ -465,7 +464,7 @@ void ScriptsTool::on_pythonPathBrowse_clicked() QByteArray array = newPath.toUtf8(); const char *path = array.constData(); - config_t *config = obs_frontend_get_global_config(); + config_t *config = obs_frontend_get_user_config(); config_set_string(config, "Python", "Path" ARCH_NAME, path); ui->pythonPath->setText(newPath); @@ -685,7 +684,7 @@ extern "C" void InitScripts() obs_module_text("Scripts")); #if PYTHON_UI - config_t *config = obs_frontend_get_global_config(); + config_t *config = obs_frontend_get_user_config(); const char *python_path = config_get_string(config, "Python", "Path" ARCH_NAME); diff --git a/UI/log-viewer.cpp b/UI/log-viewer.cpp index 322781da13d793..fca16551b58fa8 100644 --- a/UI/log-viewer.cpp +++ b/UI/log-viewer.cpp @@ -23,12 +23,12 @@ OBSLogViewer::OBSLogViewer(QWidget *parent) ui->setupUi(this); bool showLogViewerOnStartup = config_get_bool( - App()->GlobalConfig(), "LogViewer", "ShowLogStartup"); + App()->GetUserConfig(), "LogViewer", "ShowLogStartup"); ui->showStartup->setChecked(showLogViewerOnStartup); - const char *geom = config_get_string(App()->GlobalConfig(), "LogViewer", - "geometry"); + const char *geom = config_get_string(App()->GetUserConfig(), + "LogViewer", "geometry"); if (geom != nullptr) { QByteArray ba = QByteArray::fromBase64(QByteArray(geom)); @@ -40,13 +40,13 @@ OBSLogViewer::OBSLogViewer(QWidget *parent) OBSLogViewer::~OBSLogViewer() { - config_set_string(App()->GlobalConfig(), "LogViewer", "geometry", + config_set_string(App()->GetUserConfig(), "LogViewer", "geometry", saveGeometry().toBase64().constData()); } void OBSLogViewer::on_showStartup_clicked(bool checked) { - config_set_bool(App()->GlobalConfig(), "LogViewer", "ShowLogStartup", + config_set_bool(App()->GetUserConfig(), "LogViewer", "ShowLogStartup", checked); } @@ -57,7 +57,7 @@ void OBSLogViewer::InitLog() char logDir[512]; std::string path; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs")) { + if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs")) { path += logDir; path += "/"; path += App()->GetCurrentLog(); @@ -124,7 +124,7 @@ void OBSLogViewer::AddLine(int type, const QString &str) void OBSLogViewer::on_openButton_clicked() { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) + if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) return; const char *log = App()->GetCurrentLog(); diff --git a/UI/media-controls.cpp b/UI/media-controls.cpp index fddc5cbf73cfa1..0353c01f2b026f 100644 --- a/UI/media-controls.cpp +++ b/UI/media-controls.cpp @@ -67,7 +67,7 @@ MediaControls::MediaControls(QWidget *parent) connect(ui->slider, &AbsoluteSlider::sliderMoved, this, &MediaControls::AbsoluteSliderMoved); - countDownTimer = config_get_bool(App()->GlobalConfig(), "BasicWindow", + countDownTimer = config_get_bool(App()->GetUserConfig(), "BasicWindow", "MediaControlsCountdownTimer"); QAction *restartAction = new QAction(this); @@ -465,7 +465,7 @@ void MediaControls::on_durationLabel_clicked() { countDownTimer = !countDownTimer; - config_set_bool(App()->GlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "MediaControlsCountdownTimer", countDownTimer); if (MediaPaused()) diff --git a/UI/obs-app-theming.cpp b/UI/obs-app-theming.cpp index b219aadd3df4ed..037bf0160fe088 100644 --- a/UI/obs-app-theming.cpp +++ b/UI/obs-app-theming.cpp @@ -409,7 +409,6 @@ static vector ParseThemeVariables(const char *themeData) void OBSApp::FindThemes() { - string themeDir; QStringList filters; filters << "*.obt" // OBS Base Theme @@ -417,18 +416,24 @@ void OBSApp::FindThemes() << "*.oha" // OBS High-contrast Adjustment layer ; - GetDataFilePath("themes/", themeDir); - QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); - while (it.hasNext()) { - auto theme = ParseThemeMeta(it.next()); - if (theme && !themes.contains(theme->id)) - themes[theme->id] = std::move(*theme); + { + string themeDir; + GetDataFilePath("themes/", themeDir); + QDirIterator it(QString::fromStdString(themeDir), filters, + QDir::Files); + while (it.hasNext()) { + auto theme = ParseThemeMeta(it.next()); + if (theme && !themes.contains(theme->id)) + themes[theme->id] = std::move(*theme); + } } - themeDir.resize(1024); - if (GetConfigPath(themeDir.data(), themeDir.capacity(), - "obs-studio/themes/") > 0) { - QDirIterator it(QT_UTF8(themeDir.c_str()), filters, + { + const std::string themeDir = + App()->userConfigLocation.u8string() + + "/obs-studio/themes"; + + QDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files); while (it.hasNext()) { @@ -876,7 +881,8 @@ bool OBSApp::SetTheme(const QString &name) filesystem::path debugOut; char configPath[512]; - if (GetConfigPath(configPath, sizeof(configPath), filename.c_str())) { + if (GetAppConfigPath(configPath, sizeof(configPath), + filename.c_str())) { debugOut = absolute(filesystem::u8path(configPath)); filesystem::create_directories(debugOut.parent_path()); } @@ -940,7 +946,7 @@ bool OBSApp::InitTheme() } char userDir[512]; - if (GetConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) { + if (GetAppConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) { auto configSearchDir = filesystem::u8path(userDir); QDir::addSearchPath("theme", absolute(configSearchDir)); } @@ -948,7 +954,7 @@ bool OBSApp::InitTheme() /* Load list of themes and read their metadata */ FindThemes(); - if (config_get_bool(globalConfig, "Appearance", "AutoReload")) { + if (config_get_bool(userConfig, "Appearance", "AutoReload")) { /* Set up Qt file watcher to automatically reload themes */ themeWatcher = new QFileSystemWatcher(this); connect(themeWatcher.get(), &QFileSystemWatcher::fileChanged, @@ -956,19 +962,19 @@ bool OBSApp::InitTheme() } /* Migrate old theme config key */ - if (config_has_user_value(globalConfig, "General", "CurrentTheme3") && - !config_has_user_value(globalConfig, "Appearance", "Theme")) { - const char *old = config_get_string(globalConfig, "General", + if (config_has_user_value(userConfig, "General", "CurrentTheme3") && + !config_has_user_value(userConfig, "Appearance", "Theme")) { + const char *old = config_get_string(userConfig, "General", "CurrentTheme3"); if (themeMigrations.count(old)) { - config_set_string(globalConfig, "Appearance", "Theme", + config_set_string(userConfig, "Appearance", "Theme", themeMigrations[old].c_str()); } } QString themeName = - config_get_string(globalConfig, "Appearance", "Theme"); + config_get_string(userConfig, "Appearance", "Theme"); if (themeName.isEmpty() || !GetTheme(themeName)) { if (!themeName.isEmpty()) { diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index aea7b0270dc054..3417f15530b5eb 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -440,99 +441,119 @@ static void do_log(int log_level, const char *msg, va_list args, void *param) bool OBSApp::InitGlobalConfigDefaults() { - config_set_default_uint(globalConfig, "General", "MaxLogs", 10); - config_set_default_int(globalConfig, "General", "InfoIncrement", -1); - config_set_default_string(globalConfig, "General", "ProcessPriority", + config_set_default_uint(appConfig, "General", "MaxLogs", 10); + config_set_default_int(appConfig, "General", "InfoIncrement", -1); + config_set_default_string(appConfig, "General", "ProcessPriority", "Normal"); - config_set_default_bool(globalConfig, "General", "EnableAutoUpdates", + config_set_default_bool(appConfig, "General", "EnableAutoUpdates", true); - config_set_default_bool(globalConfig, "General", "ConfirmOnExit", true); - #if _WIN32 - config_set_default_string(globalConfig, "Video", "Renderer", + config_set_default_string(appConfig, "Video", "Renderer", "Direct3D 11"); #else - config_set_default_string(globalConfig, "Video", "Renderer", "OpenGL"); + config_set_default_string(appConfig, "Video", "Renderer", "OpenGL"); #endif - config_set_default_bool(globalConfig, "BasicWindow", "PreviewEnabled", +#ifdef _WIN32 + config_set_default_bool(appConfig, "Audio", "DisableAudioDucking", + true); + config_set_default_bool(appConfig, "General", "BrowserHWAccel", true); +#endif + +#ifdef __APPLE__ + config_set_default_bool(appConfig, "General", "BrowserHWAccel", true); + config_set_default_bool(appConfig, "Video", "DisableOSXVSync", true); + config_set_default_bool(appConfig, "Video", "ResetOSXVSyncOnExit", + true); +#endif + + return true; +} + +bool OBSApp::InitGlobalLocationDefaults() +{ + char path[512]; + + int len = GetAppConfigPath(path, sizeof(path), nullptr); + if (len <= 0) { + OBSErrorBox(NULL, "Unable to get global configuration path."); + return false; + } + + config_set_default_string(appConfig, "Locations", "Configuration", + path); + config_set_default_string(appConfig, "Locations", "SceneCollections", + path); + config_set_default_string(appConfig, "Locations", "Profiles", path); + + return true; +} + +void OBSApp::InitUserConfigDefaults() +{ + config_set_default_bool(userConfig, "General", "ConfirmOnExit", true); + + config_set_default_string(userConfig, "General", "HotkeyFocusType", + "NeverDisableHotkeys"); + + config_set_default_bool(userConfig, "BasicWindow", "PreviewEnabled", true); - config_set_default_bool(globalConfig, "BasicWindow", - "PreviewProgramMode", false); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "PreviewProgramMode", + false); + config_set_default_bool(userConfig, "BasicWindow", "SceneDuplicationMode", true); - config_set_default_bool(globalConfig, "BasicWindow", "SwapScenesMode", + config_set_default_bool(userConfig, "BasicWindow", "SwapScenesMode", true); - config_set_default_bool(globalConfig, "BasicWindow", "SnappingEnabled", + config_set_default_bool(userConfig, "BasicWindow", "SnappingEnabled", true); - config_set_default_bool(globalConfig, "BasicWindow", "ScreenSnapping", + config_set_default_bool(userConfig, "BasicWindow", "ScreenSnapping", true); - config_set_default_bool(globalConfig, "BasicWindow", "SourceSnapping", + config_set_default_bool(userConfig, "BasicWindow", "SourceSnapping", true); - config_set_default_bool(globalConfig, "BasicWindow", "CenterSnapping", + config_set_default_bool(userConfig, "BasicWindow", "CenterSnapping", false); - config_set_default_double(globalConfig, "BasicWindow", "SnapDistance", + config_set_default_double(userConfig, "BasicWindow", "SnapDistance", 10.0); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "SpacingHelpersEnabled", true); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "RecordWhenStreaming", false); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "KeepRecordingWhenStreamStops", false); - config_set_default_bool(globalConfig, "BasicWindow", "SysTrayEnabled", + config_set_default_bool(userConfig, "BasicWindow", "SysTrayEnabled", true); - config_set_default_bool(globalConfig, "BasicWindow", - "SysTrayWhenStarted", false); - config_set_default_bool(globalConfig, "BasicWindow", "SaveProjectors", + config_set_default_bool(userConfig, "BasicWindow", "SysTrayWhenStarted", + false); + config_set_default_bool(userConfig, "BasicWindow", "SaveProjectors", false); - config_set_default_bool(globalConfig, "BasicWindow", "ShowTransitions", + config_set_default_bool(userConfig, "BasicWindow", "ShowTransitions", true); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "ShowListboxToolbars", true); - config_set_default_bool(globalConfig, "BasicWindow", "ShowStatusBar", + config_set_default_bool(userConfig, "BasicWindow", "ShowStatusBar", true); - config_set_default_bool(globalConfig, "BasicWindow", "ShowSourceIcons", + config_set_default_bool(userConfig, "BasicWindow", "ShowSourceIcons", true); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "ShowContextToolbars", true); - config_set_default_bool(globalConfig, "BasicWindow", "StudioModeLabels", + config_set_default_bool(userConfig, "BasicWindow", "StudioModeLabels", true); - config_set_default_string(globalConfig, "General", "HotkeyFocusType", - "NeverDisableHotkeys"); - - config_set_default_bool(globalConfig, "BasicWindow", - "VerticalVolControl", false); + config_set_default_bool(userConfig, "BasicWindow", "VerticalVolControl", + false); - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "MultiviewMouseSwitch", true); - config_set_default_bool(globalConfig, "BasicWindow", - "MultiviewDrawNames", true); - - config_set_default_bool(globalConfig, "BasicWindow", - "MultiviewDrawAreas", true); - -#ifdef _WIN32 - config_set_default_bool(globalConfig, "Audio", "DisableAudioDucking", + config_set_default_bool(userConfig, "BasicWindow", "MultiviewDrawNames", true); - config_set_default_bool(globalConfig, "General", "BrowserHWAccel", - true); -#endif -#ifdef __APPLE__ - config_set_default_bool(globalConfig, "General", "BrowserHWAccel", + config_set_default_bool(userConfig, "BasicWindow", "MultiviewDrawAreas", true); - config_set_default_bool(globalConfig, "Video", "DisableOSXVSync", true); - config_set_default_bool(globalConfig, "Video", "ResetOSXVSyncOnExit", - true); -#endif - config_set_default_bool(globalConfig, "BasicWindow", + config_set_default_bool(userConfig, "BasicWindow", "MediaControlsCountdownTimer", true); - - return true; } static bool do_mkdir(const char *path) @@ -549,36 +570,38 @@ static bool MakeUserDirs() { char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/basic") <= 0) return false; if (!do_mkdir(path)) return false; - if (GetConfigPath(path, sizeof(path), "obs-studio/logs") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/logs") <= 0) return false; if (!do_mkdir(path)) return false; - if (GetConfigPath(path, sizeof(path), "obs-studio/profiler_data") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/profiler_data") <= + 0) return false; if (!do_mkdir(path)) return false; #ifdef _WIN32 - if (GetConfigPath(path, sizeof(path), "obs-studio/crashes") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/crashes") <= 0) return false; if (!do_mkdir(path)) return false; #endif #ifdef WHATSNEW_ENABLED - if (GetConfigPath(path, sizeof(path), "obs-studio/updates") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/updates") <= 0) return false; if (!do_mkdir(path)) return false; #endif - if (GetConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= + 0) return false; if (!do_mkdir(path)) return false; @@ -698,7 +721,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) if (astrcmpi(layout, "horizontaltop") == 0) { config_set_int( - globalConfig, "BasicWindow", "MultiviewLayout", + userConfig, "BasicWindow", "MultiviewLayout", static_cast( MultiviewLayout::HORIZONTAL_TOP_8_SCENES)); return true; @@ -706,7 +729,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) if (astrcmpi(layout, "horizontalbottom") == 0) { config_set_int( - globalConfig, "BasicWindow", "MultiviewLayout", + userConfig, "BasicWindow", "MultiviewLayout", static_cast( MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES)); return true; @@ -714,7 +737,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) if (astrcmpi(layout, "verticalleft") == 0) { config_set_int( - globalConfig, "BasicWindow", "MultiviewLayout", + userConfig, "BasicWindow", "MultiviewLayout", static_cast( MultiviewLayout::VERTICAL_LEFT_8_SCENES)); return true; @@ -722,7 +745,7 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) if (astrcmpi(layout, "verticalright") == 0) { config_set_int( - globalConfig, "BasicWindow", "MultiviewLayout", + userConfig, "BasicWindow", "MultiviewLayout", static_cast( MultiviewLayout::VERTICAL_RIGHT_8_SCENES)); return true; @@ -734,120 +757,212 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) bool OBSApp::InitGlobalConfig() { char path[512]; - bool changed = false; - int len = GetConfigPath(path, sizeof(path), "obs-studio/global.ini"); + int len = GetAppConfigPath(path, sizeof(path), "obs-studio/global.ini"); if (len <= 0) { return false; } - int errorcode = globalConfig.Open(path, CONFIG_OPEN_ALWAYS); + int errorcode = appConfig.Open(path, CONFIG_OPEN_ALWAYS); if (errorcode != CONFIG_SUCCESS) { OBSErrorBox(NULL, "Failed to open global.ini: %d", errorcode); return false; } + uint32_t lastVersion = + config_get_int(appConfig, "General", "LastVersion"); + + if (lastVersion < MAKE_SEMANTIC_VERSION(31, 0, 0)) { + bool migratedUserSettings = + config_get_bool(appConfig, "General", "Pre31Migrated"); + + if (!migratedUserSettings) { + bool migrated = MigrateGlobalSettings(); + + config_set_bool(appConfig, "General", "Pre31Migrated", + migrated); + config_save_safe(appConfig, "tmp", nullptr); + } + } + + InitGlobalConfigDefaults(); + InitGlobalLocationDefaults(); + + userConfigLocation = std::filesystem::u8path( + config_get_string(appConfig, "Locations", "Configuration")); + userScenesLocation = std::filesystem::u8path( + config_get_string(appConfig, "Locations", "SceneCollections")); + userProfilesLocation = std::filesystem::u8path( + config_get_string(appConfig, "Locations", "Profiles")); + + bool userConfigResult = InitUserConfig(userConfigLocation, lastVersion); + + return userConfigResult; +} + +bool OBSApp::InitUserConfig(std::filesystem::path &userConfigLocation, + uint32_t lastVersion) +{ + bool hasChanges = false; + + const std::string userConfigFile = + userConfigLocation.u8string() + "/obs-studio/user.ini"; + + int errorCode = + userConfig.Open(userConfigFile.c_str(), CONFIG_OPEN_ALWAYS); + + if (errorCode != CONFIG_SUCCESS) { + OBSErrorBox(nullptr, "Failed to open user.ini: %d", errorCode); + return false; + } + + hasChanges = MigrateLegacySettings(lastVersion); + if (!opt_starting_collection.empty()) { - string path = GetSceneCollectionFileFromName( - opt_starting_collection.c_str()); - if (!path.empty()) { - config_set_string(globalConfig, "Basic", + const OBSBasic *basic = + reinterpret_cast(GetMainWindow()); + const std::optional foundCollection = + basic->GetSceneCollectionByName( + opt_starting_collection); + + if (foundCollection) { + config_set_string(userConfig, "Basic", "SceneCollection", - opt_starting_collection.c_str()); - config_set_string(globalConfig, "Basic", - "SceneCollectionFile", path.c_str()); - changed = true; + foundCollection.value().name.c_str()); + config_set_string( + userConfig, "Basic", "SceneCollectionFile", + foundCollection.value().fileName.c_str()); + hasChanges = true; } } if (!opt_starting_profile.empty()) { - string path = - GetProfileDirFromName(opt_starting_profile.c_str()); - if (!path.empty()) { - config_set_string(globalConfig, "Basic", "Profile", - opt_starting_profile.c_str()); - config_set_string(globalConfig, "Basic", "ProfileDir", - path.c_str()); - changed = true; - } - } + const OBSBasic *basic = + reinterpret_cast(GetMainWindow()); - uint32_t lastVersion = - config_get_int(globalConfig, "General", "LastVersion"); + const std::optional foundProfile = + basic->GetProfileByName(opt_starting_profile); - if (!config_has_user_value(globalConfig, "General", "Pre19Defaults")) { - bool useOldDefaults = lastVersion && - lastVersion < - MAKE_SEMANTIC_VERSION(19, 0, 0); + if (foundProfile) { + config_set_string(userConfig, "Basic", "Profile", + foundProfile.value().name.c_str()); + config_set_string( + userConfig, "Basic", "ProfileDir", + foundProfile.value().directoryName.c_str()); - config_set_bool(globalConfig, "General", "Pre19Defaults", - useOldDefaults); - changed = true; + hasChanges = true; + } } - if (!config_has_user_value(globalConfig, "General", "Pre21Defaults")) { - bool useOldDefaults = lastVersion && - lastVersion < - MAKE_SEMANTIC_VERSION(21, 0, 0); - - config_set_bool(globalConfig, "General", "Pre21Defaults", - useOldDefaults); - changed = true; + if (hasChanges) { + config_save_safe(userConfig, "tmp", nullptr); } - if (!config_has_user_value(globalConfig, "General", "Pre23Defaults")) { - bool useOldDefaults = lastVersion && - lastVersion < - MAKE_SEMANTIC_VERSION(23, 0, 0); + InitUserConfigDefaults(); - config_set_bool(globalConfig, "General", "Pre23Defaults", - useOldDefaults); - changed = true; - } + return true; +} -#define PRE_24_1_DEFS "Pre24.1Defaults" - if (!config_has_user_value(globalConfig, "General", PRE_24_1_DEFS)) { - bool useOldDefaults = lastVersion && - lastVersion < - MAKE_SEMANTIC_VERSION(24, 1, 0); +bool OBSApp::MigrateLegacySettings(const uint32_t lastVersion) +{ + bool hasChanges = false; - config_set_bool(globalConfig, "General", PRE_24_1_DEFS, - useOldDefaults); - changed = true; + const uint32_t v19 = MAKE_SEMANTIC_VERSION(19, 0, 0); + const uint32_t v21 = MAKE_SEMANTIC_VERSION(21, 0, 0); + const uint32_t v23 = MAKE_SEMANTIC_VERSION(23, 0, 0); + const uint32_t v24 = MAKE_SEMANTIC_VERSION(24, 0, 0); + const uint32_t v24_1 = MAKE_SEMANTIC_VERSION(24, 1, 0); + + const map defaultsMap{{{v19, "Pre19Defaults"}, + {v21, "Pre21Defaults"}, + {v23, "Pre23Defaults"}, + {v24_1, "Pre24.1Defaults"}}}; + + for (auto &[version, configKey] : defaultsMap) { + if (!config_has_user_value(userConfig, "General", + configKey.c_str())) { + bool useOldDefaults = lastVersion && + lastVersion < version; + config_set_bool(userConfig, "General", + configKey.c_str(), useOldDefaults); + + hasChanges = true; + } } -#undef PRE_24_1_DEFS - if (config_has_user_value(globalConfig, "BasicWindow", + if (config_has_user_value(userConfig, "BasicWindow", "MultiviewLayout")) { const char *layout = config_get_string( - globalConfig, "BasicWindow", "MultiviewLayout"); - changed |= UpdatePre22MultiviewLayout(layout); + userConfig, "BasicWindow", "MultiviewLayout"); + + bool layoutUpdated = UpdatePre22MultiviewLayout(layout); + + hasChanges = hasChanges | layoutUpdated; } - if (lastVersion && lastVersion < MAKE_SEMANTIC_VERSION(24, 0, 0)) { + if (lastVersion && lastVersion < v24) { bool disableHotkeysInFocus = config_get_bool( - globalConfig, "General", "DisableHotkeysInFocus"); - if (disableHotkeysInFocus) - config_set_string(globalConfig, "General", + userConfig, "General", "DisableHotkeysInFocus"); + + if (disableHotkeysInFocus) { + config_set_string(userConfig, "General", "HotkeyFocusType", "DisableHotkeysInFocus"); - changed = true; + } + + hasChanges = true; + } + + return hasChanges; +} + +static constexpr string_view OBSGlobalIniPath = "/obs-studio/global.ini"; +static constexpr string_view OBSUserIniPath = "/obs-studio/user.ini"; + +bool OBSApp::MigrateGlobalSettings() +{ + char path[512]; + + int len = GetAppConfigPath(path, sizeof(path), nullptr); + if (len <= 0) { + OBSErrorBox(nullptr, + "Unable to get global configuration path."); + return false; } - if (changed) - config_save_safe(globalConfig, "tmp", nullptr); + std::string legacyConfigFileString; + legacyConfigFileString.reserve(strlen(path) + OBSGlobalIniPath.size()); + legacyConfigFileString.append(path).append(OBSGlobalIniPath); - return InitGlobalConfigDefaults(); + const std::filesystem::path legacyGlobalConfigFile = + std::filesystem::u8path(legacyConfigFileString); + + std::string configFileString; + configFileString.reserve(strlen(path) + OBSUserIniPath.size()); + configFileString.append(path).append(OBSUserIniPath); + + const std::filesystem::path userConfigFile = + std::filesystem::u8path(configFileString); + + if (std::filesystem::exists(userConfigFile)) { + OBSErrorBox( + nullptr, + "Unable to migrate global configuration - user configuration file already exists."); + return false; + } + + std::filesystem::copy(legacyGlobalConfigFile, userConfigFile); + + return true; } bool OBSApp::InitLocale() { ProfileScope("OBSApp::InitLocale"); - const char *lang = - config_get_string(globalConfig, "General", "Language"); + const char *lang = config_get_string(userConfig, "General", "Language"); bool userLocale = - config_has_user_value(globalConfig, "General", "Language"); + config_has_user_value(userConfig, "General", "Language"); if (!userLocale || !lang || lang[0] == '\0') lang = DEFAULT_LANG; @@ -963,7 +1078,7 @@ bool LoadBranchesFile(vector &out) string branchesText; BPtr branchesFilePath = - GetConfigPathPtr("obs-studio/updates/branches.json"); + GetAppConfigPathPtr("obs-studio/updates/branches.json"); QFile branchesFile(branchesFilePath.Get()); if (!branchesFile.open(QIODevice::ReadOnly)) { @@ -1069,7 +1184,7 @@ OBSApp::~OBSApp() { #ifdef _WIN32 bool disableAudioDucking = - config_get_bool(globalConfig, "Audio", "DisableAudioDucking"); + config_get_bool(userConfig, "Audio", "DisableAudioDucking"); if (disableAudioDucking) DisableAudioDucking(false); #else @@ -1080,9 +1195,9 @@ OBSApp::~OBSApp() #ifdef __APPLE__ bool vsyncDisabled = - config_get_bool(globalConfig, "Video", "DisableOSXVSync"); + config_get_bool(userConfig, "Video", "DisableOSXVSync"); bool resetVSync = - config_get_bool(globalConfig, "Video", "ResetOSXVSyncOnExit"); + config_get_bool(userConfig, "Video", "ResetOSXVSyncOnExit"); if (vsyncDisabled && resetVSync) EnableOSXVSync(true); #endif @@ -1188,40 +1303,40 @@ void OBSApp::AppInit() if (!InitTheme()) throw "Failed to load theme"; - config_set_default_string(globalConfig, "Basic", "Profile", + config_set_default_string(userConfig, "Basic", "Profile", Str("Untitled")); - config_set_default_string(globalConfig, "Basic", "ProfileDir", + config_set_default_string(userConfig, "Basic", "ProfileDir", Str("Untitled")); - config_set_default_string(globalConfig, "Basic", "SceneCollection", + config_set_default_string(userConfig, "Basic", "SceneCollection", Str("Untitled")); - config_set_default_string(globalConfig, "Basic", "SceneCollectionFile", + config_set_default_string(userConfig, "Basic", "SceneCollectionFile", Str("Untitled")); - config_set_default_bool(globalConfig, "Basic", "ConfigOnNewProfile", + config_set_default_bool(userConfig, "Basic", "ConfigOnNewProfile", true); - if (!config_has_user_value(globalConfig, "Basic", "Profile")) { - config_set_string(globalConfig, "Basic", "Profile", + if (!config_has_user_value(userConfig, "Basic", "Profile")) { + config_set_string(userConfig, "Basic", "Profile", Str("Untitled")); - config_set_string(globalConfig, "Basic", "ProfileDir", + config_set_string(userConfig, "Basic", "ProfileDir", Str("Untitled")); } - if (!config_has_user_value(globalConfig, "Basic", "SceneCollection")) { - config_set_string(globalConfig, "Basic", "SceneCollection", + if (!config_has_user_value(userConfig, "Basic", "SceneCollection")) { + config_set_string(userConfig, "Basic", "SceneCollection", Str("Untitled")); - config_set_string(globalConfig, "Basic", "SceneCollectionFile", + config_set_string(userConfig, "Basic", "SceneCollectionFile", Str("Untitled")); } #ifdef _WIN32 bool disableAudioDucking = - config_get_bool(globalConfig, "Audio", "DisableAudioDucking"); + config_get_bool(userConfig, "Audio", "DisableAudioDucking"); if (disableAudioDucking) DisableAudioDucking(true); #endif #ifdef __APPLE__ - if (config_get_bool(globalConfig, "Video", "DisableOSXVSync")) + if (config_get_bool(userConfig, "Video", "DisableOSXVSync")) EnableOSXVSync(false); #endif @@ -1237,7 +1352,7 @@ void OBSApp::AppInit() const char *OBSApp::GetRenderModule() const { const char *renderer = - config_get_string(globalConfig, "Video", "Renderer"); + config_get_string(userConfig, "Video", "Renderer"); return (astrcmpi(renderer, "Direct3D 11") == 0) ? DL_D3D11 : DL_OPENGL; } @@ -1246,7 +1361,8 @@ static bool StartupOBS(const char *locale, profiler_name_store_t *store) { char path[512]; - if (GetConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0) + if (GetAppConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= + 0) return false; return obs_startup(locale, path, store); @@ -1265,7 +1381,7 @@ void OBSApp::UpdateHotkeyFocusSetting(bool resetState) enableHotkeysOutOfFocus = true; const char *hotkeyFocusType = - config_get_string(globalConfig, "General", "HotkeyFocusType"); + config_get_string(userConfig, "General", "HotkeyFocusType"); if (astrcmpi(hotkeyFocusType, "DisableHotkeysInFocus") == 0) { enableHotkeysInFocus = false; @@ -1341,7 +1457,7 @@ bool OBSApp::OBSInit() #if defined(_WIN32) || defined(__APPLE__) bool browserHWAccel = - config_get_bool(globalConfig, "General", "BrowserHWAccel"); + config_get_bool(userConfig, "General", "BrowserHWAccel"); OBSDataAutoRelease settings = obs_data_create(); obs_data_set_bool(settings, "BrowserHWAccel", browserHWAccel); @@ -1354,7 +1470,7 @@ bool OBSApp::OBSInit() browserHWAccel ? "true" : "false"); #endif #ifdef _WIN32 - bool hideFromCapture = config_get_bool(globalConfig, "BasicWindow", + bool hideFromCapture = config_get_bool(userConfig, "BasicWindow", "HideOBSWindowsFromCapture"); blog(LOG_INFO, "Hide OBS windows from screen capture: %s", hideFromCapture ? "true" : "false"); @@ -1597,13 +1713,13 @@ static uint64_t convert_log_name(bool has_prefix, const char *name) static void delete_oldest_file(bool has_prefix, const char *location) { - BPtr logDir(GetConfigPathPtr(location)); + BPtr logDir(GetAppConfigPathPtr(location)); string oldestLog; uint64_t oldest_ts = (uint64_t)-1; struct os_dirent *entry; unsigned int maxLogs = (unsigned int)config_get_uint( - App()->GlobalConfig(), "General", "MaxLogs"); + App()->GetUserConfig(), "General", "MaxLogs"); os_dir_t *dir = os_opendir(logDir); if (dir) { @@ -1640,7 +1756,7 @@ static void delete_oldest_file(bool has_prefix, const char *location) static void get_last_log(bool has_prefix, const char *subdir_to_use, std::string &last) { - BPtr logDir(GetConfigPathPtr(subdir_to_use)); + BPtr logDir(GetAppConfigPathPtr(subdir_to_use)); struct os_dirent *entry; os_dir_t *dir = os_opendir(logDir); uint64_t highest_ts = 0; @@ -1866,7 +1982,7 @@ static void create_log_file(fstream &logFile) currentLogFile = GenerateTimeDateFilename("txt"); dst << "obs-studio/logs/" << currentLogFile.c_str(); - BPtr path(GetConfigPathPtr(dst.str().c_str())); + BPtr path(GetAppConfigPathPtr(dst.str().c_str())); #ifdef _WIN32 BPtr wpath; @@ -1925,7 +2041,7 @@ static void SaveProfilerData(const ProfilerSnapshot &snap) dst.write(LITERAL_SIZE(".csv.gz")); #undef LITERAL_SIZE - BPtr path = GetConfigPathPtr(dst.str().c_str()); + BPtr path = GetAppConfigPathPtr(dst.str().c_str()); if (!profiler_snapshot_dump_csv_gz(snap.get(), path)) blog(LOG_WARNING, "Could not save profiler data to '%s'", static_cast(path)); @@ -2160,7 +2276,7 @@ static int run_program(fstream &logFile, int argc, char *argv[]) CheckPermission(kScreenCapture); int permissionsDialogLastShown = - config_get_int(GetGlobalConfig(), "General", + config_get_int(App()->GetAppConfig(), "General", "MacOSPermissionsDialogLastShown"); if (permissionsDialogLastShown < MACOS_PERMISSIONS_DIALOG_VERSION) { @@ -2246,7 +2362,7 @@ static void main_crash_handler(const char *format, va_list args, string name = crashFilePath + "/"; name += "Crash " + GenerateTimeDateFilename("txt"); - BPtr path(GetConfigPathPtr(name.c_str())); + BPtr path(GetAppConfigPathPtr(name.c_str())); fstream file; @@ -2350,7 +2466,7 @@ static void load_debug_privilege(void) #define ALLOW_PORTABLE_MODE 0 #endif -int GetConfigPath(char *path, size_t size, const char *name) +int GetAppConfigPath(char *path, size_t size, const char *name) { #if ALLOW_PORTABLE_MODE if (portable_mode) { @@ -2367,7 +2483,7 @@ int GetConfigPath(char *path, size_t size, const char *name) #endif } -char *GetConfigPathPtr(const char *name) +char *GetAppConfigPathPtr(const char *name) { #if ALLOW_PORTABLE_MODE if (portable_mode) { @@ -2511,7 +2627,7 @@ static void check_safe_mode_sentinel(void) if (disable_shutdown_check) return; - BPtr sentinelPath = GetConfigPathPtr("obs-studio/safe_mode"); + BPtr sentinelPath = GetAppConfigPathPtr("obs-studio/safe_mode"); if (os_file_exists(sentinelPath)) { unclean_shutdown = true; return; @@ -2523,8 +2639,12 @@ static void check_safe_mode_sentinel(void) static void delete_safe_mode_sentinel(void) { - BPtr sentinelPath = GetConfigPathPtr("obs-studio/safe_mode"); +#ifndef NDEBUG + return; +#else + BPtr sentinelPath = GetAppConfigPathPtr("obs-studio/safe_mode"); os_unlink(sentinelPath); +#endif } #ifndef _WIN32 diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index ee5c24848fb4bf..308bdaeb98e471 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "window-main.hpp" #include "obs-app-theming.hpp" @@ -91,7 +92,8 @@ class OBSApp : public QApplication { private: std::string locale; - ConfigFile globalConfig; + ConfigFile appConfig; + ConfigFile userConfig; TextLookup textLookup; QPointer mainWindow; profiler_name_store_t *profilerNameStore = nullptr; @@ -112,6 +114,15 @@ class OBSApp : public QApplication { bool InitGlobalConfig(); bool InitGlobalConfigDefaults(); + bool InitGlobalLocationDefaults(); + + bool MigrateGlobalSettings(); + bool MigrateLegacySettings(uint32_t lastVersion); + + bool InitUserConfig(std::filesystem::path &userConfigLocation, + uint32_t lastVersion); + void InitUserConfigDefaults(); + bool InitLocale(); bool InitTheme(); @@ -154,7 +165,11 @@ private slots: inline QMainWindow *GetMainWindow() const { return mainWindow.data(); } - inline config_t *GlobalConfig() const { return globalConfig; } + inline config_t *GetAppConfig() const { return appConfig; } + inline config_t *GetUserConfig() const { return userConfig; } + std::filesystem::path userConfigLocation; + std::filesystem::path userScenesLocation; + std::filesystem::path userProfilesLocation; inline const char *GetLocale() const { return locale.c_str(); } @@ -235,8 +250,8 @@ public slots: void StyleChanged(); }; -int GetConfigPath(char *path, size_t size, const char *name); -char *GetConfigPathPtr(const char *name); +int GetAppConfigPath(char *path, size_t size, const char *name); +char *GetAppConfigPathPtr(const char *name); int GetProgramDataPath(char *path, size_t size, const char *name); char *GetProgramDataPathPtr(const char *name); @@ -246,11 +261,6 @@ inline OBSApp *App() return static_cast(qApp); } -inline config_t *GetGlobalConfig() -{ - return App()->GlobalConfig(); -} - std::vector> GetLocaleNames(); inline const char *Str(const char *lookup) { @@ -267,13 +277,6 @@ bool GetUnusedSceneCollectionFile(std::string &name, std::string &file); bool WindowPositionValid(QRect rect); -static inline int GetProfilePath(char *path, size_t size, const char *file) -{ - OBSMainWindow *window = - reinterpret_cast(App()->GetMainWindow()); - return window->GetProfilePath(path, size, file); -} - extern bool portable_mode; extern bool steam; extern bool safe_mode; diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 7a98d5b0fdbe3c..0e74ee376bb4be 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -394,12 +394,24 @@ config_t *obs_frontend_get_profile_config(void) : nullptr; } -config_t *obs_frontend_get_global_config(void) +config_t *obs_frontend_get_app_config(void) +{ + return !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr; +} + +config_t *obs_frontend_get_user_config(void) { - return !!callbacks_valid() ? c->obs_frontend_get_global_config() + return !!callbacks_valid() ? c->obs_frontend_get_user_config() : nullptr; } +config_t *obs_frontend_get_global_config(void) +{ + blog(LOG_WARNING, + "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead."); + return !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr; +} + void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name) { diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index 063aa4aa184a18..1282b2819674be 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -208,7 +208,9 @@ EXPORT obs_output_t *obs_frontend_get_recording_output(void); EXPORT obs_output_t *obs_frontend_get_replay_buffer_output(void); EXPORT config_t *obs_frontend_get_profile_config(void); -EXPORT config_t *obs_frontend_get_global_config(void); +OBS_DEPRECATED EXPORT config_t *obs_frontend_get_global_config(void); +EXPORT config_t *obs_frontend_get_app_config(void); +EXPORT config_t *obs_frontend_get_user_config(void); EXPORT void obs_frontend_set_streaming_service(obs_service_t *service); EXPORT obs_service_t *obs_frontend_get_streaming_service(void); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index 1e600619962f15..d5f1cf46d96ca6 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -86,7 +86,11 @@ struct obs_frontend_callbacks { virtual obs_output_t *obs_frontend_get_replay_buffer_output(void) = 0; virtual config_t *obs_frontend_get_profile_config(void) = 0; - virtual config_t *obs_frontend_get_global_config(void) = 0; + OBS_DEPRECATED virtual config_t * + obs_frontend_get_global_config(void) = 0; + + virtual config_t *obs_frontend_get_app_config(void) = 0; + virtual config_t *obs_frontend_get_user_config(void) = 0; virtual void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, diff --git a/UI/platform-windows.cpp b/UI/platform-windows.cpp index a5a03c575595d0..457d0402ef3a29 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -307,7 +307,7 @@ RunOnceMutex CheckIfAlreadyRunning(bool &already_running) char absPath[512]; *path = 0; *absPath = 0; - GetConfigPath(path, sizeof(path), ""); + GetAppConfigPath(path, sizeof(path), ""); os_get_abs_path(path, absPath, sizeof(absPath)); name = "OBSStudioPortable"; name += absPath; diff --git a/UI/update/mac-update.cpp b/UI/update/mac-update.cpp index 7ee0c073174d27..6e8965c3cb2442 100644 --- a/UI/update/mac-update.cpp +++ b/UI/update/mac-update.cpp @@ -18,8 +18,8 @@ static const char *MAC_DEFAULT_BRANCH = "stable"; bool GetBranch(std::string &selectedBranch) { - const char *config_branch = - config_get_string(GetGlobalConfig(), "General", "UpdateBranch"); + const char *config_branch = config_get_string( + App()->GetAppConfig(), "General", "UpdateBranch"); if (!config_branch) return true; @@ -70,8 +70,8 @@ try { * Validate branch selection */ if (!GetBranch(branch)) { - config_set_string(GetGlobalConfig(), "General", "UpdateBranch", - MAC_DEFAULT_BRANCH); + config_set_string(App()->GetAppConfig(), "General", + "UpdateBranch", MAC_DEFAULT_BRANCH); info(QTStr("Updater.BranchNotFound.Title"), QTStr("Updater.BranchNotFound.Text")); } diff --git a/UI/update/shared-update.cpp b/UI/update/shared-update.cpp index 844f857676ce12..cc70406b839c2a 100644 --- a/UI/update/shared-update.cpp +++ b/UI/update/shared-update.cpp @@ -147,8 +147,8 @@ std::string GetProgramGUID() /* NOTE: this is an arbitrary random number that we use to count the * number of unique OBS installations and is not associated with any * kind of identifiable information */ - const char *pguid = - config_get_string(GetGlobalConfig(), "General", "InstallGUID"); + const char *pguid = config_get_string(App()->GetAppConfig(), "General", + "InstallGUID"); std::string guid; if (pguid) guid = pguid; @@ -157,7 +157,7 @@ std::string GetProgramGUID() GenerateGUID(guid); if (!guid.empty()) - config_set_string(GetGlobalConfig(), "General", + config_set_string(App()->GetAppConfig(), "General", "InstallGUID", guid.c_str()); } @@ -220,7 +220,7 @@ bool FetchAndVerifyFile(const char *name, const char *file, const char *url, uint8_t fileHash[BLAKE2_HASH_LENGTH]; bool success; - BPtr filePath = GetConfigPathPtr(file); + BPtr filePath = GetAppConfigPathPtr(file); if (!extraHeaders.empty()) { headers.insert(headers.end(), extraHeaders.begin(), diff --git a/UI/update/win-update.cpp b/UI/update/win-update.cpp index a0263ad829c3bf..b0f19746237062 100644 --- a/UI/update/win-update.cpp +++ b/UI/update/win-update.cpp @@ -112,8 +112,8 @@ try { bool GetBranchAndUrl(string &selectedBranch, string &manifestUrl) { - const char *config_branch = - config_get_string(GetGlobalConfig(), "General", "UpdateBranch"); + const char *config_branch = config_get_string( + App()->GetAppConfig(), "General", "UpdateBranch"); if (!config_branch) return true; @@ -219,8 +219,8 @@ try { * check branch and get manifest url */ if (!GetBranchAndUrl(branch, manifestUrl)) { - config_set_string(GetGlobalConfig(), "General", "UpdateBranch", - WIN_DEFAULT_BRANCH); + config_set_string(App()->GetAppConfig(), "General", + "UpdateBranch", WIN_DEFAULT_BRANCH); info(QTStr("Updater.BranchNotFound.Title"), QTStr("Updater.BranchNotFound.Text")); } @@ -264,7 +264,7 @@ try { * skip this version if set to skip */ const char *skipUpdateVer = config_get_string( - GetGlobalConfig(), "General", "SkipUpdateVersion"); + App()->GetAppConfig(), "General", "SkipUpdateVersion"); if (!manualUpdate && !repairMode && skipUpdateVer && updateVer == skipUpdateVer) return; @@ -288,13 +288,13 @@ try { if (queryResult == OBSUpdate::No) { if (!manualUpdate) { long long t = (long long)time(nullptr); - config_set_int(GetGlobalConfig(), "General", + config_set_int(App()->GetAppConfig(), "General", "LastUpdateCheck", t); } return; } else if (queryResult == OBSUpdate::Skip) { - config_set_string(GetGlobalConfig(), "General", + config_set_string(App()->GetAppConfig(), "General", "SkipUpdateVersion", updateVer.c_str()); return; @@ -314,7 +314,7 @@ try { * execute updater */ BPtr updateFilePath = - GetConfigPathPtr("obs-studio\\updates\\updater.exe"); + GetAppConfigPathPtr("obs-studio\\updates\\updater.exe"); BPtr wUpdateFilePath; size_t size = os_utf8_to_wcs_ptr(updateFilePath, 0, &wUpdateFilePath); @@ -366,8 +366,8 @@ try { /* force OBS to perform another update check immediately after updating * in case of issues with the new version */ - config_set_int(GetGlobalConfig(), "General", "LastUpdateCheck", 0); - config_set_string(GetGlobalConfig(), "General", "SkipUpdateVersion", + config_set_int(App()->GetAppConfig(), "General", "LastUpdateCheck", 0); + config_set_string(App()->GetAppConfig(), "General", "SkipUpdateVersion", "0"); QMetaObject::invokeMethod(App()->GetMainWindow(), "close"); diff --git a/UI/volume-control.cpp b/UI/volume-control.cpp index 0111858ab1671f..25ad2048404bd3 100644 --- a/UI/volume-control.cpp +++ b/UI/volume-control.cpp @@ -61,9 +61,10 @@ static void ShowUnassignedWarning(const char *name) msgbox.exec(); if (cb->isChecked()) { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutUnassignedSources", true); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", + nullptr); } }; @@ -150,7 +151,7 @@ void VolControl::SetMuted(bool) mute->setCheckState(Qt::PartiallyChecked); /* Show notice about the source no being assigned to any tracks */ bool has_shown_warning = - config_get_bool(App()->GlobalConfig(), "General", + config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutUnassignedSources"); if (!has_shown_warning) ShowUnassignedWarning(obs_source_get_name(source)); @@ -459,10 +460,10 @@ void VolumeMeter::setBackgroundNominalColor(QColor c) { p_backgroundNominalColor = std::move(c); - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { backgroundNominalColor = color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "MixerGreen")); + App()->GetUserConfig(), "Accessibility", "MixerGreen")); } else { backgroundNominalColor = p_backgroundNominalColor; } @@ -487,10 +488,11 @@ void VolumeMeter::setBackgroundWarningColor(QColor c) { p_backgroundWarningColor = std::move(c); - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { - backgroundWarningColor = color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "MixerYellow")); + backgroundWarningColor = color_from_int( + config_get_int(App()->GetUserConfig(), "Accessibility", + "MixerYellow")); } else { backgroundWarningColor = p_backgroundWarningColor; } @@ -515,10 +517,10 @@ void VolumeMeter::setBackgroundErrorColor(QColor c) { p_backgroundErrorColor = std::move(c); - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { backgroundErrorColor = color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "MixerRed")); + App()->GetUserConfig(), "Accessibility", "MixerRed")); } else { backgroundErrorColor = p_backgroundErrorColor; } @@ -543,10 +545,10 @@ void VolumeMeter::setForegroundNominalColor(QColor c) { p_foregroundNominalColor = std::move(c); - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { foregroundNominalColor = color_from_int( - config_get_int(GetGlobalConfig(), "Accessibility", + config_get_int(App()->GetUserConfig(), "Accessibility", "MixerGreenActive")); } else { foregroundNominalColor = p_foregroundNominalColor; @@ -572,10 +574,10 @@ void VolumeMeter::setForegroundWarningColor(QColor c) { p_foregroundWarningColor = std::move(c); - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { foregroundWarningColor = color_from_int( - config_get_int(GetGlobalConfig(), "Accessibility", + config_get_int(App()->GetUserConfig(), "Accessibility", "MixerYellowActive")); } else { foregroundWarningColor = p_foregroundWarningColor; @@ -601,10 +603,11 @@ void VolumeMeter::setForegroundErrorColor(QColor c) { p_foregroundErrorColor = std::move(c); - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { - foregroundErrorColor = color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "MixerRedActive")); + foregroundErrorColor = color_from_int( + config_get_int(App()->GetUserConfig(), "Accessibility", + "MixerRedActive")); } else { foregroundErrorColor = p_foregroundErrorColor; } diff --git a/UI/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp index cc3cb3aaf848d4..b93fe3b14e66cb 100644 --- a/UI/window-basic-adv-audio.cpp +++ b/UI/window-basic-adv-audio.cpp @@ -24,7 +24,7 @@ OBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) sigs.emplace_back(sh, "source_deactivate", OBSSourceRemoved, this); VolumeType volType = (VolumeType)config_get_int( - GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType"); + App()->GetUserConfig(), "BasicWindow", "AdvAudioVolumeType"); if (volType == VolumeType::Percent) ui->usePercent->setChecked(true); @@ -140,8 +140,8 @@ void OBSBasicAdvAudio::on_usePercent_toggled(bool checked) for (size_t i = 0; i < controls.size(); i++) controls[i]->SetVolumeWidget(type); - config_set_int(GetGlobalConfig(), "BasicWindow", "AdvAudioVolumeType", - (int)type); + config_set_int(App()->GetUserConfig(), "BasicWindow", + "AdvAudioVolumeType", (int)type); } void OBSBasicAdvAudio::on_activeOnly_toggled(bool checked) diff --git a/UI/window-basic-interaction.cpp b/UI/window-basic-interaction.cpp index 716cbf24176fe0..b51b0f1f324778 100644 --- a/UI/window-basic-interaction.cpp +++ b/UI/window-basic-interaction.cpp @@ -44,10 +44,10 @@ OBSBasicInteraction::OBSBasicInteraction(QWidget *parent, OBSSource source_) OBSBasicInteraction::SourceRenamed, this), eventFilter(BuildEventFilter()) { - int cx = (int)config_get_int(App()->GlobalConfig(), "InteractionWindow", - "cx"); - int cy = (int)config_get_int(App()->GlobalConfig(), "InteractionWindow", - "cy"); + int cx = (int)config_get_int(App()->GetUserConfig(), + "InteractionWindow", "cx"); + int cy = (int)config_get_int(App()->GetUserConfig(), + "InteractionWindow", "cy"); Qt::WindowFlags flags = windowFlags(); Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; @@ -166,9 +166,9 @@ void OBSBasicInteraction::closeEvent(QCloseEvent *event) if (!event->isAccepted()) return; - config_set_int(App()->GlobalConfig(), "InteractionWindow", "cx", + config_set_int(App()->GetAppConfig(), "InteractionWindow", "cx", width()); - config_set_int(App()->GlobalConfig(), "InteractionWindow", "cy", + config_set_int(App()->GetAppConfig(), "InteractionWindow", "cy", height()); obs_display_remove_draw_callback(ui->preview->GetDisplay(), diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index aad5fcfb85dfce..93ab1d7e33377e 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1246,8 +1246,9 @@ bool SimpleOutput::IsVodTrackEnabled(obs_service_t *service) config_get_bool(main->Config(), "SimpleOutput", "UseAdvanced"); bool enable = config_get_bool(main->Config(), "SimpleOutput", "VodTrackEnabled"); - bool enableForCustomServer = config_get_bool( - GetGlobalConfig(), "General", "EnableCustomServerVodTrack"); + bool enableForCustomServer = + config_get_bool(App()->GetUserConfig(), "General", + "EnableCustomServerVodTrack"); OBSDataAutoRelease settings = obs_service_get_settings(service); const char *name = obs_data_get_string(settings, "service"); @@ -2252,8 +2253,9 @@ AdvancedOutput::VodTrackMixerIdx(obs_service_t *service) config_get_bool(main->Config(), "AdvOut", "VodTrackEnabled"); int vodTrackIndex = config_get_int(main->Config(), "AdvOut", "VodTrackIndex"); - bool enableForCustomServer = config_get_bool( - GetGlobalConfig(), "General", "EnableCustomServerVodTrack"); + bool enableForCustomServer = + config_get_bool(App()->GetUserConfig(), "General", + "EnableCustomServerVodTrack"); const char *id = obs_service_get_id(service); if (strcmp(id, "rtmp_custom") == 0) { diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 5a6a58a021935d..6b96aba44d6c7c 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -1839,10 +1839,11 @@ int OBSBasic::GetOverrideTransitionDuration(OBSSource source) void OBSBasic::UpdatePreviewProgramIndicators() { - bool labels = previewProgramMode ? config_get_bool(GetGlobalConfig(), - "BasicWindow", - "StudioModeLabels") - : false; + bool labels = previewProgramMode + ? config_get_bool(App()->GetUserConfig(), + "BasicWindow", + "StudioModeLabels") + : false; ui->previewLabel->setVisible(labels); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 3e0d9b17e46639..19c0481af8af42 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -191,11 +191,11 @@ static void AddExtraModulePaths() int ret = GetProgramDataPath(base_module_dir, sizeof(base_module_dir), "obs-studio/plugins/%module%"); #elif defined(__APPLE__) - int ret = GetConfigPath(base_module_dir, sizeof(base_module_dir), - "obs-studio/plugins/%module%.plugin"); + int ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir), + "obs-studio/plugins/%module%.plugin"); #else - int ret = GetConfigPath(base_module_dir, sizeof(base_module_dir), - "obs-studio/plugins/%module%"); + int ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir), + "obs-studio/plugins/%module%"); #endif if (ret <= 0) @@ -219,8 +219,8 @@ static void AddExtraModulePaths() /* Legacy User Application Support Search Path */ char user_legacy_module_dir[PATH_MAX]; - GetConfigPath(user_legacy_module_dir, sizeof(user_legacy_module_dir), - "obs-studio/plugins/%module%"); + GetAppConfigPath(user_legacy_module_dir, sizeof(user_legacy_module_dir), + "obs-studio/plugins/%module%"); std::string path_user_legacy = user_legacy_module_dir; obs_add_module_path((path_user_legacy + "/bin").c_str(), (path_user_legacy + "/data").c_str()); @@ -446,7 +446,7 @@ OBSBasic::OBSBasic(QWidget *parent) ui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false); ui->sources->setAttribute(Qt::WA_MacShowFocusRect, false); - bool sceneGrid = config_get_bool(App()->GlobalConfig(), "BasicWindow", + bool sceneGrid = config_get_bool(App()->GetUserConfig(), "BasicWindow", "gridMode"); ui->scenes->SetGridMode(sceneGrid); @@ -602,7 +602,7 @@ OBSBasic::OBSBasic(QWidget *parent) QPoint curPos; //restore parent window geometry - const char *geometry = config_get_string(App()->GlobalConfig(), + const char *geometry = config_get_string(App()->GetUserConfig(), "BasicWindow", "geometry"); if (geometry != NULL) { QByteArray byteArray = @@ -726,7 +726,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, const char *programName = obs_source_get_name(curProgramScene); const char *sceneCollection = config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollection"); + App()->GetUserConfig(), "Basic", "SceneCollection"); obs_data_set_string(saveData, "current_scene", sceneName); obs_data_set_string(saveData, "current_program_scene", programName); @@ -1244,12 +1244,16 @@ void OBSBasic::Load(const char *file, bool remigrate) } } - config_set_string(App()->GlobalConfig(), "Basic", + config_set_string(App()->GetUserConfig(), "Basic", "SceneCollection", name.c_str()); - config_set_string(App()->GlobalConfig(), "Basic", + config_set_string(App()->GetUserConfig(), "Basic", "SceneCollectionFile", name.c_str()); blog(LOG_INFO, "No scene file found, creating default scene"); - CreateDefaultScene(true); + + bool hasFirstRun = config_get_bool(App()->GetUserConfig(), + "General", "FirstRun"); + + CreateDefaultScene(!hasFirstRun); SaveProject(); return; } @@ -1342,7 +1346,7 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate) transitionName = obs_source_get_name(fadeTransition); const char *curSceneCollection = config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollection"); + App()->GetUserConfig(), "Basic", "SceneCollection"); obs_data_set_default_string(data, "name", curSceneCollection); @@ -1475,8 +1479,8 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate) /* ------------------- */ - bool projectorSave = config_get_bool(GetGlobalConfig(), "BasicWindow", - "SaveProjectors"); + bool projectorSave = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "SaveProjectors"); if (projectorSave) { OBSDataArrayAutoRelease savedProjectors = @@ -1494,10 +1498,10 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate) std::string file_base = strrchr(file, '/') + 1; file_base.erase(file_base.size() - 5, 5); - config_set_string(App()->GlobalConfig(), "Basic", "SceneCollection", + config_set_string(App()->GetUserConfig(), "Basic", "SceneCollection", name); - config_set_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile", - file_base.c_str()); + config_set_string(App()->GetUserConfig(), "Basic", + "SceneCollectionFile", file_base.c_str()); OBSDataArrayAutoRelease quickTransitionData = obs_data_get_array(data, "quick_transitions"); @@ -1733,7 +1737,7 @@ bool OBSBasic::InitBasicConfigDefaults() cy *= devicePixelRatioF(); bool oldResolutionDefaults = config_get_bool( - App()->GlobalConfig(), "General", "Pre19Defaults"); + App()->GetUserConfig(), "General", "Pre19Defaults"); /* use 1920x1080 for new default base res if main monitor is above * 1920x1080, but don't apply for people from older builds -- only to @@ -1774,7 +1778,7 @@ bool OBSBasic::InitBasicConfigDefaults() /* ----------------------------------------------------- */ /* set twitch chat extensions to "both" if prev version */ /* is under 24.1 */ - if (config_get_bool(GetGlobalConfig(), "General", "Pre24.1Defaults") && + if (config_get_bool(App()->GetUserConfig(), "General", !config_has_user_value(basicConfig, "Twitch", "AddonChoice")) { config_set_int(basicConfig, "Twitch", "AddonChoice", 3); changed = true; @@ -2036,7 +2040,7 @@ extern bool EncoderAvailable(const char *encoder); void OBSBasic::InitBasicConfigDefaults2() { - bool oldEncDefaults = config_get_bool(App()->GlobalConfig(), "General", + bool oldEncDefaults = config_get_bool(App()->GetUserConfig(), "General", "Pre23Defaults"); bool useNV = EncoderAvailable("ffmpeg_nvenc") && !oldEncDefaults; @@ -2370,14 +2374,14 @@ void OBSBasic::OBSInit() InitPrimitives(); sceneDuplicationMode = config_get_bool( - App()->GlobalConfig(), "BasicWindow", "SceneDuplicationMode"); - swapScenesMode = config_get_bool(App()->GlobalConfig(), "BasicWindow", + App()->GetUserConfig(), "BasicWindow", "SceneDuplicationMode"); + swapScenesMode = config_get_bool(App()->GetUserConfig(), "BasicWindow", "SwapScenesMode"); editPropertiesMode = config_get_bool( - App()->GlobalConfig(), "BasicWindow", "EditPropertiesMode"); + App()->GetUserConfig(), "BasicWindow", "EditPropertiesMode"); if (!opt_studio_mode) { - SetPreviewProgramMode(config_get_bool(App()->GlobalConfig(), + SetPreviewProgramMode(config_get_bool(App()->GetUserConfig(), "BasicWindow", "PreviewProgramMode")); } else { @@ -2385,14 +2389,14 @@ void OBSBasic::OBSInit() opt_studio_mode = false; } -#define SET_VISIBILITY(name, control) \ - do { \ - if (config_has_user_value(App()->GlobalConfig(), \ - "BasicWindow", name)) { \ - bool visible = config_get_bool(App()->GlobalConfig(), \ - "BasicWindow", name); \ - ui->control->setChecked(visible); \ - } \ +#define SET_VISIBILITY(name, control) \ + do { \ + if (config_has_user_value(App()->GetUserConfig(), \ + "BasicWindow", name)) { \ + bool visible = config_get_bool(App()->GetUserConfig(), \ + "BasicWindow", name); \ + ui->control->setChecked(visible); \ + } \ } while (false) SET_VISIBILITY("ShowListboxToolbars", toggleListboxToolbars); @@ -2400,11 +2404,11 @@ void OBSBasic::OBSInit() #undef SET_VISIBILITY bool sourceIconsVisible = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ShowSourceIcons"); + App()->GetUserConfig(), "BasicWindow", "ShowSourceIcons"); ui->toggleSourceIcons->setChecked(sourceIconsVisible); bool contextVisible = config_get_bool( - App()->GlobalConfig(), "BasicWindow", "ShowContextToolbars"); + App()->GetUserConfig(), "BasicWindow", "ShowContextToolbars"); ui->toggleContextBar->setChecked(contextVisible); ui->contextContainer->setVisible(contextVisible); if (contextVisible) @@ -2420,7 +2424,7 @@ void OBSBasic::OBSInit() loaded = true; - previewEnabled = config_get_bool(App()->GlobalConfig(), "BasicWindow", + previewEnabled = config_get_bool(App()->GetUserConfig(), "BasicWindow", "PreviewEnabled"); if (!previewEnabled && !IsPreviewProgramMode()) @@ -2449,10 +2453,10 @@ void OBSBasic::OBSInit() /* Show the main window, unless the tray icon isn't available * or neither the setting nor flag for starting minimized is set. */ - bool sysTrayEnabled = config_get_bool(App()->GlobalConfig(), + bool sysTrayEnabled = config_get_bool(App()->GetUserConfig(), "BasicWindow", "SysTrayEnabled"); bool sysTrayWhenStarted = config_get_bool( - App()->GlobalConfig(), "BasicWindow", "SysTrayWhenStarted"); + App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted"); bool hideWindowOnStart = QSystemTrayIcon::isSystemTrayAvailable() && sysTrayEnabled && (opt_minimize_tray || sysTrayWhenStarted); @@ -2464,8 +2468,8 @@ void OBSBasic::OBSInit() show(); #endif - bool alwaysOnTop = config_get_bool(App()->GlobalConfig(), "BasicWindow", - "AlwaysOnTop"); + bool alwaysOnTop = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "AlwaysOnTop"); #ifdef ENABLE_WAYLAND bool isWayland = obs_get_nix_platform() == OBS_NIX_PLATFORM_WAYLAND; @@ -2522,7 +2526,7 @@ void OBSBasic::OBSInit() #endif const char *dockStateStr = config_get_string( - App()->GlobalConfig(), "BasicWindow", "DockState"); + App()->GetUserConfig(), "BasicWindow", "DockState"); if (!dockStateStr) { on_resetDocks_triggered(true); @@ -2533,28 +2537,29 @@ void OBSBasic::OBSInit() on_resetDocks_triggered(true); } - bool pre23Defaults = config_get_bool(App()->GlobalConfig(), "General", + bool pre23Defaults = config_get_bool(App()->GetUserConfig(), "General", "Pre23Defaults"); if (pre23Defaults) { bool resetDockLock23 = config_get_bool( - App()->GlobalConfig(), "General", "ResetDockLock23"); + App()->GetUserConfig(), "General", "ResetDockLock23"); if (!resetDockLock23) { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "ResetDockLock23", true); - config_remove_value(App()->GlobalConfig(), + config_remove_value(App()->GetUserConfig(), "BasicWindow", "DocksLocked"); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", + nullptr); } } - bool docksLocked = config_get_bool(App()->GlobalConfig(), "BasicWindow", - "DocksLocked"); + bool docksLocked = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "DocksLocked"); on_lockDocks_toggled(docksLocked); ui->lockDocks->blockSignals(true); ui->lockDocks->setChecked(docksLocked); ui->lockDocks->blockSignals(false); - bool sideDocks = config_get_bool(App()->GlobalConfig(), "BasicWindow", + bool sideDocks = config_get_bool(App()->GetUserConfig(), "BasicWindow", "SideDocks"); on_sideDocks_toggled(sideDocks); ui->sideDocks->blockSignals(true); @@ -2569,15 +2574,15 @@ void OBSBasic::OBSInit() disableColorSpaceConversion(this); #endif - bool has_last_version = config_has_user_value(App()->GlobalConfig(), + bool has_last_version = config_has_user_value(App()->GetUserConfig(), "General", "LastVersion"); bool first_run = - config_get_bool(App()->GlobalConfig(), "General", "FirstRun"); + config_get_bool(App()->GetUserConfig(), "General", "FirstRun"); if (!first_run) { - config_set_bool(App()->GlobalConfig(), "General", "FirstRun", + config_set_bool(App()->GetUserConfig(), "General", "FirstRun", true); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); } if (!first_run && !has_last_version && !Active()) @@ -2587,18 +2592,18 @@ void OBSBasic::OBSInit() #if (defined(_WIN32) || defined(__APPLE__)) && \ (OBS_RELEASE_CANDIDATE > 0 || OBS_BETA > 0) /* Automatically set branch to "beta" the first time a pre-release build is run. */ - if (!config_get_bool(App()->GlobalConfig(), "General", + if (!config_get_bool(App()->GetUserConfig(), "General", "AutoBetaOptIn")) { - config_set_string(App()->GlobalConfig(), "General", + config_set_string(App()->GetUserConfig(), "General", "UpdateBranch", "beta"); - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "AutoBetaOptIn", true); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); } #endif TimedCheckForUpdates(); - ToggleMixerLayout(config_get_bool(App()->GlobalConfig(), "BasicWindow", + ToggleMixerLayout(config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); if (config_get_bool(basicConfig, "General", "OpenStatsOnStartup")) @@ -2707,7 +2712,7 @@ void OBSBasic::OnFirstLoad() Auth::Load(); bool showLogViewerOnStartup = config_get_bool( - App()->GlobalConfig(), "LogViewer", "ShowLogStartup"); + App()->GetUserConfig(), "LogViewer", "ShowLogStartup"); if (showLogViewerOnStartup) on_actionViewCurrentLog_triggered(); @@ -2775,28 +2780,28 @@ void OBSBasic::ReceivedIntroJson(const QString &text) constexpr uint64_t currentVersion = (uint64_t)LIBOBS_API_VER << 16ULL | OBS_RELEASE_CANDIDATE << 8ULL | OBS_BETA; - uint64_t lastVersion = config_get_uint(App()->GlobalConfig(), "General", + uint64_t lastVersion = config_get_uint(App()->GetAppConfig(), "General", lastInfoVersion); int current_version_increment = -1; if ((lastVersion & ~0xFFFF0000ULL) < (currentVersion & ~0xFFFF0000ULL)) { - config_set_int(App()->GlobalConfig(), "General", + config_set_int(App()->GetAppConfig(), "General", "InfoIncrement", -1); - config_set_uint(App()->GlobalConfig(), "General", + config_set_uint(App()->GetAppConfig(), "General", lastInfoVersion, currentVersion); } else { current_version_increment = config_get_int( - App()->GlobalConfig(), "General", "InfoIncrement"); + App()->GetAppConfig(), "General", "InfoIncrement"); } if (info_increment <= current_version_increment) { return; } - config_set_int(App()->GlobalConfig(), "General", "InfoIncrement", + config_set_int(App()->GetAppConfig(), "General", "InfoIncrement", info_increment); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetAppConfig(), "tmp", nullptr); cef->init_browser(); @@ -3284,26 +3289,27 @@ OBSBasic::~OBSBasic() * expect or want it to. */ QApplication::sendPostedEvents(nullptr); - config_set_int(App()->GlobalConfig(), "General", "LastVersion", + config_set_int(App()->GetAppConfig(), "General", "LastVersion", LIBOBS_API_VER); + config_save_safe(App()->GetAppConfig(), "tmp", nullptr); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "PreviewEnabled", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "PreviewEnabled", previewEnabled); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "AlwaysOnTop", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "AlwaysOnTop", ui->actionAlwaysOnTop->isChecked()); - config_set_bool(App()->GlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SceneDuplicationMode", sceneDuplicationMode); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "SwapScenesMode", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SwapScenesMode", swapScenesMode); - config_set_bool(App()->GlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "EditPropertiesMode", editPropertiesMode); - config_set_bool(App()->GlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "PreviewProgramMode", IsPreviewProgramMode()); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "DocksLocked", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "DocksLocked", ui->lockDocks->isChecked()); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "SideDocks", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SideDocks", ui->sideDocks->isChecked()); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); #ifdef BROWSER_AVAILABLE DestroyPanelCookieManager(); @@ -4027,7 +4033,7 @@ void OBSBasic::VolControlContextMenu() QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); toggleControlLayoutAction.setCheckable(true); toggleControlLayoutAction.setChecked(config_get_bool( - GetGlobalConfig(), "BasicWindow", "VerticalVolControl")); + App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); /* ------------------- */ @@ -4126,7 +4132,7 @@ void OBSBasic::StackedMixerAreaContextMenuRequested() QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); toggleControlLayoutAction.setCheckable(true); toggleControlLayoutAction.setChecked(config_get_bool( - GetGlobalConfig(), "BasicWindow", "VerticalVolControl")); + App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); /* ------------------- */ @@ -4166,10 +4172,10 @@ void OBSBasic::ToggleMixerLayout(bool vertical) void OBSBasic::ToggleVolControlLayout() { - bool vertical = !config_get_bool(GetGlobalConfig(), "BasicWindow", + bool vertical = !config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl"); - config_set_bool(GetGlobalConfig(), "BasicWindow", "VerticalVolControl", - vertical); + config_set_bool(App()->GetUserConfig(), "BasicWindow", + "VerticalVolControl", vertical); ToggleMixerLayout(vertical); // We need to store it so we can delete current and then add @@ -4193,7 +4199,7 @@ void OBSBasic::ActivateAudioSource(OBSSource source) if (!obs_source_audio_active(source)) return; - bool vertical = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool vertical = config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl"); VolControl *vol = new VolControl(source, true, vertical); @@ -4286,21 +4292,21 @@ void OBSBasic::TimedCheckForUpdates() { if (App()->IsUpdaterDisabled()) return; - if (!config_get_bool(App()->GlobalConfig(), "General", + if (!config_get_bool(App()->GetUserConfig(), "General", "EnableAutoUpdates")) return; #if defined(ENABLE_SPARKLE_UPDATER) CheckForUpdates(false); #elif _WIN32 - long long lastUpdate = config_get_int(App()->GlobalConfig(), "General", + long long lastUpdate = config_get_int(App()->GetAppConfig(), "General", "LastUpdateCheck"); uint32_t lastVersion = - config_get_int(App()->GlobalConfig(), "General", "LastVersion"); + config_get_int(App()->GetAppConfig(), "General", "LastVersion"); if (lastVersion < LIBOBS_API_VER) { lastUpdate = 0; - config_set_int(App()->GlobalConfig(), "General", + config_set_int(App()->GetAppConfig(), "General", "LastUpdateCheck", 0); } @@ -4977,7 +4983,7 @@ static inline enum video_colorspace GetVideoColorSpaceFromName(const char *name) void OBSBasic::ResetUI() { bool studioPortraitLayout = config_get_bool( - GetGlobalConfig(), "BasicWindow", "StudioPortraitLayout"); + App()->GetUserConfig(), "BasicWindow", "StudioPortraitLayout"); if (studioPortraitLayout) ui->previewLayout->setDirection(QBoxLayout::BottomToTop); @@ -5100,7 +5106,7 @@ bool OBSBasic::ResetAudio() ai.speakers = SPEAKERS_STEREO; bool lowLatencyAudioBuffering = config_get_bool( - GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering"); + App()->GetUserConfig(), "Audio", "LowLatencyAudioBuffering"); if (lowLatencyAudioBuffering) { ai.max_buffering_ms = 20; ai.fixed_buffering = true; @@ -5365,12 +5371,12 @@ void OBSBasic::closeEvent(QCloseEvent *event) #endif if (isVisible()) - config_set_string(App()->GlobalConfig(), "BasicWindow", + config_set_string(App()->GetUserConfig(), "BasicWindow", "geometry", saveGeometry().toBase64().constData()); - bool confirmOnExit = - config_get_bool(GetGlobalConfig(), "General", "ConfirmOnExit"); + bool confirmOnExit = config_get_bool(App()->GetUserConfig(), "General", + "ConfirmOnExit"); if (confirmOnExit && outputHandler && outputHandler->Active() && !clearingFailed) { @@ -5437,7 +5443,7 @@ void OBSBasic::closeEvent(QCloseEvent *event) delete extraBrowsers; - config_set_string(App()->GlobalConfig(), "BasicWindow", "DockState", + config_set_string(App()->GetUserConfig(), "BasicWindow", "DockState", saveState().toBase64().constData()); #ifdef BROWSER_AVAILABLE @@ -5642,7 +5648,7 @@ void OBSBasic::on_actionAdvAudioProperties_triggered() return; } - bool iconsVisible = config_get_bool(App()->GlobalConfig(), + bool iconsVisible = config_get_bool(App()->GetUserConfig(), "BasicWindow", "ShowSourceIcons"); advAudioWindow = new OBSBasicAdvAudio(this); @@ -5665,7 +5671,7 @@ void OBSBasic::on_actionMixerToolbarMenu_triggered() QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); toggleControlLayoutAction.setCheckable(true); toggleControlLayoutAction.setChecked(config_get_bool( - GetGlobalConfig(), "BasicWindow", "VerticalVolControl")); + App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); connect(&toggleControlLayoutAction, &QAction::changed, this, &OBSBasic::ToggleVolControlLayout, Qt::DirectConnection); @@ -5861,14 +5867,15 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) void OBSBasic::on_actionSceneListMode_triggered() { ui->scenes->SetGridMode(false); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", false); } void OBSBasic::on_actionSceneGridMode_triggered() { ui->scenes->SetGridMode(true); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", true); + config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", + true); } void OBSBasic::GridActionClicked() @@ -5881,7 +5888,7 @@ void OBSBasic::GridActionClicked() else ui->actionSceneListMode->setChecked(true); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", gridMode); } @@ -6405,7 +6412,7 @@ void OBSBasic::on_scenes_itemDoubleClicked(QListWidgetItem *witem) if (IsPreviewProgramMode()) { bool doubleClickSwitch = - config_get_bool(App()->GlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "TransitionOnDoubleClick"); if (doubleClickSwitch) @@ -6795,7 +6802,7 @@ void OBSBasic::on_actionMoveToBottom_triggered() static BPtr ReadLogFile(const char *subdir, const char *log) { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), subdir) <= 0) + if (GetAppConfigPath(logDir, sizeof(logDir), subdir) <= 0) return nullptr; string path = logDir; @@ -6850,7 +6857,7 @@ void OBSBasic::UploadLog(const char *subdir, const char *file, const bool crash) void OBSBasic::on_actionShowLogs_triggered() { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) + if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) return; QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir)); @@ -6883,7 +6890,7 @@ void OBSBasic::on_actionViewCurrentLog_triggered() void OBSBasic::on_actionShowCrashLogs_triggered() { char logDir[512]; - if (GetConfigPath(logDir, sizeof(logDir), "obs-studio/crashes") <= 0) + if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/crashes") <= 0) return; QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir)); @@ -7208,13 +7215,14 @@ void OBSBasic::ShowYouTubeAutoStartWarning() msgbox.exec(); if (cb->isChecked()) { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutYouTubeAutoStart", true); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", + nullptr); } }; - bool warned = config_get_bool(App()->GlobalConfig(), "General", + bool warned = config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutYouTubeAutoStart"); if (!warned) { QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection, @@ -7285,13 +7293,13 @@ void OBSBasic::StartStreaming() } bool recordWhenStreaming = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming"); if (recordWhenStreaming) StartRecording(); bool replayBufferWhileStreaming = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "ReplayBufferWhileStreaming"); if (replayBufferWhileStreaming) StartReplayBuffer(); @@ -7344,7 +7352,8 @@ void OBSBasic::BroadcastButtonClicked() emit BroadcastStreamStarted(autoStopBroadcast); } else if (!autoStopBroadcast) { #ifdef YOUTUBE_ENABLED - bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool confirm = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "WarnBeforeStoppingStream"); if (confirm && isVisible()) { QMessageBox::StandardButton button = OBSMessageBox::question( @@ -7409,7 +7418,7 @@ void OBSBasic::SetupBroadcast() #ifdef _WIN32 static inline void UpdateProcessPriority() { - const char *priority = config_get_string(App()->GlobalConfig(), + const char *priority = config_get_string(App()->GetAppConfig(), "General", "ProcessPriority"); if (priority && strcmp(priority, "Normal") != 0) SetProcessPriority(priority); @@ -7417,7 +7426,7 @@ static inline void UpdateProcessPriority() static inline void ClearProcessPriority() { - const char *priority = config_get_string(App()->GlobalConfig(), + const char *priority = config_get_string(App()->GetAppConfig(), "General", "ProcessPriority"); if (priority && strcmp(priority, "Normal") != 0) SetProcessPriority("Normal"); @@ -7539,17 +7548,18 @@ void OBSBasic::StopStreaming() OnDeactivate(); bool recordWhenStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); + App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming"); bool keepRecordingWhenStreamStops = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepRecordingWhenStreamStops"); if (recordWhenStreaming && !keepRecordingWhenStreamStops) StopRecording(); - bool replayBufferWhileStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming"); + bool replayBufferWhileStreaming = + config_get_bool(App()->GetUserConfig(), "BasicWindow", + "ReplayBufferWhileStreaming"); bool keepReplayBufferStreamStops = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepReplayBufferStreamStops"); if (replayBufferWhileStreaming && !keepReplayBufferStreamStops) StopReplayBuffer(); @@ -7581,17 +7591,18 @@ void OBSBasic::ForceStopStreaming() OnDeactivate(); bool recordWhenStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); + App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming"); bool keepRecordingWhenStreamStops = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepRecordingWhenStreamStops"); if (recordWhenStreaming && !keepRecordingWhenStreamStops) StopRecording(); - bool replayBufferWhileStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming"); + bool replayBufferWhileStreaming = + config_get_bool(App()->GetUserConfig(), "BasicWindow", + "ReplayBufferWhileStreaming"); bool keepReplayBufferStreamStops = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepReplayBufferStreamStops"); if (replayBufferWhileStreaming && !keepReplayBufferStreamStops) StopReplayBuffer(); @@ -7997,13 +8008,14 @@ void OBSBasic::ShowReplayBufferPauseWarning() msgbox.exec(); if (cb->isChecked()) { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutReplayBufferPausing", true); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", + nullptr); } }; - bool warned = config_get_bool(App()->GlobalConfig(), "General", + bool warned = config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutReplayBufferPausing"); if (!warned) { QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection, @@ -8242,7 +8254,8 @@ void OBSBasic::OnVirtualCamStop(int) void OBSBasic::StreamActionTriggered() { if (outputHandler->StreamingActive()) { - bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool confirm = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "WarnBeforeStoppingStream"); #ifdef YOUTUBE_ENABLED @@ -8294,7 +8307,8 @@ void OBSBasic::StreamActionTriggered() return; } - bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool confirm = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "WarnBeforeStartingStream"); bool bwtest = false; @@ -8336,7 +8350,8 @@ void OBSBasic::StreamActionTriggered() void OBSBasic::RecordActionTriggered() { if (outputHandler->RecordingActive()) { - bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool confirm = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "WarnBeforeStoppingRecord"); if (confirm && isVisible()) { @@ -8461,7 +8476,7 @@ void OBSBasic::on_actionShowWhatsNew_triggered() if (!cef) return; - config_set_int(App()->GlobalConfig(), "General", "InfoIncrement", -1); + config_set_int(App()->GetUserConfig(), "General", "InfoIncrement", -1); WhatsNewInfoThread *wnit = new WhatsNewInfoThread(); connect(wnit, &WhatsNewInfoThread::Result, this, @@ -8482,12 +8497,12 @@ void OBSBasic::on_actionReleaseNotes_triggered() void OBSBasic::on_actionShowSettingsFolder_triggered() { - char path[512]; - int ret = GetConfigPath(path, 512, "obs-studio"); - if (ret <= 0) - return; + const std::string userConfigPath = + App()->userConfigLocation.u8string() + "/obs-studio"; + const QString userConfigLocation = + QString::fromStdString(userConfigPath); - QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + QDesktopServices::openUrl(QUrl::fromLocalFile(userConfigLocation)); } void OBSBasic::on_actionShowProfileFolder_triggered() @@ -9449,7 +9464,8 @@ OBSProjector *OBSBasic::OpenProjector(obs_source_t *source, int monitor, if (monitor > 9 || monitor > QGuiApplication::screens().size() - 1) return nullptr; - bool closeProjectors = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool closeProjectors = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "CloseExistingProjectors"); if (closeProjectors && monitor > -1) { @@ -9604,9 +9620,9 @@ void OBSBasic::UpdateTitleBar() stringstream name; const char *profile = - config_get_string(App()->GlobalConfig(), "Basic", "Profile"); + config_get_string(App()->GetUserConfig(), "Basic", "Profile"); const char *sceneCollection = config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollection"); + App()->GetUserConfig(), "Basic", "SceneCollection"); name << "OBS "; if (previewProgramMode) @@ -9627,8 +9643,8 @@ void OBSBasic::UpdateTitleBar() int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const { char profiles_path[512]; - const char *profile = - config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir"); + const char *profile = config_get_string(App()->GetUserConfig(), "Basic", + "ProfileDir"); int ret; if (!profile) @@ -9638,7 +9654,7 @@ int OBSBasic::GetProfilePath(char *path, size_t size, const char *file) const if (!file) file = ""; - ret = GetConfigPath(profiles_path, 512, "obs-studio/basic/profiles"); + ret = GetAppConfigPath(profiles_path, 512, "obs-studio/basic/profiles"); if (ret <= 0) return ret; @@ -9803,7 +9819,7 @@ void OBSBasic::on_resetUI_triggered() ui->scenes->SetGridMode(false); ui->actionSceneListMode->setChecked(true); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "gridMode", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", false); } @@ -9818,7 +9834,7 @@ void OBSBasic::on_toggleListboxToolbars_toggled(bool visible) ui->scenesToolbar->setVisible(visible); ui->mixerToolbar->setVisible(visible); - config_set_bool(App()->GlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ShowListboxToolbars", visible); } @@ -9836,7 +9852,7 @@ void OBSBasic::HideContextBar() void OBSBasic::on_toggleContextBar_toggled(bool visible) { - config_set_bool(App()->GlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ShowContextToolbars", visible); this->ui->contextContainer->setVisible(visible); UpdateContextBar(true); @@ -9846,7 +9862,7 @@ void OBSBasic::on_toggleStatusBar_toggled(bool visible) { ui->statusbar->setVisible(visible); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "ShowStatusBar", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ShowStatusBar", visible); } @@ -9856,8 +9872,8 @@ void OBSBasic::on_toggleSourceIcons_toggled(bool visible) if (advAudioWindow != nullptr) advAudioWindow->SetIconsVisible(visible); - config_set_bool(App()->GlobalConfig(), "BasicWindow", "ShowSourceIcons", - visible); + config_set_bool(App()->GetUserConfig(), "BasicWindow", + "ShowSourceIcons", visible); } void OBSBasic::on_actionLockPreview_triggered() @@ -9922,7 +9938,7 @@ void OBSBasic::on_actionScaleOutput_triggered() void OBSBasic::SetShowing(bool showing) { if (!showing && isVisible()) { - config_set_string(App()->GlobalConfig(), "BasicWindow", + config_set_string(App()->GetUserConfig(), "BasicWindow", "geometry", saveGeometry().toBase64().constData()); @@ -10107,9 +10123,9 @@ void OBSBasic::SystemTray(bool firstStarted) return; bool sysTrayWhenStarted = config_get_bool( - GetGlobalConfig(), "BasicWindow", "SysTrayWhenStarted"); - bool sysTrayEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow", - "SysTrayEnabled"); + App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted"); + bool sysTrayEnabled = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "SysTrayEnabled"); if (firstStarted) SystemTrayInit(); @@ -10135,7 +10151,7 @@ void OBSBasic::SystemTray(bool firstStarted) bool OBSBasic::sysTrayMinimizeToTray() { - return config_get_bool(GetGlobalConfig(), "BasicWindow", + return config_get_bool(App()->GetUserConfig(), "BasicWindow", "SysTrayMinimizeToTray"); } @@ -11118,17 +11134,17 @@ void OBSBasic::ShowStatusBarMessage(const QString &message) void OBSBasic::UpdatePreviewSafeAreas() { - drawSafeAreas = config_get_bool(App()->GlobalConfig(), "BasicWindow", + drawSafeAreas = config_get_bool(App()->GetUserConfig(), "BasicWindow", "ShowSafeAreas"); } void OBSBasic::UpdatePreviewOverflowSettings() { - bool hidden = config_get_bool(App()->GlobalConfig(), "BasicWindow", + bool hidden = config_get_bool(App()->GetUserConfig(), "BasicWindow", "OverflowHidden"); - bool select = config_get_bool(App()->GlobalConfig(), "BasicWindow", + bool select = config_get_bool(App()->GetUserConfig(), "BasicWindow", "OverflowSelectionHidden"); - bool always = config_get_bool(App()->GlobalConfig(), "BasicWindow", + bool always = config_get_bool(App()->GetUserConfig(), "BasicWindow", "OverflowAlwaysVisible"); ui->preview->SetOverflowHidden(hidden); @@ -11141,7 +11157,7 @@ void OBSBasic::SetDisplayAffinity(QWindow *window) if (!SetDisplayAffinitySupported()) return; - bool hideFromCapture = config_get_bool(App()->GlobalConfig(), + bool hideFromCapture = config_get_bool(App()->GetUserConfig(), "BasicWindow", "HideOBSWindowsFromCapture"); @@ -11175,10 +11191,10 @@ static inline QColor color_from_int(long long val) QColor OBSBasic::GetSelectionColor() const { - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { return color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "SelectRed")); + App()->GetUserConfig(), "Accessibility", "SelectRed")); } else { return QColor::fromRgb(255, 0, 0); } @@ -11186,10 +11202,11 @@ QColor OBSBasic::GetSelectionColor() const QColor OBSBasic::GetCropColor() const { - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { - return color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "SelectGreen")); + return color_from_int(config_get_int(App()->GetUserConfig(), + "Accessibility", + "SelectGreen")); } else { return QColor::fromRgb(0, 255, 0); } @@ -11197,10 +11214,10 @@ QColor OBSBasic::GetCropColor() const QColor OBSBasic::GetHoverColor() const { - if (config_get_bool(GetGlobalConfig(), "Accessibility", + if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) { return color_from_int(config_get_int( - GetGlobalConfig(), "Accessibility", "SelectBlue")); + App()->GetUserConfig(), "Accessibility", "SelectBlue")); } else { return QColor::fromRgb(0, 127, 255); } @@ -11209,7 +11226,7 @@ QColor OBSBasic::GetHoverColor() const void OBSBasic::UpdatePreviewSpacingHelpers() { drawSpacingHelpers = config_get_bool( - App()->GlobalConfig(), "BasicWindow", "SpacingHelpersEnabled"); + App()->GetUserConfig(), "BasicWindow", "SpacingHelpersEnabled"); } float OBSBasic::GetDevicePixelRatio() diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 3f213483219201..c1d21c99190a2d 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -308,7 +308,7 @@ class OBSBasic : public OBSMainWindow { int previewCX = 0, previewCY = 0; float previewScale = 0.0f; - ConfigFile basicConfig; + ConfigFile activeConfiguration; std::vector savedProjectorsArray; std::vector projectors; diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 9778169ad10eef..6806150c054171 100644 --- a/UI/window-basic-preview.cpp +++ b/UI/window-basic-preview.cpp @@ -192,17 +192,17 @@ vec3 OBSBasicPreview::GetSnapOffset(const vec3 &tl, const vec3 &br) vec3_zero(&clampOffset); - const bool snap = config_get_bool(GetGlobalConfig(), "BasicWindow", + const bool snap = config_get_bool(App()->GetUserConfig(), "BasicWindow", "SnappingEnabled"); if (snap == false) return clampOffset; const bool screenSnap = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ScreenSnapping"); + App()->GetUserConfig(), "BasicWindow", "ScreenSnapping"); const bool centerSnap = config_get_bool( - GetGlobalConfig(), "BasicWindow", "CenterSnapping"); + App()->GetUserConfig(), "BasicWindow", "CenterSnapping"); - const float clampDist = config_get_double(GetGlobalConfig(), + const float clampDist = config_get_double(App()->GetUserConfig(), "BasicWindow", "SnapDistance") / main->previewScale; @@ -995,10 +995,10 @@ void OBSBasicPreview::SnapItemMovement(vec2 &offset) vec3 snapOffset = GetSnapOffset(data.tl, data.br); - const bool snap = config_get_bool(GetGlobalConfig(), "BasicWindow", + const bool snap = config_get_bool(App()->GetUserConfig(), "BasicWindow", "SnappingEnabled"); const bool sourcesSnap = config_get_bool( - GetGlobalConfig(), "BasicWindow", "SourceSnapping"); + App()->GetUserConfig(), "BasicWindow", "SourceSnapping"); if (snap == false) return; if (sourcesSnap == false) { @@ -1007,7 +1007,7 @@ void OBSBasicPreview::SnapItemMovement(vec2 &offset) return; } - const float clampDist = config_get_double(GetGlobalConfig(), + const float clampDist = config_get_double(App()->GetUserConfig(), "BasicWindow", "SnapDistance") / main->previewScale; diff --git a/UI/window-basic-properties.cpp b/UI/window-basic-properties.cpp index 47703418a2c8c1..e9decdc852bae4 100644 --- a/UI/window-basic-properties.cpp +++ b/UI/window-basic-properties.cpp @@ -53,9 +53,9 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_) OBSBasicProperties::SourceRenamed, this), oldSettings(obs_data_create()) { - int cx = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow", + int cx = (int)config_get_int(App()->GetAppConfig(), "PropertiesWindow", "cx"); - int cy = (int)config_get_int(App()->GlobalConfig(), "PropertiesWindow", + int cy = (int)config_get_int(App()->GetAppConfig(), "PropertiesWindow", "cy"); enum obs_source_type type = obs_source_get_type(source); @@ -450,9 +450,9 @@ void OBSBasicProperties::DrawTransitionPreview(void *data, uint32_t cx, void OBSBasicProperties::Cleanup() { - config_set_int(App()->GlobalConfig(), "PropertiesWindow", "cx", + config_set_int(App()->GetAppConfig(), "PropertiesWindow", "cx", width()); - config_set_int(App()->GlobalConfig(), "PropertiesWindow", "cy", + config_set_int(App()->GetAppConfig(), "PropertiesWindow", "cy", height()); obs_display_remove_draw_callback(ui->preview->GetDisplay(), diff --git a/UI/window-basic-settings-a11y.cpp b/UI/window-basic-settings-a11y.cpp index 2437b2c11de0f8..2cce25ba6c6bf7 100644 --- a/UI/window-basic-settings-a11y.cpp +++ b/UI/window-basic-settings-a11y.cpp @@ -43,7 +43,7 @@ QColor OBSBasicSettings::GetColor(uint32_t colorVal, QString label) void OBSBasicSettings::LoadA11ySettings(bool presetChange) { - config_t *config = GetGlobalConfig(); + config_t *config = App()->GetUserConfig(); loading = true; if (!presetChange) { @@ -109,7 +109,7 @@ void OBSBasicSettings::LoadA11ySettings(bool presetChange) void OBSBasicSettings::SaveA11ySettings() { - config_t *config = GetGlobalConfig(); + config_t *config = App()->GetUserConfig(); config_set_bool(config, "Accessibility", "OverrideColors", ui->colorsGroupBox->isChecked()); @@ -163,7 +163,7 @@ void OBSBasicSettings::UpdateA11yColors() void OBSBasicSettings::SetDefaultColors() { - config_t *config = GetGlobalConfig(); + config_t *config = App()->GetUserConfig(); config_set_default_int(config, "Accessibility", "SelectRed", selectRed); config_set_default_int(config, "Accessibility", "SelectGreen", selectGreen); diff --git a/UI/window-basic-settings-appearance.cpp b/UI/window-basic-settings-appearance.cpp index 58c8401b6f0be4..20614c4b8ec67c 100644 --- a/UI/window-basic-settings-appearance.cpp +++ b/UI/window-basic-settings-appearance.cpp @@ -107,7 +107,7 @@ void OBSBasicSettings::LoadAppearanceSettings(bool reload) void OBSBasicSettings::SaveAppearanceSettings() { - config_t *config = GetGlobalConfig(); + config_t *config = App()->GetUserConfig(); OBSTheme *currentTheme = App()->GetTheme(); if (savedTheme != currentTheme) { diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index f005faf8f7dbe5..d0741a0a7f7b24 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -1032,8 +1032,9 @@ void OBSBasicSettings::on_server_currentIndexChanged(int /*index*/) void OBSBasicSettings::UpdateVodTrackSetting() { - bool enableForCustomServer = config_get_bool( - GetGlobalConfig(), "General", "EnableCustomServerVodTrack"); + bool enableForCustomServer = + config_get_bool(App()->GetUserConfig(), "General", + "EnableCustomServerVodTrack"); bool enableVodTrack = ui->service->currentText() == "Twitch"; bool wasEnabled = !!vodTrackCheckbox; diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index b85a53ad608c49..7cb863044e2fc6 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -1333,8 +1333,8 @@ void OBSBasicSettings::LoadBranchesList() { #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) bool configBranchRemoved = true; - QString configBranch = - config_get_string(GetGlobalConfig(), "General", "UpdateBranch"); + QString configBranch = config_get_string(App()->GetAppConfig(), + "General", "UpdateBranch"); for (const UpdateBranch &branch : App()->GetBranches()) { if (branch.name == configBranch) @@ -1387,8 +1387,8 @@ void OBSBasicSettings::LoadGeneralSettings() LoadLanguageList(); #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) - bool enableAutoUpdates = config_get_bool(GetGlobalConfig(), "General", - "EnableAutoUpdates"); + bool enableAutoUpdates = config_get_bool( + App()->GetUserConfig(), "General", "EnableAutoUpdates"); ui->enableAutoUpdates->setChecked(enableAutoUpdates); LoadBranchesList(); @@ -1400,7 +1400,7 @@ void OBSBasicSettings::LoadGeneralSettings() #if defined(_WIN32) if (ui->hideOBSFromCapture) { bool hideWindowFromCapture = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "HideOBSWindowsFromCapture"); ui->hideOBSFromCapture->setChecked(hideWindowFromCapture); @@ -1415,129 +1415,136 @@ void OBSBasicSettings::LoadGeneralSettings() #endif bool recordWhenStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); + App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming"); ui->recordWhenStreaming->setChecked(recordWhenStreaming); bool keepRecordStreamStops = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepRecordingWhenStreamStops"); ui->keepRecordStreamStops->setChecked(keepRecordStreamStops); - bool replayWhileStreaming = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ReplayBufferWhileStreaming"); + bool replayWhileStreaming = + config_get_bool(App()->GetUserConfig(), "BasicWindow", + "ReplayBufferWhileStreaming"); ui->replayWhileStreaming->setChecked(replayWhileStreaming); bool keepReplayStreamStops = - config_get_bool(GetGlobalConfig(), "BasicWindow", + config_get_bool(App()->GetUserConfig(), "BasicWindow", "KeepReplayBufferStreamStops"); ui->keepReplayStreamStops->setChecked(keepReplayStreamStops); bool systemTrayEnabled = config_get_bool( - GetGlobalConfig(), "BasicWindow", "SysTrayEnabled"); + App()->GetUserConfig(), "BasicWindow", "SysTrayEnabled"); ui->systemTrayEnabled->setChecked(systemTrayEnabled); bool systemTrayWhenStarted = config_get_bool( - GetGlobalConfig(), "BasicWindow", "SysTrayWhenStarted"); + App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted"); ui->systemTrayWhenStarted->setChecked(systemTrayWhenStarted); bool systemTrayAlways = config_get_bool( - GetGlobalConfig(), "BasicWindow", "SysTrayMinimizeToTray"); + App()->GetUserConfig(), "BasicWindow", "SysTrayMinimizeToTray"); ui->systemTrayAlways->setChecked(systemTrayAlways); - bool saveProjectors = config_get_bool(GetGlobalConfig(), "BasicWindow", - "SaveProjectors"); + bool saveProjectors = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "SaveProjectors"); ui->saveProjectors->setChecked(saveProjectors); - bool closeProjectors = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool closeProjectors = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "CloseExistingProjectors"); ui->closeProjectors->setChecked(closeProjectors); - bool snappingEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow", - "SnappingEnabled"); + bool snappingEnabled = config_get_bool( + App()->GetUserConfig(), "BasicWindow", "SnappingEnabled"); ui->snappingEnabled->setChecked(snappingEnabled); - bool screenSnapping = config_get_bool(GetGlobalConfig(), "BasicWindow", - "ScreenSnapping"); + bool screenSnapping = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "ScreenSnapping"); ui->screenSnapping->setChecked(screenSnapping); - bool centerSnapping = config_get_bool(GetGlobalConfig(), "BasicWindow", - "CenterSnapping"); + bool centerSnapping = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "CenterSnapping"); ui->centerSnapping->setChecked(centerSnapping); - bool sourceSnapping = config_get_bool(GetGlobalConfig(), "BasicWindow", - "SourceSnapping"); + bool sourceSnapping = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "SourceSnapping"); ui->sourceSnapping->setChecked(sourceSnapping); - double snapDistance = config_get_double(GetGlobalConfig(), + double snapDistance = config_get_double(App()->GetUserConfig(), "BasicWindow", "SnapDistance"); ui->snapDistance->setValue(snapDistance); - bool warnBeforeStreamStart = config_get_bool( - GetGlobalConfig(), "BasicWindow", "WarnBeforeStartingStream"); + bool warnBeforeStreamStart = + config_get_bool(App()->GetUserConfig(), "BasicWindow", + "WarnBeforeStartingStream"); ui->warnBeforeStreamStart->setChecked(warnBeforeStreamStart); bool spacingHelpersEnabled = config_get_bool( - GetGlobalConfig(), "BasicWindow", "SpacingHelpersEnabled"); + App()->GetUserConfig(), "BasicWindow", "SpacingHelpersEnabled"); ui->previewSpacingHelpers->setChecked(spacingHelpersEnabled); - bool warnBeforeStreamStop = config_get_bool( - GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingStream"); + bool warnBeforeStreamStop = config_get_bool(App()->GetUserConfig(), + "BasicWindow", + "WarnBeforeStoppingStream"); ui->warnBeforeStreamStop->setChecked(warnBeforeStreamStop); - bool warnBeforeRecordStop = config_get_bool( - GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingRecord"); + bool warnBeforeRecordStop = config_get_bool(App()->GetUserConfig(), + "BasicWindow", + "WarnBeforeStoppingRecord"); ui->warnBeforeRecordStop->setChecked(warnBeforeRecordStop); bool hideProjectorCursor = config_get_bool( - GetGlobalConfig(), "BasicWindow", "HideProjectorCursor"); + App()->GetUserConfig(), "BasicWindow", "HideProjectorCursor"); ui->hideProjectorCursor->setChecked(hideProjectorCursor); bool projectorAlwaysOnTop = config_get_bool( - GetGlobalConfig(), "BasicWindow", "ProjectorAlwaysOnTop"); + App()->GetUserConfig(), "BasicWindow", "ProjectorAlwaysOnTop"); ui->projectorAlwaysOnTop->setChecked(projectorAlwaysOnTop); - bool overflowHide = config_get_bool(GetGlobalConfig(), "BasicWindow", - "OverflowHidden"); + bool overflowHide = config_get_bool(App()->GetUserConfig(), + "BasicWindow", "OverflowHidden"); ui->overflowHide->setChecked(overflowHide); bool overflowAlwaysVisible = config_get_bool( - GetGlobalConfig(), "BasicWindow", "OverflowAlwaysVisible"); + App()->GetUserConfig(), "BasicWindow", "OverflowAlwaysVisible"); ui->overflowAlwaysVisible->setChecked(overflowAlwaysVisible); - bool overflowSelectionHide = config_get_bool( - GetGlobalConfig(), "BasicWindow", "OverflowSelectionHidden"); + bool overflowSelectionHide = config_get_bool(App()->GetUserConfig(), + "BasicWindow", + "OverflowSelectionHidden"); ui->overflowSelectionHide->setChecked(overflowSelectionHide); - bool safeAreas = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool safeAreas = config_get_bool(App()->GetUserConfig(), "BasicWindow", "ShowSafeAreas"); ui->previewSafeAreas->setChecked(safeAreas); - bool automaticSearch = config_get_bool(GetGlobalConfig(), "General", - "AutomaticCollectionSearch"); + bool automaticSearch = config_get_bool( + App()->GetUserConfig(), "General", "AutomaticCollectionSearch"); ui->automaticSearch->setChecked(automaticSearch); - bool doubleClickSwitch = config_get_bool( - GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick"); + bool doubleClickSwitch = config_get_bool(App()->GetUserConfig(), + "BasicWindow", + "TransitionOnDoubleClick"); ui->doubleClickSwitch->setChecked(doubleClickSwitch); bool studioPortraitLayout = config_get_bool( - GetGlobalConfig(), "BasicWindow", "StudioPortraitLayout"); + App()->GetUserConfig(), "BasicWindow", "StudioPortraitLayout"); ui->studioPortraitLayout->setChecked(studioPortraitLayout); - bool prevProgLabels = config_get_bool(GetGlobalConfig(), "BasicWindow", - "StudioModeLabels"); + bool prevProgLabels = config_get_bool( + App()->GetUserConfig(), "BasicWindow", "StudioModeLabels"); ui->prevProgLabelToggle->setChecked(prevProgLabels); bool multiviewMouseSwitch = config_get_bool( - GetGlobalConfig(), "BasicWindow", "MultiviewMouseSwitch"); + App()->GetUserConfig(), "BasicWindow", "MultiviewMouseSwitch"); ui->multiviewMouseSwitch->setChecked(multiviewMouseSwitch); bool multiviewDrawNames = config_get_bool( - GetGlobalConfig(), "BasicWindow", "MultiviewDrawNames"); + App()->GetUserConfig(), "BasicWindow", "MultiviewDrawNames"); ui->multiviewDrawNames->setChecked(multiviewDrawNames); bool multiviewDrawAreas = config_get_bool( - GetGlobalConfig(), "BasicWindow", "MultiviewDrawAreas"); + App()->GetUserConfig(), "BasicWindow", "MultiviewDrawAreas"); ui->multiviewDrawAreas->setChecked(multiviewDrawAreas); ui->multiviewLayout->addItem( @@ -1571,9 +1578,10 @@ void OBSBasicSettings::LoadGeneralSettings() QTStr("Basic.Settings.General.MultiviewLayout.25Scene"), static_cast(MultiviewLayout::SCENES_ONLY_25_SCENES)); - ui->multiviewLayout->setCurrentIndex(ui->multiviewLayout->findData( - QVariant::fromValue(config_get_int( - GetGlobalConfig(), "BasicWindow", "MultiviewLayout")))); + ui->multiviewLayout->setCurrentIndex( + ui->multiviewLayout->findData(QVariant::fromValue( + config_get_int(App()->GetUserConfig(), "BasicWindow", + "MultiviewLayout")))); prevLangIndex = ui->language->currentIndex(); @@ -1587,7 +1595,7 @@ void OBSBasicSettings::LoadRendererList() { #ifdef _WIN32 const char *renderer = - config_get_string(GetGlobalConfig(), "Video", "Renderer"); + config_get_string(App()->GetUserConfig(), "Video", "Renderer"); ui->renderer->addItem(QT_UTF8("Direct3D 11")); if (opt_allow_opengl || strcmp(renderer, "OpenGL") == 0) @@ -2792,7 +2800,7 @@ void OBSBasicSettings::LoadAudioSettings() uint32_t peakMeterTypeIdx = config_get_uint(main->Config(), "Audio", "PeakMeterType"); bool enableLLAudioBuffering = config_get_bool( - GetGlobalConfig(), "Audio", "LowLatencyAudioBuffering"); + App()->GetUserConfig(), "Audio", "LowLatencyAudioBuffering"); loading = true; @@ -2918,13 +2926,13 @@ void OBSBasicSettings::LoadAdvancedSettings() int rbSize = config_get_int(main->Config(), "AdvOut", "RecRBSize"); bool autoRemux = config_get_bool(main->Config(), "Video", "AutoRemux"); const char *hotkeyFocusType = config_get_string( - App()->GlobalConfig(), "General", "HotkeyFocusType"); + App()->GetUserConfig(), "General", "HotkeyFocusType"); bool dynBitrate = config_get_bool(main->Config(), "Output", "DynamicBitrate"); const char *ipFamily = config_get_string(main->Config(), "Output", "IPFamily"); - bool confirmOnExit = - config_get_bool(GetGlobalConfig(), "General", "ConfirmOnExit"); + bool confirmOnExit = config_get_bool(App()->GetUserConfig(), "General", + "ConfirmOnExit"); loading = true; @@ -2971,20 +2979,20 @@ void OBSBasicSettings::LoadAdvancedSettings() } #ifdef __APPLE__ - bool disableOSXVSync = config_get_bool(App()->GlobalConfig(), "Video", + bool disableOSXVSync = config_get_bool(App()->GetUserConfig(), "Video", "DisableOSXVSync"); - bool resetOSXVSync = config_get_bool(App()->GlobalConfig(), "Video", + bool resetOSXVSync = config_get_bool(App()->GetUserConfig(), "Video", "ResetOSXVSyncOnExit"); ui->disableOSXVSync->setChecked(disableOSXVSync); ui->resetOSXVSync->setChecked(resetOSXVSync); ui->resetOSXVSync->setEnabled(disableOSXVSync); #elif _WIN32 bool disableAudioDucking = config_get_bool( - App()->GlobalConfig(), "Audio", "DisableAudioDucking"); + App()->GetUserConfig(), "Audio", "DisableAudioDucking"); ui->disableAudioDucking->setChecked(disableAudioDucking); const char *processPriority = config_get_string( - App()->GlobalConfig(), "General", "ProcessPriority"); + App()->GetAppConfig(), "General", "ProcessPriority"); bool enableNewSocketLoop = config_get_bool(main->Config(), "Output", "NewSocketLoopEnable"); bool enableLowLatencyMode = @@ -3001,7 +3009,7 @@ void OBSBasicSettings::LoadAdvancedSettings() QTStr("Basic.Settings.Advanced.Network.TCPPacing.Tooltip")); #endif #if defined(_WIN32) || defined(__APPLE__) - bool browserHWAccel = config_get_bool(App()->GlobalConfig(), "General", + bool browserHWAccel = config_get_bool(App()->GetUserConfig(), "General", "BrowserHWAccel"); ui->browserHWAccel->setChecked(browserHWAccel); prevBrowserAccel = ui->browserHWAccel->isChecked(); @@ -3360,12 +3368,12 @@ void OBSBasicSettings::SaveGeneralSettings() string language = langData.toString().toStdString(); if (WidgetChanged(ui->language)) - config_set_string(GetGlobalConfig(), "General", "Language", + config_set_string(App()->GetUserConfig(), "General", "Language", language.c_str()); #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) if (WidgetChanged(ui->enableAutoUpdates)) - config_set_bool(GetGlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "EnableAutoUpdates", ui->enableAutoUpdates->isChecked()); int branchIdx = ui->updateChannelBox->currentIndex(); @@ -3373,15 +3381,15 @@ void OBSBasicSettings::SaveGeneralSettings() ui->updateChannelBox->itemData(branchIdx).toString(); if (WidgetChanged(ui->updateChannelBox)) { - config_set_string(GetGlobalConfig(), "General", "UpdateBranch", - QT_TO_UTF8(branchName)); + config_set_string(App()->GetAppConfig(), "General", + "UpdateBranch", QT_TO_UTF8(branchName)); forceUpdateCheck = true; } #endif #ifdef _WIN32 if (ui->hideOBSFromCapture && WidgetChanged(ui->hideOBSFromCapture)) { bool hide_window = ui->hideOBSFromCapture->isChecked(); - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "HideOBSWindowsFromCapture", hide_window); QWindowList windows = QGuiApplication::allWindows(); @@ -3399,80 +3407,80 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_bool(main->Config(), "General", "OpenStatsOnStartup", ui->openStatsOnStartup->isChecked()); if (WidgetChanged(ui->snappingEnabled)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SnappingEnabled", ui->snappingEnabled->isChecked()); if (WidgetChanged(ui->screenSnapping)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ScreenSnapping", ui->screenSnapping->isChecked()); if (WidgetChanged(ui->centerSnapping)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "CenterSnapping", ui->centerSnapping->isChecked()); if (WidgetChanged(ui->sourceSnapping)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SourceSnapping", ui->sourceSnapping->isChecked()); if (WidgetChanged(ui->snapDistance)) - config_set_double(GetGlobalConfig(), "BasicWindow", + config_set_double(App()->GetUserConfig(), "BasicWindow", "SnapDistance", ui->snapDistance->value()); if (WidgetChanged(ui->overflowAlwaysVisible) || WidgetChanged(ui->overflowHide) || WidgetChanged(ui->overflowSelectionHide)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "OverflowAlwaysVisible", ui->overflowAlwaysVisible->isChecked()); - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "OverflowHidden", ui->overflowHide->isChecked()); - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "OverflowSelectionHidden", ui->overflowSelectionHide->isChecked()); main->UpdatePreviewOverflowSettings(); } if (WidgetChanged(ui->previewSafeAreas)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ShowSafeAreas", ui->previewSafeAreas->isChecked()); main->UpdatePreviewSafeAreas(); } if (WidgetChanged(ui->previewSpacingHelpers)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SpacingHelpersEnabled", ui->previewSpacingHelpers->isChecked()); main->UpdatePreviewSpacingHelpers(); } if (WidgetChanged(ui->doubleClickSwitch)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "TransitionOnDoubleClick", ui->doubleClickSwitch->isChecked()); if (WidgetChanged(ui->automaticSearch)) - config_set_bool(GetGlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", ui->automaticSearch->isChecked()); - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "WarnBeforeStartingStream", ui->warnBeforeStreamStart->isChecked()); - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "WarnBeforeStoppingStream", ui->warnBeforeStreamStop->isChecked()); - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "WarnBeforeStoppingRecord", ui->warnBeforeRecordStop->isChecked()); if (WidgetChanged(ui->hideProjectorCursor)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "HideProjectorCursor", ui->hideProjectorCursor->isChecked()); main->UpdateProjectorHideCursor(); } if (WidgetChanged(ui->projectorAlwaysOnTop)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ProjectorAlwaysOnTop", ui->projectorAlwaysOnTop->isChecked()); #if defined(_WIN32) || defined(__APPLE__) @@ -3484,25 +3492,25 @@ void OBSBasicSettings::SaveGeneralSettings() } if (WidgetChanged(ui->recordWhenStreaming)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "RecordWhenStreaming", ui->recordWhenStreaming->isChecked()); if (WidgetChanged(ui->keepRecordStreamStops)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "KeepRecordingWhenStreamStops", ui->keepRecordStreamStops->isChecked()); if (WidgetChanged(ui->replayWhileStreaming)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "ReplayBufferWhileStreaming", ui->replayWhileStreaming->isChecked()); if (WidgetChanged(ui->keepReplayStreamStops)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "KeepReplayBufferStreamStops", ui->keepReplayStreamStops->isChecked()); if (WidgetChanged(ui->systemTrayEnabled)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SysTrayEnabled", ui->systemTrayEnabled->isChecked()); @@ -3510,27 +3518,27 @@ void OBSBasicSettings::SaveGeneralSettings() } if (WidgetChanged(ui->systemTrayWhenStarted)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SysTrayWhenStarted", ui->systemTrayWhenStarted->isChecked()); if (WidgetChanged(ui->systemTrayAlways)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SysTrayMinimizeToTray", ui->systemTrayAlways->isChecked()); if (WidgetChanged(ui->saveProjectors)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "SaveProjectors", ui->saveProjectors->isChecked()); if (WidgetChanged(ui->closeProjectors)) - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "CloseExistingProjectors", ui->closeProjectors->isChecked()); if (WidgetChanged(ui->studioPortraitLayout)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "StudioPortraitLayout", ui->studioPortraitLayout->isChecked()); @@ -3538,7 +3546,7 @@ void OBSBasicSettings::SaveGeneralSettings() } if (WidgetChanged(ui->prevProgLabelToggle)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "StudioModeLabels", ui->prevProgLabelToggle->isChecked()); @@ -3547,28 +3555,28 @@ void OBSBasicSettings::SaveGeneralSettings() bool multiviewChanged = false; if (WidgetChanged(ui->multiviewMouseSwitch)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "MultiviewMouseSwitch", ui->multiviewMouseSwitch->isChecked()); multiviewChanged = true; } if (WidgetChanged(ui->multiviewDrawNames)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "MultiviewDrawNames", ui->multiviewDrawNames->isChecked()); multiviewChanged = true; } if (WidgetChanged(ui->multiviewDrawAreas)) { - config_set_bool(GetGlobalConfig(), "BasicWindow", + config_set_bool(App()->GetUserConfig(), "BasicWindow", "MultiviewDrawAreas", ui->multiviewDrawAreas->isChecked()); multiviewChanged = true; } if (WidgetChanged(ui->multiviewLayout)) { - config_set_int(GetGlobalConfig(), "BasicWindow", + config_set_int(App()->GetUserConfig(), "BasicWindow", "MultiviewLayout", ui->multiviewLayout->currentData().toInt()); multiviewChanged = true; @@ -3616,12 +3624,12 @@ void OBSBasicSettings::SaveAdvancedSettings() #ifdef _WIN32 if (WidgetChanged(ui->renderer)) - config_set_string(App()->GlobalConfig(), "Video", "Renderer", + config_set_string(App()->GetUserConfig(), "Video", "Renderer", QT_TO_UTF8(ui->renderer->currentText())); std::string priority = QT_TO_UTF8(ui->processPriority->currentData().toString()); - config_set_string(App()->GlobalConfig(), "General", "ProcessPriority", + config_set_string(App()->GetAppConfig(), "General", "ProcessPriority", priority.c_str()); if (main->Active()) SetProcessPriority(priority.c_str()); @@ -3631,25 +3639,25 @@ void OBSBasicSettings::SaveAdvancedSettings() #endif #if defined(_WIN32) || defined(__APPLE__) bool browserHWAccel = ui->browserHWAccel->isChecked(); - config_set_bool(App()->GlobalConfig(), "General", "BrowserHWAccel", + config_set_bool(App()->GetUserConfig(), "General", "BrowserHWAccel", browserHWAccel); #endif if (WidgetChanged(ui->hotkeyFocusType)) { QString str = GetComboData(ui->hotkeyFocusType); - config_set_string(App()->GlobalConfig(), "General", + config_set_string(App()->GetUserConfig(), "General", "HotkeyFocusType", QT_TO_UTF8(str)); } #ifdef __APPLE__ if (WidgetChanged(ui->disableOSXVSync)) { bool disable = ui->disableOSXVSync->isChecked(); - config_set_bool(App()->GlobalConfig(), "Video", + config_set_bool(App()->GetUserConfig(), "Video", "DisableOSXVSync", disable); EnableOSXVSync(!disable); } if (WidgetChanged(ui->resetOSXVSync)) - config_set_bool(App()->GlobalConfig(), "Video", + config_set_bool(App()->GetUserConfig(), "Video", "ResetOSXVSyncOnExit", ui->resetOSXVSync->isChecked()); #endif @@ -3669,14 +3677,15 @@ void OBSBasicSettings::SaveAdvancedSettings() #ifdef _WIN32 if (WidgetChanged(ui->disableAudioDucking)) { bool disable = ui->disableAudioDucking->isChecked(); - config_set_bool(App()->GlobalConfig(), "Audio", + config_set_bool(App()->GetUserConfig(), "Audio", "DisableAudioDucking", disable); DisableAudioDucking(disable); } #endif if (WidgetChanged(ui->confirmOnExit)) - config_set_bool(GetGlobalConfig(), "General", "ConfirmOnExit", + config_set_bool(App()->GetUserConfig(), "General", + "ConfirmOnExit", ui->confirmOnExit->isChecked()); SaveEdit(ui->filenameFormatting, "Output", "FilenameFormatting"); @@ -4049,7 +4058,7 @@ void OBSBasicSettings::SaveAudioSettings() if (WidgetChanged(ui->lowLatencyBuffering)) { bool enableLLAudioBuffering = ui->lowLatencyBuffering->isChecked(); - config_set_bool(GetGlobalConfig(), "Audio", + config_set_bool(App()->GetUserConfig(), "Audio", "LowLatencyAudioBuffering", enableLLAudioBuffering); } @@ -4159,7 +4168,7 @@ void OBSBasicSettings::SaveSettings() main->ResetVideo(); config_save_safe(main->Config(), "tmp", nullptr); - config_save_safe(GetGlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); main->SaveProject(); if (Changed()) { @@ -4788,7 +4797,7 @@ void OBSBasicSettings::HideOBSWindowWarning(int state) if (loading || state == Qt::Unchecked) return; - if (config_get_bool(GetGlobalConfig(), "General", + if (config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutHideOBSFromCapture")) return; @@ -4796,9 +4805,9 @@ void OBSBasicSettings::HideOBSWindowWarning(int state) this, QTStr("Basic.Settings.General.HideOBSWindowsFromCapture"), QTStr("Basic.Settings.General.HideOBSWindowsFromCapture.Message")); - config_set_bool(GetGlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutHideOBSFromCapture", true); - config_save_safe(GetGlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); } /* diff --git a/UI/window-dock-youtube-app.cpp b/UI/window-dock-youtube-app.cpp index 498a68a057597d..888c329ce2cf56 100644 --- a/UI/window-dock-youtube-app.cpp +++ b/UI/window-dock-youtube-app.cpp @@ -431,16 +431,16 @@ void YouTubeAppDock::CleanupYouTubeUrls() // remove legacy YouTube Browser Docks (once) bool youtube_cleanup_done = config_get_bool( - App()->GlobalConfig(), "General", "YtDockCleanupDone"); + App()->GetUserConfig(), "General", "YtDockCleanupDone"); if (youtube_cleanup_done) return; - config_set_bool(App()->GlobalConfig(), "General", "YtDockCleanupDone", + config_set_bool(App()->GetUserConfig(), "General", "YtDockCleanupDone", true); const char *jsonStr = config_get_string( - App()->GlobalConfig(), "BasicWindow", "ExtraBrowserDocks"); + App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks"); if (!jsonStr) return; @@ -472,7 +472,7 @@ void YouTubeAppDock::CleanupYouTubeUrls() OBSMessageBox::warning(OBSBasic::Get(), msg_title, msg_text); std::string output = save_array.dump(); - config_set_string(App()->GlobalConfig(), "BasicWindow", + config_set_string(App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks", output.c_str()); } } diff --git a/UI/window-dock.cpp b/UI/window-dock.cpp index 7e2aeff89e9e39..37a9b3dd9ce8fa 100644 --- a/UI/window-dock.cpp +++ b/UI/window-dock.cpp @@ -20,13 +20,14 @@ void OBSDock::closeEvent(QCloseEvent *event) msgbox.exec(); if (cb->isChecked()) { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutClosingDocks", true); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + config_save_safe(App()->GetUserConfig(), "tmp", + nullptr); } }; - bool warned = config_get_bool(App()->GlobalConfig(), "General", + bool warned = config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutClosingDocks"); if (!OBSBasic::Get()->Closing() && !warned) { QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection, diff --git a/UI/window-extra-browsers.cpp b/UI/window-extra-browsers.cpp index e9b7265b62f278..c5bd177e217e14 100644 --- a/UI/window-extra-browsers.cpp +++ b/UI/window-extra-browsers.cpp @@ -473,7 +473,7 @@ void OBSBasic::ClearExtraBrowserDocks() void OBSBasic::LoadExtraBrowserDocks() { const char *jsonStr = config_get_string( - App()->GlobalConfig(), "BasicWindow", "ExtraBrowserDocks"); + App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks"); std::string err; Json json = Json::parse(jsonStr, err); @@ -511,7 +511,7 @@ void OBSBasic::SaveExtraBrowserDocks() } std::string output = Json(array).dump(); - config_set_string(App()->GlobalConfig(), "BasicWindow", + config_set_string(App()->GetUserConfig(), "BasicWindow", "ExtraBrowserDocks", output.c_str()); } diff --git a/UI/window-importer.cpp b/UI/window-importer.cpp index 61508a2acd5dc6..2dc9314f32d79f 100644 --- a/UI/window-importer.cpp +++ b/UI/window-importer.cpp @@ -437,7 +437,7 @@ OBSImporter::OBSImporter(QWidget *parent) ImportersInit(); - bool autoSearchPrompt = config_get_bool(App()->GlobalConfig(), + bool autoSearchPrompt = config_get_bool(App()->GetUserConfig(), "General", "AutoSearchPrompt"); if (!autoSearchPrompt) { @@ -446,18 +446,18 @@ OBSImporter::OBSImporter(QWidget *parent) QTStr("Importer.AutomaticCollectionText")); if (button == QMessageBox::Yes) { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", true); } else { - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch", false); } - config_set_bool(App()->GlobalConfig(), "General", + config_set_bool(App()->GetUserConfig(), "General", "AutoSearchPrompt", true); } - bool autoSearch = config_get_bool(App()->GlobalConfig(), "General", + bool autoSearch = config_get_bool(App()->GetUserConfig(), "General", "AutomaticCollectionSearch"); OBSImporterFiles f; diff --git a/UI/window-permissions.cpp b/UI/window-permissions.cpp index eabea1b15a1861..794cf27919c021 100644 --- a/UI/window-permissions.cpp +++ b/UI/window-permissions.cpp @@ -96,7 +96,7 @@ void OBSPermissions::on_accessibilityPermissionButton_clicked() void OBSPermissions::on_continueButton_clicked() { - config_set_int(GetGlobalConfig(), "General", + config_set_int(App()->GetAppConfig(), "General", "MacOSPermissionsDialogLastShown", MACOS_PERMISSIONS_DIALOG_VERSION); close(); diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index cf0d57b972de3c..94212a9d01a41c 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -28,7 +28,7 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, "destroy", OBSSourceDestroyed, this); } - isAlwaysOnTop = config_get_bool(GetGlobalConfig(), "BasicWindow", + isAlwaysOnTop = config_get_bool(App()->GetUserConfig(), "BasicWindow", "ProjectorAlwaysOnTop"); if (isAlwaysOnTop) @@ -144,7 +144,7 @@ void OBSProjector::SetHideCursor() if (savedMonitor == -1) return; - bool hideCursor = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool hideCursor = config_get_bool(App()->GetUserConfig(), "BasicWindow", "HideProjectorCursor"); if (hideCursor && type != ProjectorType::Multiview) @@ -331,20 +331,21 @@ void OBSProjector::EscapeTriggered() void OBSProjector::UpdateMultiview() { MultiviewLayout multiviewLayout = static_cast( - config_get_int(GetGlobalConfig(), "BasicWindow", + config_get_int(App()->GetUserConfig(), "BasicWindow", "MultiviewLayout")); - bool drawLabel = config_get_bool(GetGlobalConfig(), "BasicWindow", + bool drawLabel = config_get_bool(App()->GetUserConfig(), "BasicWindow", "MultiviewDrawNames"); - bool drawSafeArea = config_get_bool(GetGlobalConfig(), "BasicWindow", - "MultiviewDrawAreas"); + bool drawSafeArea = config_get_bool( + App()->GetUserConfig(), "BasicWindow", "MultiviewDrawAreas"); - mouseSwitching = config_get_bool(GetGlobalConfig(), "BasicWindow", + mouseSwitching = config_get_bool(App()->GetUserConfig(), "BasicWindow", "MultiviewMouseSwitch"); - transitionOnDoubleClick = config_get_bool( - GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick"); + transitionOnDoubleClick = config_get_bool(App()->GetUserConfig(), + "BasicWindow", + "TransitionOnDoubleClick"); multiview->Update(multiviewLayout, drawLabel, drawSafeArea); } From 607d37b4235a998580e1629e4b4f7124d8adacec Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 3 Sep 2024 16:29:30 +0200 Subject: [PATCH 0531/1073] UI: Rewrite profile system to enable user-provided storage location This change enables loading profiles from locations different than OBS' own configuration directory. It also rewrites profile management in the app to work off an in-memory collection of profiles found on disk and does not require iterating over directory contents for most profile interactions by the app. --- UI/api-interface.cpp | 580 +++++++------- UI/obs-app.cpp | 209 ++--- UI/window-basic-auto-config.cpp | 20 +- UI/window-basic-main-outputs.cpp | 19 +- UI/window-basic-main-profiles.cpp | 1200 +++++++++++++++-------------- UI/window-basic-main.cpp | 713 ++++++++++------- UI/window-basic-main.hpp | 107 ++- UI/window-basic-settings.cpp | 45 +- UI/window-youtube-actions.cpp | 78 +- 9 files changed, 1571 insertions(+), 1400 deletions(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index b41945f425062c..742e1051553713 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -16,7 +16,6 @@ template static T GetOBSRef(QListWidgetItem *item) return item->data(static_cast(QtDataRole::OBSRef)).value(); } -void EnumProfiles(function &&cb); void EnumSceneCollections(function &&cb); extern volatile bool streaming_active; @@ -218,29 +217,24 @@ struct OBSStudioAPI : obs_frontend_callbacks { void obs_frontend_get_profiles(std::vector &strings) override { - auto addProfile = [&](const char *name, const char *) { - strings.emplace_back(name); - return true; - }; + const OBSProfileCache &profiles = main->GetProfileCache(); - EnumProfiles(addProfile); + for (auto &[profileName, profile] : profiles) { + strings.emplace_back(profileName); + } } char *obs_frontend_get_current_profile(void) override { - const char *name = config_get_string(App()->GlobalConfig(), - "Basic", "Profile"); - return bstrdup(name); + const OBSProfile &profile = main->GetCurrentProfile(); + return bstrdup(profile.name.c_str()); } char *obs_frontend_get_current_profile_path(void) override { - char profilePath[512]; - int ret = GetProfilePath(profilePath, sizeof(profilePath), ""); - if (ret <= 0) - return nullptr; + const OBSProfile &profile = main->GetCurrentProfile(); - return bstrdup(profilePath); + return bstrdup(profile.path.u8string().c_str()); } void obs_frontend_set_current_profile(const char *profile) override @@ -510,353 +504,323 @@ struct OBSStudioAPI : obs_frontend_callbacks { config_t *obs_frontend_get_profile_config(void) override { - return main->basicConfig; - config_t *obs_frontend_get_global_config(void) override - { - blog(LOG_WARNING, - "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead."); - return App()->GetAppConfig(); - } - - config_t *obs_frontend_get_app_config(void) override - { - return App()->GetAppConfig(); - } + return main->activeConfiguration; + } - config_t *obs_frontend_get_user_config(void) override - { - return App()->GetUserConfig(); - } + config_t *obs_frontend_get_global_config(void) override + { + blog(LOG_WARNING, + "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead."); + return App()->GetAppConfig(); + } - void obs_frontend_open_projector(const char *type, int monitor, - const char *geometry, - const char *name) override - { - SavedProjectorInfo proj = { - ProjectorType::Preview, - monitor, - geometry ? geometry : "", - name ? name : "", - }; - if (type) { - if (astrcmpi(type, "Source") == 0) - proj.type = ProjectorType::Source; - else if (astrcmpi(type, "Scene") == 0) - proj.type = ProjectorType::Scene; - else if (astrcmpi(type, "StudioProgram") == 0) - proj.type = - ProjectorType::StudioProgram; - else if (astrcmpi(type, "Multiview") == 0) - proj.type = ProjectorType::Multiview; - } - QMetaObject::invokeMethod( - main, "OpenSavedProjector", WaitConnection(), - Q_ARG(SavedProjectorInfo *, &proj)); - } + config_t *obs_frontend_get_app_config(void) override + { + return App()->GetAppConfig(); + } - void obs_frontend_save(void) override - { - main->SaveProject(); - } + config_t *obs_frontend_get_user_config(void) override + { + return App()->GetUserConfig(); + } - void obs_frontend_defer_save_begin(void) override - { - QMetaObject::invokeMethod(main, "DeferSaveBegin"); - } + void obs_frontend_open_projector(const char *type, int monitor, + const char *geometry, + const char *name) override + { + SavedProjectorInfo proj = { + ProjectorType::Preview, + monitor, + geometry ? geometry : "", + name ? name : "", + }; + if (type) { + if (astrcmpi(type, "Source") == 0) + proj.type = ProjectorType::Source; + else if (astrcmpi(type, "Scene") == 0) + proj.type = ProjectorType::Scene; + else if (astrcmpi(type, "StudioProgram") == 0) + proj.type = ProjectorType::StudioProgram; + else if (astrcmpi(type, "Multiview") == 0) + proj.type = ProjectorType::Multiview; + } + QMetaObject::invokeMethod(main, "OpenSavedProjector", + WaitConnection(), + Q_ARG(SavedProjectorInfo *, &proj)); + } - void obs_frontend_defer_save_end(void) override - { - QMetaObject::invokeMethod(main, "DeferSaveEnd"); - } + void obs_frontend_save(void) override { main->SaveProject(); } - void obs_frontend_add_save_callback( - obs_frontend_save_cb callback, void *private_data) - override - { - size_t idx = GetCallbackIdx(saveCallbacks, callback, - private_data); - if (idx == (size_t)-1) - saveCallbacks.emplace_back(callback, - private_data); - } + void obs_frontend_defer_save_begin(void) override + { + QMetaObject::invokeMethod(main, "DeferSaveBegin"); + } - void obs_frontend_remove_save_callback( - obs_frontend_save_cb callback, void *private_data) - override - { - size_t idx = GetCallbackIdx(saveCallbacks, callback, - private_data); - if (idx == (size_t)-1) - return; + void obs_frontend_defer_save_end(void) override + { + QMetaObject::invokeMethod(main, "DeferSaveEnd"); + } - saveCallbacks.erase(saveCallbacks.begin() + idx); - } + void obs_frontend_add_save_callback(obs_frontend_save_cb callback, + void *private_data) override + { + size_t idx = + GetCallbackIdx(saveCallbacks, callback, private_data); + if (idx == (size_t)-1) + saveCallbacks.emplace_back(callback, private_data); + } - void obs_frontend_add_preload_callback( - obs_frontend_save_cb callback, void *private_data) - override - { - size_t idx = GetCallbackIdx(preloadCallbacks, callback, - private_data); - if (idx == (size_t)-1) - preloadCallbacks.emplace_back(callback, - private_data); - } + void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, + void *private_data) override + { + size_t idx = + GetCallbackIdx(saveCallbacks, callback, private_data); + if (idx == (size_t)-1) + return; - void obs_frontend_remove_preload_callback( - obs_frontend_save_cb callback, void *private_data) - override - { - size_t idx = GetCallbackIdx(preloadCallbacks, callback, - private_data); - if (idx == (size_t)-1) - return; + saveCallbacks.erase(saveCallbacks.begin() + idx); + } - preloadCallbacks.erase(preloadCallbacks.begin() + idx); - } + void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, + void *private_data) override + { + size_t idx = GetCallbackIdx(preloadCallbacks, callback, + private_data); + if (idx == (size_t)-1) + preloadCallbacks.emplace_back(callback, private_data); + } - void obs_frontend_push_ui_translation( - obs_frontend_translate_ui_cb translate) override - { - App()->PushUITranslation(translate); - } + void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, + void *private_data) override + { + size_t idx = GetCallbackIdx(preloadCallbacks, callback, + private_data); + if (idx == (size_t)-1) + return; - void obs_frontend_pop_ui_translation(void) override - { - App()->PopUITranslation(); - } + preloadCallbacks.erase(preloadCallbacks.begin() + idx); + } - void obs_frontend_set_streaming_service(obs_service_t * service) - override - { - main->SetService(service); - } + void obs_frontend_push_ui_translation( + obs_frontend_translate_ui_cb translate) override + { + App()->PushUITranslation(translate); + } - obs_service_t *obs_frontend_get_streaming_service(void) override - { - return main->GetService(); - } + void obs_frontend_pop_ui_translation(void) override + { + App()->PopUITranslation(); + } - void obs_frontend_save_streaming_service(void) override - { - main->SaveService(); - } + void obs_frontend_set_streaming_service(obs_service_t *service) override + { + main->SetService(service); + } - bool obs_frontend_preview_program_mode_active(void) override - { - return main->IsPreviewProgramMode(); - } + obs_service_t *obs_frontend_get_streaming_service(void) override + { + return main->GetService(); + } - void obs_frontend_set_preview_program_mode(bool enable) override - { - main->SetPreviewProgramMode(enable); - } + void obs_frontend_save_streaming_service(void) override + { + main->SaveService(); + } - void obs_frontend_preview_program_trigger_transition(void) - override - { - QMetaObject::invokeMethod(main, "TransitionClicked"); - } + bool obs_frontend_preview_program_mode_active(void) override + { + return main->IsPreviewProgramMode(); + } - bool obs_frontend_preview_enabled(void) override - { - return main->previewEnabled; - } + void obs_frontend_set_preview_program_mode(bool enable) override + { + main->SetPreviewProgramMode(enable); + } - void obs_frontend_set_preview_enabled(bool enable) override - { - if (main->previewEnabled != enable) - main->EnablePreviewDisplay(enable); - } + void obs_frontend_preview_program_trigger_transition(void) override + { + QMetaObject::invokeMethod(main, "TransitionClicked"); + } - obs_source_t *obs_frontend_get_current_preview_scene(void) - override - { - if (main->IsPreviewProgramMode()) { - OBSSource source = - main->GetCurrentSceneSource(); - return obs_source_get_ref(source); - } + bool obs_frontend_preview_enabled(void) override + { + return main->previewEnabled; + } - return nullptr; - } + void obs_frontend_set_preview_enabled(bool enable) override + { + if (main->previewEnabled != enable) + main->EnablePreviewDisplay(enable); + } - void obs_frontend_set_current_preview_scene(obs_source_t * - scene) override - { - if (main->IsPreviewProgramMode()) { - QMetaObject::invokeMethod( - main, "SetCurrentScene", - Q_ARG(OBSSource, OBSSource(scene)), - Q_ARG(bool, false)); - } + obs_source_t *obs_frontend_get_current_preview_scene(void) override + { + if (main->IsPreviewProgramMode()) { + OBSSource source = main->GetCurrentSceneSource(); + return obs_source_get_ref(source); } - void obs_frontend_take_screenshot(void) override - { - QMetaObject::invokeMethod(main, "Screenshot"); - } + return nullptr; + } - void obs_frontend_take_source_screenshot(obs_source_t * source) - override - { - QMetaObject::invokeMethod(main, "Screenshot", + void + obs_frontend_set_current_preview_scene(obs_source_t *scene) override + { + if (main->IsPreviewProgramMode()) { + QMetaObject::invokeMethod(main, "SetCurrentScene", Q_ARG(OBSSource, - OBSSource(source))); + OBSSource(scene)), + Q_ARG(bool, false)); } + } - obs_output_t *obs_frontend_get_virtualcam_output(void) override - { - OBSOutput output = - main->outputHandler->virtualCam.Get(); - return obs_output_get_ref(output); - } + void obs_frontend_take_screenshot(void) override + { + QMetaObject::invokeMethod(main, "Screenshot"); + } - void obs_frontend_start_virtualcam(void) override - { - QMetaObject::invokeMethod(main, "StartVirtualCam"); - } + void obs_frontend_take_source_screenshot(obs_source_t *source) override + { + QMetaObject::invokeMethod(main, "Screenshot", + Q_ARG(OBSSource, OBSSource(source))); + } - void obs_frontend_stop_virtualcam(void) override - { - QMetaObject::invokeMethod(main, "StopVirtualCam"); - } + obs_output_t *obs_frontend_get_virtualcam_output(void) override + { + OBSOutput output = main->outputHandler->virtualCam.Get(); + return obs_output_get_ref(output); + } - bool obs_frontend_virtualcam_active(void) override - { - return os_atomic_load_bool(&virtualcam_active); - } + void obs_frontend_start_virtualcam(void) override + { + QMetaObject::invokeMethod(main, "StartVirtualCam"); + } - void obs_frontend_reset_video(void) override - { - main->ResetVideo(); - } + void obs_frontend_stop_virtualcam(void) override + { + QMetaObject::invokeMethod(main, "StopVirtualCam"); + } - void obs_frontend_open_source_properties(obs_source_t * source) - override - { - QMetaObject::invokeMethod(main, "OpenProperties", - Q_ARG(OBSSource, - OBSSource(source))); - } + bool obs_frontend_virtualcam_active(void) override + { + return os_atomic_load_bool(&virtualcam_active); + } - void obs_frontend_open_source_filters(obs_source_t * source) - override - { - QMetaObject::invokeMethod(main, "OpenFilters", - Q_ARG(OBSSource, - OBSSource(source))); - } + void obs_frontend_reset_video(void) override { main->ResetVideo(); } - void obs_frontend_open_source_interaction(obs_source_t * source) - override - { - QMetaObject::invokeMethod(main, "OpenInteraction", - Q_ARG(OBSSource, - OBSSource(source))); - } + void obs_frontend_open_source_properties(obs_source_t *source) override + { + QMetaObject::invokeMethod(main, "OpenProperties", + Q_ARG(OBSSource, OBSSource(source))); + } - void obs_frontend_open_sceneitem_edit_transform( - obs_sceneitem_t * item) override - { - QMetaObject::invokeMethod(main, "OpenEditTransform", - Q_ARG(OBSSceneItem, - OBSSceneItem(item))); - } + void obs_frontend_open_source_filters(obs_source_t *source) override + { + QMetaObject::invokeMethod(main, "OpenFilters", + Q_ARG(OBSSource, OBSSource(source))); + } - char *obs_frontend_get_current_record_output_path(void) override - { - const char *recordOutputPath = - main->GetCurrentOutputPath(); + void obs_frontend_open_source_interaction(obs_source_t *source) override + { + QMetaObject::invokeMethod(main, "OpenInteraction", + Q_ARG(OBSSource, OBSSource(source))); + } - return bstrdup(recordOutputPath); - } + void obs_frontend_open_sceneitem_edit_transform( + obs_sceneitem_t *item) override + { + QMetaObject::invokeMethod(main, "OpenEditTransform", + Q_ARG(OBSSceneItem, + OBSSceneItem(item))); + } - const char *obs_frontend_get_locale_string(const char *string) - override - { - return Str(string); - } + char *obs_frontend_get_current_record_output_path(void) override + { + const char *recordOutputPath = main->GetCurrentOutputPath(); - bool obs_frontend_is_theme_dark(void) override - { - return App()->IsThemeDark(); - } + return bstrdup(recordOutputPath); + } - char *obs_frontend_get_last_recording(void) override - { - return bstrdup( - main->outputHandler->lastRecordingPath.c_str()); - } + const char *obs_frontend_get_locale_string(const char *string) override + { + return Str(string); + } - char *obs_frontend_get_last_screenshot(void) override - { - return bstrdup(main->lastScreenshot.c_str()); - } + bool obs_frontend_is_theme_dark(void) override + { + return App()->IsThemeDark(); + } - char *obs_frontend_get_last_replay(void) override - { - return bstrdup(main->lastReplay.c_str()); - } + char *obs_frontend_get_last_recording(void) override + { + return bstrdup(main->outputHandler->lastRecordingPath.c_str()); + } - void obs_frontend_add_undo_redo_action( - const char *name, const undo_redo_cb undo, - const undo_redo_cb redo, const char *undo_data, - const char *redo_data, bool repeatable) override - { - main->undo_s.add_action( - name, - [undo](const std::string &data) { - undo(data.c_str()); - }, - [redo](const std::string &data) { - redo(data.c_str()); - }, - undo_data, redo_data, repeatable); - } + char *obs_frontend_get_last_screenshot(void) override + { + return bstrdup(main->lastScreenshot.c_str()); + } - void on_load(obs_data_t * settings) override - { - for (size_t i = saveCallbacks.size(); i > 0; i--) { - auto cb = saveCallbacks[i - 1]; - cb.callback(settings, false, cb.private_data); - } - } + char *obs_frontend_get_last_replay(void) override + { + return bstrdup(main->lastReplay.c_str()); + } - void on_preload(obs_data_t * settings) override - { - for (size_t i = preloadCallbacks.size(); i > 0; i--) { - auto cb = preloadCallbacks[i - 1]; - cb.callback(settings, false, cb.private_data); - } + void obs_frontend_add_undo_redo_action(const char *name, + const undo_redo_cb undo, + const undo_redo_cb redo, + const char *undo_data, + const char *redo_data, + bool repeatable) override + { + main->undo_s.add_action( + name, + [undo](const std::string &data) { undo(data.c_str()); }, + [redo](const std::string &data) { redo(data.c_str()); }, + undo_data, redo_data, repeatable); + } + + void on_load(obs_data_t *settings) override + { + for (size_t i = saveCallbacks.size(); i > 0; i--) { + auto cb = saveCallbacks[i - 1]; + cb.callback(settings, false, cb.private_data); } + } - void on_save(obs_data_t * settings) override - { - for (size_t i = saveCallbacks.size(); i > 0; i--) { - auto cb = saveCallbacks[i - 1]; - cb.callback(settings, true, cb.private_data); - } + void on_preload(obs_data_t *settings) override + { + for (size_t i = preloadCallbacks.size(); i > 0; i--) { + auto cb = preloadCallbacks[i - 1]; + cb.callback(settings, false, cb.private_data); } + } - void on_event(enum obs_frontend_event event) override - { - if (main->disableSaving && - event != - OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP && - event != OBS_FRONTEND_EVENT_EXIT) - return; - - for (size_t i = callbacks.size(); i > 0; i--) { - auto cb = callbacks[i - 1]; - cb.callback(event, cb.private_data); - } + void on_save(obs_data_t *settings) override + { + for (size_t i = saveCallbacks.size(); i > 0; i--) { + auto cb = saveCallbacks[i - 1]; + cb.callback(settings, true, cb.private_data); } - }; + } - obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main) + void on_event(enum obs_frontend_event event) override { - obs_frontend_callbacks *api = new OBSStudioAPI(main); - obs_frontend_set_callbacks_internal(api); - return api; + if (main->disableSaving && + event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP && + event != OBS_FRONTEND_EVENT_EXIT) + return; + + for (size_t i = callbacks.size(); i > 0; i--) { + auto cb = callbacks[i - 1]; + cb.callback(event, cb.private_data); + } } +}; + +obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main) +{ + obs_frontend_callbacks *api = new OBSStudioAPI(main); + obs_frontend_set_callbacks_internal(api); + return api; +} diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 3417f15530b5eb..f4b9bf50986d10 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -609,109 +609,41 @@ static bool MakeUserDirs() return true; } -static bool MakeUserProfileDirs() -{ - char path[512]; - - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles") <= 0) - return false; - if (!do_mkdir(path)) - return false; +constexpr std::string_view OBSProfileSubDirectory = "obs-studio/basic/profiles"; +constexpr std::string_view OBSScenesSubDirectory = "obs-studio/basic/scenes"; - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes") <= 0) - return false; - if (!do_mkdir(path)) - return false; - - return true; -} - -static string GetProfileDirFromName(const char *name) +static bool MakeUserProfileDirs() { - string outputPath; - os_glob_t *glob; - char path[512]; - - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles") <= 0) - return outputPath; - - strcat(path, "/*"); - - if (os_glob(path, 0, &glob) != 0) - return outputPath; - - for (size_t i = 0; i < glob->gl_pathc; i++) { - struct os_globent ent = glob->gl_pathv[i]; - if (!ent.directory) - continue; - - strcpy(path, ent.path); - strcat(path, "/basic.ini"); - - ConfigFile config; - if (config.Open(path, CONFIG_OPEN_EXISTING) != 0) - continue; - - const char *curName = - config_get_string(config, "General", "Name"); - if (astrcmpi(curName, name) == 0) { - outputPath = ent.path; - break; + const std::filesystem::path userProfilePath = + App()->userProfilesLocation / + std::filesystem::u8path(OBSProfileSubDirectory); + const std::filesystem::path userScenesPath = + App()->userScenesLocation / + std::filesystem::u8path(OBSScenesSubDirectory); + + if (!std::filesystem::exists(userProfilePath)) { + try { + std::filesystem::create_directories(userProfilePath); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to create user profile directory '%s'\n%s", + userProfilePath.u8string().c_str(), error.what()); + return false; } } - os_globfree(glob); - - if (!outputPath.empty()) { - replace(outputPath.begin(), outputPath.end(), '\\', '/'); - const char *start = strrchr(outputPath.c_str(), '/'); - if (start) - outputPath.erase(0, start - outputPath.c_str() + 1); - } - - return outputPath; -} - -static string GetSceneCollectionFileFromName(const char *name) -{ - string outputPath; - os_glob_t *glob; - char path[512]; - - if (GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes") <= 0) - return outputPath; - - strcat(path, "/*.json"); - - if (os_glob(path, 0, &glob) != 0) - return outputPath; - - for (size_t i = 0; i < glob->gl_pathc; i++) { - struct os_globent ent = glob->gl_pathv[i]; - if (ent.directory) - continue; - - OBSDataAutoRelease data = - obs_data_create_from_json_file_safe(ent.path, "bak"); - const char *curName = obs_data_get_string(data, "name"); - - if (astrcmpi(name, curName) == 0) { - outputPath = ent.path; - break; + if (!std::filesystem::exists(userScenesPath)) { + try { + std::filesystem::create_directories(userScenesPath); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to create user scene collection directory '%s'\n%s", + userScenesPath.u8string().c_str(), error.what()); + return false; } } - os_globfree(glob); - - if (!outputPath.empty()) { - outputPath.resize(outputPath.size() - 5); - replace(outputPath.begin(), outputPath.end(), '\\', '/'); - const char *start = strrchr(outputPath.c_str(), '/'); - if (start) - outputPath.erase(0, start - outputPath.c_str() + 1); - } - - return outputPath; + return true; } bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) @@ -1212,56 +1144,77 @@ OBSApp::~OBSApp() static void move_basic_to_profiles(void) { char path[512]; - char new_path[512]; - os_glob_t *glob; - /* if not first time use */ - if (GetConfigPath(path, 512, "obs-studio/basic") <= 0) - return; - if (!os_file_exists(path)) + if (GetAppConfigPath(path, 512, "obs-studio/basic") <= 0) { return; + } - /* if the profiles directory doesn't already exist */ - if (GetConfigPath(new_path, 512, "obs-studio/basic/profiles") <= 0) - return; - if (os_file_exists(new_path)) - return; + const std::filesystem::path basicPath = std::filesystem::u8path(path); - if (os_mkdir(new_path) == MKDIR_ERROR) + if (!std::filesystem::exists(basicPath)) { return; + } - strcat(new_path, "/"); - strcat(new_path, Str("Untitled")); - if (os_mkdir(new_path) == MKDIR_ERROR) - return; + const std::filesystem::path profilesPath = + App()->userProfilesLocation / + std::filesystem::u8path("obs-studio/basic/profiles"); - strcat(path, "/*.*"); - if (os_glob(path, 0, &glob) != 0) + if (std::filesystem::exists(profilesPath)) { return; + } - strcpy(path, new_path); + try { + std::filesystem::create_directories(profilesPath); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to create profiles directory for migration from basic profile\n%s", + error.what()); + return; + } - for (size_t i = 0; i < glob->gl_pathc; i++) { - struct os_globent ent = glob->gl_pathv[i]; - char *file; + const std::filesystem::path newProfilePath = + profilesPath / std::filesystem::u8path(Str("Untitled")); - if (ent.directory) + for (auto &entry : std::filesystem::directory_iterator(basicPath)) { + if (entry.is_directory()) { continue; + } - file = strrchr(ent.path, '/'); - if (!file++) + if (entry.path().filename().u8string() == "scenes.json") { continue; + } - if (astrcmpi(file, "scenes.json") == 0) - continue; + if (!std::filesystem::exists(newProfilePath)) { + try { + std::filesystem::create_directory( + newProfilePath); + } catch ( + const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to create profile directory for 'Untitled'\n%s", + error.what()); + return; + } + } - strcpy(new_path, path); - strcat(new_path, "/"); - strcat(new_path, file); - os_rename(ent.path, new_path); - } + const filesystem::path destinationFile = + newProfilePath / entry.path().filename(); - os_globfree(glob); + const auto copyOptions = + std::filesystem::copy_options::overwrite_existing; + + try { + std::filesystem::copy(entry.path(), destinationFile, + copyOptions); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to copy basic profile file '%s' to new profile 'Untitled'\n%s", + entry.path().filename().u8string().c_str(), + error.what()); + + return; + } + } } static void move_basic_to_scene_collections(void) diff --git a/UI/window-basic-auto-config.cpp b/UI/window-basic-auto-config.cpp index 82907841d25de0..1133cddd6b3902 100644 --- a/UI/window-basic-auto-config.cpp +++ b/UI/window-basic-auto-config.cpp @@ -39,18 +39,24 @@ extern QCefCookieManager *panel_cookies; /* ------------------------------------------------------------------------- */ -#define SERVICE_PATH "service.json" +constexpr std::string_view OBSServiceFileName = "service.json"; static OBSData OpenServiceSettings(std::string &type) { - char serviceJsonPath[512]; - int ret = GetProfilePath(serviceJsonPath, sizeof(serviceJsonPath), - SERVICE_PATH); - if (ret <= 0) + const OBSBasic *basic = + reinterpret_cast(App()->GetMainWindow()); + const OBSProfile ¤tProfile = basic->GetCurrentProfile(); + + const std::filesystem::path jsonFilePath = + currentProfile.path / + std::filesystem::u8path(OBSServiceFileName); + + if (!std::filesystem::exists(jsonFilePath)) { return OBSData(); + } - OBSDataAutoRelease data = - obs_data_create_from_json_file_safe(serviceJsonPath, "bak"); + OBSDataAutoRelease data = obs_data_create_from_json_file_safe( + jsonFilePath.u8string().c_str(), "bak"); obs_data_set_default_string(data, "type", "rtmp_common"); type = obs_data_get_string(data, "type"); diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 93ab1d7e33377e..9177f108421d61 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -1616,19 +1616,28 @@ struct AdvancedOutput : BasicOutputHandler { static OBSData GetDataFromJsonFile(const char *jsonFile) { - char fullPath[512]; + const OBSBasic *basic = + reinterpret_cast(App()->GetMainWindow()); + + const OBSProfile ¤tProfile = basic->GetCurrentProfile(); + + const std::filesystem::path jsonFilePath = + currentProfile.path / std::filesystem::u8path(jsonFile); + OBSDataAutoRelease data = nullptr; - int ret = GetProfilePath(fullPath, sizeof(fullPath), jsonFile); - if (ret > 0) { - BPtr jsonData = os_quick_read_utf8_file(fullPath); + if (!jsonFilePath.empty()) { + BPtr jsonData = os_quick_read_utf8_file( + jsonFilePath.u8string().c_str()); + if (!!jsonData) { data = obs_data_create_from_json(jsonData); } } - if (!data) + if (!data) { data = obs_data_create(); + } return data.Get(); } diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index 7492e51d7e4c48..3a54e542269415 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -15,6 +15,11 @@ along with this program. If not, see . ******************************************************************************/ +#include +#include +#include +#include +#include #include #include #include @@ -26,792 +31,767 @@ #include "window-basic-auto-config.hpp" #include "window-namedialog.hpp" +// MARK: Constant Expressions + +constexpr std::string_view OBSProfilePath = "/obs-studio/basic/profiles/"; +constexpr std::string_view OBSProfileSettingsFile = "basic.ini"; + +// MARK: Forward Declarations + extern void DestroyPanelCookieManager(); extern void DuplicateCurrentCookieProfile(ConfigFile &config); extern void CheckExistingCookieId(); extern void DeleteCookies(); -void EnumProfiles(std::function &&cb) +// MARK: - Main Profile Management Functions + +void OBSBasic::SetupNewProfile(const std::string &profileName, bool useWizard) { - char path[512]; - os_glob_t *glob; + const OBSProfile &newProfile = CreateProfile(profileName); - int ret = GetConfigPath(path, sizeof(path), - "obs-studio/basic/profiles/*"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get profiles config path"); - return; - } + config_set_bool(App()->GetUserConfig(), "Basic", "ConfigOnNewProfile", + useWizard); - if (os_glob(path, 0, &glob) != 0) { - blog(LOG_WARNING, "Failed to glob profiles"); - return; + ActivateProfile(newProfile, true); + + blog(LOG_INFO, "Created profile '%s' (clean, %s)", + newProfile.name.c_str(), newProfile.directoryName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); + + if (useWizard) { + AutoConfig wizard(this); + wizard.setModal(true); + wizard.show(); + wizard.exec(); } +} - for (size_t i = 0; i < glob->gl_pathc; i++) { - const char *filePath = glob->gl_pathv[i].path; - const char *dirName = strrchr(filePath, '/') + 1; +void OBSBasic::SetupDuplicateProfile(const std::string &profileName) +{ + const OBSProfile &newProfile = CreateProfile(profileName); + const OBSProfile ¤tProfile = GetCurrentProfile(); - if (!glob->gl_pathv[i].directory) - continue; + const auto copyOptions = + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::overwrite_existing; - if (strcmp(dirName, ".") == 0 || strcmp(dirName, "..") == 0) - continue; + try { + std::filesystem::copy(currentProfile.path, newProfile.path, + copyOptions); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_DEBUG, "%s", error.what()); + throw std::logic_error( + "Failed to copy files for cloned profile: " + + newProfile.name); + } - std::string file = filePath; - file += "/basic.ini"; + ActivateProfile(newProfile); - ConfigFile config; - int ret = config.Open(file.c_str(), CONFIG_OPEN_EXISTING); - if (ret != CONFIG_SUCCESS) - continue; + blog(LOG_INFO, "Created profile '%s' (duplicate, %s)", + newProfile.name.c_str(), newProfile.directoryName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); +} + +void OBSBasic::SetupRenameProfile(const std::string &profileName) +{ + const OBSProfile &newProfile = CreateProfile(profileName); + const OBSProfile currentProfile = GetCurrentProfile(); - const char *name = config_get_string(config, "General", "Name"); - if (!name) - name = strrchr(filePath, '/') + 1; + const auto copyOptions = + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::overwrite_existing; - if (!cb(name, filePath)) - break; + try { + std::filesystem::copy(currentProfile.path, newProfile.path, + copyOptions); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_DEBUG, "%s", error.what()); + throw std::logic_error("Failed to copy files for profile: " + + currentProfile.name); } - os_globfree(glob); -} + profiles.erase(currentProfile.name); -static bool GetProfileDir(const char *findName, const char *&profileDir) -{ - bool found = false; - auto func = [&](const char *name, const char *path) { - if (strcmp(name, findName) == 0) { - found = true; - profileDir = strrchr(path, '/') + 1; - return false; - } - return true; - }; + ActivateProfile(newProfile); + RemoveProfile(currentProfile); - EnumProfiles(func); - return found; -} + blog(LOG_INFO, "Renamed profile '%s' to '%s' (%s)", + currentProfile.name.c_str(), newProfile.name.c_str(), + newProfile.directoryName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); -static bool ProfileExists(const char *findName) -{ - const char *profileDir = nullptr; - return GetProfileDir(findName, profileDir); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_RENAMED); } -static bool AskForProfileName(QWidget *parent, std::string &name, - const char *title, const char *text, - const bool showWizard, bool &wizardChecked, - const char *oldName = nullptr) +// MARK: - Profile File Management Functions + +const OBSProfile &OBSBasic::CreateProfile(const std::string &profileName) { - for (;;) { - bool success = false; - - if (showWizard) { - success = NameDialog::AskForNameWithOption( - parent, title, text, name, - QTStr("AddProfile.WizardCheckbox"), - wizardChecked, QT_UTF8(oldName)); - } else { - success = NameDialog::AskForName( - parent, title, text, name, QT_UTF8(oldName)); - } + if (const auto &foundProfile = GetProfileByName(profileName)) { + throw std::invalid_argument("Profile already exists: " + + profileName); + } - if (!success) { - return false; - } - if (name.empty()) { - OBSMessageBox::warning(parent, - QTStr("NoNameEntered.Title"), - QTStr("NoNameEntered.Text")); - continue; - } - if (ProfileExists(name.c_str())) { - OBSMessageBox::warning(parent, - QTStr("NameExists.Title"), - QTStr("NameExists.Text")); - continue; - } - break; + std::string directoryName; + if (!GetFileSafeName(profileName.c_str(), directoryName)) { + throw std::invalid_argument( + "Failed to create safe directory for new profile: " + + profileName); } - return true; -} -static bool FindSafeProfileDirName(const std::string &profileName, - std::string &dirName) -{ - char path[512]; - int ret; + std::string profileDirectory; + profileDirectory.reserve(App()->userProfilesLocation.u8string().size() + + OBSProfilePath.size() + directoryName.size()); + profileDirectory.append(App()->userProfilesLocation.u8string()) + .append(OBSProfilePath) + .append(directoryName); - if (ProfileExists(profileName.c_str())) { - blog(LOG_WARNING, "Profile '%s' exists", profileName.c_str()); - return false; + if (!GetClosestUnusedFileName(profileDirectory, nullptr)) { + throw std::invalid_argument( + "Failed to get closest directory name for new profile: " + + profileName); } - if (!GetFileSafeName(profileName.c_str(), dirName)) { - blog(LOG_WARNING, "Failed to create safe file name for '%s'", - profileName.c_str()); - return false; - } + const std::filesystem::path profileDirectoryPath = + std::filesystem::u8path(profileDirectory); - ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/profiles/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get profiles config path"); - return false; + try { + std::filesystem::create_directory(profileDirectoryPath); + } catch (const std::filesystem::filesystem_error error) { + throw std::logic_error( + "Failed to create directory for new profile: " + + profileDirectory); } - dirName.insert(0, path); + const std::filesystem::path profileFile = + profileDirectoryPath / + std::filesystem::u8path(OBSProfileSettingsFile); - if (!GetClosestUnusedFileName(dirName, nullptr)) { - blog(LOG_WARNING, "Failed to get closest file name for %s", - dirName.c_str()); - return false; - } + auto [iterator, success] = profiles.try_emplace( + profileName, + OBSProfile{profileName, + profileDirectoryPath.filename().u8string(), + profileDirectoryPath, profileFile}); + + OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); - dirName.erase(0, ret); - return true; + return iterator->second; } -static bool CopyProfile(const char *fromPartial, const char *to) +void OBSBasic::RemoveProfile(OBSProfile profile) { - os_glob_t *glob; - char path[514]; - char dir[512]; - int ret; - - ret = GetConfigPath(dir, sizeof(dir), "obs-studio/basic/profiles/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get profiles config path"); - return false; + try { + std::filesystem::remove_all(profile.path); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_DEBUG, "%s", error.what()); + throw std::logic_error("Failed to remove profile directory: " + + profile.directoryName); } - snprintf(path, sizeof(path), "%s%s/*", dir, fromPartial); + blog(LOG_INFO, "------------------------------------------------"); + blog(LOG_INFO, "Removed profile '%s' (%s)", profile.name.c_str(), + profile.directoryName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); +} + +// MARK: - Profile UI Handling Functions - if (os_glob(path, 0, &glob) != 0) { - blog(LOG_WARNING, "Failed to glob profile '%s'", fromPartial); +bool OBSBasic::CreateNewProfile(const QString &name) +{ + try { + SetupNewProfile(name.toStdString()); + return true; + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); return false; } +} - for (size_t i = 0; i < glob->gl_pathc; i++) { - const char *filePath = glob->gl_pathv[i].path; - if (glob->gl_pathv[i].directory) - continue; - - ret = snprintf(path, sizeof(path), "%s/%s", to, - strrchr(filePath, '/') + 1); - if (ret > 0) { - if (os_copyfile(filePath, path) != 0) { - blog(LOG_WARNING, - "CopyProfile: Failed to " - "copy file %s to %s", - filePath, path); - } - } +bool OBSBasic::CreateDuplicateProfile(const QString &name) +{ + try { + SetupDuplicateProfile(name.toStdString()); + return true; + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; } - - os_globfree(glob); - - return true; } -static bool ProfileNeedsRestart(config_t *newConfig, QString &settings) +void OBSBasic::DeleteProfile(const QString &name) { - OBSBasic *main = OBSBasic::Get(); - - const char *oldSpeakers = - config_get_string(main->Config(), "Audio", "ChannelSetup"); - uint oldSampleRate = - config_get_uint(main->Config(), "Audio", "SampleRate"); + const std::string_view currentProfileName{ + config_get_string(App()->GetUserConfig(), "Basic", "Profile")}; - const char *newSpeakers = - config_get_string(newConfig, "Audio", "ChannelSetup"); - uint newSampleRate = config_get_uint(newConfig, "Audio", "SampleRate"); + if (currentProfileName == name.toStdString()) { + on_actionRemoveProfile_triggered(); + return; + } - auto appendSetting = [&settings](const char *name) { - settings += QStringLiteral("\n") + QTStr(name); - }; + auto foundProfile = GetProfileByName(name.toStdString()); - bool result = false; - if (oldSpeakers != NULL && newSpeakers != NULL) { - result = strcmp(oldSpeakers, newSpeakers) != 0; - appendSetting("Basic.Settings.Audio.Channels"); - } - if (oldSampleRate != 0 && newSampleRate != 0) { - result |= oldSampleRate != newSampleRate; - appendSetting("Basic.Settings.Audio.SampleRate"); + if (!foundProfile) { + blog(LOG_ERROR, "Invalid profile name: %s", QT_TO_UTF8(name)); + return; } - return result; -} + RemoveProfile(foundProfile.value()); + profiles.erase(name.toStdString()); -bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text, - const char *init_text, bool rename) -{ - std::string name; - - bool showWizardChecked = config_get_bool(App()->GlobalConfig(), "Basic", - "ConfigOnNewProfile"); + RefreshProfiles(); - if (!AskForProfileName(this, name, title, text, create_new, - showWizardChecked, init_text)) - return false; + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); - return CreateProfile(name, create_new, showWizardChecked, rename); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); } -bool OBSBasic::CreateProfile(const std::string &newName, bool create_new, - bool showWizardChecked, bool rename) +void OBSBasic::ChangeProfile() { - std::string newDir; - std::string newPath; - ConfigFile config; - - if (!FindSafeProfileDirName(newName, newDir)) - return false; + QAction *action = reinterpret_cast(sender()); - if (create_new) { - config_set_bool(App()->GlobalConfig(), "Basic", - "ConfigOnNewProfile", showWizardChecked); + if (!action) { + return; } - std::string curDir = - config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir"); + const std::string_view currentProfileName{ + config_get_string(App()->GetUserConfig(), "Basic", "Profile")}; + const std::string selectedProfileName{action->text().toStdString()}; - char baseDir[512]; - int ret = GetConfigPath(baseDir, sizeof(baseDir), - "obs-studio/basic/profiles/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get profiles config path"); - return false; + if (currentProfileName == selectedProfileName) { + action->setChecked(true); + return; } - newPath = baseDir; - newPath += newDir; + const std::optional foundProfile = + GetProfileByName(selectedProfileName); - if (os_mkdir(newPath.c_str()) < 0) { - blog(LOG_WARNING, "Failed to create profile directory '%s'", - newDir.c_str()); - return false; + if (!foundProfile) { + const std::string errorMessage{"Selected profile not found: "}; + + throw std::invalid_argument(errorMessage + + currentProfileName.data()); } - if (!create_new) - CopyProfile(curDir.c_str(), newPath.c_str()); + const OBSProfile &selectedProfile = foundProfile.value(); - newPath += "/basic.ini"; + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); - if (config.Open(newPath.c_str(), CONFIG_OPEN_ALWAYS) != 0) { - blog(LOG_ERROR, "Failed to open new config file '%s'", - newDir.c_str()); - return false; - } + ActivateProfile(selectedProfile, true); - if (!rename) - OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); + blog(LOG_INFO, "Switched to profile '%s' (%s)", + selectedProfile.name.c_str(), + selectedProfile.directoryName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); +} - config_set_string(App()->GlobalConfig(), "Basic", "Profile", - newName.c_str()); - config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", - newDir.c_str()); +void OBSBasic::RefreshProfiles(bool refreshCache) +{ + std::string_view currentProfileName{ + config_get_string(App()->GetUserConfig(), "Basic", "Profile")}; - Auth::Save(); - if (create_new) { - auth.reset(); - DestroyPanelCookieManager(); -#ifdef YOUTUBE_ENABLED - if (youtubeAppDock) - DeleteYouTubeAppDock(); -#endif - } else if (!rename) { - DuplicateCurrentCookieProfile(config); - } + QList menuActions = ui->profileMenu->actions(); - config_set_string(config, "General", "Name", newName.c_str()); - basicConfig.SaveSafe("tmp"); - config.SaveSafe("tmp"); - config.Swap(basicConfig); - InitBasicConfigDefaults(); - InitBasicConfigDefaults2(); - RefreshProfiles(); + for (auto &action : menuActions) { + QVariant variant = action->property("file_name"); - if (create_new) - ResetProfileData(); + if (variant.typeName() != nullptr) { + delete action; + } + } - blog(LOG_INFO, "Created profile '%s' (%s, %s)", newName.c_str(), - create_new ? "clean" : "duplicate", newDir.c_str()); - blog(LOG_INFO, "------------------------------------------------"); + if (refreshCache) { + RefreshProfileCache(); + } - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); - UpdateTitleBar(); - UpdateVolumeControlsDecayRate(); + size_t numAddedProfiles = 0; + for (auto &[profileName, profile] : profiles) { + QAction *action = + new QAction(QString().fromStdString(profileName), this); + action->setProperty( + "file_name", + QString().fromStdString(profile.directoryName)); + connect(action, &QAction::triggered, this, + &OBSBasic::ChangeProfile); + action->setCheckable(true); + action->setChecked(profileName == currentProfileName); - Auth::Load(); + ui->profileMenu->addAction(action); - // Run auto configuration setup wizard when a new profile is made to assist - // setting up blank settings - if (create_new && showWizardChecked) { - AutoConfig wizard(this); - wizard.setModal(true); - wizard.show(); - wizard.exec(); + numAddedProfiles += 1; } - if (!rename) { - OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); - OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED); - } - return true; + ui->actionRemoveProfile->setEnabled(numAddedProfiles > 1); } -bool OBSBasic::NewProfile(const QString &name) -{ - return CreateProfile(name.toStdString(), true, false, false); -} +// MARK: - Profile Cache Functions -bool OBSBasic::DuplicateProfile(const QString &name) +/// Refreshes profile cache data with profile state found on local file system. +void OBSBasic::RefreshProfileCache() { - return CreateProfile(name.toStdString(), false, false, false); -} + std::map foundProfiles{}; -void OBSBasic::DeleteProfile(const char *profileName, const char *profileDir) -{ - char profilePath[512]; - char basePath[512]; + const std::filesystem::path profilesPath = + App()->userProfilesLocation / + std::filesystem::u8path(OBSProfilePath.substr(1)); - int ret = GetConfigPath(basePath, sizeof(basePath), - "obs-studio/basic/profiles"); - if (ret <= 0) { + if (!std::filesystem::exists(profilesPath)) { blog(LOG_WARNING, "Failed to get profiles config path"); return; } - ret = snprintf(profilePath, sizeof(profilePath), "%s/%s/*", basePath, - profileDir); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get path for profile dir '%s'", - profileDir); - return; - } + for (const auto &entry : + std::filesystem::directory_iterator(profilesPath)) { + if (!entry.is_directory()) { + continue; + } - os_glob_t *glob; - if (os_glob(profilePath, 0, &glob) != 0) { - blog(LOG_WARNING, "Failed to glob profile dir '%s'", - profileDir); - return; - } + std::string profileCandidate; + profileCandidate.reserve(entry.path().u8string().size() + + OBSProfileSettingsFile.size() + 1); + profileCandidate.append(entry.path().u8string()) + .append("/") + .append(OBSProfileSettingsFile); - for (size_t i = 0; i < glob->gl_pathc; i++) { - const char *filePath = glob->gl_pathv[i].path; + ConfigFile config; - if (glob->gl_pathv[i].directory) + if (config.Open(profileCandidate.c_str(), + CONFIG_OPEN_EXISTING) != CONFIG_SUCCESS) { continue; + } - os_unlink(filePath); - } + std::string candidateName; + const char *configName = + config_get_string(config, "General", "Name"); - os_globfree(glob); + if (configName) { + candidateName = configName; + } else { + candidateName = entry.path().filename().u8string(); + } - ret = snprintf(profilePath, sizeof(profilePath), "%s/%s", basePath, - profileDir); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get path for profile dir '%s'", - profileDir); - return; + foundProfiles.try_emplace( + candidateName, + OBSProfile{candidateName, + entry.path().filename().u8string(), + entry.path(), profileCandidate}); } - os_rmdir(profilePath); - - blog(LOG_INFO, "------------------------------------------------"); - blog(LOG_INFO, "Removed profile '%s' (%s)", profileName, profileDir); - blog(LOG_INFO, "------------------------------------------------"); + profiles.swap(foundProfiles); } -void OBSBasic::DeleteProfile(const QString &profileName) +const OBSProfile &OBSBasic::GetCurrentProfile() const { - std::string name = profileName.toStdString(); - const char *curName = - config_get_string(App()->GlobalConfig(), "Basic", "Profile"); + std::string currentProfileName{ + config_get_string(App()->GetUserConfig(), "Basic", "Profile")}; - if (strcmp(curName, name.c_str()) == 0) { - on_actionRemoveProfile_triggered(true); - return; + if (currentProfileName.empty()) { + throw std::invalid_argument( + "No valid profile name in configuration Basic->Profile"); } - const char *profileDir = nullptr; - if (!GetProfileDir(name.c_str(), profileDir)) { - blog(LOG_WARNING, "Profile '%s' not found", name.c_str()); - return; - } + const auto &foundProfile = profiles.find(currentProfileName); - if (!profileDir) { - blog(LOG_WARNING, "Failed to get profile dir for profile '%s'", - name.c_str()); - return; + if (foundProfile != profiles.end()) { + return foundProfile->second; + } else { + throw std::invalid_argument( + "Profile not found in profile list: " + + currentProfileName); } - - DeleteProfile(name.c_str(), profileDir); - RefreshProfiles(); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); - OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); } -void OBSBasic::RefreshProfiles() +std::optional +OBSBasic::GetProfileByName(const std::string &profileName) const { - QList menuActions = ui->profileMenu->actions(); - int count = 0; + auto foundProfile = profiles.find(profileName); - for (int i = 0; i < menuActions.count(); i++) { - QVariant v = menuActions[i]->property("file_name"); - if (v.typeName() != nullptr) - delete menuActions[i]; + if (foundProfile == profiles.end()) { + return {}; + } else { + return foundProfile->second; } +} - const char *curName = - config_get_string(App()->GlobalConfig(), "Basic", "Profile"); - - auto addProfile = [&](const char *name, const char *path) { - std::string file = strrchr(path, '/') + 1; - - QAction *action = new QAction(QT_UTF8(name), this); - action->setProperty("file_name", QT_UTF8(path)); - connect(action, &QAction::triggered, this, - &OBSBasic::ChangeProfile); - action->setCheckable(true); +std::optional +OBSBasic::GetProfileByDirectoryName(const std::string &directoryName) const +{ + for (auto &[iterator, profile] : profiles) { + if (profile.directoryName == directoryName) { + return profile; + } + } - action->setChecked(strcmp(name, curName) == 0); + return {}; +} - ui->profileMenu->addAction(action); - count++; - return true; - }; +// MARK: - Qt Slot Functions - EnumProfiles(addProfile); +void OBSBasic::on_actionNewProfile_triggered() +{ + bool useProfileWizard = config_get_bool(App()->GetUserConfig(), "Basic", + "ConfigOnNewProfile"); - ui->actionRemoveProfile->setEnabled(count > 1); -} + const OBSPromptCallback profilePromptCallback = + [this](const OBSPromptResult &result) { + if (GetProfileByName(result.promptValue)) { + return false; + } -void OBSBasic::ResetProfileData() -{ - ResetVideo(); - service = nullptr; - InitService(); - ResetOutputs(); - ClearHotkeys(); - CreateHotkeys(); + return true; + }; - /* load audio monitoring */ - if (obs_audio_monitoring_available()) { - const char *device_name = config_get_string( - basicConfig, "Audio", "MonitoringDeviceName"); - const char *device_id = config_get_string(basicConfig, "Audio", - "MonitoringDeviceId"); + const OBSPromptRequest request{Str("AddProfile.Title"), + Str("AddProfile.Text"), + "", + true, + Str("AddProfile.WizardCheckbox"), + useProfileWizard}; - obs_set_audio_monitoring_device(device_name, device_id); + OBSPromptResult result = PromptForName(request, profilePromptCallback); - blog(LOG_INFO, "Audio monitoring device:\n\tname: %s\n\tid: %s", - device_name, device_id); + if (!result.success) { + return; } -} -void OBSBasic::on_actionNewProfile_triggered() -{ - AddProfile(true, Str("AddProfile.Title"), Str("AddProfile.Text")); + try { + SetupNewProfile(result.promptValue, result.optionValue); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } } void OBSBasic::on_actionDupProfile_triggered() { - AddProfile(false, Str("AddProfile.Title"), Str("AddProfile.Text")); -} + const OBSPromptCallback profilePromptCallback = + [this](const OBSPromptResult &result) { + if (GetProfileByName(result.promptValue)) { + return false; + } -void OBSBasic::on_actionRenameProfile_triggered() -{ - std::string curDir = - config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir"); - std::string curName = - config_get_string(App()->GlobalConfig(), "Basic", "Profile"); + return true; + }; + + const OBSPromptRequest request{Str("AddProfile.Title"), + Str("AddProfile.Text")}; - /* Duplicate and delete in case there are any issues in the process */ - bool success = AddProfile(false, Str("RenameProfile.Title"), - Str("AddProfile.Text"), curName.c_str(), - true); - if (success) { - DeleteProfile(curName.c_str(), curDir.c_str()); - RefreshProfiles(); + OBSPromptResult result = PromptForName(request, profilePromptCallback); + + if (!result.success) { + return; } - OnEvent(OBS_FRONTEND_EVENT_PROFILE_RENAMED); + try { + SetupDuplicateProfile(result.promptValue); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } } -void OBSBasic::on_actionRemoveProfile_triggered(bool skipConfirmation) +void OBSBasic::on_actionRenameProfile_triggered() { - std::string newName; - std::string newPath; - ConfigFile config; + const std::string currentProfileName = + config_get_string(App()->GetUserConfig(), "Basic", "Profile"); - std::string oldDir = - config_get_string(App()->GlobalConfig(), "Basic", "ProfileDir"); - std::string oldName = - config_get_string(App()->GlobalConfig(), "Basic", "Profile"); + const OBSPromptCallback profilePromptCallback = + [this](const OBSPromptResult &result) { + if (GetProfileByName(result.promptValue)) { + return false; + } - auto cb = [&](const char *name, const char *filePath) { - if (strcmp(oldName.c_str(), name) != 0) { - newName = name; - newPath = filePath; - return false; - } + return true; + }; - return true; - }; + const OBSPromptRequest request{Str("RenameProfile.Title"), + Str("RenameProfile.Text"), + currentProfileName}; - EnumProfiles(cb); + OBSPromptResult result = PromptForName(request, profilePromptCallback); - /* this should never be true due to menu item being grayed out */ - if (newPath.empty()) + if (!result.success) { return; - - if (!skipConfirmation) { - QString text = QTStr("ConfirmRemove.Text") - .arg(QT_UTF8(oldName.c_str())); - - QMessageBox::StandardButton button = OBSMessageBox::question( - this, QTStr("ConfirmRemove.Title"), text); - if (button == QMessageBox::No) - return; } - size_t newPath_len = newPath.size(); - newPath += "/basic.ini"; + try { + SetupRenameProfile(result.promptValue); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } +} - if (config.Open(newPath.c_str(), CONFIG_OPEN_ALWAYS) != 0) { - blog(LOG_ERROR, "ChangeProfile: Failed to load file '%s'", - newPath.c_str()); +void OBSBasic::on_actionRemoveProfile_triggered(bool skipConfirmation) +{ + if (profiles.size() < 2) { return; } - OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); - - newPath.resize(newPath_len); + OBSProfile currentProfile; - const char *newDir = strrchr(newPath.c_str(), '/') + 1; + try { + currentProfile = GetCurrentProfile(); - config_set_string(App()->GlobalConfig(), "Basic", "Profile", - newName.c_str()); - config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir); + if (!skipConfirmation) { + const QString confirmationText = + QTStr("ConfirmRemove.Text") + .arg(QString::fromStdString( + currentProfile.name)); - QString settingsRequiringRestart; - bool needsRestart = - ProfileNeedsRestart(config, settingsRequiringRestart); + const QMessageBox::StandardButton button = + OBSMessageBox::question( + this, QTStr("ConfirmRemove.Title"), + confirmationText); - Auth::Save(); - auth.reset(); - DeleteCookies(); - DestroyPanelCookieManager(); - - config.Swap(basicConfig); - InitBasicConfigDefaults(); - InitBasicConfigDefaults2(); - ResetProfileData(); - DeleteProfile(oldName.c_str(), oldDir.c_str()); - RefreshProfiles(); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + if (button == QMessageBox::No) { + return; + } + } - blog(LOG_INFO, "Switched to profile '%s' (%s)", newName.c_str(), - newDir); - blog(LOG_INFO, "------------------------------------------------"); + OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); - UpdateTitleBar(); - UpdateVolumeControlsDecayRate(); + profiles.erase(currentProfile.name); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } - Auth::Load(); + const OBSProfile &newProfile = profiles.rbegin()->second; - OnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); - OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED); + ActivateProfile(newProfile, true); + RemoveProfile(currentProfile); - if (needsRestart) { - QMessageBox::StandardButton button = OBSMessageBox::question( - this, QTStr("Restart"), - QTStr("LoadProfileNeedsRestart") - .arg(settingsRequiringRestart)); +#ifdef YOUTUBE_ENABLED + if (YouTubeAppDock::IsYTServiceSelected() && !youtubeAppDock) + NewYouTubeAppDock(); +#endif - if (button == QMessageBox::Yes) { - restart = true; - close(); - } - } + blog(LOG_INFO, "Switched to profile '%s' (%s)", newProfile.name.c_str(), + newProfile.directoryName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); } void OBSBasic::on_actionImportProfile_triggered() { - char path[512]; - - QString home = QDir::homePath(); + const QString home = QDir::homePath(); - int ret = GetConfigPath(path, 512, "obs-studio/basic/profiles/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get profile config path"); - return; - } - - QString dir = SelectDirectory( + const QString sourceDirectory = SelectDirectory( this, QTStr("Basic.MainMenu.Profile.Import"), home); - if (!dir.isEmpty() && !dir.isNull()) { - QString inputPath = QString::fromUtf8(path); - QFileInfo finfo(dir); - QString directory = finfo.fileName(); - QString profileDir = inputPath + directory; + if (!sourceDirectory.isEmpty() && !sourceDirectory.isNull()) { + const std::filesystem::path sourcePath = + std::filesystem::u8path(sourceDirectory.toStdString()); + const std::string directoryName = + sourcePath.filename().string(); - if (ProfileExists(directory.toStdString().c_str())) { + if (auto profile = GetProfileByDirectoryName(directoryName)) { OBSMessageBox::warning( this, QTStr("Basic.MainMenu.Profile.Import"), QTStr("Basic.MainMenu.Profile.Exists")); - } else if (os_mkdir(profileDir.toStdString().c_str()) < 0) { + return; + } + + std::string destinationPathString; + destinationPathString.reserve( + App()->userProfilesLocation.u8string().size() + + OBSProfilePath.size() + directoryName.size()); + destinationPathString + .append(App()->userProfilesLocation.u8string()) + .append(OBSProfilePath) + .append(directoryName); + + const std::filesystem::path destinationPath = + std::filesystem::u8path(destinationPathString); + + try { + std::filesystem::create_directory(destinationPath); + } catch (const std::filesystem::filesystem_error &error) { blog(LOG_WARNING, - "Failed to create profile directory '%s'", - directory.toStdString().c_str()); - } else { - QFile::copy(dir + "/basic.ini", - profileDir + "/basic.ini"); - QFile::copy(dir + "/service.json", - profileDir + "/service.json"); - QFile::copy(dir + "/streamEncoder.json", - profileDir + "/streamEncoder.json"); - QFile::copy(dir + "/recordEncoder.json", - profileDir + "/recordEncoder.json"); - RefreshProfiles(); + "Failed to create profile directory '%s':\n%s", + directoryName.c_str(), error.what()); + return; } + + const std::array, 4> profileFiles{{ + {"basic.ini", true}, + {"service.json", false}, + {"streamEncoder.json", false}, + {"recordEncoder.json", false}, + }}; + + for (auto &[file, isMandatory] : profileFiles) { + const std::filesystem::path sourceFile = + sourcePath / std::filesystem::u8path(file); + + if (!std::filesystem::exists(sourceFile)) { + if (isMandatory) { + blog(LOG_ERROR, + "Failed to import profile from directory '%s' - necessary file '%s' not found", + directoryName.c_str(), + file.c_str()); + return; + } + continue; + } + + const std::filesystem::path destinationFile = + destinationPath / std::filesystem::u8path(file); + + try { + std::filesystem::copy(sourceFile, + destinationFile); + } catch ( + const std::filesystem::filesystem_error &error) { + blog(LOG_WARNING, + "Failed to copy import file '%s' for profile '%s':\n%s", + file.c_str(), directoryName.c_str(), + error.what()); + return; + } + } + + RefreshProfiles(true); } } void OBSBasic::on_actionExportProfile_triggered() { - char path[512]; + const OBSProfile ¤tProfile = GetCurrentProfile(); - QString home = QDir::homePath(); + const QString home = QDir::homePath(); - QString currentProfile = QString::fromUtf8(config_get_string( - App()->GlobalConfig(), "Basic", "ProfileDir")); + const QString destinationDirectory = SelectDirectory( + this, QTStr("Basic.MainMenu.Profile.Export"), home); - int ret = GetConfigPath(path, 512, "obs-studio/basic/profiles/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get profile config path"); - return; - } + const std::array profileFiles{"basic.ini", + "service.json", + "streamEncoder.json", + "recordEncoder.json"}; - QString dir = SelectDirectory( - this, QTStr("Basic.MainMenu.Profile.Export"), home); + if (!destinationDirectory.isEmpty() && !destinationDirectory.isNull()) { + const std::filesystem::path sourcePath = currentProfile.path; + const std::filesystem::path destinationPath = + std::filesystem::u8path( + destinationDirectory.toStdString()) / + std::filesystem::u8path(currentProfile.directoryName); - if (!dir.isEmpty() && !dir.isNull()) { - QString outputDir = dir + "/" + currentProfile; - QString inputPath = QString::fromUtf8(path); - QDir folder(outputDir); + if (!std::filesystem::exists(destinationPath)) { + std::filesystem::create_directory(destinationPath); + } - if (!folder.exists()) { - folder.mkpath(outputDir); - } else { - if (QFile::exists(outputDir + "/basic.ini")) - QFile::remove(outputDir + "/basic.ini"); + std::filesystem::copy_options copyOptions = + std::filesystem::copy_options::overwrite_existing; - if (QFile::exists(outputDir + "/service.json")) - QFile::remove(outputDir + "/service.json"); + for (auto &file : profileFiles) { + const std::filesystem::path sourceFile = + sourcePath / std::filesystem::u8path(file); - if (QFile::exists(outputDir + "/streamEncoder.json")) - QFile::remove(outputDir + - "/streamEncoder.json"); + if (!std::filesystem::exists(sourceFile)) { + continue; + } - if (QFile::exists(outputDir + "/recordEncoder.json")) - QFile::remove(outputDir + - "/recordEncoder.json"); - } + const std::filesystem::path destinationFile = + destinationPath / std::filesystem::u8path(file); - QFile::copy(inputPath + currentProfile + "/basic.ini", - outputDir + "/basic.ini"); - QFile::copy(inputPath + currentProfile + "/service.json", - outputDir + "/service.json"); - QFile::copy(inputPath + currentProfile + "/streamEncoder.json", - outputDir + "/streamEncoder.json"); - QFile::copy(inputPath + currentProfile + "/recordEncoder.json", - outputDir + "/recordEncoder.json"); + try { + std::filesystem::copy(sourceFile, + destinationFile, + copyOptions); + } catch ( + const std::filesystem::filesystem_error &error) { + blog(LOG_WARNING, + "Failed to copy export file '%s' for profile '%s'\n%s", + file.c_str(), currentProfile.name.c_str(), + error.what()); + return; + } + } } } -void OBSBasic::ChangeProfile() +// MARK: - Profile Management Helper Functions + +void OBSBasic::ActivateProfile(const OBSProfile &profile, bool reset) { - QAction *action = reinterpret_cast(sender()); ConfigFile config; - std::string path; + if (config.Open(profile.profileFile.u8string().c_str(), + CONFIG_OPEN_ALWAYS) != CONFIG_SUCCESS) { + throw std::logic_error( + "failed to open configuration file of new profile: " + + profile.profileFile.string()); + } - if (!action) - return; + config_set_string(config, "General", "Name", profile.name.c_str()); + config.SaveSafe("tmp"); - path = QT_TO_UTF8(action->property("file_name").value()); - if (path.empty()) - return; + std::vector restartRequirements; - const char *oldName = - config_get_string(App()->GlobalConfig(), "Basic", "Profile"); - if (action->text().compare(QT_UTF8(oldName)) == 0) { - action->setChecked(true); - return; - } + if (activeConfiguration) { + Auth::Save(); - size_t path_len = path.size(); - path += "/basic.ini"; + if (reset) { + auth.reset(); + DestroyPanelCookieManager(); +#ifdef YOUTUBE_ENABLED + if (youtubeAppDock) { + DeleteYouTubeAppDock(); + } +#endif + } + restartRequirements = GetRestartRequirements(config); - if (config.Open(path.c_str(), CONFIG_OPEN_ALWAYS) != 0) { - blog(LOG_ERROR, "ChangeProfile: Failed to load file '%s'", - path.c_str()); - return; + activeConfiguration.SaveSafe("tmp"); } - OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING); + activeConfiguration.Swap(config); - path.resize(path_len); + config_set_string(App()->GetUserConfig(), "Basic", "Profile", + profile.name.c_str()); + config_set_string(App()->GetUserConfig(), "Basic", "ProfileDir", + profile.directoryName.c_str()); - const char *newName = config_get_string(config, "General", "Name"); - const char *newDir = strrchr(path.c_str(), '/') + 1; + config_save_safe(App()->GetUserConfig(), "tmp", nullptr); - QString settingsRequiringRestart; - bool needsRestart = - ProfileNeedsRestart(config, settingsRequiringRestart); + InitBasicConfigDefaults(); + InitBasicConfigDefaults2(); - config_set_string(App()->GlobalConfig(), "Basic", "Profile", newName); - config_set_string(App()->GlobalConfig(), "Basic", "ProfileDir", newDir); + if (reset) { + ResetProfileData(); + } - Auth::Save(); - auth.reset(); - DestroyPanelCookieManager(); -#ifdef YOUTUBE_ENABLED - if (youtubeAppDock) - DeleteYouTubeAppDock(); -#endif + CheckForSimpleModeX264Fallback(); - config.Swap(basicConfig); - InitBasicConfigDefaults(); - InitBasicConfigDefaults2(); - ResetProfileData(); RefreshProfiles(); - config_save_safe(App()->GlobalConfig(), "tmp", nullptr); + UpdateTitleBar(); UpdateVolumeControlsDecayRate(); Auth::Load(); -#ifdef YOUTUBE_ENABLED - if (YouTubeAppDock::IsYTServiceSelected() && !youtubeAppDock) - NewYouTubeAppDock(); -#endif - - CheckForSimpleModeX264Fallback(); - - blog(LOG_INFO, "Switched to profile '%s' (%s)", newName, newDir); - blog(LOG_INFO, "------------------------------------------------"); OnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED); - if (needsRestart) { + if (!restartRequirements.empty()) { + std::string requirements = std::accumulate( + std::next(restartRequirements.begin()), + restartRequirements.end(), restartRequirements[0], + [](std::string a, std::string b) { + return std::move(a) + "\n" + b; + }); + QMessageBox::StandardButton button = OBSMessageBox::question( this, QTStr("Restart"), QTStr("LoadProfileNeedsRestart") - .arg(settingsRequiringRestart)); + .arg(requirements.c_str())); if (button == QMessageBox::Yes) { restart = true; @@ -820,12 +800,67 @@ void OBSBasic::ChangeProfile() } } +void OBSBasic::ResetProfileData() +{ + ResetVideo(); + service = nullptr; + InitService(); + ResetOutputs(); + ClearHotkeys(); + CreateHotkeys(); + + /* load audio monitoring */ + if (obs_audio_monitoring_available()) { + const char *device_name = config_get_string( + activeConfiguration, "Audio", "MonitoringDeviceName"); + const char *device_id = config_get_string( + activeConfiguration, "Audio", "MonitoringDeviceId"); + + obs_set_audio_monitoring_device(device_name, device_id); + + blog(LOG_INFO, "Audio monitoring device:\n\tname: %s\n\tid: %s", + device_name, device_id); + } +} + +std::vector +OBSBasic::GetRestartRequirements(const ConfigFile &config) const +{ + std::vector result; + + const char *oldSpeakers = + config_get_string(activeConfiguration, "Audio", "ChannelSetup"); + const char *newSpeakers = + config_get_string(config, "Audio", "ChannelSetup"); + + uint64_t oldSampleRate = + config_get_uint(activeConfiguration, "Audio", "SampleRate"); + uint64_t newSampleRate = config_get_uint(config, "Audio", "SampleRate"); + + if (oldSpeakers != NULL && newSpeakers != NULL) { + if (std::string_view{oldSpeakers} != + std::string_view{newSpeakers}) { + result.emplace_back( + Str("Basic.Settings.Audio.Channels")); + } + } + + if (oldSampleRate != 0 && newSampleRate != 0) { + if (oldSampleRate != newSampleRate) { + result.emplace_back( + Str("Basic.Settings.Audio.SampleRate")); + } + } + + return result; +} + void OBSBasic::CheckForSimpleModeX264Fallback() { - const char *curStreamEncoder = - config_get_string(basicConfig, "SimpleOutput", "StreamEncoder"); - const char *curRecEncoder = - config_get_string(basicConfig, "SimpleOutput", "RecEncoder"); + const char *curStreamEncoder = config_get_string( + activeConfiguration, "SimpleOutput", "StreamEncoder"); + const char *curRecEncoder = config_get_string( + activeConfiguration, "SimpleOutput", "RecEncoder"); bool qsv_supported = false; bool qsv_av1_supported = false; bool amd_supported = false; @@ -941,11 +976,12 @@ void OBSBasic::CheckForSimpleModeX264Fallback() }; if (!CheckEncoder(curStreamEncoder)) - config_set_string(basicConfig, "SimpleOutput", "StreamEncoder", - curStreamEncoder); + config_set_string(activeConfiguration, "SimpleOutput", + "StreamEncoder", curStreamEncoder); if (!CheckEncoder(curRecEncoder)) - config_set_string(basicConfig, "SimpleOutput", "RecEncoder", - curRecEncoder); - if (changed) - config_save_safe(basicConfig, "tmp", nullptr); + config_set_string(activeConfiguration, "SimpleOutput", + "RecEncoder", curRecEncoder); + if (changed) { + activeConfiguration.SaveSafe("tmp"); + } } diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 19c0481af8af42..c29d7a9d1c2ad8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -162,6 +162,8 @@ template static void SetOBSRef(QListWidgetItem *item, T &&val) QVariant::fromValue(val)); } +constexpr std::string_view OBSProfilePath = "/obs-studio/basic/profiles/"; + static void AddExtraModulePaths() { string plugins_path, plugins_data_path; @@ -786,8 +788,8 @@ void OBSBasic::copyActionsDynamicProperties() void OBSBasic::UpdateVolumeControlsDecayRate() { - double meterDecayRate = - config_get_double(basicConfig, "Audio", "MeterDecayRate"); + double meterDecayRate = config_get_double(activeConfiguration, "Audio", + "MeterDecayRate"); for (size_t i = 0; i < volumes.size(); i++) { volumes[i]->SetMeterDecayRate(meterDecayRate); @@ -797,7 +799,7 @@ void OBSBasic::UpdateVolumeControlsDecayRate() void OBSBasic::UpdateVolumeControlsPeakMeterType() { uint32_t peakMeterTypeIdx = - config_get_uint(basicConfig, "Audio", "PeakMeterType"); + config_get_uint(activeConfiguration, "Audio", "PeakMeterType"); enum obs_peak_meter_type peakMeterType; switch (peakMeterTypeIdx) { @@ -1616,18 +1618,18 @@ void OBSBasic::LoadData(obs_data_t *data, const char *file, bool remigrate) OnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); } -#define SERVICE_PATH "service.json" +constexpr std::string_view OBSServiceFileName = "service.json"; void OBSBasic::SaveService() { if (!service) return; - char serviceJsonPath[512]; - int ret = GetProfilePath(serviceJsonPath, sizeof(serviceJsonPath), - SERVICE_PATH); - if (ret <= 0) - return; + const OBSProfile ¤tProfile = GetCurrentProfile(); + + const std::filesystem::path jsonFilePath = + currentProfile.path / + std::filesystem::u8path(OBSServiceFileName); OBSDataAutoRelease data = obs_data_create(); OBSDataAutoRelease settings = obs_service_get_settings(service); @@ -1635,26 +1637,35 @@ void OBSBasic::SaveService() obs_data_set_string(data, "type", obs_service_get_type(service)); obs_data_set_obj(data, "settings", settings); - if (!obs_data_save_json_safe(data, serviceJsonPath, "tmp", "bak")) + if (!obs_data_save_json_safe(data, jsonFilePath.u8string().c_str(), + "tmp", "bak")) { blog(LOG_WARNING, "Failed to save service"); + } } bool OBSBasic::LoadService() { - const char *type; + OBSDataAutoRelease data; - char serviceJsonPath[512]; - int ret = GetProfilePath(serviceJsonPath, sizeof(serviceJsonPath), - SERVICE_PATH); - if (ret <= 0) - return false; + try { + const OBSProfile ¤tProfile = GetCurrentProfile(); - OBSDataAutoRelease data = - obs_data_create_from_json_file_safe(serviceJsonPath, "bak"); + const std::filesystem::path jsonFilePath = + currentProfile.path / + std::filesystem::u8path(OBSServiceFileName); - if (!data) + data = obs_data_create_from_json_file_safe( + jsonFilePath.u8string().c_str(), "bak"); + + if (!data) { + return false; + } + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); return false; + } + const char *type; obs_data_set_default_string(data, "type", "rtmp_common"); type = obs_data_get_string(data, "type"); @@ -1670,19 +1681,20 @@ bool OBSBasic::LoadService() /* Enforce Opus on WHIP if needed */ if (strcmp(obs_service_get_protocol(service), "WHIP") == 0) { - const char *option = config_get_string( - basicConfig, "SimpleOutput", "StreamAudioEncoder"); + const char *option = config_get_string(activeConfiguration, + "SimpleOutput", + "StreamAudioEncoder"); if (strcmp(option, "opus") != 0) - config_set_string(basicConfig, "SimpleOutput", + config_set_string(activeConfiguration, "SimpleOutput", "StreamAudioEncoder", "opus"); - option = config_get_string(basicConfig, "AdvOut", + option = config_get_string(activeConfiguration, "AdvOut", "AudioEncoder"); const char *encoder_codec = obs_get_encoder_codec(option); if (!encoder_codec || strcmp(encoder_codec, "opus") != 0) - config_set_string(basicConfig, "AdvOut", "AudioEncoder", - "ffmpeg_opus"); + config_set_string(activeConfiguration, "AdvOut", + "AudioEncoder", "ffmpeg_opus"); } return true; @@ -1751,27 +1763,34 @@ bool OBSBasic::InitBasicConfigDefaults() /* ----------------------------------------------------- */ /* move over old FFmpeg track settings */ - if (config_has_user_value(basicConfig, "AdvOut", "FFAudioTrack") && - !config_has_user_value(basicConfig, "AdvOut", "Pre22.1Settings")) { + if (config_has_user_value(activeConfiguration, "AdvOut", + "FFAudioTrack") && + !config_has_user_value(activeConfiguration, "AdvOut", + "Pre22.1Settings")) { - int track = (int)config_get_int(basicConfig, "AdvOut", + int track = (int)config_get_int(activeConfiguration, "AdvOut", "FFAudioTrack"); - config_set_int(basicConfig, "AdvOut", "FFAudioMixes", + config_set_int(activeConfiguration, "AdvOut", "FFAudioMixes", 1LL << (track - 1)); - config_set_bool(basicConfig, "AdvOut", "Pre22.1Settings", true); + config_set_bool(activeConfiguration, "AdvOut", + "Pre22.1Settings", true); changed = true; } /* ----------------------------------------------------- */ /* move over mixer values in advanced if older config */ - if (config_has_user_value(basicConfig, "AdvOut", "RecTrackIndex") && - !config_has_user_value(basicConfig, "AdvOut", "RecTracks")) { + if (config_has_user_value(activeConfiguration, "AdvOut", + "RecTrackIndex") && + !config_has_user_value(activeConfiguration, "AdvOut", + "RecTracks")) { - uint64_t track = - config_get_uint(basicConfig, "AdvOut", "RecTrackIndex"); + uint64_t track = config_get_uint(activeConfiguration, "AdvOut", + "RecTrackIndex"); track = 1ULL << (track - 1); - config_set_uint(basicConfig, "AdvOut", "RecTracks", track); - config_remove_value(basicConfig, "AdvOut", "RecTrackIndex"); + config_set_uint(activeConfiguration, "AdvOut", "RecTracks", + track); + config_remove_value(activeConfiguration, "AdvOut", + "RecTrackIndex"); changed = true; } @@ -1779,34 +1798,39 @@ bool OBSBasic::InitBasicConfigDefaults() /* set twitch chat extensions to "both" if prev version */ /* is under 24.1 */ if (config_get_bool(App()->GetUserConfig(), "General", - !config_has_user_value(basicConfig, "Twitch", "AddonChoice")) { - config_set_int(basicConfig, "Twitch", "AddonChoice", 3); + "Pre24.1Defaults") && + !config_has_user_value(activeConfiguration, "Twitch", + "AddonChoice")) { + config_set_int(activeConfiguration, "Twitch", "AddonChoice", 3); changed = true; } /* ----------------------------------------------------- */ /* move bitrate enforcement setting to new value */ - if (config_has_user_value(basicConfig, "SimpleOutput", + if (config_has_user_value(activeConfiguration, "SimpleOutput", "EnforceBitrate") && - !config_has_user_value(basicConfig, "Stream1", + !config_has_user_value(activeConfiguration, "Stream1", "IgnoreRecommended") && - !config_has_user_value(basicConfig, "Stream1", "MovedOldEnforce")) { - bool enforce = config_get_bool(basicConfig, "SimpleOutput", - "EnforceBitrate"); - config_set_bool(basicConfig, "Stream1", "IgnoreRecommended", - !enforce); - config_set_bool(basicConfig, "Stream1", "MovedOldEnforce", - true); + !config_has_user_value(activeConfiguration, "Stream1", + "MovedOldEnforce")) { + bool enforce = config_get_bool( + activeConfiguration, "SimpleOutput", "EnforceBitrate"); + config_set_bool(activeConfiguration, "Stream1", + "IgnoreRecommended", !enforce); + config_set_bool(activeConfiguration, "Stream1", + "MovedOldEnforce", true); changed = true; } /* ----------------------------------------------------- */ /* enforce minimum retry delay of 1 second prior to 27.1 */ - if (config_has_user_value(basicConfig, "Output", "RetryDelay")) { - int retryDelay = - config_get_uint(basicConfig, "Output", "RetryDelay"); + if (config_has_user_value(activeConfiguration, "Output", + "RetryDelay")) { + int retryDelay = config_get_uint(activeConfiguration, "Output", + "RetryDelay"); if (retryDelay < 1) { - config_set_uint(basicConfig, "Output", "RetryDelay", 1); + config_set_uint(activeConfiguration, "Output", + "RetryDelay", 1); changed = true; } } @@ -1815,15 +1839,15 @@ bool OBSBasic::InitBasicConfigDefaults() /* Migrate old container selection (if any) to new key. */ auto MigrateFormat = [&](const char *section) { - bool has_old_key = config_has_user_value(basicConfig, section, - "RecFormat"); - bool has_new_key = config_has_user_value(basicConfig, section, - "RecFormat2"); + bool has_old_key = config_has_user_value(activeConfiguration, + section, "RecFormat"); + bool has_new_key = config_has_user_value(activeConfiguration, + section, "RecFormat2"); if (!has_new_key && !has_old_key) return; string old_format = config_get_string( - basicConfig, section, + activeConfiguration, section, has_new_key ? "RecFormat2" : "RecFormat"); string new_format = old_format; if (old_format == "ts") @@ -1836,8 +1860,8 @@ bool OBSBasic::InitBasicConfigDefaults() new_format = "fragmented_mov"; if (new_format != old_format || !has_new_key) { - config_set_string(basicConfig, section, "RecFormat2", - new_format.c_str()); + config_set_string(activeConfiguration, section, + "RecFormat2", new_format.c_str()); changed = true; } }; @@ -1848,137 +1872,175 @@ bool OBSBasic::InitBasicConfigDefaults() /* ----------------------------------------------------- */ /* Migrate output scale setting to GPU scaling options. */ - if (config_get_bool(basicConfig, "AdvOut", "Rescale") && - !config_has_user_value(basicConfig, "AdvOut", "RescaleFilter")) { - config_set_int(basicConfig, "AdvOut", "RescaleFilter", + if (config_get_bool(activeConfiguration, "AdvOut", "Rescale") && + !config_has_user_value(activeConfiguration, "AdvOut", + "RescaleFilter")) { + config_set_int(activeConfiguration, "AdvOut", "RescaleFilter", OBS_SCALE_BILINEAR); } - if (config_get_bool(basicConfig, "AdvOut", "RecRescale") && - !config_has_user_value(basicConfig, "AdvOut", "RecRescaleFilter")) { - config_set_int(basicConfig, "AdvOut", "RecRescaleFilter", - OBS_SCALE_BILINEAR); + if (config_get_bool(activeConfiguration, "AdvOut", "RecRescale") && + !config_has_user_value(activeConfiguration, "AdvOut", + "RecRescaleFilter")) { + config_set_int(activeConfiguration, "AdvOut", + "RecRescaleFilter", OBS_SCALE_BILINEAR); } /* ----------------------------------------------------- */ - if (changed) - config_save_safe(basicConfig, "tmp", nullptr); + if (changed) { + activeConfiguration.SaveSafe("tmp"); + } /* ----------------------------------------------------- */ - config_set_default_string(basicConfig, "Output", "Mode", "Simple"); + config_set_default_string(activeConfiguration, "Output", "Mode", + "Simple"); - config_set_default_bool(basicConfig, "Stream1", "IgnoreRecommended", - false); - config_set_default_bool(basicConfig, "Stream1", "EnableMultitrackVideo", - false); - config_set_default_bool(basicConfig, "Stream1", + config_set_default_bool(activeConfiguration, "Stream1", + "IgnoreRecommended", false); + config_set_default_bool(activeConfiguration, "Stream1", + "EnableMultitrackVideo", false); + config_set_default_bool(activeConfiguration, "Stream1", "MultitrackVideoMaximumAggregateBitrateAuto", true); - config_set_default_bool(basicConfig, "Stream1", + config_set_default_bool(activeConfiguration, "Stream1", "MultitrackVideoMaximumVideoTracksAuto", true); - config_set_default_string(basicConfig, "SimpleOutput", "FilePath", + config_set_default_string(activeConfiguration, "SimpleOutput", + "FilePath", GetDefaultVideoSavePath().c_str()); - config_set_default_string(basicConfig, "SimpleOutput", "RecFormat2", - DEFAULT_CONTAINER); - config_set_default_uint(basicConfig, "SimpleOutput", "VBitrate", 2500); - config_set_default_uint(basicConfig, "SimpleOutput", "ABitrate", 160); - config_set_default_bool(basicConfig, "SimpleOutput", "UseAdvanced", - false); - config_set_default_string(basicConfig, "SimpleOutput", "Preset", + config_set_default_string(activeConfiguration, "SimpleOutput", + "RecFormat2", DEFAULT_CONTAINER); + config_set_default_uint(activeConfiguration, "SimpleOutput", "VBitrate", + 2500); + config_set_default_uint(activeConfiguration, "SimpleOutput", "ABitrate", + 160); + config_set_default_bool(activeConfiguration, "SimpleOutput", + "UseAdvanced", false); + config_set_default_string(activeConfiguration, "SimpleOutput", "Preset", "veryfast"); - config_set_default_string(basicConfig, "SimpleOutput", "NVENCPreset2", - "p5"); - config_set_default_string(basicConfig, "SimpleOutput", "RecQuality", - "Stream"); - config_set_default_bool(basicConfig, "SimpleOutput", "RecRB", false); - config_set_default_int(basicConfig, "SimpleOutput", "RecRBTime", 20); - config_set_default_int(basicConfig, "SimpleOutput", "RecRBSize", 512); - config_set_default_string(basicConfig, "SimpleOutput", "RecRBPrefix", - "Replay"); - config_set_default_string(basicConfig, "SimpleOutput", + config_set_default_string(activeConfiguration, "SimpleOutput", + "NVENCPreset2", "p5"); + config_set_default_string(activeConfiguration, "SimpleOutput", + "RecQuality", "Stream"); + config_set_default_bool(activeConfiguration, "SimpleOutput", "RecRB", + false); + config_set_default_int(activeConfiguration, "SimpleOutput", "RecRBTime", + 20); + config_set_default_int(activeConfiguration, "SimpleOutput", "RecRBSize", + 512); + config_set_default_string(activeConfiguration, "SimpleOutput", + "RecRBPrefix", "Replay"); + config_set_default_string(activeConfiguration, "SimpleOutput", "StreamAudioEncoder", "aac"); - config_set_default_string(basicConfig, "SimpleOutput", + config_set_default_string(activeConfiguration, "SimpleOutput", "RecAudioEncoder", "aac"); - config_set_default_uint(basicConfig, "SimpleOutput", "RecTracks", - (1 << 0)); + config_set_default_uint(activeConfiguration, "SimpleOutput", + "RecTracks", (1 << 0)); - config_set_default_bool(basicConfig, "AdvOut", "ApplyServiceSettings", - true); - config_set_default_bool(basicConfig, "AdvOut", "UseRescale", false); - config_set_default_uint(basicConfig, "AdvOut", "TrackIndex", 1); - config_set_default_uint(basicConfig, "AdvOut", "VodTrackIndex", 2); - config_set_default_string(basicConfig, "AdvOut", "Encoder", "obs_x264"); + config_set_default_bool(activeConfiguration, "AdvOut", + "ApplyServiceSettings", true); + config_set_default_bool(activeConfiguration, "AdvOut", "UseRescale", + false); + config_set_default_uint(activeConfiguration, "AdvOut", "TrackIndex", 1); + config_set_default_uint(activeConfiguration, "AdvOut", "VodTrackIndex", + 2); + config_set_default_string(activeConfiguration, "AdvOut", "Encoder", + "obs_x264"); - config_set_default_string(basicConfig, "AdvOut", "RecType", "Standard"); + config_set_default_string(activeConfiguration, "AdvOut", "RecType", + "Standard"); - config_set_default_string(basicConfig, "AdvOut", "RecFilePath", + config_set_default_string(activeConfiguration, "AdvOut", "RecFilePath", GetDefaultVideoSavePath().c_str()); - config_set_default_string(basicConfig, "AdvOut", "RecFormat2", + config_set_default_string(activeConfiguration, "AdvOut", "RecFormat2", DEFAULT_CONTAINER); - config_set_default_bool(basicConfig, "AdvOut", "RecUseRescale", false); - config_set_default_uint(basicConfig, "AdvOut", "RecTracks", (1 << 0)); - config_set_default_string(basicConfig, "AdvOut", "RecEncoder", "none"); - config_set_default_uint(basicConfig, "AdvOut", "FLVTrack", 1); - config_set_default_uint(basicConfig, "AdvOut", + config_set_default_bool(activeConfiguration, "AdvOut", "RecUseRescale", + false); + config_set_default_uint(activeConfiguration, "AdvOut", "RecTracks", + (1 << 0)); + config_set_default_string(activeConfiguration, "AdvOut", "RecEncoder", + "none"); + config_set_default_uint(activeConfiguration, "AdvOut", "FLVTrack", 1); + config_set_default_uint(activeConfiguration, "AdvOut", "StreamMultiTrackAudioMixes", 1); - config_set_default_bool(basicConfig, "AdvOut", "FFOutputToFile", true); - config_set_default_string(basicConfig, "AdvOut", "FFFilePath", + config_set_default_bool(activeConfiguration, "AdvOut", "FFOutputToFile", + true); + config_set_default_string(activeConfiguration, "AdvOut", "FFFilePath", GetDefaultVideoSavePath().c_str()); - config_set_default_string(basicConfig, "AdvOut", "FFExtension", "mp4"); - config_set_default_uint(basicConfig, "AdvOut", "FFVBitrate", 2500); - config_set_default_uint(basicConfig, "AdvOut", "FFVGOPSize", 250); - config_set_default_bool(basicConfig, "AdvOut", "FFUseRescale", false); - config_set_default_bool(basicConfig, "AdvOut", "FFIgnoreCompat", false); - config_set_default_uint(basicConfig, "AdvOut", "FFABitrate", 160); - config_set_default_uint(basicConfig, "AdvOut", "FFAudioMixes", 1); - - config_set_default_uint(basicConfig, "AdvOut", "Track1Bitrate", 160); - config_set_default_uint(basicConfig, "AdvOut", "Track2Bitrate", 160); - config_set_default_uint(basicConfig, "AdvOut", "Track3Bitrate", 160); - config_set_default_uint(basicConfig, "AdvOut", "Track4Bitrate", 160); - config_set_default_uint(basicConfig, "AdvOut", "Track5Bitrate", 160); - config_set_default_uint(basicConfig, "AdvOut", "Track6Bitrate", 160); - - config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileTime", 15); - config_set_default_uint(basicConfig, "AdvOut", "RecSplitFileSize", - 2048); - - config_set_default_bool(basicConfig, "AdvOut", "RecRB", false); - config_set_default_uint(basicConfig, "AdvOut", "RecRBTime", 20); - config_set_default_int(basicConfig, "AdvOut", "RecRBSize", 512); - - config_set_default_uint(basicConfig, "Video", "BaseCX", cx); - config_set_default_uint(basicConfig, "Video", "BaseCY", cy); + config_set_default_string(activeConfiguration, "AdvOut", "FFExtension", + "mp4"); + config_set_default_uint(activeConfiguration, "AdvOut", "FFVBitrate", + 2500); + config_set_default_uint(activeConfiguration, "AdvOut", "FFVGOPSize", + 250); + config_set_default_bool(activeConfiguration, "AdvOut", "FFUseRescale", + false); + config_set_default_bool(activeConfiguration, "AdvOut", "FFIgnoreCompat", + false); + config_set_default_uint(activeConfiguration, "AdvOut", "FFABitrate", + 160); + config_set_default_uint(activeConfiguration, "AdvOut", "FFAudioMixes", + 1); + + config_set_default_uint(activeConfiguration, "AdvOut", "Track1Bitrate", + 160); + config_set_default_uint(activeConfiguration, "AdvOut", "Track2Bitrate", + 160); + config_set_default_uint(activeConfiguration, "AdvOut", "Track3Bitrate", + 160); + config_set_default_uint(activeConfiguration, "AdvOut", "Track4Bitrate", + 160); + config_set_default_uint(activeConfiguration, "AdvOut", "Track5Bitrate", + 160); + config_set_default_uint(activeConfiguration, "AdvOut", "Track6Bitrate", + 160); + + config_set_default_uint(activeConfiguration, "AdvOut", + "RecSplitFileTime", 15); + config_set_default_uint(activeConfiguration, "AdvOut", + "RecSplitFileSize", 2048); + + config_set_default_bool(activeConfiguration, "AdvOut", "RecRB", false); + config_set_default_uint(activeConfiguration, "AdvOut", "RecRBTime", 20); + config_set_default_int(activeConfiguration, "AdvOut", "RecRBSize", 512); + + config_set_default_uint(activeConfiguration, "Video", "BaseCX", cx); + config_set_default_uint(activeConfiguration, "Video", "BaseCY", cy); /* don't allow BaseCX/BaseCY to be susceptible to defaults changing */ - if (!config_has_user_value(basicConfig, "Video", "BaseCX") || - !config_has_user_value(basicConfig, "Video", "BaseCY")) { - config_set_uint(basicConfig, "Video", "BaseCX", cx); - config_set_uint(basicConfig, "Video", "BaseCY", cy); - config_save_safe(basicConfig, "tmp", nullptr); + if (!config_has_user_value(activeConfiguration, "Video", "BaseCX") || + !config_has_user_value(activeConfiguration, "Video", "BaseCY")) { + config_set_uint(activeConfiguration, "Video", "BaseCX", cx); + config_set_uint(activeConfiguration, "Video", "BaseCY", cy); + config_save_safe(activeConfiguration, "tmp", nullptr); } - config_set_default_string(basicConfig, "Output", "FilenameFormatting", + config_set_default_string(activeConfiguration, "Output", + "FilenameFormatting", "%CCYY-%MM-%DD %hh-%mm-%ss"); - config_set_default_bool(basicConfig, "Output", "DelayEnable", false); - config_set_default_uint(basicConfig, "Output", "DelaySec", 20); - config_set_default_bool(basicConfig, "Output", "DelayPreserve", true); + config_set_default_bool(activeConfiguration, "Output", "DelayEnable", + false); + config_set_default_uint(activeConfiguration, "Output", "DelaySec", 20); + config_set_default_bool(activeConfiguration, "Output", "DelayPreserve", + true); - config_set_default_bool(basicConfig, "Output", "Reconnect", true); - config_set_default_uint(basicConfig, "Output", "RetryDelay", 2); - config_set_default_uint(basicConfig, "Output", "MaxRetries", 25); + config_set_default_bool(activeConfiguration, "Output", "Reconnect", + true); + config_set_default_uint(activeConfiguration, "Output", "RetryDelay", 2); + config_set_default_uint(activeConfiguration, "Output", "MaxRetries", + 25); - config_set_default_string(basicConfig, "Output", "BindIP", "default"); - config_set_default_string(basicConfig, "Output", "IPFamily", + config_set_default_string(activeConfiguration, "Output", "BindIP", + "default"); + config_set_default_string(activeConfiguration, "Output", "IPFamily", "IPv4+IPv6"); - config_set_default_bool(basicConfig, "Output", "NewSocketLoopEnable", - false); - config_set_default_bool(basicConfig, "Output", "LowLatencyEnable", - false); + config_set_default_bool(activeConfiguration, "Output", + "NewSocketLoopEnable", false); + config_set_default_bool(activeConfiguration, "Output", + "LowLatencyEnable", false); int i = 0; uint32_t scale_cx = cx; @@ -1992,44 +2054,55 @@ bool OBSBasic::InitBasicConfigDefaults() scale_cy = uint32_t(double(cy) / scale); } - config_set_default_uint(basicConfig, "Video", "OutputCX", scale_cx); - config_set_default_uint(basicConfig, "Video", "OutputCY", scale_cy); + config_set_default_uint(activeConfiguration, "Video", "OutputCX", + scale_cx); + config_set_default_uint(activeConfiguration, "Video", "OutputCY", + scale_cy); /* don't allow OutputCX/OutputCY to be susceptible to defaults * changing */ - if (!config_has_user_value(basicConfig, "Video", "OutputCX") || - !config_has_user_value(basicConfig, "Video", "OutputCY")) { - config_set_uint(basicConfig, "Video", "OutputCX", scale_cx); - config_set_uint(basicConfig, "Video", "OutputCY", scale_cy); - config_save_safe(basicConfig, "tmp", nullptr); - } - - config_set_default_uint(basicConfig, "Video", "FPSType", 0); - config_set_default_string(basicConfig, "Video", "FPSCommon", "30"); - config_set_default_uint(basicConfig, "Video", "FPSInt", 30); - config_set_default_uint(basicConfig, "Video", "FPSNum", 30); - config_set_default_uint(basicConfig, "Video", "FPSDen", 1); - config_set_default_string(basicConfig, "Video", "ScaleType", "bicubic"); - config_set_default_string(basicConfig, "Video", "ColorFormat", "NV12"); - config_set_default_string(basicConfig, "Video", "ColorSpace", "709"); - config_set_default_string(basicConfig, "Video", "ColorRange", + if (!config_has_user_value(activeConfiguration, "Video", "OutputCX") || + !config_has_user_value(activeConfiguration, "Video", "OutputCY")) { + config_set_uint(activeConfiguration, "Video", "OutputCX", + scale_cx); + config_set_uint(activeConfiguration, "Video", "OutputCY", + scale_cy); + config_save_safe(activeConfiguration, "tmp", nullptr); + } + + config_set_default_uint(activeConfiguration, "Video", "FPSType", 0); + config_set_default_string(activeConfiguration, "Video", "FPSCommon", + "30"); + config_set_default_uint(activeConfiguration, "Video", "FPSInt", 30); + config_set_default_uint(activeConfiguration, "Video", "FPSNum", 30); + config_set_default_uint(activeConfiguration, "Video", "FPSDen", 1); + config_set_default_string(activeConfiguration, "Video", "ScaleType", + "bicubic"); + config_set_default_string(activeConfiguration, "Video", "ColorFormat", + "NV12"); + config_set_default_string(activeConfiguration, "Video", "ColorSpace", + "709"); + config_set_default_string(activeConfiguration, "Video", "ColorRange", "Partial"); - config_set_default_uint(basicConfig, "Video", "SdrWhiteLevel", 300); - config_set_default_uint(basicConfig, "Video", "HdrNominalPeakLevel", - 1000); + config_set_default_uint(activeConfiguration, "Video", "SdrWhiteLevel", + 300); + config_set_default_uint(activeConfiguration, "Video", + "HdrNominalPeakLevel", 1000); - config_set_default_string(basicConfig, "Audio", "MonitoringDeviceId", - "default"); + config_set_default_string(activeConfiguration, "Audio", + "MonitoringDeviceId", "default"); config_set_default_string( - basicConfig, "Audio", "MonitoringDeviceName", + activeConfiguration, "Audio", "MonitoringDeviceName", Str("Basic.Settings.Advanced.Audio.MonitoringDevice" ".Default")); - config_set_default_uint(basicConfig, "Audio", "SampleRate", 48000); - config_set_default_string(basicConfig, "Audio", "ChannelSetup", + config_set_default_uint(activeConfiguration, "Audio", "SampleRate", + 48000); + config_set_default_string(activeConfiguration, "Audio", "ChannelSetup", "Stereo"); - config_set_default_double(basicConfig, "Audio", "MeterDecayRate", - VOLUME_METER_DECAY_FAST); - config_set_default_uint(basicConfig, "Audio", "PeakMeterType", 0); + config_set_default_double(activeConfiguration, "Audio", + "MeterDecayRate", VOLUME_METER_DECAY_FAST); + config_set_default_uint(activeConfiguration, "Audio", "PeakMeterType", + 0); CheckExistingCookieId(); @@ -2044,12 +2117,12 @@ void OBSBasic::InitBasicConfigDefaults2() "Pre23Defaults"); bool useNV = EncoderAvailable("ffmpeg_nvenc") && !oldEncDefaults; - config_set_default_string(basicConfig, "SimpleOutput", "StreamEncoder", - useNV ? SIMPLE_ENCODER_NVENC - : SIMPLE_ENCODER_X264); - config_set_default_string(basicConfig, "SimpleOutput", "RecEncoder", - useNV ? SIMPLE_ENCODER_NVENC - : SIMPLE_ENCODER_X264); + config_set_default_string( + activeConfiguration, "SimpleOutput", "StreamEncoder", + useNV ? SIMPLE_ENCODER_NVENC : SIMPLE_ENCODER_X264); + config_set_default_string( + activeConfiguration, "SimpleOutput", "RecEncoder", + useNV ? SIMPLE_ENCODER_NVENC : SIMPLE_ENCODER_X264); const char *aac_default = "ffmpeg_aac"; if (EncoderAvailable("CoreAudio_AAC")) @@ -2057,47 +2130,37 @@ void OBSBasic::InitBasicConfigDefaults2() else if (EncoderAvailable("libfdk_aac")) aac_default = "libfdk_aac"; - config_set_default_string(basicConfig, "AdvOut", "AudioEncoder", - aac_default); - config_set_default_string(basicConfig, "AdvOut", "RecAudioEncoder", + config_set_default_string(activeConfiguration, "AdvOut", "AudioEncoder", aac_default); + config_set_default_string(activeConfiguration, "AdvOut", + "RecAudioEncoder", aac_default); } bool OBSBasic::InitBasicConfig() { ProfileScope("OBSBasic::InitBasicConfig"); - char configPath[512]; + RefreshProfiles(true); - int ret = GetProfilePath(configPath, sizeof(configPath), ""); - if (ret <= 0) { - OBSErrorBox(nullptr, "Failed to get profile path"); - return false; - } + std::string currentProfileName{ + config_get_string(App()->GetUserConfig(), "Basic", "Profile")}; - if (os_mkdir(configPath) == MKDIR_ERROR) { - OBSErrorBox(nullptr, "Failed to create profile path"); - return false; - } - - ret = GetProfilePath(configPath, sizeof(configPath), "basic.ini"); - if (ret <= 0) { - OBSErrorBox(nullptr, "Failed to get basic.ini path"); - return false; - } + auto foundProfile = GetProfileByName(currentProfileName); - int code = basicConfig.Open(configPath, CONFIG_OPEN_ALWAYS); - if (code != CONFIG_SUCCESS) { - OBSErrorBox(NULL, "Failed to open basic.ini: %d", code); - return false; - } + if (!foundProfile) { + const OBSProfile &newProfile = + CreateProfile(currentProfileName); - if (config_get_string(basicConfig, "General", "Name") == nullptr) { - const char *curName = config_get_string(App()->GlobalConfig(), - "Basic", "Profile"); - - config_set_string(basicConfig, "General", "Name", curName); - basicConfig.SaveSafe("tmp"); + ActivateProfile(newProfile); + } else { + // TODO: Remove duplicate code from OBS initialization and just use ActivateProfile here instead + int code = activeConfiguration.Open( + foundProfile.value().profileFile.u8string().c_str(), + CONFIG_OPEN_ALWAYS); + if (code != CONFIG_SUCCESS) { + OBSErrorBox(NULL, "Failed to open basic.ini: %d", code); + return false; + } } return InitBasicConfigDefaults(); @@ -2200,7 +2263,8 @@ void OBSBasic::ResetOutputs() { ProfileScope("OBSBasic::ResetOutputs"); - const char *mode = config_get_string(basicConfig, "Output", "Mode"); + const char *mode = + config_get_string(activeConfiguration, "Output", "Mode"); bool advOut = astrcmpi(mode, "Advanced") == 0; if ((!outputHandler || !outputHandler->Active()) && @@ -2308,9 +2372,9 @@ void OBSBasic::OBSInit() /* load audio monitoring */ if (obs_audio_monitoring_available()) { const char *device_name = config_get_string( - basicConfig, "Audio", "MonitoringDeviceName"); - const char *device_id = config_get_string(basicConfig, "Audio", - "MonitoringDeviceId"); + activeConfiguration, "Audio", "MonitoringDeviceName"); + const char *device_id = config_get_string( + activeConfiguration, "Audio", "MonitoringDeviceId"); obs_set_audio_monitoring_device(device_name, device_id); @@ -2437,7 +2501,6 @@ void OBSBasic::OBSInit() Q_ARG(bool, true)); RefreshSceneCollections(); - RefreshProfiles(); disableSaving--; auto addDisplay = [this](OBSQTDisplay *window) { @@ -2606,7 +2669,8 @@ void OBSBasic::OBSInit() ToggleMixerLayout(config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); - if (config_get_bool(basicConfig, "General", "OpenStatsOnStartup")) + if (config_get_bool(activeConfiguration, "General", + "OpenStatsOnStartup")) on_stats_triggered(); OBSBasicStats::InitializeValues(); @@ -2945,7 +3009,7 @@ void OBSBasic::CreateHotkeys() auto LoadHotkeyData = [&](const char *name) -> OBSData { const char *info = - config_get_string(basicConfig, "Hotkeys", name); + config_get_string(activeConfiguration, "Hotkeys", name); if (!info) return {}; @@ -2967,16 +3031,16 @@ void OBSBasic::CreateHotkeys() const char *name1, const char *oldName = NULL) { if (oldName) { - const auto info = config_get_string(basicConfig, + const auto info = config_get_string(activeConfiguration, "Hotkeys", oldName); if (info) { - config_set_string(basicConfig, "Hotkeys", name0, - info); - config_set_string(basicConfig, "Hotkeys", name1, - info); - config_remove_value(basicConfig, "Hotkeys", - oldName); - config_save(basicConfig); + config_set_string(activeConfiguration, + "Hotkeys", name0, info); + config_set_string(activeConfiguration, + "Hotkeys", name1, info); + config_remove_value(activeConfiguration, + "Hotkeys", oldName); + activeConfiguration.Save(); } } OBSDataArrayAutoRelease array0 = @@ -4205,12 +4269,12 @@ void OBSBasic::ActivateAudioSource(OBSSource source) vol->EnableSlider(!SourceVolumeLocked(source)); - double meterDecayRate = - config_get_double(basicConfig, "Audio", "MeterDecayRate"); + double meterDecayRate = config_get_double(activeConfiguration, "Audio", + "MeterDecayRate"); vol->SetMeterDecayRate(meterDecayRate); uint32_t peakMeterTypeIdx = - config_get_uint(basicConfig, "Audio", "PeakMeterType"); + config_get_uint(activeConfiguration, "Audio", "PeakMeterType"); enum obs_peak_meter_type peakMeterType; switch (peakMeterTypeIdx) { @@ -4922,10 +4986,10 @@ static inline int AttemptToResetVideo(struct obs_video_info *ovi) return obs_reset_video(ovi); } -static inline enum obs_scale_type GetScaleType(ConfigFile &basicConfig) +static inline enum obs_scale_type GetScaleType(ConfigFile &activeConfiguration) { const char *scaleTypeStr = - config_get_string(basicConfig, "Video", "ScaleType"); + config_get_string(activeConfiguration, "Video", "ScaleType"); if (astrcmpi(scaleTypeStr, "bilinear") == 0) return OBS_SCALE_BILINEAR; @@ -5006,43 +5070,43 @@ int OBSBasic::ResetVideo() GetConfigFPS(ovi.fps_num, ovi.fps_den); const char *colorFormat = - config_get_string(basicConfig, "Video", "ColorFormat"); + config_get_string(activeConfiguration, "Video", "ColorFormat"); const char *colorSpace = - config_get_string(basicConfig, "Video", "ColorSpace"); + config_get_string(activeConfiguration, "Video", "ColorSpace"); const char *colorRange = - config_get_string(basicConfig, "Video", "ColorRange"); + config_get_string(activeConfiguration, "Video", "ColorRange"); ovi.graphics_module = App()->GetRenderModule(); - ovi.base_width = - (uint32_t)config_get_uint(basicConfig, "Video", "BaseCX"); - ovi.base_height = - (uint32_t)config_get_uint(basicConfig, "Video", "BaseCY"); - ovi.output_width = - (uint32_t)config_get_uint(basicConfig, "Video", "OutputCX"); - ovi.output_height = - (uint32_t)config_get_uint(basicConfig, "Video", "OutputCY"); + ovi.base_width = (uint32_t)config_get_uint(activeConfiguration, "Video", + "BaseCX"); + ovi.base_height = (uint32_t)config_get_uint(activeConfiguration, + "Video", "BaseCY"); + ovi.output_width = (uint32_t)config_get_uint(activeConfiguration, + "Video", "OutputCX"); + ovi.output_height = (uint32_t)config_get_uint(activeConfiguration, + "Video", "OutputCY"); ovi.output_format = GetVideoFormatFromName(colorFormat); ovi.colorspace = GetVideoColorSpaceFromName(colorSpace); ovi.range = astrcmpi(colorRange, "Full") == 0 ? VIDEO_RANGE_FULL : VIDEO_RANGE_PARTIAL; ovi.adapter = - config_get_uint(App()->GlobalConfig(), "Video", "AdapterIdx"); + config_get_uint(App()->GetUserConfig(), "Video", "AdapterIdx"); ovi.gpu_conversion = true; - ovi.scale_type = GetScaleType(basicConfig); + ovi.scale_type = GetScaleType(activeConfiguration); if (ovi.base_width < 32 || ovi.base_height < 32) { ovi.base_width = 1920; ovi.base_height = 1080; - config_set_uint(basicConfig, "Video", "BaseCX", 1920); - config_set_uint(basicConfig, "Video", "BaseCY", 1080); + config_set_uint(activeConfiguration, "Video", "BaseCX", 1920); + config_set_uint(activeConfiguration, "Video", "BaseCY", 1080); } if (ovi.output_width < 32 || ovi.output_height < 32) { ovi.output_width = ovi.base_width; ovi.output_height = ovi.base_height; - config_set_uint(basicConfig, "Video", "OutputCX", + config_set_uint(activeConfiguration, "Video", "OutputCX", ovi.base_width); - config_set_uint(basicConfig, "Video", "OutputCY", + config_set_uint(activeConfiguration, "Video", "OutputCY", ovi.base_height); } @@ -5058,9 +5122,9 @@ int OBSBasic::ResetVideo() ResizeProgram(ovi.base_width, ovi.base_height); const float sdr_white_level = (float)config_get_uint( - basicConfig, "Video", "SdrWhiteLevel"); + activeConfiguration, "Video", "SdrWhiteLevel"); const float hdr_nominal_peak_level = (float)config_get_uint( - basicConfig, "Video", "HdrNominalPeakLevel"); + activeConfiguration, "Video", "HdrNominalPeakLevel"); obs_set_video_levels(sdr_white_level, hdr_nominal_peak_level); OBSBasicStats::InitializeValues(); OBSProjector::UpdateMultiviewProjectors(); @@ -5085,10 +5149,10 @@ bool OBSBasic::ResetAudio() struct obs_audio_info2 ai = {}; ai.samples_per_sec = - config_get_uint(basicConfig, "Audio", "SampleRate"); + config_get_uint(activeConfiguration, "Audio", "SampleRate"); const char *channelSetupStr = - config_get_string(basicConfig, "Audio", "ChannelSetup"); + config_get_string(activeConfiguration, "Audio", "ChannelSetup"); if (strcmp(channelSetupStr, "Mono") == 0) ai.speakers = SPEAKERS_MONO; @@ -5519,15 +5583,18 @@ void OBSBasic::changeEvent(QEvent *event) void OBSBasic::on_actionShow_Recordings_triggered() { - const char *mode = config_get_string(basicConfig, "Output", "Mode"); - const char *type = config_get_string(basicConfig, "AdvOut", "RecType"); + const char *mode = + config_get_string(activeConfiguration, "Output", "Mode"); + const char *type = + config_get_string(activeConfiguration, "AdvOut", "RecType"); const char *adv_path = strcmp(type, "Standard") - ? config_get_string(basicConfig, "AdvOut", "FFFilePath") - : config_get_string(basicConfig, "AdvOut", + ? config_get_string(activeConfiguration, "AdvOut", + "FFFilePath") + : config_get_string(activeConfiguration, "AdvOut", "RecFilePath"); const char *path = strcmp(mode, "Advanced") - ? config_get_string(basicConfig, + ? config_get_string(activeConfiguration, "SimpleOutput", "FilePath") : adv_path; @@ -5542,13 +5609,14 @@ void OBSBasic::on_actionRemux_triggered() return; } - const char *mode = config_get_string(basicConfig, "Output", "Mode"); + const char *mode = + config_get_string(activeConfiguration, "Output", "Mode"); const char *path = strcmp(mode, "Advanced") - ? config_get_string(basicConfig, + ? config_get_string(activeConfiguration, "SimpleOutput", "FilePath") - : config_get_string(basicConfig, "AdvOut", - "RecFilePath"); + : config_get_string(activeConfiguration, + "AdvOut", "RecFilePath"); OBSRemux *remuxDlg; remuxDlg = new OBSRemux(path, this); @@ -8507,12 +8575,16 @@ void OBSBasic::on_actionShowSettingsFolder_triggered() void OBSBasic::on_actionShowProfileFolder_triggered() { - char path[512]; - int ret = GetProfilePath(path, 512, ""); - if (ret <= 0) - return; + std::string userProfilePath; + userProfilePath.reserve(App()->userProfilesLocation.u8string().size() + + OBSProfilePath.size()); + userProfilePath.append(App()->userProfilesLocation.u8string()) + .append(OBSProfilePath); - QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + const QString userProfileLocation = + QString::fromStdString(userProfilePath); + + QDesktopServices::openUrl(QUrl::fromLocalFile(userProfileLocation)); } int OBSBasic::GetTopSelectedSourceItem() @@ -8599,7 +8671,8 @@ void OBSBasic::ToggleAlwaysOnTop() void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const { - const char *val = config_get_string(basicConfig, "Video", "FPSCommon"); + const char *val = + config_get_string(activeConfiguration, "Video", "FPSCommon"); if (strcmp(val, "10") == 0) { num = 10; @@ -8636,25 +8709,26 @@ void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const void OBSBasic::GetFPSInteger(uint32_t &num, uint32_t &den) const { - num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSInt"); + num = (uint32_t)config_get_uint(activeConfiguration, "Video", "FPSInt"); den = 1; } void OBSBasic::GetFPSFraction(uint32_t &num, uint32_t &den) const { - num = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNum"); - den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSDen"); + num = (uint32_t)config_get_uint(activeConfiguration, "Video", "FPSNum"); + den = (uint32_t)config_get_uint(activeConfiguration, "Video", "FPSDen"); } void OBSBasic::GetFPSNanoseconds(uint32_t &num, uint32_t &den) const { num = 1000000000; - den = (uint32_t)config_get_uint(basicConfig, "Video", "FPSNS"); + den = (uint32_t)config_get_uint(activeConfiguration, "Video", "FPSNS"); } void OBSBasic::GetConfigFPS(uint32_t &num, uint32_t &den) const { - uint32_t type = config_get_uint(basicConfig, "Video", "FPSType"); + uint32_t type = + config_get_uint(activeConfiguration, "Video", "FPSType"); if (type == 1) //"Integer" GetFPSInteger(num, den); @@ -8670,7 +8744,7 @@ void OBSBasic::GetConfigFPS(uint32_t &num, uint32_t &den) const config_t *OBSBasic::Config() const { - return basicConfig; + return activeConfiguration; } #ifdef YOUTUBE_ENABLED @@ -10589,14 +10663,14 @@ void OBSBasic::ResizeOutputSizeOfSource() int width = obs_source_get_width(source); int height = obs_source_get_height(source); - config_set_uint(basicConfig, "Video", "BaseCX", width); - config_set_uint(basicConfig, "Video", "BaseCY", height); - config_set_uint(basicConfig, "Video", "OutputCX", width); - config_set_uint(basicConfig, "Video", "OutputCY", height); + config_set_uint(activeConfiguration, "Video", "BaseCX", width); + config_set_uint(activeConfiguration, "Video", "BaseCY", height); + config_set_uint(activeConfiguration, "Video", "OutputCX", width); + config_set_uint(activeConfiguration, "Video", "OutputCY", height); ResetVideo(); ResetOutputs(); - config_save_safe(basicConfig, "tmp", nullptr); + activeConfiguration.SaveSafe("tmp"); on_actionFitToScreen_triggered(); } @@ -10929,25 +11003,26 @@ void OBSBasic::RecordPauseToggled() void OBSBasic::UpdateIsRecordingPausable() { - const char *mode = config_get_string(basicConfig, "Output", "Mode"); + const char *mode = + config_get_string(activeConfiguration, "Output", "Mode"); bool adv = astrcmpi(mode, "Advanced") == 0; bool shared = true; if (adv) { - const char *recType = - config_get_string(basicConfig, "AdvOut", "RecType"); + const char *recType = config_get_string(activeConfiguration, + "AdvOut", "RecType"); if (astrcmpi(recType, "FFmpeg") == 0) { - shared = config_get_bool(basicConfig, "AdvOut", + shared = config_get_bool(activeConfiguration, "AdvOut", "FFOutputToFile"); } else { const char *recordEncoder = config_get_string( - basicConfig, "AdvOut", "RecEncoder"); + activeConfiguration, "AdvOut", "RecEncoder"); shared = astrcmpi(recordEncoder, "none") == 0; } } else { const char *quality = config_get_string( - basicConfig, "SimpleOutput", "RecQuality"); + activeConfiguration, "SimpleOutput", "RecQuality"); shared = strcmp(quality, "Stream") == 0; } @@ -11272,3 +11347,57 @@ void OBSBasic::PreviewScalingModeChanged(int value) break; }; } + +// MARK: - Generic UI Helper Functions + +OBSPromptResult OBSBasic::PromptForName(const OBSPromptRequest &request, + const OBSPromptCallback &callback) +{ + OBSPromptResult result; + + for (;;) { + result.success = false; + + if (request.withOption && !request.optionPrompt.empty()) { + result.optionValue = request.optionValue; + + result.success = NameDialog::AskForNameWithOption( + this, request.title.c_str(), + request.prompt.c_str(), result.promptValue, + request.optionPrompt.c_str(), + result.optionValue, + (request.promptValue.empty() + ? nullptr + : request.promptValue.c_str())); + + } else { + result.success = NameDialog::AskForName( + this, request.title.c_str(), + request.prompt.c_str(), result.promptValue, + (request.promptValue.empty() + ? nullptr + : request.promptValue.c_str())); + } + + if (!result.success) { + break; + } + + if (result.promptValue.empty()) { + OBSMessageBox::warning(this, + QTStr("NoNameEntered.Title"), + QTStr("NoNameEntered.Text")); + continue; + } + + if (!callback(result)) { + OBSMessageBox::warning(this, QTStr("NameExists.Title"), + QTStr("NameExists.Text")); + continue; + } + + break; + } + + return result; +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index c1d21c99190a2d..4bda02c2008956 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -134,6 +134,37 @@ struct QuickTransition { std::shared_ptr renamedSignal; }; +struct OBSProfile { + std::string name; + std::string directoryName; + std::filesystem::path path; + std::filesystem::path profileFile; +}; + +struct OBSSceneCollection { + std::string name; + std::string fileName; + std::filesystem::path collectionFile; +}; + +struct OBSPromptResult { + bool success; + std::string promptValue; + bool optionValue; +}; + +struct OBSPromptRequest { + std::string title; + std::string prompt; + std::string promptValue; + bool withOption; + std::string optionPrompt; + bool optionValue; +}; + +using OBSPromptCallback = std::function; + +using OBSProfileCache = std::map; class ColorSelect : public QWidget { public: @@ -443,17 +474,6 @@ class OBSBasic : public OBSMainWindow { void RefreshSceneCollections(); void ChangeSceneCollection(); void LogScenes(); - - void ResetProfileData(); - bool AddProfile(bool create_new, const char *title, const char *text, - const char *init_text = nullptr, bool rename = false); - bool CreateProfile(const std::string &newName, bool create_new, - bool showWizardChecked, bool rename = false); - void DeleteProfile(const char *profile_name, const char *profile_dir); - void RefreshProfiles(); - void ChangeProfile(); - void CheckForSimpleModeX264Fallback(); - void SaveProjectNow(); int GetTopSelectedSourceItem(); @@ -742,11 +762,6 @@ public slots: bool AddSceneCollection(bool create_new, const QString &name = QString()); - - bool NewProfile(const QString &name); - bool DuplicateProfile(const QString &name); - void DeleteProfile(const QString &profileName); - void UpdatePatronJson(const QString &text, const QString &error); void ShowContextBar(); @@ -1156,13 +1171,6 @@ private slots: void on_actionExportSceneCollection_triggered(); void on_actionRemigrateSceneCollection_triggered(); - void on_actionNewProfile_triggered(); - void on_actionDupProfile_triggered(); - void on_actionRenameProfile_triggered(); - void on_actionRemoveProfile_triggered(bool skipConfirmation = false); - void on_actionImportProfile_triggered(); - void on_actionExportProfile_triggered(); - void on_actionShowSettingsFolder_triggered(); void on_actionShowProfileFolder_triggered(); @@ -1337,6 +1345,59 @@ public slots: void DeleteYouTubeAppDock(); YouTubeAppDock *GetYouTubeAppDock(); #endif + // MARK: - Generic UI Helper Functions + OBSPromptResult PromptForName(const OBSPromptRequest &request, + const OBSPromptCallback &callback); + + // MARK: - OBS Profile Management +private: + OBSProfileCache profiles{}; + + void SetupNewProfile(const std::string &profileName, + bool useWizard = false); + void SetupDuplicateProfile(const std::string &profileName); + void SetupRenameProfile(const std::string &profileName); + + const OBSProfile &CreateProfile(const std::string &profileName); + void RemoveProfile(OBSProfile profile); + + void ChangeProfile(); + + void RefreshProfileCache(); + + void RefreshProfiles(bool refreshCache = false); + + void ActivateProfile(const OBSProfile &profile, bool reset = false); + std::vector + GetRestartRequirements(const ConfigFile &config) const; + void ResetProfileData(); + void CheckForSimpleModeX264Fallback(); + +public: + inline const OBSProfileCache &GetProfileCache() const noexcept + { + return profiles; + }; + + const OBSProfile &GetCurrentProfile() const; + + std::optional + GetProfileByName(const std::string &profileName) const; + std::optional + GetProfileByDirectoryName(const std::string &directoryName) const; + +private slots: + void on_actionNewProfile_triggered(); + void on_actionDupProfile_triggered(); + void on_actionRenameProfile_triggered(); + void on_actionRemoveProfile_triggered(bool skipConfirmation = false); + void on_actionImportProfile_triggered(); + void on_actionExportProfile_triggered(); + +public slots: + bool CreateNewProfile(const QString &name); + bool CreateDuplicateProfile(const QString &name); + void DeleteProfile(const QString &profileName); }; extern bool cef_js_avail; diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 7cb863044e2fc6..9244050777c79e 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -2136,12 +2136,16 @@ OBSBasicSettings::CreateEncoderPropertyView(const char *encoder, OBSPropertiesView *view; if (path) { - char encoderJsonPath[512]; - int ret = GetProfilePath(encoderJsonPath, - sizeof(encoderJsonPath), path); - if (ret > 0) { + const OBSBasic *basic = + reinterpret_cast(App()->GetMainWindow()); + const OBSProfile ¤tProfile = basic->GetCurrentProfile(); + + const std::filesystem::path jsonFilePath = + currentProfile.path / std::filesystem::u8path(path); + + if (!jsonFilePath.empty()) { obs_data_t *data = obs_data_create_from_json_file_safe( - encoderJsonPath, "bak"); + jsonFilePath.u8string().c_str(), "bak"); obs_data_apply(settings, data); obs_data_release(data); } @@ -3748,17 +3752,22 @@ static inline const char *SplitFileTypeFromIdx(int idx) static void WriteJsonData(OBSPropertiesView *view, const char *path) { - char full_path[512]; - if (!view || !WidgetChanged(view)) return; - int ret = GetProfilePath(full_path, sizeof(full_path), path); - if (ret > 0) { + const OBSBasic *basic = + reinterpret_cast(App()->GetMainWindow()); + const OBSProfile ¤tProfile = basic->GetCurrentProfile(); + + const std::filesystem::path jsonFilePath = + currentProfile.path / std::filesystem::u8path(path); + + if (!jsonFilePath.empty()) { obs_data_t *settings = view->GetSettings(); if (settings) { - obs_data_save_json_safe(settings, full_path, "tmp", - "bak"); + obs_data_save_json_safe(settings, + jsonFilePath.u8string().c_str(), + "tmp", "bak"); } } } @@ -5691,14 +5700,16 @@ void OBSBasicSettings::AdvReplayBufferChanged() if (!settings) return; - char encoderJsonPath[512]; - int ret = GetProfilePath(encoderJsonPath, - sizeof(encoderJsonPath), - "recordEncoder.json"); - if (ret > 0) { + const OBSProfile ¤tProfile = main->GetCurrentProfile(); + + const std::filesystem::path jsonFilePath = + currentProfile.path / + std::filesystem::u8path("recordEncoder.json"); + + if (!jsonFilePath.empty()) { OBSDataAutoRelease data = obs_data_create_from_json_file_safe( - encoderJsonPath, "bak"); + jsonFilePath.u8string().c_str(), "bak"); obs_data_apply(settings, data); } } diff --git a/UI/window-youtube-actions.cpp b/UI/window-youtube-actions.cpp index e2d1926dfbaaaa..574881e6efe987 100644 --- a/UI/window-youtube-actions.cpp +++ b/UI/window-youtube-actions.cpp @@ -287,8 +287,8 @@ OBSYoutubeActions::OBSYoutubeActions(QWidget *parent, Auth *auth, workerThread->start(); OBSBasic *main = OBSBasic::Get(); - bool rememberSettings = config_get_bool(main->basicConfig, "YouTube", - "RememberSettings"); + bool rememberSettings = config_get_bool(main->activeConfiguration, + "YouTube", "RememberSettings"); if (rememberSettings) LoadSettings(); @@ -749,83 +749,85 @@ void OBSYoutubeActions::SaveSettings(BroadcastDescription &broadcast) { OBSBasic *main = OBSBasic::Get(); - config_set_string(main->basicConfig, "YouTube", "Title", + config_set_string(main->activeConfiguration, "YouTube", "Title", QT_TO_UTF8(broadcast.title)); - config_set_string(main->basicConfig, "YouTube", "Description", + config_set_string(main->activeConfiguration, "YouTube", "Description", QT_TO_UTF8(broadcast.description)); - config_set_string(main->basicConfig, "YouTube", "Privacy", + config_set_string(main->activeConfiguration, "YouTube", "Privacy", QT_TO_UTF8(broadcast.privacy)); - config_set_string(main->basicConfig, "YouTube", "CategoryID", + config_set_string(main->activeConfiguration, "YouTube", "CategoryID", QT_TO_UTF8(broadcast.category.id)); - config_set_string(main->basicConfig, "YouTube", "Latency", + config_set_string(main->activeConfiguration, "YouTube", "Latency", QT_TO_UTF8(broadcast.latency)); - config_set_bool(main->basicConfig, "YouTube", "MadeForKids", + config_set_bool(main->activeConfiguration, "YouTube", "MadeForKids", broadcast.made_for_kids); - config_set_bool(main->basicConfig, "YouTube", "AutoStart", + config_set_bool(main->activeConfiguration, "YouTube", "AutoStart", broadcast.auto_start); - config_set_bool(main->basicConfig, "YouTube", "AutoStop", + config_set_bool(main->activeConfiguration, "YouTube", "AutoStop", broadcast.auto_start); - config_set_bool(main->basicConfig, "YouTube", "DVR", broadcast.dvr); - config_set_bool(main->basicConfig, "YouTube", "ScheduleForLater", - broadcast.schedul_for_later); - config_set_string(main->basicConfig, "YouTube", "Projection", + config_set_bool(main->activeConfiguration, "YouTube", "DVR", + broadcast.dvr); + config_set_bool(main->activeConfiguration, "YouTube", + "ScheduleForLater", broadcast.schedul_for_later); + config_set_string(main->activeConfiguration, "YouTube", "Projection", QT_TO_UTF8(broadcast.projection)); - config_set_string(main->basicConfig, "YouTube", "ThumbnailFile", + config_set_string(main->activeConfiguration, "YouTube", "ThumbnailFile", QT_TO_UTF8(thumbnailFile)); - config_set_bool(main->basicConfig, "YouTube", "RememberSettings", true); + config_set_bool(main->activeConfiguration, "YouTube", + "RememberSettings", true); } void OBSYoutubeActions::LoadSettings() { OBSBasic *main = OBSBasic::Get(); - const char *title = - config_get_string(main->basicConfig, "YouTube", "Title"); + const char *title = config_get_string(main->activeConfiguration, + "YouTube", "Title"); ui->title->setText(QT_UTF8(title)); - const char *desc = - config_get_string(main->basicConfig, "YouTube", "Description"); + const char *desc = config_get_string(main->activeConfiguration, + "YouTube", "Description"); ui->description->setPlainText(QT_UTF8(desc)); - const char *priv = - config_get_string(main->basicConfig, "YouTube", "Privacy"); + const char *priv = config_get_string(main->activeConfiguration, + "YouTube", "Privacy"); int index = ui->privacyBox->findData(priv); ui->privacyBox->setCurrentIndex(index); - const char *catID = - config_get_string(main->basicConfig, "YouTube", "CategoryID"); + const char *catID = config_get_string(main->activeConfiguration, + "YouTube", "CategoryID"); index = ui->categoryBox->findData(catID); ui->categoryBox->setCurrentIndex(index); - const char *latency = - config_get_string(main->basicConfig, "YouTube", "Latency"); + const char *latency = config_get_string(main->activeConfiguration, + "YouTube", "Latency"); index = ui->latencyBox->findData(latency); ui->latencyBox->setCurrentIndex(index); - bool dvr = config_get_bool(main->basicConfig, "YouTube", "DVR"); + bool dvr = config_get_bool(main->activeConfiguration, "YouTube", "DVR"); ui->checkDVR->setChecked(dvr); - bool forKids = - config_get_bool(main->basicConfig, "YouTube", "MadeForKids"); + bool forKids = config_get_bool(main->activeConfiguration, "YouTube", + "MadeForKids"); if (forKids) ui->yesMakeForKids->setChecked(true); else ui->notMakeForKids->setChecked(true); - bool schedLater = config_get_bool(main->basicConfig, "YouTube", + bool schedLater = config_get_bool(main->activeConfiguration, "YouTube", "ScheduleForLater"); ui->checkScheduledLater->setChecked(schedLater); - bool autoStart = - config_get_bool(main->basicConfig, "YouTube", "AutoStart"); + bool autoStart = config_get_bool(main->activeConfiguration, "YouTube", + "AutoStart"); ui->checkAutoStart->setChecked(autoStart); - bool autoStop = - config_get_bool(main->basicConfig, "YouTube", "AutoStop"); + bool autoStop = config_get_bool(main->activeConfiguration, "YouTube", + "AutoStop"); ui->checkAutoStop->setChecked(autoStop); - const char *projection = - config_get_string(main->basicConfig, "YouTube", "Projection"); + const char *projection = config_get_string(main->activeConfiguration, + "YouTube", "Projection"); if (projection && *projection) { if (strcmp(projection, "360") == 0) ui->check360Video->setChecked(true); @@ -833,8 +835,8 @@ void OBSYoutubeActions::LoadSettings() ui->check360Video->setChecked(false); } - const char *thumbFile = config_get_string(main->basicConfig, "YouTube", - "ThumbnailFile"); + const char *thumbFile = config_get_string(main->activeConfiguration, + "YouTube", "ThumbnailFile"); if (thumbFile && *thumbFile) { QFileInfo tFile(thumbFile); // Re-check validity before setting path again From 3e0592dc207c912e206777e3b3e942960ddf4af1 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 3 Sep 2024 16:29:58 +0200 Subject: [PATCH 0532/1073] UI: Rewrite scene collection system to enable user-provided storage This change enables loading scene collections from locations different than OBS' own configuration directory. It also rewrites profile management in the app to work off an in-memory collection of profiles found on disk and does not require iterating over directory contents for most profile interactions by the app. --- UI/api-interface.cpp | 21 +- UI/obs-app.cpp | 75 +- UI/window-basic-main-scene-collections.cpp | 818 +++++++++++++-------- UI/window-basic-main.cpp | 67 +- UI/window-basic-main.hpp | 62 +- UI/window-importer.cpp | 47 +- 6 files changed, 663 insertions(+), 427 deletions(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 742e1051553713..ee6dae7da87d47 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -16,8 +16,6 @@ template static T GetOBSRef(QListWidgetItem *item) return item->data(static_cast(QtDataRole::OBSRef)).value(); } -void EnumSceneCollections(function &&cb); - extern volatile bool streaming_active; extern volatile bool recording_active; extern volatile bool recording_paused; @@ -168,19 +166,17 @@ struct OBSStudioAPI : obs_frontend_callbacks { void obs_frontend_get_scene_collections( std::vector &strings) override { - auto addCollection = [&](const char *name, const char *) { - strings.emplace_back(name); - return true; - }; - - EnumSceneCollections(addCollection); + for (auto &[collectionName, collection] : + main->GetSceneCollectionCache()) { + strings.emplace_back(collectionName); + } } char *obs_frontend_get_current_scene_collection(void) override { - const char *cur_name = config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollection"); - return bstrdup(cur_name); + const OBSSceneCollection ¤tCollection = + main->GetCurrentSceneCollection(); + return bstrdup(currentCollection.name.c_str()); } void obs_frontend_set_current_scene_collection( @@ -206,10 +202,9 @@ struct OBSStudioAPI : obs_frontend_callbacks { bool obs_frontend_add_scene_collection(const char *name) override { bool success = false; - QMetaObject::invokeMethod(main, "AddSceneCollection", + QMetaObject::invokeMethod(main, "NewSceneCollection", WaitConnection(), Q_RETURN_ARG(bool, success), - Q_ARG(bool, true), Q_ARG(QString, QT_UTF8(name))); return success; } diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index f4b9bf50986d10..5f37267a372dfa 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1220,27 +1220,48 @@ static void move_basic_to_profiles(void) static void move_basic_to_scene_collections(void) { char path[512]; - char new_path[512]; - if (GetConfigPath(path, 512, "obs-studio/basic") <= 0) - return; - if (!os_file_exists(path)) + if (GetAppConfigPath(path, 512, "obs-studio/basic") <= 0) { return; + } + + const std::filesystem::path basicPath = std::filesystem::u8path(path); - if (GetConfigPath(new_path, 512, "obs-studio/basic/scenes") <= 0) + if (!std::filesystem::exists(basicPath)) { return; - if (os_file_exists(new_path)) + } + + const std::filesystem::path sceneCollectionPath = + App()->userScenesLocation / + std::filesystem::u8path("obs-studio/basic/scenes"); + + if (std::filesystem::exists(sceneCollectionPath)) { return; + } - if (os_mkdir(new_path) == MKDIR_ERROR) + try { + std::filesystem::create_directories(sceneCollectionPath); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to create scene collection directory for migration from basic scene collection\n%s", + error.what()); return; + } - strcat(path, "/scenes.json"); - strcat(new_path, "/"); - strcat(new_path, Str("Untitled")); - strcat(new_path, ".json"); + const std::filesystem::path sourceFile = + basicPath / std::filesystem::u8path("scenes.json"); + const std::filesystem::path destinationFile = + (sceneCollectionPath / std::filesystem::u8path(Str("Untitled"))) + .replace_extension(".json"); - os_rename(path, new_path); + try { + std::filesystem::rename(sourceFile, destinationFile); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_ERROR, + "Failed to rename basic scene collection file:\n%s", + error.what()); + return; + } } void OBSApp::AppInit() @@ -2524,36 +2545,6 @@ bool GetClosestUnusedFileName(std::string &path, const char *extension) return true; } -bool GetUnusedSceneCollectionFile(std::string &name, std::string &file) -{ - char path[512]; - int ret; - - if (!GetFileSafeName(name.c_str(), file)) { - blog(LOG_WARNING, "Failed to create safe file name for '%s'", - name.c_str()); - return false; - } - - ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get scene collection config path"); - return false; - } - - file.insert(0, path); - - if (!GetClosestUnusedFileName(file, "json")) { - blog(LOG_WARNING, "Failed to get closest file name for %s", - file.c_str()); - return false; - } - - file.erase(file.size() - 5, 5); - file.erase(0, strlen(path)); - return true; -} - bool WindowPositionValid(QRect rect) { for (QScreen *screen : QGuiApplication::screens()) { diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index a1ce86cbb8ad0d..4de7c29d4ccb4a 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -15,6 +15,9 @@ along with this program. If not, see . ******************************************************************************/ +#include +#include + #include #include #include @@ -27,209 +30,319 @@ #include "window-importer.hpp" #include "window-namedialog.hpp" -using namespace std; +// MARK: Constant Expressions + +constexpr std::string_view OBSSceneCollectionPath = "/obs-studio/basic/scenes/"; -void EnumSceneCollections(std::function &&cb) +// MARK: - Main Scene Collection Management Functions + +void OBSBasic::SetupNewSceneCollection(const std::string &collectionName) { - char path[512]; - os_glob_t *glob; - - int ret = GetConfigPath(path, sizeof(path), - "obs-studio/basic/scenes/*.json"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get config path for scene " - "collections"); - return; - } + const OBSSceneCollection &newCollection = + CreateSceneCollection(collectionName); - if (os_glob(path, 0, &glob) != 0) { - blog(LOG_WARNING, "Failed to glob scene collections"); - return; + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + + ActivateSceneCollection(newCollection); + + blog(LOG_INFO, "Created scene collection '%s' (clean, %s)", + newCollection.name.c_str(), newCollection.fileName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); +} + +void OBSBasic::SetupDuplicateSceneCollection(const std::string &collectionName) +{ + const OBSSceneCollection &newCollection = + CreateSceneCollection(collectionName); + const OBSSceneCollection ¤tCollection = + GetCurrentSceneCollection(); + + SaveProjectNow(); + + const auto copyOptions = + std::filesystem::copy_options::overwrite_existing; + + try { + std::filesystem::copy(currentCollection.collectionFile, + newCollection.collectionFile, + copyOptions); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_DEBUG, "%s", error.what()); + throw std::logic_error( + "Failed to copy file for cloned scene collection: " + + newCollection.name); } - for (size_t i = 0; i < glob->gl_pathc; i++) { - const char *filePath = glob->gl_pathv[i].path; + OBSDataAutoRelease collection = obs_data_create_from_json_file( + newCollection.collectionFile.u8string().c_str()); - if (glob->gl_pathv[i].directory) - continue; + obs_data_set_string(collection, "name", newCollection.name.c_str()); - OBSDataAutoRelease data = - obs_data_create_from_json_file_safe(filePath, "bak"); - std::string name = obs_data_get_string(data, "name"); + OBSDataArrayAutoRelease sources = + obs_data_get_array(collection, "sources"); - /* if no name found, use the file name as the name - * (this only happens when switching to the new version) */ - if (name.empty()) { - name = strrchr(filePath, '/') + 1; - name.resize(name.size() - 5); - } + if (sources) { + obs_data_erase(collection, "sources"); + + obs_data_array_enum( + sources, + [](obs_data_t *data, void *) -> void { + const char *uuid = os_generate_uuid(); - if (!cb(name.c_str(), filePath)) - break; + obs_data_set_string(data, "uuid", uuid); + + bfree((void *)uuid); + }, + nullptr); + + obs_data_set_array(collection, "sources", sources); } - os_globfree(glob); + obs_data_save_json_safe(collection, + newCollection.collectionFile.u8string().c_str(), + "tmp", nullptr); + + ActivateSceneCollection(newCollection); + + blog(LOG_INFO, "Created scene collection '%s' (duplicate, %s)", + newCollection.name.c_str(), newCollection.fileName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); } -bool SceneCollectionExists(const char *findName) +void OBSBasic::SetupRenameSceneCollection(const std::string &collectionName) { - bool found = false; - auto func = [&](const char *name, const char *) { - if (strcmp(name, findName) == 0) { - found = true; - return false; - } + const OBSSceneCollection &newCollection = + CreateSceneCollection(collectionName); + const OBSSceneCollection currentCollection = + GetCurrentSceneCollection(); - return true; - }; + SaveProjectNow(); + + const auto copyOptions = + std::filesystem::copy_options::overwrite_existing; + + try { + std::filesystem::copy(currentCollection.collectionFile, + newCollection.collectionFile, + copyOptions); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_DEBUG, "%s", error.what()); + throw std::logic_error( + "Failed to copy file for scene collection: " + + currentCollection.name); + } + + collections.erase(currentCollection.name); + + OBSDataAutoRelease collection = obs_data_create_from_json_file( + newCollection.collectionFile.u8string().c_str()); + + obs_data_set_string(collection, "name", newCollection.name.c_str()); - EnumSceneCollections(func); - return found; + obs_data_save_json_safe(collection, + newCollection.collectionFile.u8string().c_str(), + "tmp", nullptr); + + ActivateSceneCollection(newCollection); + RemoveSceneCollection(currentCollection); + + blog(LOG_INFO, "Renamed scene collection '%s' to '%s' (%s)", + currentCollection.name.c_str(), newCollection.name.c_str(), + newCollection.fileName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); + + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED); } -static bool GetSceneCollectionName(QWidget *parent, std::string &name, - std::string &file, - const char *oldName = nullptr) +// MARK: - Scene Collection File Management Functions + +const OBSSceneCollection & +OBSBasic::CreateSceneCollection(const std::string &collectionName) { - bool rename = oldName != nullptr; - const char *title; - const char *text; + if (const auto &foundCollection = + GetSceneCollectionByName(collectionName)) { + throw std::invalid_argument( + "Scene collection already exists: " + collectionName); + } - if (rename) { - title = Str("Basic.Main.RenameSceneCollection.Title"); - text = Str("Basic.Main.AddSceneCollection.Text"); - } else { - title = Str("Basic.Main.AddSceneCollection.Title"); - text = Str("Basic.Main.AddSceneCollection.Text"); + std::string fileName; + if (!GetFileSafeName(collectionName.c_str(), fileName)) { + throw std::invalid_argument( + "Failed to create safe directory for new scene collection: " + + collectionName); } - for (;;) { - bool success = NameDialog::AskForName(parent, title, text, name, - QT_UTF8(oldName)); - if (!success) { - return false; - } - if (name.empty()) { - OBSMessageBox::warning(parent, - QTStr("NoNameEntered.Title"), - QTStr("NoNameEntered.Text")); - continue; - } - if (SceneCollectionExists(name.c_str())) { - OBSMessageBox::warning(parent, - QTStr("NameExists.Title"), - QTStr("NameExists.Text")); - continue; - } - break; + std::string collectionFile; + collectionFile.reserve(App()->userScenesLocation.u8string().size() + + OBSSceneCollectionPath.size() + fileName.size()); + collectionFile.append(App()->userScenesLocation.u8string()) + .append(OBSSceneCollectionPath) + .append(fileName); + + if (!GetClosestUnusedFileName(collectionFile, "json")) { + throw std::invalid_argument( + "Failed to get closest file name for new scene collection: " + + fileName); } - if (!GetUnusedSceneCollectionFile(name, file)) { - return false; + const std::filesystem::path collectionFilePath = + std::filesystem::u8path(collectionFile); + + auto [iterator, success] = collections.try_emplace( + collectionName, + OBSSceneCollection{collectionName, + collectionFilePath.filename().u8string(), + collectionFilePath}); + + return iterator->second; +} + +void OBSBasic::RemoveSceneCollection(OBSSceneCollection collection) +{ + std::filesystem::path collectionBackupFile{collection.collectionFile}; + collectionBackupFile.replace_extension("json.bak"); + + try { + std::filesystem::remove(collection.collectionFile); + std::filesystem::remove(collectionBackupFile); + } catch (const std::filesystem::filesystem_error &error) { + blog(LOG_DEBUG, "%s", error.what()); + throw std::logic_error( + "Failed to remove scene collection file: " + + collection.fileName); } - return true; + blog(LOG_INFO, "Removed scene collection '%s' (%s)", + collection.name.c_str(), collection.fileName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); +} + +// MARK: - Scene Collection UI Handling Functions + +bool OBSBasic::CreateNewSceneCollection(const QString &name) +{ + try { + SetupNewSceneCollection(name.toStdString()); + return true; + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; + } } -bool OBSBasic::AddSceneCollection(bool create_new, const QString &qname) +bool OBSBasic::CreateDuplicateSceneCollection(const QString &name) { - std::string name; - std::string file; + try { + SetupDuplicateSceneCollection(name.toStdString()); + return true; + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + return false; + } +} - if (qname.isEmpty()) { - if (!GetSceneCollectionName(this, name, file)) - return false; - } else { - name = QT_TO_UTF8(qname); - if (SceneCollectionExists(name.c_str())) - return false; +void OBSBasic::DeleteSceneCollection(const QString &name) +{ + const std::string_view currentCollectionName{config_get_string( + App()->GetUserConfig(), "Basic", "SceneCollection")}; - if (!GetUnusedSceneCollectionFile(name, file)) { - return false; - } + if (currentCollectionName == name.toStdString()) { + on_actionRemoveSceneCollection_triggered(); + return; } - auto new_collection = [this, create_new](const std::string &file, - const std::string &name) { - SaveProjectNow(); + OBSSceneCollection currentCollection = GetCurrentSceneCollection(); - config_set_string(App()->GlobalConfig(), "Basic", - "SceneCollection", name.c_str()); - config_set_string(App()->GlobalConfig(), "Basic", - "SceneCollectionFile", file.c_str()); + RemoveSceneCollection(currentCollection); - if (create_new) { - CreateDefaultScene(false); - } else { - obs_reset_source_uuids(); - } + collections.erase(name.toStdString()); - SaveProjectNow(); - RefreshSceneCollections(); - }; + RefreshSceneCollections(); - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); +} - new_collection(file, name); +void OBSBasic::ChangeSceneCollection() +{ + QAction *action = reinterpret_cast(sender()); - blog(LOG_INFO, "Added scene collection '%s' (%s, %s.json)", - name.c_str(), create_new ? "clean" : "duplicate", file.c_str()); - blog(LOG_INFO, "------------------------------------------------"); + if (!action) { + return; + } - UpdateTitleBar(); + const std::string_view currentCollectionName{config_get_string( + App()->GetUserConfig(), "Basic", "SceneCollection")}; + const std::string selectedCollectionName{action->text().toStdString()}; - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + if (currentCollectionName == selectedCollectionName) { + action->setChecked(true); + return; + } + + const std::optional foundCollection = + GetSceneCollectionByName(selectedCollectionName); + + if (!foundCollection) { + const std::string errorMessage{ + "Selected scene collection not found: "}; + + throw std::invalid_argument(errorMessage + + currentCollectionName.data()); + } + + const OBSSceneCollection &selectedCollection = foundCollection.value(); + + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + + ActivateSceneCollection(selectedCollection); - return true; + blog(LOG_INFO, "Switched to scene collection '%s' (%s)", + selectedCollection.name.c_str(), + selectedCollection.fileName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); } -void OBSBasic::RefreshSceneCollections() +void OBSBasic::RefreshSceneCollections(bool refreshCache) { + std::string_view currentCollectionName{config_get_string( + App()->GetUserConfig(), "Basic", "SceneCollection")}; + QList menuActions = ui->sceneCollectionMenu->actions(); - int count = 0; - for (int i = 0; i < menuActions.count(); i++) { - QVariant v = menuActions[i]->property("file_name"); - if (v.typeName() != nullptr) - delete menuActions[i]; + for (auto &action : menuActions) { + QVariant variant = action->property("file_name"); + if (variant.typeName() != nullptr) { + delete action; + } } - const char *cur_name = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollection"); - - auto addCollection = [&](const char *name, const char *path) { - std::string file = strrchr(path, '/') + 1; - file.erase(file.size() - 5, 5); + if (refreshCache) { + RefreshSceneCollectionCache(); + } - QAction *action = new QAction(QT_UTF8(name), this); - action->setProperty("file_name", QT_UTF8(path)); + size_t numAddedCollections = 0; + for (auto &[collectionName, collection] : collections) { + QAction *action = new QAction( + QString().fromStdString(collectionName), this); + action->setProperty("file_name", QString().fromStdString( + collection.fileName)); connect(action, &QAction::triggered, this, &OBSBasic::ChangeSceneCollection); action->setCheckable(true); - - action->setChecked(strcmp(name, cur_name) == 0); + action->setChecked(collectionName == currentCollectionName); ui->sceneCollectionMenu->addAction(action); - count++; - return true; - }; - EnumSceneCollections(addCollection); - - /* force saving of first scene collection on first run, otherwise - * no scene collections will show up */ - if (!count) { - long prevDisableVal = disableSaving; - - disableSaving = 0; - SaveProjectNow(); - disableSaving = prevDisableVal; - - EnumSceneCollections(addCollection); + numAddedCollections += 1; } - ui->actionRemoveSceneCollection->setEnabled(count > 1); + ui->actionRemoveSceneCollection->setEnabled(numAddedCollections > 1); OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); @@ -238,125 +351,243 @@ void OBSBasic::RefreshSceneCollections() main->ui->actionPasteDup->setEnabled(false); } -void OBSBasic::on_actionNewSceneCollection_triggered() +// MARK: - Scene Collection Cache Functions + +void OBSBasic::RefreshSceneCollectionCache() +{ + OBSSceneCollectionCache foundCollections{}; + + const std::filesystem::path collectionsPath = + App()->userScenesLocation / + std::filesystem::u8path(OBSSceneCollectionPath.substr(1)); + + if (!std::filesystem::exists(collectionsPath)) { + blog(LOG_WARNING, + "Failed to get scene collections config path"); + return; + } + + for (const auto &entry : + std::filesystem::directory_iterator(collectionsPath)) { + if (entry.is_directory()) { + continue; + } + + if (entry.path().extension().u8string() != ".json") { + continue; + } + + OBSDataAutoRelease collectionData = + obs_data_create_from_json_file_safe( + entry.path().u8string().c_str(), "bak"); + + std::string candidateName; + const char *collectionName = + obs_data_get_string(collectionData, "name"); + + if (!collectionName) { + candidateName = entry.path().filename().u8string(); + } else { + candidateName = collectionName; + } + + foundCollections.try_emplace( + candidateName, + OBSSceneCollection{candidateName, + entry.path().filename().u8string(), + entry.path()}); + } + + collections.swap(foundCollections); +} + +const OBSSceneCollection &OBSBasic::GetCurrentSceneCollection() const { - AddSceneCollection(true); + std::string currentCollectionName{config_get_string( + App()->GetUserConfig(), "Basic", "SceneCollection")}; + + if (currentCollectionName.empty()) { + throw std::invalid_argument( + "No valid scene collection name in configuration Basic->SceneCollection"); + } + + const auto &foundCollection = collections.find(currentCollectionName); + + if (foundCollection != collections.end()) { + return foundCollection->second; + } else { + throw std::invalid_argument( + "Scene collection not found in collection list: " + + currentCollectionName); + } } -void OBSBasic::on_actionDupSceneCollection_triggered() +std::optional +OBSBasic::GetSceneCollectionByName(const std::string &collectionName) const { - AddSceneCollection(false); + auto foundCollection = collections.find(collectionName); + + if (foundCollection == collections.end()) { + return {}; + } else { + return foundCollection->second; + } } -void OBSBasic::on_actionRenameSceneCollection_triggered() +std::optional +OBSBasic::GetSceneCollectionByFileName(const std::string &fileName) const { - std::string name; - std::string file; - std::string oname; - - std::string oldFile = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollectionFile"); - const char *oldName = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollection"); - oname = std::string(oldName); - - bool success = GetSceneCollectionName(this, name, file, oldName); - if (!success) - return; + for (auto &[iterator, collection] : collections) { + if (collection.fileName == fileName) { + return collection; + } + } - config_set_string(App()->GlobalConfig(), "Basic", "SceneCollection", - name.c_str()); - config_set_string(App()->GlobalConfig(), "Basic", "SceneCollectionFile", - file.c_str()); - SaveProjectNow(); + return {}; +} + +// MARK: - Qt Slot Functions - char path[512]; - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get scene collection config path"); +void OBSBasic::on_actionNewSceneCollection_triggered() +{ + const OBSPromptCallback sceneCollectionCallback = + [this](const OBSPromptResult &result) { + if (GetSceneCollectionByName(result.promptValue)) { + return false; + } + + return true; + }; + + const OBSPromptRequest request{ + Str("Basic.Main.AddSceneCollection.Title"), + Str("Basic.Main.AddSceneCollection.Text")}; + + OBSPromptResult result = + PromptForName(request, sceneCollectionCallback); + + if (!result.success) { return; } - oldFile.insert(0, path); - oldFile += ".json"; - os_unlink(oldFile.c_str()); - oldFile += ".bak"; - os_unlink(oldFile.c_str()); + try { + SetupNewSceneCollection(result.promptValue); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } +} + +void OBSBasic::on_actionDupSceneCollection_triggered() +{ + const OBSPromptCallback sceneCollectionCallback = + [this](const OBSPromptResult &result) { + if (GetSceneCollectionByName(result.promptValue)) { + return false; + } - blog(LOG_INFO, "------------------------------------------------"); - blog(LOG_INFO, "Renamed scene collection to '%s' (%s.json)", - name.c_str(), file.c_str()); - blog(LOG_INFO, "------------------------------------------------"); + return true; + }; - UpdateTitleBar(); - RefreshSceneCollections(); + const OBSPromptRequest request{ + Str("Basic.Main.AddSceneCollection.Title"), + Str("Basic.Main.AddSceneCollection.Text")}; - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED); + OBSPromptResult result = + PromptForName(request, sceneCollectionCallback); + + if (!result.success) { + return; + } + + try { + SetupDuplicateSceneCollection(result.promptValue); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } } -void OBSBasic::on_actionRemoveSceneCollection_triggered() +void OBSBasic::on_actionRenameSceneCollection_triggered() { - std::string newName; - std::string newPath; - - std::string oldFile = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollectionFile"); - std::string oldName = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollection"); - - auto cb = [&](const char *name, const char *filePath) { - if (strcmp(oldName.c_str(), name) != 0) { - newName = name; - newPath = filePath; - return false; - } + const OBSSceneCollection ¤tCollection = + GetCurrentSceneCollection(); - return true; - }; + const OBSPromptCallback sceneCollectionCallback = + [this](const OBSPromptResult &result) { + if (GetSceneCollectionByName(result.promptValue)) { + return false; + } - EnumSceneCollections(cb); + return true; + }; - /* this should never be true due to menu item being grayed out */ - if (newPath.empty()) - return; + const OBSPromptRequest request{ + Str("Basic.Main.RenameSceneCollection.Title"), + Str("Basic.Main.AddSceneCollection.Text"), + currentCollection.name}; - QString text = - QTStr("ConfirmRemove.Text").arg(QT_UTF8(oldName.c_str())); + OBSPromptResult result = + PromptForName(request, sceneCollectionCallback); - QMessageBox::StandardButton button = OBSMessageBox::question( - this, QTStr("ConfirmRemove.Title"), text); - if (button == QMessageBox::No) + if (!result.success) { return; + } + + try { + SetupRenameSceneCollection(result.promptValue); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } +} - char path[512]; - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get scene collection config path"); +void OBSBasic::on_actionRemoveSceneCollection_triggered(bool skipConfirmation) +{ + if (collections.size() < 2) { return; } - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + OBSSceneCollection currentCollection; - oldFile.insert(0, path); - /* os_rename() overwrites if necessary, only the .bak file will remain. */ - os_rename((oldFile + ".json").c_str(), (oldFile + ".json.bak").c_str()); + try { + currentCollection = GetCurrentSceneCollection(); - Load(newPath.c_str()); - RefreshSceneCollections(); + if (!skipConfirmation) { + const QString confirmationText = + QTStr("ConfirmRemove.Text") + .arg(QString::fromStdString( + currentCollection.name)); + const QMessageBox::StandardButton button = + OBSMessageBox::question( + this, QTStr("ConfirmRemove.Title"), + confirmationText); - const char *newFile = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollectionFile"); + if (button == QMessageBox::No) { + return; + } + } - blog(LOG_INFO, - "Removed scene collection '%s' (%s.json), " - "switched to '%s' (%s.json)", - oldName.c_str(), oldFile.c_str(), newName.c_str(), newFile); - blog(LOG_INFO, "------------------------------------------------"); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); - UpdateTitleBar(); + collections.erase(currentCollection.name); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } catch (const std::logic_error &error) { + blog(LOG_ERROR, "%s", error.what()); + } - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + const OBSSceneCollection &newCollection = collections.rbegin()->second; + + ActivateSceneCollection(newCollection); + RemoveSceneCollection(currentCollection); + + blog(LOG_INFO, "Switched to scene collection '%s' (%s)", + newCollection.name.c_str(), newCollection.fileName.c_str()); + blog(LOG_INFO, "------------------------------------------------"); } void OBSBasic::on_actionImportSceneCollection_triggered() @@ -364,37 +595,32 @@ void OBSBasic::on_actionImportSceneCollection_triggered() OBSImporter imp(this); imp.exec(); - RefreshSceneCollections(); + RefreshSceneCollections(true); } void OBSBasic::on_actionExportSceneCollection_triggered() { SaveProjectNow(); - char path[512]; - - QString home = QDir::homePath(); + const OBSSceneCollection ¤tCollection = + GetCurrentSceneCollection(); - QString currentFile = QT_UTF8(config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollectionFile")); + const QString home = QDir::homePath(); - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get scene collection config path"); - return; - } - - QString exportFile = + const QString destinationFileName = SaveFile(this, QTStr("Basic.MainMenu.SceneCollection.Export"), - home + "/" + currentFile, "JSON Files (*.json)"); - - string file = QT_TO_UTF8(exportFile); + home + "/" + currentCollection.fileName.c_str(), + "JSON Files (*.json)"); - if (!exportFile.isEmpty() && !exportFile.isNull()) { - QString inputFile = path + currentFile + ".json"; + if (!destinationFileName.isEmpty() && !destinationFileName.isNull()) { + const std::filesystem::path sourceFile = + currentCollection.collectionFile; + const std::filesystem::path destinationFile = + std::filesystem::u8path( + destinationFileName.toStdString()); - OBSDataAutoRelease collection = - obs_data_create_from_json_file(QT_TO_UTF8(inputFile)); + OBSDataAutoRelease collection = obs_data_create_from_json_file( + sourceFile.u8string().c_str()); OBSDataArrayAutoRelease sources = obs_data_get_array(collection, "sources"); @@ -403,15 +629,17 @@ void OBSBasic::on_actionExportSceneCollection_triggered() "No sources in exported scene collection"); return; } + obs_data_erase(collection, "sources"); - // We're just using std::sort on a vector to make life easier. - vector sourceItems; + using OBSDataVector = std::vector; + + OBSDataVector sourceItems; obs_data_array_enum( sources, - [](obs_data_t *data, void *pVec) -> void { - auto &sourceItems = - *static_cast *>(pVec); + [](obs_data_t *data, void *vector) -> void { + OBSDataVector &sourceItems{ + *static_cast(vector)}; sourceItems.push_back(data); }, &sourceItems); @@ -425,12 +653,14 @@ void OBSBasic::on_actionExportSceneCollection_triggered() }); OBSDataArrayAutoRelease newSources = obs_data_array_create(); - for (auto &item : sourceItems) + for (auto &item : sourceItems) { obs_data_array_push_back(newSources, item); + } obs_data_set_array(collection, "sources", newSources); obs_data_save_json_pretty_safe( - collection, QT_TO_UTF8(exportFile), "tmp", "bak"); + collection, destinationFile.u8string().c_str(), "tmp", + "bak"); } } @@ -467,8 +697,10 @@ void OBSBasic::on_actionRemigrateSceneCollection_triggered() return; } - QString name = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollection"); + const OBSSceneCollection ¤tCollection = + GetCurrentSceneCollection(); + + QString name = QString::fromStdString(currentCollection.name); QString message = QTStr("Basic.Main.RemigrateSceneCollection.Text") .arg(name) .arg(ovi.base_width) @@ -497,71 +729,43 @@ void OBSBasic::on_actionRemigrateSceneCollection_triggered() } } - char path[512]; - int ret = GetConfigPath(path, 512, "obs-studio/basic/scenes/"); - if (ret <= 0) { - blog(LOG_WARNING, "Failed to get scene collection config path"); - return; - } - - std::string fileName = path; - fileName += config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollectionFile"); - fileName += ".json"; - - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); /* Save and immediately reload to (re-)run migrations. */ SaveProjectNow(); /* Reset video if we potentially changed to a temporary resolution */ - if (!usingAbsoluteCoordinates) + if (!usingAbsoluteCoordinates) { ResetVideo(); + } - Load(fileName.c_str(), !usingAbsoluteCoordinates); - RefreshSceneCollections(); - - if (api) - api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + ActivateSceneCollection(currentCollection); } -void OBSBasic::ChangeSceneCollection() -{ - QAction *action = reinterpret_cast(sender()); - std::string fileName; - - if (!action) - return; - - fileName = QT_TO_UTF8(action->property("file_name").value()); - if (fileName.empty()) - return; +// MARK: - Scene Collection Management Helper Functions - const char *oldName = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollection"); +void OBSBasic::ActivateSceneCollection(const OBSSceneCollection &collection) +{ + const std::string currentCollectionName{config_get_string( + App()->GetUserConfig(), "Basic", "SceneCollection")}; - if (action->text().compare(QT_UTF8(oldName)) == 0) { - action->setChecked(true); - return; + if (auto foundCollection = + GetSceneCollectionByName(currentCollectionName)) { + if (collection.name != foundCollection.value().name) { + SaveProjectNow(); + } } - OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING); + config_set_string(App()->GetUserConfig(), "Basic", "SceneCollection", + collection.name.c_str()); + config_set_string(App()->GetUserConfig(), "Basic", + "SceneCollectionFile", collection.fileName.c_str()); - SaveProjectNow(); + Load(collection.collectionFile.u8string().c_str()); - Load(fileName.c_str()); RefreshSceneCollections(); - const char *newName = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollection"); - const char *newFile = config_get_string(App()->GlobalConfig(), "Basic", - "SceneCollectionFile"); - - blog(LOG_INFO, "Switched to scene collection '%s' (%s.json)", newName, - newFile); - blog(LOG_INFO, "------------------------------------------------"); - UpdateTitleBar(); + OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); OnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); } diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index c29d7a9d1c2ad8..8c6e646fa2e726 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2332,29 +2332,13 @@ void OBSBasic::OBSInit() { ProfileScope("OBSBasic::OBSInit"); - const char *sceneCollection = config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollectionFile"); - char savePath[1024]; - char fileName[1024]; - int ret; - - if (!sceneCollection) - throw "Failed to get scene collection name"; - - ret = snprintf(fileName, sizeof(fileName), - "obs-studio/basic/scenes/%s.json", sceneCollection); - if (ret <= 0) - throw "Failed to create scene collection file name"; - - ret = GetConfigPath(savePath, sizeof(savePath), fileName); - if (ret <= 0) - throw "Failed to get scene collection json file path"; - if (!InitBasicConfig()) throw "Failed to load basic.ini"; if (!ResetAudio()) throw "Failed to initialize audio"; + int ret = 0; + ret = ResetVideo(); switch (ret) { @@ -2401,6 +2385,12 @@ void OBSBasic::OBSInit() AddExtraModulePaths(); } + /* Modules can access frontend information (i.e. profile and scene collection data) during their initialization, and some modules (e.g. obs-websockets) are known to use the filesystem location of the current profile in their own code. + + Thus the profile and scene collection discovery needs to happen before any access to that information (but after intializing global settings) to ensure legacy code gets valid path information. + */ + RefreshSceneCollections(true); + blog(LOG_INFO, "---------------------------------"); obs_load_all_modules2(&mfi); blog(LOG_INFO, "---------------------------------"); @@ -2482,7 +2472,19 @@ void OBSBasic::OBSInit() { ProfileScope("OBSBasic::Load"); disableSaving--; - Load(savePath); + + try { + const OBSSceneCollection ¤tCollection = + GetCurrentSceneCollection(); + ActivateSceneCollection(currentCollection); + } catch (const std::invalid_argument &) { + const std::string collectionName = + config_get_string(App()->GetUserConfig(), + "Basic", "SceneCollection"); + + SetupNewSceneCollection(collectionName); + } + disableSaving++; } @@ -2500,7 +2502,6 @@ void OBSBasic::OBSInit() Qt::QueuedConnection, Q_ARG(bool, true)); - RefreshSceneCollections(); disableSaving--; auto addDisplay = [this](OBSQTDisplay *window) { @@ -3411,26 +3412,14 @@ void OBSBasic::SaveProjectDeferred() projectChanged = false; - const char *sceneCollection = config_get_string( - App()->GlobalConfig(), "Basic", "SceneCollectionFile"); - - char savePath[1024]; - char fileName[1024]; - int ret; - - if (!sceneCollection) - return; - - ret = snprintf(fileName, sizeof(fileName), - "obs-studio/basic/scenes/%s.json", sceneCollection); - if (ret <= 0) - return; - - ret = GetConfigPath(savePath, sizeof(savePath), fileName); - if (ret <= 0) - return; + try { + const OBSSceneCollection ¤tCollection = + GetCurrentSceneCollection(); - Save(savePath); + Save(currentCollection.collectionFile.u8string().c_str()); + } catch (const std::invalid_argument &error) { + blog(LOG_ERROR, "%s", error.what()); + } } OBSSource OBSBasic::GetProgramSource() diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 4bda02c2008956..2131e16de93e91 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -165,6 +165,8 @@ struct OBSPromptRequest { using OBSPromptCallback = std::function; using OBSProfileCache = std::map; +using OBSSceneCollectionCache = std::map; + class ColorSelect : public QWidget { public: @@ -471,8 +473,6 @@ class OBSBasic : public OBSMainWindow { void ToggleVolControlLayout(); void ToggleMixerLayout(bool vertical); - void RefreshSceneCollections(); - void ChangeSceneCollection(); void LogScenes(); void SaveProjectNow(); @@ -760,8 +760,6 @@ public slots: bool manual = false); void SetCurrentScene(OBSSource scene, bool force = false); - bool AddSceneCollection(bool create_new, - const QString &name = QString()); void UpdatePatronJson(const QString &text, const QString &error); void ShowContextBar(); @@ -1163,14 +1161,6 @@ private slots: void ProgramViewContextMenuRequested(); void on_previewDisabledWidget_customContextMenuRequested(); - void on_actionNewSceneCollection_triggered(); - void on_actionDupSceneCollection_triggered(); - void on_actionRenameSceneCollection_triggered(); - void on_actionRemoveSceneCollection_triggered(); - void on_actionImportSceneCollection_triggered(); - void on_actionExportSceneCollection_triggered(); - void on_actionRemigrateSceneCollection_triggered(); - void on_actionShowSettingsFolder_triggered(); void on_actionShowProfileFolder_triggered(); @@ -1398,6 +1388,54 @@ public slots: bool CreateNewProfile(const QString &name); bool CreateDuplicateProfile(const QString &name); void DeleteProfile(const QString &profileName); + + // MARK: - OBS Scene Collection Management +private: + OBSSceneCollectionCache collections{}; + + void SetupNewSceneCollection(const std::string &collectionName); + void SetupDuplicateSceneCollection(const std::string &collectionName); + void SetupRenameSceneCollection(const std::string &collectionName); + + const OBSSceneCollection & + CreateSceneCollection(const std::string &collectionName); + void RemoveSceneCollection(OBSSceneCollection collection); + + bool CreateDuplicateSceneCollection(const QString &name); + void DeleteSceneCollection(const QString &name); + void ChangeSceneCollection(); + + void RefreshSceneCollectionCache(); + + void RefreshSceneCollections(bool refreshCache = false); + void ActivateSceneCollection(const OBSSceneCollection &collection); + +public: + inline const OBSSceneCollectionCache & + GetSceneCollectionCache() const noexcept + { + return collections; + }; + + const OBSSceneCollection &GetCurrentSceneCollection() const; + + std::optional + GetSceneCollectionByName(const std::string &collectionName) const; + std::optional + GetSceneCollectionByFileName(const std::string &fileName) const; + +private slots: + void on_actionNewSceneCollection_triggered(); + void on_actionDupSceneCollection_triggered(); + void on_actionRenameSceneCollection_triggered(); + void + on_actionRemoveSceneCollection_triggered(bool skipConfirmation = false); + void on_actionImportSceneCollection_triggered(); + void on_actionExportSceneCollection_triggered(); + void on_actionRemigrateSceneCollection_triggered(); + +public slots: + bool CreateNewSceneCollection(const QString &name); }; extern bool cef_js_avail; diff --git a/UI/window-importer.cpp b/UI/window-importer.cpp index 2dc9314f32d79f..e3e50d5598ca5a 100644 --- a/UI/window-importer.cpp +++ b/UI/window-importer.cpp @@ -536,8 +536,11 @@ void OBSImporter::browseImport() bool GetUnusedName(std::string &name) { - if (!SceneCollectionExists(name.c_str())) + OBSBasic *basic = reinterpret_cast(App()->GetMainWindow()); + + if (!basic->GetSceneCollectionByName(name)) { return false; + } std::string newName; int inc = 2; @@ -545,18 +548,21 @@ bool GetUnusedName(std::string &name) newName = name; newName += " "; newName += std::to_string(inc++); - } while (SceneCollectionExists(newName.c_str())); + } while (basic->GetSceneCollectionByName(newName)); name = newName; return true; } +constexpr std::string_view OBSSceneCollectionPath = "obs-studio/basic/scenes/"; + void OBSImporter::importCollections() { setEnabled(false); - char dst[512]; - GetConfigPath(dst, 512, "obs-studio/basic/scenes/"); + const std::filesystem::path sceneCollectionLocation = + App()->userScenesLocation / + std::filesystem::u8path(OBSSceneCollectionPath); for (int i = 0; i < optionsModel->rowCount() - 1; i++) { int selected = optionsModel->index(i, ImporterColumn::Selected) @@ -591,22 +597,35 @@ void OBSImporter::importCollections() out = newOut; } - GetUnusedSceneCollectionFile(name, file); + std::string fileName; + if (!GetFileSafeName(name.c_str(), fileName)) { + blog(LOG_WARNING, + "Failed to create safe file name for '%s'", + fileName.c_str()); + } - std::string save = dst; - save += "/"; - save += file; - save += ".json"; + std::string collectionFile; + collectionFile.reserve( + sceneCollectionLocation.u8string().size() + + fileName.size()); + collectionFile + .append(sceneCollectionLocation.u8string()) + .append(fileName); + + if (!GetClosestUnusedFileName(collectionFile, "json")) { + blog(LOG_WARNING, + "Failed to get closest file name for %s", + fileName.c_str()); + } std::string out_str = json11::Json(out).dump(); - bool success = os_quick_write_utf8_file(save.c_str(), - out_str.c_str(), - out_str.size(), - false); + bool success = os_quick_write_utf8_file( + collectionFile.c_str(), out_str.c_str(), + out_str.size(), false); blog(LOG_INFO, "Import Scene Collection: %s (%s) - %s", - name.c_str(), file.c_str(), + name.c_str(), fileName.c_str(), success ? "SUCCESS" : "FAILURE"); } } From 4dac84999d0a9ec5709b444ef18755ccf9dbd5d1 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Mon, 12 Aug 2024 21:51:55 +0200 Subject: [PATCH 0533/1073] libobs: Update macOS platform implementation Cleans up the source code by sorting functions by topic and also removes macros for hotkey implementation, removing the need to disable compiler warnings. Key codes and descriptions are now contained in arrays which either use the OBS key code or the macOS key code as lookup keys with all necessary context information directly associated with the key. The implementation also uses the zero-initialization rule for arrays of structs in such a way that the struct's "is_valid" boolean is "false" by default so that only values explicitly initialized with a key are ever "valid". --- libobs/obs-cocoa.m | 1117 +++++++++++++++++++++----------------------- 1 file changed, 527 insertions(+), 590 deletions(-) diff --git a/libobs/obs-cocoa.m b/libobs/obs-cocoa.m index fc93f8eeca4c88..7df53403b434e5 100644 --- a/libobs/obs-cocoa.m +++ b/libobs/obs-cocoa.m @@ -29,6 +29,8 @@ #import +// MARK: macOS Bundle Management + bool is_in_bundle() { NSRunningApplication *app = [NSRunningApplication currentApplication]; @@ -62,6 +64,8 @@ void add_default_module_paths(void) return path; } +// MARK: - macOS Hardware Info Helpers + static void log_processor_name(void) { char *name = NULL; @@ -164,6 +168,8 @@ void log_system_info(void) log_kernel_version(); } +// MARK: - Type Conversion Utilities + static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref) { CFIndex length = CFStringGetLength(ref); @@ -177,6 +183,247 @@ static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref) return true; } +// MARK: - Graphics Thread Wrappers + +void *obs_graphics_thread_autorelease(void *param) +{ + @autoreleasepool { + return obs_graphics_thread(param); + } +} + +bool obs_graphics_thread_loop_autorelease(struct obs_graphics_context *context) +{ + @autoreleasepool { + return obs_graphics_thread_loop(context); + } +} + +// MARK: - macOS Hotkey Management + +typedef struct obs_key_code { + int code; + bool is_valid; +} obs_key_code_t; + +typedef struct macOS_glyph_desc { + UniChar glyph; + char *desc; + bool is_glyph; + bool is_valid; +} macOS_glyph_desc_t; + +typedef struct obs_key_desc { + char *desc; + bool is_valid; +} obs_key_desc_t; + +static int INVALID_KEY = 0xFF; + +/* clang-format off */ +static const obs_key_code_t virtual_keys[OBS_KEY_LAST_VALUE] = { + [OBS_KEY_A] = {.code = kVK_ANSI_A, .is_valid = true}, + [OBS_KEY_B] = {.code = kVK_ANSI_B, .is_valid = true}, + [OBS_KEY_C] = {.code = kVK_ANSI_C, .is_valid = true}, + [OBS_KEY_D] = {.code = kVK_ANSI_D, .is_valid = true}, + [OBS_KEY_E] = {.code = kVK_ANSI_E, .is_valid = true}, + [OBS_KEY_F] = {.code = kVK_ANSI_F, .is_valid = true}, + [OBS_KEY_G] = {.code = kVK_ANSI_G, .is_valid = true}, + [OBS_KEY_H] = {.code = kVK_ANSI_H, .is_valid = true}, + [OBS_KEY_I] = {.code = kVK_ANSI_I, .is_valid = true}, + [OBS_KEY_J] = {.code = kVK_ANSI_J, .is_valid = true}, + [OBS_KEY_K] = {.code = kVK_ANSI_K, .is_valid = true}, + [OBS_KEY_L] = {.code = kVK_ANSI_L, .is_valid = true}, + [OBS_KEY_M] = {.code = kVK_ANSI_M, .is_valid = true}, + [OBS_KEY_N] = {.code = kVK_ANSI_N, .is_valid = true}, + [OBS_KEY_O] = {.code = kVK_ANSI_O, .is_valid = true}, + [OBS_KEY_P] = {.code = kVK_ANSI_P, .is_valid = true}, + [OBS_KEY_Q] = {.code = kVK_ANSI_Q, .is_valid = true}, + [OBS_KEY_R] = {.code = kVK_ANSI_R, .is_valid = true}, + [OBS_KEY_S] = {.code = kVK_ANSI_S, .is_valid = true}, + [OBS_KEY_T] = {.code = kVK_ANSI_T, .is_valid = true}, + [OBS_KEY_U] = {.code = kVK_ANSI_U, .is_valid = true}, + [OBS_KEY_V] = {.code = kVK_ANSI_V, .is_valid = true}, + [OBS_KEY_W] = {.code = kVK_ANSI_W, .is_valid = true}, + [OBS_KEY_X] = {.code = kVK_ANSI_X, .is_valid = true}, + [OBS_KEY_Y] = {.code = kVK_ANSI_Y, .is_valid = true}, + [OBS_KEY_Z] = {.code = kVK_ANSI_Z, .is_valid = true}, + [OBS_KEY_1] = {.code = kVK_ANSI_1, .is_valid = true}, + [OBS_KEY_2] = {.code = kVK_ANSI_2, .is_valid = true}, + [OBS_KEY_3] = {.code = kVK_ANSI_3, .is_valid = true}, + [OBS_KEY_4] = {.code = kVK_ANSI_4, .is_valid = true}, + [OBS_KEY_5] = {.code = kVK_ANSI_5, .is_valid = true}, + [OBS_KEY_6] = {.code = kVK_ANSI_6, .is_valid = true}, + [OBS_KEY_7] = {.code = kVK_ANSI_7, .is_valid = true}, + [OBS_KEY_8] = {.code = kVK_ANSI_8, .is_valid = true}, + [OBS_KEY_9] = {.code = kVK_ANSI_9, .is_valid = true}, + [OBS_KEY_0] = {.code = kVK_ANSI_0, .is_valid = true}, + [OBS_KEY_RETURN] = {.code = kVK_Return, .is_valid = true}, + [OBS_KEY_ESCAPE] = {.code = kVK_Escape, .is_valid = true}, + [OBS_KEY_BACKSPACE] = {.code = kVK_Delete, .is_valid = true}, + [OBS_KEY_TAB] = {.code = kVK_Tab, .is_valid = true}, + [OBS_KEY_SPACE] = {.code = kVK_Space, .is_valid = true}, + [OBS_KEY_MINUS] = {.code = kVK_ANSI_Minus, .is_valid = true}, + [OBS_KEY_EQUAL] = {.code = kVK_ANSI_Equal, .is_valid = true}, + [OBS_KEY_BRACKETLEFT] = {.code = kVK_ANSI_LeftBracket, .is_valid = true}, + [OBS_KEY_BRACKETRIGHT] = {.code = kVK_ANSI_RightBracket, .is_valid = true}, + [OBS_KEY_BACKSLASH] = {.code = kVK_ANSI_Backslash, .is_valid = true}, + [OBS_KEY_SEMICOLON] = {.code = kVK_ANSI_Semicolon, .is_valid = true}, + [OBS_KEY_QUOTE] = {.code = kVK_ANSI_Quote, .is_valid = true}, + [OBS_KEY_DEAD_GRAVE] = {.code = kVK_ANSI_Grave, .is_valid = true}, + [OBS_KEY_COMMA] = {.code = kVK_ANSI_Comma, .is_valid = true}, + [OBS_KEY_PERIOD] = {.code = kVK_ANSI_Period, .is_valid = true}, + [OBS_KEY_SLASH] = {.code = kVK_ANSI_Slash, .is_valid = true}, + [OBS_KEY_CAPSLOCK] = {.code = kVK_CapsLock, .is_valid = true}, + [OBS_KEY_SECTION] = {.code = kVK_ISO_Section, .is_valid = true}, + [OBS_KEY_F1] = {.code = kVK_F1, .is_valid = true}, + [OBS_KEY_F2] = {.code = kVK_F2, .is_valid = true}, + [OBS_KEY_F3] = {.code = kVK_F3, .is_valid = true}, + [OBS_KEY_F4] = {.code = kVK_F4, .is_valid = true}, + [OBS_KEY_F5] = {.code = kVK_F5, .is_valid = true}, + [OBS_KEY_F6] = {.code = kVK_F6, .is_valid = true}, + [OBS_KEY_F7] = {.code = kVK_F7, .is_valid = true}, + [OBS_KEY_F8] = {.code = kVK_F8, .is_valid = true}, + [OBS_KEY_F9] = {.code = kVK_F9, .is_valid = true}, + [OBS_KEY_F10] = {.code = kVK_F10, .is_valid = true}, + [OBS_KEY_F11] = {.code = kVK_F11, .is_valid = true}, + [OBS_KEY_F12] = {.code = kVK_F12, .is_valid = true}, + [OBS_KEY_HELP] = {.code = kVK_Help, .is_valid = true}, + [OBS_KEY_HOME] = {.code = kVK_Home, .is_valid = true}, + [OBS_KEY_PAGEUP] = {.code = kVK_PageUp, .is_valid = true}, + [OBS_KEY_DELETE] = {.code = kVK_ForwardDelete, .is_valid = true}, + [OBS_KEY_END] = {.code = kVK_End, .is_valid = true}, + [OBS_KEY_PAGEDOWN] = {.code = kVK_PageDown, .is_valid = true}, + [OBS_KEY_RIGHT] = {.code = kVK_RightArrow, .is_valid = true}, + [OBS_KEY_LEFT] = {.code = kVK_LeftArrow, .is_valid = true}, + [OBS_KEY_DOWN] = {.code = kVK_DownArrow, .is_valid = true}, + [OBS_KEY_UP] = {.code = kVK_UpArrow, .is_valid = true}, + [OBS_KEY_CLEAR] = {.code = kVK_ANSI_KeypadClear, .is_valid = true}, + [OBS_KEY_NUMSLASH] = {.code = kVK_ANSI_KeypadDivide, .is_valid = true}, + [OBS_KEY_NUMASTERISK] = {.code = kVK_ANSI_KeypadMultiply, .is_valid = true}, + [OBS_KEY_NUMMINUS] = {.code = kVK_ANSI_KeypadMinus, .is_valid = true}, + [OBS_KEY_NUMPLUS] = {.code = kVK_ANSI_KeypadPlus, .is_valid = true}, + [OBS_KEY_ENTER] = {.code = kVK_ANSI_KeypadEnter, .is_valid = true}, + [OBS_KEY_NUM1] = {.code = kVK_ANSI_Keypad1, .is_valid = true}, + [OBS_KEY_NUM2] = {.code = kVK_ANSI_Keypad2, .is_valid = true}, + [OBS_KEY_NUM3] = {.code = kVK_ANSI_Keypad3, .is_valid = true}, + [OBS_KEY_NUM4] = {.code = kVK_ANSI_Keypad4, .is_valid = true}, + [OBS_KEY_NUM5] = {.code = kVK_ANSI_Keypad5, .is_valid = true}, + [OBS_KEY_NUM6] = {.code = kVK_ANSI_Keypad6, .is_valid = true}, + [OBS_KEY_NUM7] = {.code = kVK_ANSI_Keypad7, .is_valid = true}, + [OBS_KEY_NUM8] = {.code = kVK_ANSI_Keypad8, .is_valid = true}, + [OBS_KEY_NUM9] = {.code = kVK_ANSI_Keypad9, .is_valid = true}, + [OBS_KEY_NUM0] = {.code = kVK_ANSI_Keypad0, .is_valid = true}, + [OBS_KEY_NUMPERIOD] = {.code = kVK_ANSI_KeypadDecimal, .is_valid = true}, + [OBS_KEY_NUMEQUAL] = {.code = kVK_ANSI_KeypadEquals, .is_valid = true}, + [OBS_KEY_F13] = {.code = kVK_F13, .is_valid = true}, + [OBS_KEY_F14] = {.code = kVK_F14, .is_valid = true}, + [OBS_KEY_F15] = {.code = kVK_F15, .is_valid = true}, + [OBS_KEY_F16] = {.code = kVK_F16, .is_valid = true}, + [OBS_KEY_F17] = {.code = kVK_F17, .is_valid = true}, + [OBS_KEY_F18] = {.code = kVK_F18, .is_valid = true}, + [OBS_KEY_F19] = {.code = kVK_F19, .is_valid = true}, + [OBS_KEY_F20] = {.code = kVK_F20, .is_valid = true}, + [OBS_KEY_CONTROL] = {.code = kVK_Control, .is_valid = true}, + [OBS_KEY_SHIFT] = {.code = kVK_Shift, .is_valid = true}, + [OBS_KEY_ALT] = {.code = kVK_Option, .is_valid = true}, + [OBS_KEY_META] = {.code = kVK_Command, .is_valid = true}, +}; + +static const obs_key_desc_t key_descriptions[OBS_KEY_LAST_VALUE] = { + [OBS_KEY_SPACE] = {.desc = "Space", .is_valid = true}, + [OBS_KEY_NUMEQUAL] = {.desc = "= (Keypad)", .is_valid = true}, + [OBS_KEY_NUMASTERISK] = {.desc = "* (Keypad)", .is_valid = true}, + [OBS_KEY_NUMPLUS] = {.desc = "+ (Keypad)", .is_valid = true}, + [OBS_KEY_NUMMINUS] = {.desc = "- (Keypad)", .is_valid = true}, + [OBS_KEY_NUMPERIOD] = {.desc = ". (Keypad)", .is_valid = true}, + [OBS_KEY_NUMSLASH] = {.desc = "/ (Keypad)", .is_valid = true}, + [OBS_KEY_NUM0] = {.desc = "0 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM1] = {.desc = "1 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM2] = {.desc = "2 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM3] = {.desc = "3 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM4] = {.desc = "4 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM5] = {.desc = "5 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM6] = {.desc = "6 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM7] = {.desc = "7 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM8] = {.desc = "8 (Keypad)", .is_valid = true}, + [OBS_KEY_NUM9] = {.desc = "9 (Keypad)", .is_valid = true}, + [OBS_KEY_MOUSE1] = {.desc = "Mouse 1", .is_valid = true}, + [OBS_KEY_MOUSE2] = {.desc = "Mouse 2", .is_valid = true}, + [OBS_KEY_MOUSE3] = {.desc = "Mouse 3", .is_valid = true}, + [OBS_KEY_MOUSE4] = {.desc = "Mouse 4", .is_valid = true}, + [OBS_KEY_MOUSE5] = {.desc = "Mouse 5", .is_valid = true}, + [OBS_KEY_MOUSE6] = {.desc = "Mouse 6", .is_valid = true}, + [OBS_KEY_MOUSE7] = {.desc = "Mouse 7", .is_valid = true}, + [OBS_KEY_MOUSE8] = {.desc = "Mouse 8", .is_valid = true}, + [OBS_KEY_MOUSE9] = {.desc = "Mouse 9", .is_valid = true}, + [OBS_KEY_MOUSE10] = {.desc = "Mouse 10", .is_valid = true}, + [OBS_KEY_MOUSE11] = {.desc = "Mouse 11", .is_valid = true}, + [OBS_KEY_MOUSE12] = {.desc = "Mouse 12", .is_valid = true}, + [OBS_KEY_MOUSE13] = {.desc = "Mouse 13", .is_valid = true}, + [OBS_KEY_MOUSE14] = {.desc = "Mouse 14", .is_valid = true}, + [OBS_KEY_MOUSE15] = {.desc = "Mouse 15", .is_valid = true}, + [OBS_KEY_MOUSE16] = {.desc = "Mouse 16", .is_valid = true}, + [OBS_KEY_MOUSE17] = {.desc = "Mouse 17", .is_valid = true}, + [OBS_KEY_MOUSE18] = {.desc = "Mouse 18", .is_valid = true}, + [OBS_KEY_MOUSE19] = {.desc = "Mouse 19", .is_valid = true}, + [OBS_KEY_MOUSE20] = {.desc = "Mouse 20", .is_valid = true}, + [OBS_KEY_MOUSE21] = {.desc = "Mouse 21", .is_valid = true}, + [OBS_KEY_MOUSE22] = {.desc = "Mouse 22", .is_valid = true}, + [OBS_KEY_MOUSE23] = {.desc = "Mouse 23", .is_valid = true}, + [OBS_KEY_MOUSE24] = {.desc = "Mouse 24", .is_valid = true}, + [OBS_KEY_MOUSE25] = {.desc = "Mouse 25", .is_valid = true}, + [OBS_KEY_MOUSE26] = {.desc = "Mouse 26", .is_valid = true}, + [OBS_KEY_MOUSE27] = {.desc = "Mouse 27", .is_valid = true}, + [OBS_KEY_MOUSE28] = {.desc = "Mouse 28", .is_valid = true}, + [OBS_KEY_MOUSE29] = {.desc = "Mouse 29", .is_valid = true}, +}; + +static const macOS_glyph_desc_t key_glyphs[(keyCodeMask >> 8)] = { + [kVK_Return] = {.glyph = 0x21A9, .is_glyph = true, .is_valid = true}, + [kVK_Escape] = {.glyph = 0x238B, .is_glyph = true, .is_valid = true}, + [kVK_Delete] = {.glyph = 0x232B, .is_glyph = true, .is_valid = true}, + [kVK_Tab] = {.glyph = 0x21e5, .is_glyph = true, .is_valid = true}, + [kVK_CapsLock] = {.glyph = 0x21EA, .is_glyph = true, .is_valid = true}, + [kVK_ANSI_KeypadClear] = {.glyph = 0x2327, .is_glyph = true, .is_valid = true}, + [kVK_ANSI_KeypadEnter] = {.glyph = 0x2305, .is_glyph = true, .is_valid = true}, + [kVK_Help] = {.glyph = 0x003F, .is_glyph = true, .is_valid = true}, + [kVK_Home] = {.glyph = 0x2196, .is_glyph = true, .is_valid = true}, + [kVK_PageUp] = {.glyph = 0x21de, .is_glyph = true, .is_valid = true}, + [kVK_ForwardDelete] = {.glyph = 0x2326, .is_glyph = true, .is_valid = true}, + [kVK_End] = {.glyph = 0x2198, .is_glyph = true, .is_valid = true}, + [kVK_PageDown] = {.glyph = 0x21df, .is_glyph = true, .is_valid = true}, + [kVK_Control] = {.glyph = kControlUnicode, .is_glyph = true, .is_valid = true}, + [kVK_Shift] = {.glyph = kShiftUnicode, .is_glyph = true, .is_valid = true}, + [kVK_Option] = {.glyph = kOptionUnicode, .is_glyph = true, .is_valid = true}, + [kVK_Command] = {.glyph = kCommandUnicode, .is_glyph = true, .is_valid = true}, + [kVK_RightControl] = {.glyph = kControlUnicode, .is_glyph = true, .is_valid = true}, + [kVK_RightShift] = {.glyph = kShiftUnicode, .is_glyph = true, .is_valid = true}, + [kVK_RightOption] = {.glyph = kOptionUnicode, .is_glyph = true, .is_valid = true}, + [kVK_F1] = {.desc = "F1", .is_valid = true}, + [kVK_F2] = {.desc = "F2", .is_valid = true}, + [kVK_F3] = {.desc = "F3", .is_valid = true}, + [kVK_F4] = {.desc = "F4", .is_valid = true}, + [kVK_F5] = {.desc = "F5", .is_valid = true}, + [kVK_F6] = {.desc = "F6", .is_valid = true}, + [kVK_F7] = {.desc = "F7", .is_valid = true}, + [kVK_F8] = {.desc = "F8", .is_valid = true}, + [kVK_F9] = {.desc = "F9", .is_valid = true}, + [kVK_F10] = {.desc = "F10", .is_valid = true}, + [kVK_F11] = {.desc = "F11", .is_valid = true}, + [kVK_F12] = {.desc = "F12", .is_valid = true}, + [kVK_F13] = {.desc = "F13", .is_valid = true}, + [kVK_F14] = {.desc = "F14", .is_valid = true}, + [kVK_F15] = {.desc = "F15", .is_valid = true}, + [kVK_F16] = {.desc = "F16", .is_valid = true}, + [kVK_F17] = {.desc = "F17", .is_valid = true}, + [kVK_F18] = {.desc = "F18", .is_valid = true}, + [kVK_F19] = {.desc = "F19", .is_valid = true}, + [kVK_F20] = {.desc = "F20", .is_valid = true} +}; + +/* clang-format on */ + struct obs_hotkeys_platform { volatile long refs; CFTypeRef monitor; @@ -187,702 +434,392 @@ static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref) UCKeyboardLayout *layout; }; -static void hotkeys_retain(struct obs_hotkeys_platform *plat) -{ - os_atomic_inc_long(&plat->refs); -} +// MARK: macOS Hotkey Implementation +#define OBS_COCOA_MODIFIER_SIZE (int) 7 -static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat); +static char string_control[OBS_COCOA_MODIFIER_SIZE]; +static char string_option[OBS_COCOA_MODIFIER_SIZE]; +static char string_shift[OBS_COCOA_MODIFIER_SIZE]; +static char string_command[OBS_COCOA_MODIFIER_SIZE]; +static dispatch_once_t onceToken; -static void hotkeys_release(struct obs_hotkeys_platform *plat) +static void hotkeys_retain(obs_hotkeys_platform_t *platform) { - if (os_atomic_dec_long(&plat->refs) == -1) - free_hotkeys_platform(plat); + os_atomic_inc_long(&platform->refs); } -#define INVALID_KEY 0xff - -#pragma GCC diagnostic ignored "-Winitializer-overrides" -static const int virtual_keys[] = { - [0 ... OBS_KEY_LAST_VALUE] = INVALID_KEY, - - [OBS_KEY_A] = kVK_ANSI_A, - [OBS_KEY_B] = kVK_ANSI_B, - [OBS_KEY_C] = kVK_ANSI_C, - [OBS_KEY_D] = kVK_ANSI_D, - [OBS_KEY_E] = kVK_ANSI_E, - [OBS_KEY_F] = kVK_ANSI_F, - [OBS_KEY_G] = kVK_ANSI_G, - [OBS_KEY_H] = kVK_ANSI_H, - [OBS_KEY_I] = kVK_ANSI_I, - [OBS_KEY_J] = kVK_ANSI_J, - [OBS_KEY_K] = kVK_ANSI_K, - [OBS_KEY_L] = kVK_ANSI_L, - [OBS_KEY_M] = kVK_ANSI_M, - [OBS_KEY_N] = kVK_ANSI_N, - [OBS_KEY_O] = kVK_ANSI_O, - [OBS_KEY_P] = kVK_ANSI_P, - [OBS_KEY_Q] = kVK_ANSI_Q, - [OBS_KEY_R] = kVK_ANSI_R, - [OBS_KEY_S] = kVK_ANSI_S, - [OBS_KEY_T] = kVK_ANSI_T, - [OBS_KEY_U] = kVK_ANSI_U, - [OBS_KEY_V] = kVK_ANSI_V, - [OBS_KEY_W] = kVK_ANSI_W, - [OBS_KEY_X] = kVK_ANSI_X, - [OBS_KEY_Y] = kVK_ANSI_Y, - [OBS_KEY_Z] = kVK_ANSI_Z, - - [OBS_KEY_1] = kVK_ANSI_1, - [OBS_KEY_2] = kVK_ANSI_2, - [OBS_KEY_3] = kVK_ANSI_3, - [OBS_KEY_4] = kVK_ANSI_4, - [OBS_KEY_5] = kVK_ANSI_5, - [OBS_KEY_6] = kVK_ANSI_6, - [OBS_KEY_7] = kVK_ANSI_7, - [OBS_KEY_8] = kVK_ANSI_8, - [OBS_KEY_9] = kVK_ANSI_9, - [OBS_KEY_0] = kVK_ANSI_0, - - [OBS_KEY_RETURN] = kVK_Return, - [OBS_KEY_ESCAPE] = kVK_Escape, - [OBS_KEY_BACKSPACE] = kVK_Delete, - [OBS_KEY_TAB] = kVK_Tab, - [OBS_KEY_SPACE] = kVK_Space, - [OBS_KEY_MINUS] = kVK_ANSI_Minus, - [OBS_KEY_EQUAL] = kVK_ANSI_Equal, - [OBS_KEY_BRACKETLEFT] = kVK_ANSI_LeftBracket, - [OBS_KEY_BRACKETRIGHT] = kVK_ANSI_RightBracket, - [OBS_KEY_BACKSLASH] = kVK_ANSI_Backslash, - [OBS_KEY_SEMICOLON] = kVK_ANSI_Semicolon, - [OBS_KEY_QUOTE] = kVK_ANSI_Quote, - [OBS_KEY_DEAD_GRAVE] = kVK_ANSI_Grave, - [OBS_KEY_COMMA] = kVK_ANSI_Comma, - [OBS_KEY_PERIOD] = kVK_ANSI_Period, - [OBS_KEY_SLASH] = kVK_ANSI_Slash, - [OBS_KEY_CAPSLOCK] = kVK_CapsLock, - [OBS_KEY_SECTION] = kVK_ISO_Section, - - [OBS_KEY_F1] = kVK_F1, - [OBS_KEY_F2] = kVK_F2, - [OBS_KEY_F3] = kVK_F3, - [OBS_KEY_F4] = kVK_F4, - [OBS_KEY_F5] = kVK_F5, - [OBS_KEY_F6] = kVK_F6, - [OBS_KEY_F7] = kVK_F7, - [OBS_KEY_F8] = kVK_F8, - [OBS_KEY_F9] = kVK_F9, - [OBS_KEY_F10] = kVK_F10, - [OBS_KEY_F11] = kVK_F11, - [OBS_KEY_F12] = kVK_F12, - - [OBS_KEY_HELP] = kVK_Help, - [OBS_KEY_HOME] = kVK_Home, - [OBS_KEY_PAGEUP] = kVK_PageUp, - [OBS_KEY_DELETE] = kVK_ForwardDelete, - [OBS_KEY_END] = kVK_End, - [OBS_KEY_PAGEDOWN] = kVK_PageDown, - - [OBS_KEY_RIGHT] = kVK_RightArrow, - [OBS_KEY_LEFT] = kVK_LeftArrow, - [OBS_KEY_DOWN] = kVK_DownArrow, - [OBS_KEY_UP] = kVK_UpArrow, - - [OBS_KEY_CLEAR] = kVK_ANSI_KeypadClear, - [OBS_KEY_NUMSLASH] = kVK_ANSI_KeypadDivide, - [OBS_KEY_NUMASTERISK] = kVK_ANSI_KeypadMultiply, - [OBS_KEY_NUMMINUS] = kVK_ANSI_KeypadMinus, - [OBS_KEY_NUMPLUS] = kVK_ANSI_KeypadPlus, - [OBS_KEY_ENTER] = kVK_ANSI_KeypadEnter, - - [OBS_KEY_NUM1] = kVK_ANSI_Keypad1, - [OBS_KEY_NUM2] = kVK_ANSI_Keypad2, - [OBS_KEY_NUM3] = kVK_ANSI_Keypad3, - [OBS_KEY_NUM4] = kVK_ANSI_Keypad4, - [OBS_KEY_NUM5] = kVK_ANSI_Keypad5, - [OBS_KEY_NUM6] = kVK_ANSI_Keypad6, - [OBS_KEY_NUM7] = kVK_ANSI_Keypad7, - [OBS_KEY_NUM8] = kVK_ANSI_Keypad8, - [OBS_KEY_NUM9] = kVK_ANSI_Keypad9, - [OBS_KEY_NUM0] = kVK_ANSI_Keypad0, - - [OBS_KEY_NUMPERIOD] = kVK_ANSI_KeypadDecimal, - [OBS_KEY_NUMEQUAL] = kVK_ANSI_KeypadEquals, - - [OBS_KEY_F13] = kVK_F13, - [OBS_KEY_F14] = kVK_F14, - [OBS_KEY_F15] = kVK_F15, - [OBS_KEY_F16] = kVK_F16, - [OBS_KEY_F17] = kVK_F17, - [OBS_KEY_F18] = kVK_F18, - [OBS_KEY_F19] = kVK_F19, - [OBS_KEY_F20] = kVK_F20, - - [OBS_KEY_CONTROL] = kVK_Control, - [OBS_KEY_SHIFT] = kVK_Shift, - [OBS_KEY_ALT] = kVK_Option, - [OBS_KEY_META] = kVK_Command, - [OBS_KEY_CONTROL] = kVK_RightControl, -}; - -int obs_key_to_virtual_key(obs_key_t code) +static void hotkeys_release(obs_hotkeys_platform_t *platform) { - return virtual_keys[code]; -} + if (os_atomic_dec_long(&platform->refs) == -1) { + if (platform->tis) { + CFRelease(platform->tis); + platform->tis = NULL; + } -obs_key_t obs_key_from_virtual_key(int code) -{ - if (code == kVK_RightShift) - return OBS_KEY_SHIFT; - if (code == kVK_RightOption) - return OBS_KEY_ALT; - if (code == kVK_RightCommand) - return OBS_KEY_META; - if (code == kVK_RightControl) - return OBS_KEY_META; - for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) { - if (virtual_keys[i] == code) { - return (obs_key_t) i; + if (platform->layout_data) { + CFRelease(platform->layout_data); + platform->layout_data = NULL; + } + + if (platform->monitor) { + [NSEvent removeMonitor:(__bridge id _Nonnull)(platform->monitor)]; + platform->monitor = NULL; + } + + if (platform->local_monitor) { + [NSEvent removeMonitor:(__bridge id _Nonnull)(platform->local_monitor)]; + platform->local_monitor = NULL; } + + bfree(platform); } - return OBS_KEY_NONE; } -static bool localized_key_to_str(obs_key_t key, struct dstr *str) +static bool obs_key_to_localized_string(obs_key_t key, struct dstr *str) { -#define MAP_KEY(k, s) \ - case k: \ - dstr_copy(str, obs_get_hotkey_translation(k, s)); \ - return true - -#define MAP_BUTTON(i) \ - case OBS_KEY_MOUSE##i: \ - dstr_copy(str, obs_get_hotkey_translation(key, "Mouse " #i)); \ - return true - - switch (key) { - MAP_KEY(OBS_KEY_SPACE, "Space"); - MAP_KEY(OBS_KEY_NUMEQUAL, "= (Keypad)"); - MAP_KEY(OBS_KEY_NUMASTERISK, "* (Keypad)"); - MAP_KEY(OBS_KEY_NUMPLUS, "+ (Keypad)"); - MAP_KEY(OBS_KEY_NUMMINUS, "- (Keypad)"); - MAP_KEY(OBS_KEY_NUMPERIOD, ". (Keypad)"); - MAP_KEY(OBS_KEY_NUMSLASH, "/ (Keypad)"); - MAP_KEY(OBS_KEY_NUM0, "0 (Keypad)"); - MAP_KEY(OBS_KEY_NUM1, "1 (Keypad)"); - MAP_KEY(OBS_KEY_NUM2, "2 (Keypad)"); - MAP_KEY(OBS_KEY_NUM3, "3 (Keypad)"); - MAP_KEY(OBS_KEY_NUM4, "4 (Keypad)"); - MAP_KEY(OBS_KEY_NUM5, "5 (Keypad)"); - MAP_KEY(OBS_KEY_NUM6, "6 (Keypad)"); - MAP_KEY(OBS_KEY_NUM7, "7 (Keypad)"); - MAP_KEY(OBS_KEY_NUM8, "8 (Keypad)"); - MAP_KEY(OBS_KEY_NUM9, "9 (Keypad)"); - - MAP_BUTTON(1); - MAP_BUTTON(2); - MAP_BUTTON(3); - MAP_BUTTON(4); - MAP_BUTTON(5); - MAP_BUTTON(6); - MAP_BUTTON(7); - MAP_BUTTON(8); - MAP_BUTTON(9); - MAP_BUTTON(10); - MAP_BUTTON(11); - MAP_BUTTON(12); - MAP_BUTTON(13); - MAP_BUTTON(14); - MAP_BUTTON(15); - MAP_BUTTON(16); - MAP_BUTTON(17); - MAP_BUTTON(18); - MAP_BUTTON(19); - MAP_BUTTON(20); - MAP_BUTTON(21); - MAP_BUTTON(22); - MAP_BUTTON(23); - MAP_BUTTON(24); - MAP_BUTTON(25); - MAP_BUTTON(26); - MAP_BUTTON(27); - MAP_BUTTON(28); - MAP_BUTTON(29); - default: - break; + if (key < OBS_KEY_LAST_VALUE && !key_descriptions[key].is_valid) { + return false; } -#undef MAP_BUTTON -#undef MAP_KEY - return false; + dstr_copy(str, obs_get_hotkey_translation(key, key_descriptions[key].desc)); + return true; } -static bool code_to_str(int code, struct dstr *str) +static bool key_code_to_string(int code, struct dstr *str) { -#define MAP_GLYPH(c, g) \ - case c: \ - dstr_from_wcs(str, (wchar_t[]) {g, 0}); \ - return true -#define MAP_STR(c, s) \ - case c: \ - dstr_copy(str, s); \ - return true - switch (code) { - MAP_GLYPH(kVK_Return, 0x21A9); - MAP_GLYPH(kVK_Escape, 0x238B); - MAP_GLYPH(kVK_Delete, 0x232B); - MAP_GLYPH(kVK_Tab, 0x21e5); - MAP_GLYPH(kVK_CapsLock, 0x21EA); - MAP_GLYPH(kVK_ANSI_KeypadClear, 0x2327); - MAP_GLYPH(kVK_ANSI_KeypadEnter, 0x2305); - MAP_GLYPH(kVK_Help, 0x003F); - MAP_GLYPH(kVK_Home, 0x2196); - MAP_GLYPH(kVK_PageUp, 0x21de); - MAP_GLYPH(kVK_ForwardDelete, 0x2326); - MAP_GLYPH(kVK_End, 0x2198); - MAP_GLYPH(kVK_PageDown, 0x21df); - - MAP_GLYPH(kVK_RightArrow, 0x2192); - MAP_GLYPH(kVK_LeftArrow, 0x2190); - MAP_GLYPH(kVK_DownArrow, 0x2193); - MAP_GLYPH(kVK_UpArrow, 0x2191); - - MAP_STR(kVK_F1, "F1"); - MAP_STR(kVK_F2, "F2"); - MAP_STR(kVK_F3, "F3"); - MAP_STR(kVK_F4, "F4"); - MAP_STR(kVK_F5, "F5"); - MAP_STR(kVK_F6, "F6"); - MAP_STR(kVK_F7, "F7"); - MAP_STR(kVK_F8, "F8"); - MAP_STR(kVK_F9, "F9"); - MAP_STR(kVK_F10, "F10"); - MAP_STR(kVK_F11, "F11"); - MAP_STR(kVK_F12, "F12"); - MAP_STR(kVK_F13, "F13"); - MAP_STR(kVK_F14, "F14"); - MAP_STR(kVK_F15, "F15"); - MAP_STR(kVK_F16, "F16"); - MAP_STR(kVK_F17, "F17"); - MAP_STR(kVK_F18, "F18"); - MAP_STR(kVK_F19, "F19"); - MAP_STR(kVK_F20, "F20"); - MAP_GLYPH(kVK_Control, kControlUnicode); - MAP_GLYPH(kVK_Shift, kShiftUnicode); - MAP_GLYPH(kVK_Option, kOptionUnicode); - MAP_GLYPH(kVK_Command, kCommandUnicode); - MAP_GLYPH(kVK_RightControl, kControlUnicode); - MAP_GLYPH(kVK_RightShift, kShiftUnicode); - MAP_GLYPH(kVK_RightOption, kOptionUnicode); + if (code < INVALID_KEY) { + macOS_glyph_desc_t glyph = key_glyphs[code]; + + if (glyph.is_valid && glyph.is_glyph && glyph.glyph > 0) { + dstr_from_wcs(str, (wchar_t[]) {glyph.glyph, 0}); + } else if (glyph.is_valid && glyph.desc) { + dstr_copy(str, glyph.desc); + } else { + return false; + } } -#undef MAP_STR -#undef MAP_GLYPH - return false; + return true; } -void obs_key_to_str(obs_key_t key, struct dstr *str) +static bool log_layout_name(TISInputSourceRef tis) { - const UniCharCount max_length = 16; - UniChar buffer[16]; - - if (localized_key_to_str(key, str)) - return; - - int code = obs_key_to_virtual_key(key); - if (code_to_str(code, str)) - return; + struct dstr layout_name = {0}; + CFStringRef sid = (CFStringRef) TISGetInputSourceProperty(tis, kTISPropertyInputSourceID); - if (code == INVALID_KEY) { - blog(LOG_ERROR, - "hotkey-cocoa: Got invalid key while " - "translating key '%d' (%s)", - key, obs_key_to_name(key)); - goto err; + if (!sid) { + blog(LOG_ERROR, "hotkeys-cocoa: Unable to get input source ID"); + return false; } - struct obs_hotkeys_platform *plat = NULL; - - if (obs) { - pthread_mutex_lock(&obs->hotkeys.mutex); - plat = obs->hotkeys.platform_context; - hotkeys_retain(plat); - pthread_mutex_unlock(&obs->hotkeys.mutex); + if (!dstr_from_cfstring(&layout_name, sid)) { + blog(LOG_ERROR, "hotkeys-cocoa: Unable to convert input source ID"); + dstr_free(&layout_name); + return false; } - if (!plat) { - blog(LOG_ERROR, - "hotkey-cocoa: Could not get hotkey platform " - "while translating key '%d' (%s)", - key, obs_key_to_name(key)); - goto err; - } + blog(LOG_INFO, "hotkeys-cocoa: Using keyboard layout '%s'", layout_name.array); - UInt32 dead_key_state = 0; - UniCharCount len = 0; + dstr_free(&layout_name); + return true; +} - OSStatus err = UCKeyTranslate(plat->layout, code, kUCKeyActionDown, - 0x104, //caps lock for upper case letters - LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &dead_key_state, max_length, &len, - buffer); +// MARK: macOS Hotkey CoreFoundation Callbacks - if (err == noErr && len <= 0 && dead_key_state) { - err = UCKeyTranslate(plat->layout, kVK_Space, kUCKeyActionDown, 0x104, LMGetKbdType(), - kUCKeyTranslateNoDeadKeysBit, &dead_key_state, max_length, &len, buffer); - } +static void MonitorEventHandlerProc(obs_hotkeys_platform_t *platform, NSEvent *event) +{ + NSEventModifierFlags flags = event.modifierFlags; + platform->is_key_down[OBS_KEY_CAPSLOCK] = !!(flags & NSEventModifierFlagCapsLock); + platform->is_key_down[OBS_KEY_SHIFT] = !!(flags & NSEventModifierFlagShift); + platform->is_key_down[OBS_KEY_ALT] = !!(flags & NSEventModifierFlagOption); + platform->is_key_down[OBS_KEY_META] = !!(flags & NSEventModifierFlagCommand); + platform->is_key_down[OBS_KEY_CONTROL] = !!(flags & NSEventModifierFlagControl); - hotkeys_release(plat); + if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { + obs_key_t obsKey = obs_key_from_virtual_key(event.keyCode); - if (err != noErr) { - blog(LOG_ERROR, - "hotkey-cocoa: Error while translating key '%d'" - " (0x%x, %s) to string: %d", - key, code, obs_key_to_name(key), err); - goto err; + platform->is_key_down[obsKey] = (event.type == NSEventTypeKeyDown); } +} - if (len == 0) { - blog(LOG_ERROR, - "hotkey-cocoa: Got 0 length string while " - "translating '%d' (0x%x, %s) to string", - key, code, obs_key_to_name(key)); - goto err; - } +static void InputMethodChangedProc(CFNotificationCenterRef center __unused, void *observer, + CFNotificationName name __unused, const void *object __unused, + CFDictionaryRef userInfo __unused) +{ + struct obs_core_hotkeys *hotkeys = observer; + obs_hotkeys_platform_t *platform = hotkeys->platform_context; - CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, buffer, len, kCFAllocatorNull); - if (!string) { - blog(LOG_ERROR, - "hotkey-cocoa: Could not create CFStringRef " - "while translating '%d' (0x%x, %s) to string", - key, code, obs_key_to_name(key)); - goto err; + pthread_mutex_lock(&hotkeys->mutex); + + if (platform->layout_data) { + CFRelease(platform->layout_data); } - if (!dstr_from_cfstring(str, string)) { - blog(LOG_ERROR, - "hotkey-cocoa: Could not translate CFStringRef " - "to CString while translating '%d' (0x%x, %s)", - key, code, obs_key_to_name(key)); + platform->tis = TISCopyCurrentKeyboardLayoutInputSource(); + platform->layout_data = (CFDataRef) TISGetInputSourceProperty(platform->tis, kTISPropertyUnicodeKeyLayoutData); - goto release; + if (!platform->layout_data) { + blog(LOG_ERROR, "hotkeys-cocoa: Failed to retrieve keyboard layout data"); + hotkeys->platform_context = NULL; + pthread_mutex_unlock(&hotkeys->mutex); + hotkeys_release(platform); + return; } - CFRelease(string); - return; - -release: - CFRelease(string); -err: - dstr_copy(str, obs_key_to_name(key)); + CFRetain(platform->layout_data); + platform->layout = (UCKeyboardLayout *) CFDataGetBytePtr(platform->layout_data); } -#define OBS_COCOA_MODIFIER_SIZE 7 +// MARK: macOS Hotkey API Implementation -static void unichar_to_utf8(const UniChar *c, char *buff) +bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys) { - CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, c, 2, kCFAllocatorNull); - if (!string) { - blog(LOG_ERROR, "hotkey-cocoa: Could not create CFStringRef " - "while populating modifier strings"); - return; - } + CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), hotkeys, InputMethodChangedProc, + kTISNotifySelectedKeyboardInputSourceChanged, NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); - if (!CFStringGetCString(string, buff, OBS_COCOA_MODIFIER_SIZE, kCFStringEncodingUTF8)) - blog(LOG_ERROR, - "hotkey-cocoa: Error while populating " - " modifier string with glyph %d (0x%x)", - c[0], c[0]); + obs_hotkeys_platform_t *platform = bzalloc(sizeof(obs_hotkeys_platform_t)); - CFRelease(string); -} + platform->monitor = + (__bridge CFTypeRef)([NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyUp | NSEventMaskKeyDown + handler:^(NSEvent *_Nonnull event) { + MonitorEventHandlerProc(platform, event); + }]); -static char ctrl_str[OBS_COCOA_MODIFIER_SIZE]; -static char opt_str[OBS_COCOA_MODIFIER_SIZE]; -static char shift_str[OBS_COCOA_MODIFIER_SIZE]; -static char cmd_str[OBS_COCOA_MODIFIER_SIZE]; + platform->local_monitor = (__bridge CFTypeRef)([NSEvent + addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp | NSEventMaskKeyDown + handler:^NSEvent *_Nullable(NSEvent *_Nonnull event) { + MonitorEventHandlerProc(platform, event); -static void init_utf_8_strings(void) -{ - const UniChar ctrl_uni[] = {kControlUnicode, 0}; - const UniChar opt_uni[] = {kOptionUnicode, 0}; - const UniChar shift_uni[] = {kShiftUnicode, 0}; - const UniChar cmd_uni[] = {kCommandUnicode, 0}; - - unichar_to_utf8(ctrl_uni, ctrl_str); - unichar_to_utf8(opt_uni, opt_str); - unichar_to_utf8(shift_uni, shift_str); - unichar_to_utf8(cmd_uni, cmd_str); -} + return event; + }]); -static pthread_once_t strings_token = PTHREAD_ONCE_INIT; + platform->tis = TISCopyCurrentKeyboardLayoutInputSource(); + platform->layout_data = (CFDataRef) TISGetInputSourceProperty(platform->tis, kTISPropertyUnicodeKeyLayoutData); -void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str) -{ - struct dstr key_str = {0}; - if (key.key != OBS_KEY_NONE) - obs_key_to_str(key.key, &key_str); - - int res = pthread_once(&strings_token, init_utf_8_strings); - if (res) { - blog(LOG_ERROR, - "hotkeys-cocoa: Error while translating " - "modifiers %d (0x%x)", - res, res); - dstr_move(str, &key_str); - return; + if (!platform->layout_data) { + blog(LOG_ERROR, "hotkeys-cocoa: Failed to retrieve keyboard layout data"); + hotkeys_release(platform); + platform = NULL; + return false; } -#define CHECK_MODIFIER(mod, str) ((key.modifiers & mod) ? str : "") - dstr_printf(str, "%s%s%s%s%s", CHECK_MODIFIER(INTERACT_CONTROL_KEY, ctrl_str), - CHECK_MODIFIER(INTERACT_ALT_KEY, opt_str), CHECK_MODIFIER(INTERACT_SHIFT_KEY, shift_str), - CHECK_MODIFIER(INTERACT_COMMAND_KEY, cmd_str), key_str.len ? key_str.array : ""); -#undef CHECK_MODIFIER + CFRetain(platform->layout_data); + platform->layout = (UCKeyboardLayout *) CFDataGetBytePtr(platform->layout_data); - dstr_free(&key_str); -} + obs_hotkeys_platform_t *currentPlatform; -static bool log_layout_name(TISInputSourceRef tis) -{ - struct dstr layout_name = {0}; - CFStringRef sid = (CFStringRef) TISGetInputSourceProperty(tis, kTISPropertyInputSourceID); - if (!sid) { - blog(LOG_ERROR, "hotkeys-cocoa: Failed getting InputSourceID"); - return false; - } + pthread_mutex_lock(&hotkeys->mutex); - if (!dstr_from_cfstring(&layout_name, sid)) { - blog(LOG_ERROR, "hotkeys-cocoa: Could not convert InputSourceID" - " to CString"); - goto fail; + currentPlatform = hotkeys->platform_context; + + if (platform && currentPlatform && platform->layout_data == currentPlatform->layout_data) { + pthread_mutex_unlock(&hotkeys->mutex); + hotkeys_release(platform); + return true; } - blog(LOG_INFO, "hotkeys-cocoa: Using layout '%s'", layout_name.array); + hotkeys->platform_context = platform; - dstr_free(&layout_name); - return true; + log_layout_name(platform->tis); -fail: - dstr_free(&layout_name); - return false; -} + pthread_mutex_unlock(&hotkeys->mutex); -static void handle_monitor_event(obs_hotkeys_platform_t *plat, NSEvent *event) -{ - NSEventModifierFlags flags = event.modifierFlags; - plat->is_key_down[OBS_KEY_CAPSLOCK] = !!(flags & NSEventModifierFlagCapsLock); - plat->is_key_down[OBS_KEY_SHIFT] = !!(flags & NSEventModifierFlagShift); - plat->is_key_down[OBS_KEY_ALT] = !!(flags & NSEventModifierFlagOption); - plat->is_key_down[OBS_KEY_META] = !!(flags & NSEventModifierFlagCommand); - plat->is_key_down[OBS_KEY_CONTROL] = !!(flags & NSEventModifierFlagControl); - if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp) { - plat->is_key_down[obs_key_from_virtual_key(event.keyCode)] = (event.type == NSEventTypeKeyDown); + calldata_t parameters = {0}; + signal_handler_signal(hotkeys->signals, "hotkey_layout_change", ¶meters); + + if (currentPlatform) { + hotkeys_release(currentPlatform); } + + bool hasPlatformContext = hotkeys->platform_context != NULL; + + return hasPlatformContext; } -static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_) +void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys) { - if (!plat_) - return false; + CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDistributedCenter(), hotkeys); - *plat_ = bzalloc(sizeof(obs_hotkeys_platform_t)); - obs_hotkeys_platform_t *plat = *plat_; - if (!plat) { - *plat_ = NULL; - return false; - } + hotkeys_release(hotkeys->platform_context); +} - void (^handler)(NSEvent *) = ^(NSEvent *event) { - handle_monitor_event(plat, event); - }; - plat->monitor = (__bridge CFTypeRef) - [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged - handler:handler]; - - NSEvent *_Nullable (^local_handler)(NSEvent *event) = ^NSEvent *_Nullable(NSEvent *event) - { - handle_monitor_event(plat, event); - - return event; - }; - plat->local_monitor = (__bridge CFTypeRef) - [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged - handler:local_handler]; - - plat->tis = TISCopyCurrentKeyboardLayoutInputSource(); - plat->layout_data = (CFDataRef) TISGetInputSourceProperty(plat->tis, kTISPropertyUnicodeKeyLayoutData); - - if (!plat->layout_data) { - blog(LOG_ERROR, "hotkeys-cocoa: Failed getting LayoutData"); - goto fail; +int obs_key_to_virtual_key(obs_key_t key) +{ + if (virtual_keys[key].is_valid) { + return virtual_keys[key].code; + } else { + return INVALID_KEY; } +} - CFRetain(plat->layout_data); - plat->layout = (UCKeyboardLayout *) CFDataGetBytePtr(plat->layout_data); - - return true; +obs_key_t obs_key_from_virtual_key(int keyCode) +{ + for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) { + if (virtual_keys[i].is_valid && virtual_keys[i].code == keyCode) { + return (obs_key_t) i; + } + } -fail: - hotkeys_release(plat); - *plat_ = NULL; - return false; + return OBS_KEY_NONE; } -static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat) +bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *platform, obs_key_t key) { - if (!plat) - return; + if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) { + int button = key - 1; - if (plat->monitor) { - [NSEvent removeMonitor:(__bridge id _Nonnull)(plat->monitor)]; - plat->monitor = NULL; + NSUInteger buttons = [NSEvent pressedMouseButtons]; + + if ((buttons & (1 << button)) != 0) { + return true; + } else { + return false; + } } - if (plat->local_monitor) { - [NSEvent removeMonitor:(__bridge id _Nonnull)(plat->local_monitor)]; - plat->local_monitor = NULL; + if (!platform) { + return false; } - if (plat->tis) { - CFRelease(plat->tis); - plat->tis = NULL; + if (key >= OBS_KEY_LAST_VALUE) { + return false; } - if (plat->layout_data) { - CFRelease(plat->layout_data); - plat->layout_data = NULL; + return platform->is_key_down[key]; +} + +static void unichar_to_utf8(const UniChar *character, char *buffer) +{ + CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, character, 2, kCFAllocatorNull); + + if (!string) { + blog(LOG_ERROR, "hotkey-cocoa: Unable to create CFStringRef with UniChar character"); + return; + } + + if (!CFStringGetCString(string, buffer, OBS_COCOA_MODIFIER_SIZE, kCFStringEncodingUTF8)) { + blog(LOG_ERROR, "hotkey-cocoa: Error while populating buffer with glyph %d (0x%x)", character[0], character[0]); } - bfree(plat); + CFRelease(string); } -static void input_method_changed(CFNotificationCenterRef nc, void *observer, CFStringRef name, const void *object, - CFDictionaryRef user_info) +void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str) { - UNUSED_PARAMETER(nc); - UNUSED_PARAMETER(name); - UNUSED_PARAMETER(object); - UNUSED_PARAMETER(user_info); + struct dstr keyString = {0}; - struct obs_core_hotkeys *hotkeys = observer; - obs_hotkeys_platform_t *new_plat; + if (key.key != OBS_KEY_NONE) { + obs_key_to_str(key.key, &keyString); + } - if (init_hotkeys_platform(&new_plat)) { - obs_hotkeys_platform_t *plat; + dispatch_once(&onceToken, ^{ + const UniChar controlCharacter[] = {kControlUnicode, 0}; + const UniChar optionCharacter[] = {kOptionUnicode, 0}; + const UniChar shiftCharacter[] = {kShiftUnicode, 0}; + const UniChar commandCharacter[] = {kCommandUnicode, 0}; - pthread_mutex_lock(&hotkeys->mutex); - plat = hotkeys->platform_context; + unichar_to_utf8(controlCharacter, string_control); + unichar_to_utf8(optionCharacter, string_option); + unichar_to_utf8(shiftCharacter, string_shift); + unichar_to_utf8(commandCharacter, string_command); + }); - if (new_plat && plat && new_plat->layout_data == plat->layout_data) { - pthread_mutex_unlock(&hotkeys->mutex); - hotkeys_release(new_plat); - return; - } + const char *modifier_control = (key.modifiers & INTERACT_CONTROL_KEY) ? string_control : ""; + const char *modifier_option = (key.modifiers & INTERACT_ALT_KEY) ? string_option : ""; + const char *modifier_shift = (key.modifiers & INTERACT_SHIFT_KEY) ? string_shift : ""; + const char *modifier_command = (key.modifiers & INTERACT_COMMAND_KEY) ? string_command : ""; + const char *modifier_key = keyString.len ? keyString.array : ""; - hotkeys->platform_context = new_plat; - if (new_plat) - log_layout_name(new_plat->tis); - pthread_mutex_unlock(&hotkeys->mutex); + dstr_printf(str, "%s%s%s%s%s", modifier_control, modifier_option, modifier_shift, modifier_command, modifier_key); - calldata_t params = {0}; - signal_handler_signal(hotkeys->signals, "hotkey_layout_change", ¶ms); - if (plat) - hotkeys_release(plat); - } + dstr_free(&keyString); } -bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys) +void obs_key_to_str(obs_key_t key, struct dstr *str) { - CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), hotkeys, input_method_changed, - kTISNotifySelectedKeyboardInputSourceChanged, NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); + const UniCharCount maxLength = 16; + UniChar buffer[16]; - input_method_changed(NULL, hotkeys, NULL, NULL, NULL); - return hotkeys->platform_context != NULL; -} + if (obs_key_to_localized_string(key, str)) { + return; + } -void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys) -{ - CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDistributedCenter(), hotkeys); + int code = obs_key_to_virtual_key(key); + if (key_code_to_string(code, str)) { + return; + } - hotkeys_release(hotkeys->platform_context); -} + if (code == INVALID_KEY) { + const char *keyName = obs_key_to_name(key); + blog(LOG_ERROR, "hotkey-cocoa: Got invalid key while translating '%d' (%s)", key, keyName); + dstr_copy(str, keyName); + return; + } -typedef unsigned long NSUInteger; + struct obs_hotkeys_platform *platform = NULL; -static bool mouse_button_pressed(obs_key_t key, bool *pressed) -{ - int button = 0; - switch (key) { -#define MAP_BUTTON(n) \ - case OBS_KEY_MOUSE##n: \ - button = n - 1; \ - break - MAP_BUTTON(1); - MAP_BUTTON(2); - MAP_BUTTON(3); - MAP_BUTTON(4); - MAP_BUTTON(5); - MAP_BUTTON(6); - MAP_BUTTON(7); - MAP_BUTTON(8); - MAP_BUTTON(9); - MAP_BUTTON(10); - MAP_BUTTON(11); - MAP_BUTTON(12); - MAP_BUTTON(13); - MAP_BUTTON(14); - MAP_BUTTON(15); - MAP_BUTTON(16); - MAP_BUTTON(17); - MAP_BUTTON(18); - MAP_BUTTON(19); - MAP_BUTTON(20); - MAP_BUTTON(21); - MAP_BUTTON(22); - MAP_BUTTON(23); - MAP_BUTTON(24); - MAP_BUTTON(25); - MAP_BUTTON(26); - MAP_BUTTON(27); - MAP_BUTTON(28); - MAP_BUTTON(29); - break; -#undef MAP_BUTTON - - default: - return false; + if (obs) { + pthread_mutex_lock(&obs->hotkeys.mutex); + platform = obs->hotkeys.platform_context; + hotkeys_retain(platform); + pthread_mutex_unlock(&obs->hotkeys.mutex); } - NSUInteger buttons = [NSEvent pressedMouseButtons]; - *pressed = (buttons & (1 << button)) != 0; - return true; -} + if (!platform) { + const char *keyName = obs_key_to_name(key); + blog(LOG_ERROR, "hotkey-cocoa: Could not get hotkey platform while translating '%d' (%s)", key, keyName); + dstr_copy(str, keyName); + return; + } -bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat, obs_key_t key) -{ - bool mouse_pressed = false; - if (mouse_button_pressed(key, &mouse_pressed)) - return mouse_pressed; + UInt32 deadKeyState = 0; + UniCharCount length = 0; - if (!plat) - return false; + OSStatus err = UCKeyTranslate(platform->layout, code, kUCKeyActionDown, ((alphaLock >> 8) & 0xFF), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, &deadKeyState, maxLength, &length, buffer); - if (key >= OBS_KEY_LAST_VALUE) - return false; + if (err == noErr && length <= 0 && deadKeyState) { + err = UCKeyTranslate(platform->layout, kVK_Space, kUCKeyActionDown, ((alphaLock >> 8) & 0xFF), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, &deadKeyState, maxLength, &length, buffer); + } - return plat->is_key_down[key]; -} + hotkeys_release(platform); -void *obs_graphics_thread_autorelease(void *param) -{ - @autoreleasepool { - return obs_graphics_thread(param); + if (err != noErr) { + const char *keyName = obs_key_to_name(key); + blog(LOG_ERROR, "hotkey-cocoa: Error while translating '%d' (0x%x, %s) to string: %d", key, code, keyName, err); + dstr_copy(str, keyName); + return; } -} -bool obs_graphics_thread_loop_autorelease(struct obs_graphics_context *context) -{ - @autoreleasepool { - return obs_graphics_thread_loop(context); + if (length == 0) { + const char *keyName = obs_key_to_name(key); + blog(LOG_ERROR, "hotkey-cocoa: Got zero length string while translating '%d' (0x%x, %s) to string", key, code, + keyName); + dstr_copy(str, keyName); + return; } + + CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, buffer, length, kCFAllocatorNull); + + if (!string) { + const char *keyName = obs_key_to_name(key); + blog(LOG_ERROR, "hotkey-cocoa: Could not create CFStringRef while translating '%d' (0x%x, %s) to string", key, + code, keyName); + dstr_copy(str, keyName); + return; + } + + if (!dstr_from_cfstring(str, string)) { + const char *keyName = obs_key_to_name(key); + blog(LOG_ERROR, "hotkey-cocoa: Could not translate CFStringRef to CString while translating '%d' (0x%x, %s)", + key, code, keyName); + CFRelease(string); + dstr_copy(str, keyName); + return; + } + + CFRelease(string); + return; } From 552a88a895cbf26d25d08727977e04fd33bec229 Mon Sep 17 00:00:00 2001 From: jcm Date: Wed, 10 Jan 2024 19:25:06 -0700 Subject: [PATCH 0534/1073] mac-virtualcam: Compare camera UUIDs using CFUUID --- plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm b/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm index b9f373e6ec2f01..115f147146fab5 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm +++ b/plugins/mac-virtualcam/src/obs-plugin/plugin-main.mm @@ -382,8 +382,10 @@ static bool virtualcam_output_start(void *data) CMIOObjectGetPropertyData(kCMIOObjectSystemObject, &address, 0, NULL, size, &used, device_data); vcam->deviceID = 0; - NSString *OBSVirtualCamUUID = [[NSBundle bundleWithIdentifier:@"com.obsproject.mac-virtualcam"] + NSString *OBSVirtualCamUUIDString = [[NSBundle bundleWithIdentifier:@"com.obsproject.mac-virtualcam"] objectForInfoDictionaryKey:@"OBSCameraDeviceUUID"]; + CFUUIDRef OBSVirtualCamUUID = + CFUUIDCreateFromString(kCFAllocatorDefault, (CFStringRef) OBSVirtualCamUUIDString); size_t num_elements = size / sizeof(CMIOObjectID); for (size_t i = 0; i < num_elements; i++) { @@ -395,15 +397,19 @@ static bool virtualcam_output_start(void *data) CMIOObjectGetPropertyDataSize(cmioDevice, &address, 0, NULL, &device_name_size); CFStringRef uid; CMIOObjectGetPropertyData(cmioDevice, &address, 0, NULL, device_name_size, &used, &uid); - const char *uid_string = CFStringGetCStringPtr(uid, kCFStringEncodingUTF8); - if (uid_string && strcmp(uid_string, OBSVirtualCamUUID.UTF8String) == 0) { + CFUUIDRef deviceUUID = CFUUIDCreateFromString(kCFAllocatorDefault, uid); + + if (CFEqual(OBSVirtualCamUUID, deviceUUID)) { vcam->deviceID = cmioDevice; CFRelease(uid); + CFRelease(deviceUUID); break; } else { CFRelease(uid); + CFRelease(deviceUUID); } } + CFRelease(OBSVirtualCamUUID); if (!vcam->deviceID) { obs_output_set_last_error(vcam->output, obs_module_text("Error.SystemExtension.CameraUnavailable")); From cfc12d05433b1077ff8fcb46316615b7f0474d16 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 11 Jun 2024 16:34:36 +0200 Subject: [PATCH 0535/1073] cmake: Disable CCache for local builds and enable by default for CI --- CMakePresets.json | 12 ++++++------ cmake/common/ccache.cmake | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 70fc423ccb5238..be85184b7ff3aa 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -52,7 +52,8 @@ "inherits": ["macos"], "warnings": {"dev": true, "deprecated": true}, "cacheVariables": { - "CMAKE_COMPILE_WARNING_AS_ERROR": true + "CMAKE_COMPILE_WARNING_AS_ERROR": true, + "ENABLE_CCACHE": true } }, { @@ -83,7 +84,8 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo", "CMAKE_COMPILE_WARNING_AS_ERROR": true, - "CMAKE_COLOR_DIAGNOSTICS": true + "CMAKE_COLOR_DIAGNOSTICS": true, + "ENABLE_CCACHE": true } }, { @@ -102,8 +104,7 @@ "cacheVariables": { "GPU_PRIORITY_VAL": {"type": "STRING", "value": "$penv{GPU_PRIORITY_VAL}"}, "VIRTUALCAM_GUID": {"type": "STRING", "value": "A3FCE0F5-3493-419F-958A-ABA1250EC20B"}, - "ENABLE_BROWSER": true, - "ENABLE_CCACHE": false + "ENABLE_BROWSER": true } }, { @@ -113,8 +114,7 @@ "inherits": ["windows-x64"], "warnings": {"dev": true, "deprecated": true}, "cacheVariables": { - "CMAKE_COMPILE_WARNING_AS_ERROR": true, - "ENABLE_CCACHE": false + "CMAKE_COMPILE_WARNING_AS_ERROR": true } } ], diff --git a/cmake/common/ccache.cmake b/cmake/common/ccache.cmake index 5083d7b810297a..577279e0cad212 100644 --- a/cmake/common/ccache.cmake +++ b/cmake/common/ccache.cmake @@ -11,7 +11,7 @@ endif() if(CCACHE_PROGRAM) message(DEBUG "Trying to find ccache on build host - done") message(DEBUG "Ccache found as ${CCACHE_PROGRAM}") - option(ENABLE_CCACHE "Enable compiler acceleration with ccache" ON) + option(ENABLE_CCACHE "Enable compiler acceleration with ccache" OFF) if(ENABLE_CCACHE) set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") From 5b79b4b74df9de61fad2612b025d4a354b4559be Mon Sep 17 00:00:00 2001 From: Exeldro Date: Fri, 6 Sep 2024 13:28:20 +0200 Subject: [PATCH 0536/1073] libobs: Add obs_encoder_get_mixer_index --- docs/sphinx/reference-encoders.rst | 6 ++++++ libobs/obs-encoder.c | 16 ++++++++++++++++ libobs/obs.h | 3 +++ 3 files changed, 25 insertions(+) diff --git a/docs/sphinx/reference-encoders.rst b/docs/sphinx/reference-encoders.rst index ba67a4017695cc..d6ae1511935ea4 100644 --- a/docs/sphinx/reference-encoders.rst +++ b/docs/sphinx/reference-encoders.rst @@ -443,6 +443,12 @@ General Encoder Functions --------------------- +.. function:: size_t obs_encoder_get_mixer_index(const obs_encoder_t *encoder) + + :return: The mixer index for the audio track which is encoded by the encoder + +--------------------- + .. function:: void obs_encoder_set_preferred_video_format(obs_encoder_t *encoder, enum video_format format) enum video_format obs_encoder_get_preferred_video_format(const obs_encoder_t *encoder) diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c index d5086915cca63e..9c97f1445585ab 100644 --- a/libobs/obs-encoder.c +++ b/libobs/obs-encoder.c @@ -1117,6 +1117,22 @@ size_t obs_encoder_get_frame_size(const obs_encoder_t *encoder) return encoder->framesize; } +size_t obs_encoder_get_mixer_index(const obs_encoder_t *encoder) +{ + if (!obs_encoder_valid(encoder, "obs_encoder_get_mixer_index")) + return 0; + + if (encoder->info.type != OBS_ENCODER_AUDIO) { + blog(LOG_WARNING, + "obs_encoder_get_mixer_index: " + "encoder '%s' is not an audio encoder", + obs_encoder_get_name(encoder)); + return 0; + } + + return encoder->mixer_idx; +} + void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video) { diff --git a/libobs/obs.h b/libobs/obs.h index b2c400c464da1d..0e366cc0aa6dc8 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -2502,6 +2502,9 @@ EXPORT uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder); /** For audio encoders, returns the frame size of the audio packet */ EXPORT size_t obs_encoder_get_frame_size(const obs_encoder_t *encoder); +/** For audio encoders, returns the mixer index */ +EXPORT size_t obs_encoder_get_mixer_index(const obs_encoder_t *encoder); + /** * Sets the preferred video format for a video encoder. If the encoder can use * the format specified, it will force a conversion to that format if the From 7a35d7c549179cceeb8ca48d1ed256105c8357f4 Mon Sep 17 00:00:00 2001 From: Exeldro Date: Fri, 6 Sep 2024 13:29:09 +0200 Subject: [PATCH 0537/1073] obs-ffmpeg: Add logging of audio track --- plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c index 9b0c7fd8114c1e..55ead370055b65 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c @@ -288,9 +288,11 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder, char buf[256]; av_channel_layout_describe(&enc->context->ch_layout, buf, 256); - info("bitrate: %" PRId64 ", channels: %d, channel_layout: %s\n", + info("bitrate: %" PRId64 + ", channels: %d, channel_layout: %s, track: %d\n", (int64_t)enc->context->bit_rate / 1000, - (int)enc->context->ch_layout.nb_channels, buf); + (int)enc->context->ch_layout.nb_channels, buf, + (int)obs_encoder_get_mixer_index(enc->encoder) + 1); init_sizes(enc, audio); /* enable experimental FFmpeg encoder if the only one available */ From 6c590805e88755f70ad62ffbb217ad8ace09563b Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Fri, 6 Oct 2023 00:59:37 +0200 Subject: [PATCH 0538/1073] cmake: Update supported CMake version range to 3.28 and 3.30 --- CMakeLists.txt | 4 ++-- CMakePresets.json | 6 +++--- UI/CMakeLists.txt | 2 +- UI/frontend-plugins/aja-output-ui/CMakeLists.txt | 2 +- .../decklink-captions/CMakeLists.txt | 2 +- .../decklink-output-ui/CMakeLists.txt | 2 +- UI/frontend-plugins/frontend-tools/CMakeLists.txt | 2 +- UI/obs-frontend-api/CMakeLists.txt | 2 +- UI/win-update/updater/CMakeLists.txt | 2 +- cmake/common/bootstrap.cmake | 3 --- cmake/common/buildspec_common.cmake | 4 +++- cmake/common/compiler_common.cmake | 6 +----- cmake/common/helpers_common.cmake | 15 +++++++++------ cmake/windows/architecture.cmake | 13 +++++++------ cmake/windows/buildspec.cmake | 4 ++-- cmake/windows/cpackconfig.cmake | 2 +- cmake/windows/defaults.cmake | 12 ------------ deps/blake2/CMakeLists.txt | 2 +- deps/glad/CMakeLists.txt | 2 +- deps/json11/CMakeLists.txt | 2 +- deps/libcaption/CMakeLists.txt | 2 +- deps/w32-pthreads/CMakeLists.txt | 2 +- libobs-d3d11/CMakeLists.txt | 2 +- libobs-opengl/CMakeLists.txt | 2 +- libobs-winrt/CMakeLists.txt | 2 +- libobs/CMakeLists.txt | 4 ++-- libobs/cmake/os-windows.cmake | 2 +- plugins/CMakeLists.txt | 4 ++-- plugins/aja/CMakeLists.txt | 2 +- plugins/coreaudio-encoder/CMakeLists.txt | 2 +- plugins/decklink/CMakeLists.txt | 2 +- plugins/image-source/CMakeLists.txt | 2 +- plugins/linux-alsa/CMakeLists.txt | 2 +- plugins/linux-capture/CMakeLists.txt | 2 +- plugins/linux-jack/CMakeLists.txt | 2 +- plugins/linux-pipewire/CMakeLists.txt | 2 +- plugins/linux-pulseaudio/CMakeLists.txt | 2 +- plugins/linux-v4l2/CMakeLists.txt | 2 +- plugins/mac-avcapture/CMakeLists.txt | 2 +- plugins/mac-avcapture/legacy/CMakeLists.txt | 2 +- plugins/mac-capture/CMakeLists.txt | 2 +- plugins/mac-syphon/CMakeLists.txt | 2 +- plugins/mac-videotoolbox/CMakeLists.txt | 2 +- plugins/mac-virtualcam/CMakeLists.txt | 2 +- .../src/camera-extension/CMakeLists.txt | 2 ++ .../mac-virtualcam/src/dal-plugin/CMakeLists.txt | 2 +- .../mac-virtualcam/src/obs-plugin/CMakeLists.txt | 2 +- plugins/nv-filters/CMakeLists.txt | 2 +- plugins/obs-ffmpeg/CMakeLists.txt | 2 +- plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt | 2 +- plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt | 2 +- plugins/obs-filters/CMakeLists.txt | 2 +- plugins/obs-libfdk/CMakeLists.txt | 2 +- plugins/obs-nvenc/CMakeLists.txt | 2 +- plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt | 2 +- plugins/obs-outputs/CMakeLists.txt | 2 +- plugins/obs-qsv11/CMakeLists.txt | 2 +- plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt | 2 +- plugins/obs-text/CMakeLists.txt | 2 +- plugins/obs-transitions/CMakeLists.txt | 2 +- plugins/obs-vst/CMakeLists.txt | 2 +- plugins/obs-webrtc/CMakeLists.txt | 2 +- plugins/obs-x264/CMakeLists.txt | 2 +- plugins/oss-audio/CMakeLists.txt | 2 +- plugins/rtmp-services/CMakeLists.txt | 2 +- plugins/sndio/CMakeLists.txt | 2 +- plugins/text-freetype2/CMakeLists.txt | 2 +- plugins/vlc-video/CMakeLists.txt | 2 +- plugins/win-capture/CMakeLists.txt | 2 +- .../get-graphics-offsets/CMakeLists.txt | 8 ++++---- plugins/win-capture/graphics-hook/CMakeLists.txt | 8 ++++---- plugins/win-capture/inject-helper/CMakeLists.txt | 8 ++++---- plugins/win-dshow/CMakeLists.txt | 2 +- .../win-dshow/virtualcam-module/CMakeLists.txt | 8 ++++---- plugins/win-wasapi/CMakeLists.txt | 2 +- shared/bpm/CMakeLists.txt | 2 +- shared/file-updater/CMakeLists.txt | 2 +- shared/happy-eyeballs/CMakeLists.txt | 2 +- shared/ipc-util/CMakeLists.txt | 2 +- shared/media-playback/CMakeLists.txt | 2 +- shared/obs-d3d8-api/CMakeLists.txt | 2 +- shared/obs-hook-config/CMakeLists.txt | 2 +- shared/obs-inject-library/CMakeLists.txt | 2 +- shared/obs-scripting/CMakeLists.txt | 2 +- shared/obs-scripting/cmake/lua.cmake | 2 -- shared/obs-scripting/cmake/python.cmake | 2 -- shared/obs-scripting/obslua/CMakeLists.txt | 2 +- shared/obs-scripting/obspython/CMakeLists.txt | 2 +- shared/obs-shared-memory-queue/CMakeLists.txt | 2 +- shared/obs-tiny-nv12-scale/CMakeLists.txt | 2 +- shared/opts-parser/CMakeLists.txt | 2 +- shared/properties-view/CMakeLists.txt | 2 +- shared/qt/icon-label/CMakeLists.txt | 2 +- shared/qt/plain-text-edit/CMakeLists.txt | 2 +- shared/qt/slider-ignorewheel/CMakeLists.txt | 2 +- shared/qt/vertical-scroll-area/CMakeLists.txt | 2 +- shared/qt/wrappers/CMakeLists.txt | 2 +- test/test-input/CMakeLists.txt | 2 +- 98 files changed, 129 insertions(+), 144 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9819235af760bd..6a45780d1b0303 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE) @@ -6,7 +6,7 @@ project(obs-studio VERSION ${OBS_VERSION_CANONICAL}) if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/architecture.cmake") - if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) + if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) return() endif() endif() diff --git a/CMakePresets.json b/CMakePresets.json index be85184b7ff3aa..4fd2afd2c4cc2d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,8 +1,8 @@ { - "version": 3, + "version": 8, "cmakeMinimumRequired": { "major": 3, - "minor": 22, + "minor": 28, "patch": 0 }, "configurePresets": [ @@ -98,7 +98,7 @@ "lhs": "${hostSystemName}", "rhs": "Windows" }, - "architecture": "x64", + "architecture": "x64,version=10.0.20348", "binaryDir": "${sourceDir}/build_x64", "generator": "Visual Studio 17 2022", "cacheVariables": { diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index d793644a084e35..0917c82c8279ab 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_subdirectory(obs-frontend-api) diff --git a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt index da8db3cada24d1..37e4ba04de05a7 100644 --- a/UI/frontend-plugins/aja-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/aja-output-ui/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT ENABLE_AJA) target_disable(aja-output-ui) diff --git a/UI/frontend-plugins/decklink-captions/CMakeLists.txt b/UI/frontend-plugins/decklink-captions/CMakeLists.txt index 5e84b2a800d7af..53dab0da14d743 100644 --- a/UI/frontend-plugins/decklink-captions/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-captions/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT ENABLE_DECKLINK) target_disable(decklink-captions) diff --git a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt index 9df137b2d6b048..d72dac269ff5c5 100644 --- a/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt +++ b/UI/frontend-plugins/decklink-output-ui/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT ENABLE_DECKLINK) target_disable(decklink-output-ui) diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt index 9f01a3d5721a78..f7ffa4ae34acc3 100644 --- a/UI/frontend-plugins/frontend-tools/CMakeLists.txt +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Widgets) diff --git a/UI/obs-frontend-api/CMakeLists.txt b/UI/obs-frontend-api/CMakeLists.txt index cd019c8d26a7be..a2fa52d34721a4 100644 --- a/UI/obs-frontend-api/CMakeLists.txt +++ b/UI/obs-frontend-api/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(obs-frontend-api SHARED) add_library(OBS::frontend-api ALIAS obs-frontend-api) diff --git a/UI/win-update/updater/CMakeLists.txt b/UI/win-update/updater/CMakeLists.txt index 2cba74e05d5028..dcd10bb4bdff96 100644 --- a/UI/win-update/updater/CMakeLists.txt +++ b/UI/win-update/updater/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(zstd) find_package(nlohmann_json 3 REQUIRED) diff --git a/cmake/common/bootstrap.cmake b/cmake/common/bootstrap.cmake index 493ef313ecda9f..e6caa7c841b392 100644 --- a/cmake/common/bootstrap.cmake +++ b/cmake/common/bootstrap.cmake @@ -40,9 +40,6 @@ if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") file(REMOVE_RECURSE "${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt" "${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles") endif() -# Use folders for source file organization with IDE generators (Visual Studio/Xcode) -set_property(GLOBAL PROPERTY USE_FOLDERS TRUE) - # Set default global project variables set(OBS_COMPANY_NAME "OBS Project") set(OBS_PRODUCT_NAME "OBS Studio") diff --git a/cmake/common/buildspec_common.cmake b/cmake/common/buildspec_common.cmake index 18d1431476e2d2..c7844f0c27d91b 100644 --- a/cmake/common/buildspec_common.cmake +++ b/cmake/common/buildspec_common.cmake @@ -29,7 +29,7 @@ function(_check_deps_version version) ) list(REMOVE_ITEM CMAKE_PREFIX_PATH "${path}") list(APPEND CMAKE_PREFIX_PATH "${path}") - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE) + continue() else() message( @@ -42,6 +42,8 @@ function(_check_deps_version version) endif() endif() endforeach() + + return(PROPAGATE found CMAKE_PREFIX_PATH) endfunction() # _check_dependencies: Fetch and extract pre-built OBS build dependencies diff --git a/cmake/common/compiler_common.cmake b/cmake/common/compiler_common.cmake index 731ecf6751dee9..e9b1d370fad2d3 100644 --- a/cmake/common/compiler_common.cmake +++ b/cmake/common/compiler_common.cmake @@ -6,11 +6,7 @@ option(OBS_COMPILE_DEPRECATION_AS_WARNING "Downgrade deprecation warnings to act mark_as_advanced(OBS_COMPILE_DEPRECATION_AS_WARNING) # Set C and C++ language standards to C17 and C++17 -if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) - set(CMAKE_C_STANDARD 17) -else() - set(CMAKE_C_STANDARD 11) -endif() +set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) diff --git a/cmake/common/helpers_common.cmake b/cmake/common/helpers_common.cmake index a8ff5067c21a81..f5534ed6660ff8 100644 --- a/cmake/common/helpers_common.cmake +++ b/cmake/common/helpers_common.cmake @@ -203,14 +203,14 @@ function(find_dependencies) endforeach() if(NOT is_root) - set(found_libraries ${found_libraries} PARENT_SCOPE) # Exit recursive branch - return() + return(PROPAGATE found_libraries) endif() list(REMOVE_DUPLICATES found_libraries) list(APPEND ${var_FOUND_VAR} ${found_libraries}) - set(${var_FOUND_VAR} ${${var_FOUND_VAR}} PARENT_SCOPE) + + return(PROPAGATE ${var_FOUND_VAR}) endfunction() # find_qt_plugins: Find and add Qt plugin libraries associated with Qt component to target @@ -278,7 +278,8 @@ function(find_qt_plugins) endforeach() endif() - set(${var_FOUND_VAR} ${plugins_list} PARENT_SCOPE) + set(${var_FOUND_VAR} ${plugins_list}) + return(PROPAGATE ${var_FOUND_VAR}) endfunction() # target_export: Helper function to export target as CMake package @@ -432,7 +433,9 @@ function(check_uuid uuid_string return_value) set(valid_uuid FALSE) endif() message(DEBUG "UUID ${uuid_string} valid: ${valid_uuid}") - set(${return_value} ${valid_uuid} PARENT_SCOPE) + + set(${return_value} ${valid_uuid}) + return(PROPAGATE ${return_value}) endfunction() # add_obs_plugin: Add plugin subdirectory if host platform is in specified list of supported platforms and architectures @@ -465,7 +468,7 @@ function(add_obs_plugin target) else() foreach(architecture IN LISTS _AOP_ARCHITECTURES) if(OS_WINDOWS) - if("${architecture}" STREQUAL CMAKE_GENERATOR_PLATFORM) + if("${architecture}" STREQUAL CMAKE_VS_PLATFORM_NAME) set(found_architecture TRUE) endif() elseif(OS_MACOS) diff --git a/cmake/windows/architecture.cmake b/cmake/windows/architecture.cmake index 4b8a34f03a7b32..1fc9ed23fa5530 100644 --- a/cmake/windows/architecture.cmake +++ b/cmake/windows/architecture.cmake @@ -5,19 +5,20 @@ include_guard(GLOBAL) include(compilerconfig) if(NOT DEFINED OBS_PARENT_ARCHITECTURE) - if(CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") - set(OBS_PARENT_ARCHITECTURE ${CMAKE_GENERATOR_PLATFORM}) + if(CMAKE_VS_PLATFORM_NAME MATCHES "(Win32|x64)") + set(OBS_PARENT_ARCHITECTURE ${CMAKE_VS_PLATFORM_NAME}) else() - message(FATAL_ERROR "Unsupported generator platform for Windows builds: ${CMAKE_GENERATOR_PLATFORM}!") + message(FATAL_ERROR "Unsupported generator platform for Windows builds: ${CMAKE_VS_PLATFORM_NAME}!") endif() endif() -if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) if(OBS_PARENT_ARCHITECTURE STREQUAL x64) execute_process( COMMAND - "${CMAKE_COMMAND}" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A Win32 -G - "${CMAKE_GENERATOR}" -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DOBS_CMAKE_VERSION:STRING=3.0.0 + "${CMAKE_COMMAND}" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A + "Win32,version=${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" -G "${CMAKE_GENERATOR}" + -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DOBS_CMAKE_VERSION:STRING=3.0.0 -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID} -DCMAKE_MESSAGE_LOG_LEVEL:STRING=${CMAKE_MESSAGE_LOG_LEVEL} -DENABLE_CCACHE:BOOL=${ENABLE_CCACHE} -DOBS_PARENT_ARCHITECTURE:STRING=x64 RESULT_VARIABLE _process_result diff --git a/cmake/windows/buildspec.cmake b/cmake/windows/buildspec.cmake index b15cbb2e2b8113..50f1a4e75d8a0d 100644 --- a/cmake/windows/buildspec.cmake +++ b/cmake/windows/buildspec.cmake @@ -14,11 +14,11 @@ function(_check_dependencies_windows) set(cef_filename "cef_binary_VERSION_windows_ARCH_REVISION.zip") set(cef_destination "cef_binary_VERSION_windows_ARCH") - if(CMAKE_GENERATOR_PLATFORM STREQUAL Win32) + if(CMAKE_VS_PLATFORM_NAME STREQUAL Win32) set(arch x86) set(dependencies_list prebuilt) else() - string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" arch) + string(TOLOWER "${CMAKE_VS_PLATFORM_NAME}" arch) set(dependencies_list prebuilt qt6 cef) endif() set(platform windows-${arch}) diff --git a/cmake/windows/cpackconfig.cmake b/cmake/windows/cpackconfig.cmake index bd82e4e8ba52c6..2191e34dccbf86 100644 --- a/cmake/windows/cpackconfig.cmake +++ b/cmake/windows/cpackconfig.cmake @@ -7,7 +7,7 @@ include(cpackconfig_common) # Add GPLv2 license file to CPack set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/UI/data/license/gplv2.txt") set(CPACK_PACKAGE_VERSION "${OBS_VERSION_CANONICAL}") -set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-windows-${CMAKE_GENERATOR_PLATFORM}") +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-windows-${CMAKE_VS_PLATFORM_NAME}") set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY FALSE) set(CPACK_GENERATOR ZIP) set(CPACK_THREADS 0) diff --git a/cmake/windows/defaults.cmake b/cmake/windows/defaults.cmake index a9c4c1b1ac2d79..c2e141105ce6bf 100644 --- a/cmake/windows/defaults.cmake +++ b/cmake/windows/defaults.cmake @@ -23,15 +23,3 @@ set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE) include(buildspec) include(cpackconfig) - -if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) - execute_process( - COMMAND - "${CMAKE_COMMAND}" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A Win32 -G - "${CMAKE_GENERATOR}" -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DOBS_CMAKE_VERSION:STRING=3.0.0 - -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID} -DCMAKE_MESSAGE_LOG_LEVEL=${CMAKE_MESSAGE_LOG_LEVEL} - -DENABLE_CCACHE=${ENABLE_CCACHE} - RESULT_VARIABLE _process_result - COMMAND_ERROR_IS_FATAL ANY - ) -endif() diff --git a/deps/blake2/CMakeLists.txt b/deps/blake2/CMakeLists.txt index f6911749f6ad2b..894412e09268b9 100644 --- a/deps/blake2/CMakeLists.txt +++ b/deps/blake2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(blake2 OBJECT) add_library(OBS::blake2 ALIAS blake2) diff --git a/deps/glad/CMakeLists.txt b/deps/glad/CMakeLists.txt index d62a7d85a14448..35a682e1e70d23 100644 --- a/deps/glad/CMakeLists.txt +++ b/deps/glad/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(OpenGL REQUIRED) diff --git a/deps/json11/CMakeLists.txt b/deps/json11/CMakeLists.txt index 9e1c6804b19fcf..cda8e1daf1416a 100644 --- a/deps/json11/CMakeLists.txt +++ b/deps/json11/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(json11 OBJECT) add_library(OBS::json11 ALIAS json11) diff --git a/deps/libcaption/CMakeLists.txt b/deps/libcaption/CMakeLists.txt index 8ee7431541e641..58baf806815206 100644 --- a/deps/libcaption/CMakeLists.txt +++ b/deps/libcaption/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(caption STATIC EXCLUDE_FROM_ALL) add_library(OBS::caption ALIAS caption) diff --git a/deps/w32-pthreads/CMakeLists.txt b/deps/w32-pthreads/CMakeLists.txt index 40c47c2298bf17..257e75249dd21a 100644 --- a/deps/w32-pthreads/CMakeLists.txt +++ b/deps/w32-pthreads/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(w32-pthreads SHARED EXCLUDE_FROM_ALL) add_library(OBS::w32-pthreads ALIAS w32-pthreads) diff --git a/libobs-d3d11/CMakeLists.txt b/libobs-d3d11/CMakeLists.txt index 89045322ef43db..5889e85015f0b3 100644 --- a/libobs-d3d11/CMakeLists.txt +++ b/libobs-d3d11/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(libobs-d3d11 MODULE) add_library(OBS::libobs-d3d11 ALIAS libobs-d3d11) diff --git a/libobs-opengl/CMakeLists.txt b/libobs-opengl/CMakeLists.txt index 61205f6478e344..c628283b663f29 100644 --- a/libobs-opengl/CMakeLists.txt +++ b/libobs-opengl/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(libobs-opengl SHARED) add_library(OBS::libobs-opengl ALIAS libobs-opengl) diff --git a/libobs-winrt/CMakeLists.txt b/libobs-winrt/CMakeLists.txt index 1926b5a56901c9..aee1fc62fbf10d 100644 --- a/libobs-winrt/CMakeLists.txt +++ b/libobs-winrt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(libobs-winrt-headers INTERFACE) add_library(OBS::winrt-headers ALIAS libobs-winrt-headers) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 828a576ead6348..f55917da422f56 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -1,8 +1,8 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) include(cmake/obs-version.cmake) -if(OS_WINDOWS AND NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) +if(OS_WINDOWS AND NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) include(cmake/os-windows.cmake) return() endif() diff --git a/libobs/cmake/os-windows.cmake b/libobs/cmake/os-windows.cmake index 59d6ec35bb77f2..89a985b904efac 100644 --- a/libobs/cmake/os-windows.cmake +++ b/libobs/cmake/os-windows.cmake @@ -30,7 +30,7 @@ if(NOT TARGET OBS::w32-pthreads) add_subdirectory("${CMAKE_SOURCE_DIR}/deps/w32-pthreads" "${CMAKE_BINARY_DIR}/deps/w32-pthreads") endif() -if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) +if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) return() endif() diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1769a02a95733f..1af782adca3c70 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_PLUGINS "Enable building OBS plugins" ON) @@ -10,7 +10,7 @@ endif() set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_ENABLED "Plugin Support") macro(check_obs_browser) - if((OS_WINDOWS AND CMAKE_GENERATOR_PLATFORM MATCHES "(Win32|x64)") OR OS_MACOS OR OS_LINUX) + if((OS_WINDOWS AND CMAKE_VS_PLATFORM_NAME MATCHES "(Win32|x64)") OR OS_MACOS OR OS_LINUX) if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") message(FATAL_ERROR "Required submodule 'obs-browser' not available.") else() diff --git a/plugins/aja/CMakeLists.txt b/plugins/aja/CMakeLists.txt index fdb96f76f5651f..88ef18cad3bd43 100644 --- a/plugins/aja/CMakeLists.txt +++ b/plugins/aja/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_AJA "Build OBS with aja support" ON) diff --git a/plugins/coreaudio-encoder/CMakeLists.txt b/plugins/coreaudio-encoder/CMakeLists.txt index 5eac410b297e86..099995d8919306 100644 --- a/plugins/coreaudio-encoder/CMakeLists.txt +++ b/plugins/coreaudio-encoder/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(OS_WINDOWS) option(ENABLE_COREAUDIO_ENCODER "Enable building with CoreAudio encoder (Windows)" ON) diff --git a/plugins/decklink/CMakeLists.txt b/plugins/decklink/CMakeLists.txt index 43e5078c60cbf6..99fa82725cf73e 100644 --- a/plugins/decklink/CMakeLists.txt +++ b/plugins/decklink/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_DECKLINK "Build OBS with Decklink support" ON) if(NOT ENABLE_DECKLINK) diff --git a/plugins/image-source/CMakeLists.txt b/plugins/image-source/CMakeLists.txt index 7a6edc07a90ee0..b5ce2b2b02ee6e 100644 --- a/plugins/image-source/CMakeLists.txt +++ b/plugins/image-source/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(image-source MODULE) add_library(OBS::image-source ALIAS image-source) diff --git a/plugins/linux-alsa/CMakeLists.txt b/plugins/linux-alsa/CMakeLists.txt index b90147da4acb64..7e7b2f5246ff17 100644 --- a/plugins/linux-alsa/CMakeLists.txt +++ b/plugins/linux-alsa/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_ALSA "Build OBS with ALSA support" ON) diff --git a/plugins/linux-capture/CMakeLists.txt b/plugins/linux-capture/CMakeLists.txt index 55415b2724b149..d498df2b34d212 100644 --- a/plugins/linux-capture/CMakeLists.txt +++ b/plugins/linux-capture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(X11 REQUIRED) diff --git a/plugins/linux-jack/CMakeLists.txt b/plugins/linux-jack/CMakeLists.txt index 65aed52e49a2e3..4e58530a78b479 100644 --- a/plugins/linux-jack/CMakeLists.txt +++ b/plugins/linux-jack/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_JACK "Build OBS with JACK support" OFF) if(NOT ENABLE_JACK) diff --git a/plugins/linux-pipewire/CMakeLists.txt b/plugins/linux-pipewire/CMakeLists.txt index 5f93636d524c0f..3df1c24d0b0e21 100644 --- a/plugins/linux-pipewire/CMakeLists.txt +++ b/plugins/linux-pipewire/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_PIPEWIRE "Enable PipeWire support" ON) if(NOT ENABLE_PIPEWIRE) diff --git a/plugins/linux-pulseaudio/CMakeLists.txt b/plugins/linux-pulseaudio/CMakeLists.txt index 6e11f8b984a306..5ab582c163854a 100644 --- a/plugins/linux-pulseaudio/CMakeLists.txt +++ b/plugins/linux-pulseaudio/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT ENABLE_PULSEAUDIO) target_disable(linux-pulseaudio) diff --git a/plugins/linux-v4l2/CMakeLists.txt b/plugins/linux-v4l2/CMakeLists.txt index 7a36555f43805a..aafbc0d28b0e36 100644 --- a/plugins/linux-v4l2/CMakeLists.txt +++ b/plugins/linux-v4l2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_V4L2 "Build OBS with v4l2 support" ON) option(ENABLE_UDEV "Build linux-v4l2 with UDEV support" ON) diff --git a/plugins/mac-avcapture/CMakeLists.txt b/plugins/mac-avcapture/CMakeLists.txt index 7a4f11c019d30b..da15892ca9f554 100644 --- a/plugins/mac-avcapture/CMakeLists.txt +++ b/plugins/mac-avcapture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(mac-avcapture-legacy MODULE) add_library(OBS::avcapture-legacy ALIAS mac-avcapture-legacy) diff --git a/plugins/mac-avcapture/legacy/CMakeLists.txt b/plugins/mac-avcapture/legacy/CMakeLists.txt index c9695a0e826f86..81c8373175b794 100644 --- a/plugins/mac-avcapture/legacy/CMakeLists.txt +++ b/plugins/mac-avcapture/legacy/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(mac-avcapture MODULE) add_library(OBS::avcapture ALIAS mac-avcapture) diff --git a/plugins/mac-capture/CMakeLists.txt b/plugins/mac-capture/CMakeLists.txt index 8998141e4062e8..31d896a0130ae5 100644 --- a/plugins/mac-capture/CMakeLists.txt +++ b/plugins/mac-capture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(mac-capture MODULE) add_library(OBS::capture ALIAS mac-capture) diff --git a/plugins/mac-syphon/CMakeLists.txt b/plugins/mac-syphon/CMakeLists.txt index eb7033e6cf4e30..a0d48604216d05 100644 --- a/plugins/mac-syphon/CMakeLists.txt +++ b/plugins/mac-syphon/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_SYPHON "Enable Syphon sharing support" ON) if(NOT ENABLE_SYPHON) diff --git a/plugins/mac-videotoolbox/CMakeLists.txt b/plugins/mac-videotoolbox/CMakeLists.txt index 4e8c5aa336106a..19bddaca381e3a 100644 --- a/plugins/mac-videotoolbox/CMakeLists.txt +++ b/plugins/mac-videotoolbox/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(mac-videotoolbox MODULE) add_library(OBS::mac-videotoolbox ALIAS mac-videotoolbox) diff --git a/plugins/mac-virtualcam/CMakeLists.txt b/plugins/mac-virtualcam/CMakeLists.txt index d4f9bc0086d7d0..cb76aff52ae9c0 100644 --- a/plugins/mac-virtualcam/CMakeLists.txt +++ b/plugins/mac-virtualcam/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_VIRTUALCAM "Build OBS Virtualcam" ON) diff --git a/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt b/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt index 9a52c5a94ee363..ff2bc441ca10dc 100644 --- a/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/camera-extension/CMakeLists.txt @@ -1,3 +1,5 @@ +cmake_minimum_required(VERSION 3.28...3.30) + foreach(_uuid IN ITEMS VIRTUALCAM_DEVICE_UUID VIRTUALCAM_SOURCE_UUID VIRTUALCAM_SINK_UUID) set(VALID_UUID FALSE) if(NOT ${_uuid}) diff --git a/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt b/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt index 40b9fbbf1bd9c3..5ca1ada19c4482 100644 --- a/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/dal-plugin/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(obs-dal-plugin MODULE) add_library(OBS::dal-plugin ALIAS obs-dal-plugin) diff --git a/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt b/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt index ef083725c9e614..96f0128c533149 100644 --- a/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt +++ b/plugins/mac-virtualcam/src/obs-plugin/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(mac-virtualcam MODULE) add_library(OBS::virtualcam ALIAS mac-virtualcam) diff --git a/plugins/nv-filters/CMakeLists.txt b/plugins/nv-filters/CMakeLists.txt index 9047e3ceef5800..0547963e139455 100644 --- a/plugins/nv-filters/CMakeLists.txt +++ b/plugins/nv-filters/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(OS_WINDOWS) add_library(nv-filters MODULE) add_library(OBS::nv-filters ALIAS nv-filters) diff --git a/plugins/obs-ffmpeg/CMakeLists.txt b/plugins/obs-ffmpeg/CMakeLists.txt index 818b64b28fa90f..6bc0ab087b47b3 100644 --- a/plugins/obs-ffmpeg/CMakeLists.txt +++ b/plugins/obs-ffmpeg/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_FFMPEG_LOGGING "Enables obs-ffmpeg logging" OFF) option(ENABLE_NEW_MPEGTS_OUTPUT "Use native SRT/RIST mpegts output" ON) diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt index 4d275ab70bfe41..c1f95297fd73e4 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt +++ b/plugins/obs-ffmpeg/ffmpeg-mux/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_FFMPEG_MUX_DEBUG "Enable FFmpeg-mux debugging" OFF) diff --git a/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt b/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt index a08b6c0d8af58b..72cbfeaa05f159 100644 --- a/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt +++ b/plugins/obs-ffmpeg/obs-amf-test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(AMF 1.4.29 REQUIRED) diff --git a/plugins/obs-filters/CMakeLists.txt b/plugins/obs-filters/CMakeLists.txt index 269f9f512d9f1a..753ec87c5a9e45 100644 --- a/plugins/obs-filters/CMakeLists.txt +++ b/plugins/obs-filters/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(obs-filters MODULE) add_library(OBS::filters ALIAS obs-filters) diff --git a/plugins/obs-libfdk/CMakeLists.txt b/plugins/obs-libfdk/CMakeLists.txt index f171c6c7485303..c785a08db18c55 100644 --- a/plugins/obs-libfdk/CMakeLists.txt +++ b/plugins/obs-libfdk/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_LIBFDK "Enable FDK AAC support" OFF) if(NOT ENABLE_LIBFDK) diff --git a/plugins/obs-nvenc/CMakeLists.txt b/plugins/obs-nvenc/CMakeLists.txt index 0b4fe2b612dcd1..93053c1edb9e13 100644 --- a/plugins/obs-nvenc/CMakeLists.txt +++ b/plugins/obs-nvenc/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_NVENC "Build NVIDIA Hardware Encoder Plugin" ON) option(ENABLE_NVENC_FFMPEG_IDS "Register FFmpeg encoder IDs" ON) diff --git a/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt b/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt index 97ece87724da57..7f839256e39716 100644 --- a/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt +++ b/plugins/obs-nvenc/obs-nvenc-test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(FFnvcodec 12 REQUIRED) diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index e8b83d2fc13e87..21818d5685560d 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(MbedTLS REQUIRED) find_package(ZLIB REQUIRED) diff --git a/plugins/obs-qsv11/CMakeLists.txt b/plugins/obs-qsv11/CMakeLists.txt index 7aa42585c1fd51..8966280e67f00d 100644 --- a/plugins/obs-qsv11/CMakeLists.txt +++ b/plugins/obs-qsv11/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_QSV11 "Build Intel QSV11 Hardware Encoder." TRUE) if(NOT ENABLE_QSV11) diff --git a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt index 668bc9cffe8ab3..f46839899f5f31 100644 --- a/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt +++ b/plugins/obs-qsv11/obs-qsv-test/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_executable(obs-qsv-test) diff --git a/plugins/obs-text/CMakeLists.txt b/plugins/obs-text/CMakeLists.txt index 118157862732e1..0ae9cd4ef3c5eb 100644 --- a/plugins/obs-text/CMakeLists.txt +++ b/plugins/obs-text/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(obs-text MODULE) add_library(OBS::text ALIAS obs-text) diff --git a/plugins/obs-transitions/CMakeLists.txt b/plugins/obs-transitions/CMakeLists.txt index 6af13a2e61ffa6..2b5c1e3231cb4a 100644 --- a/plugins/obs-transitions/CMakeLists.txt +++ b/plugins/obs-transitions/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(obs-transitions MODULE) add_library(OBS::transition ALIAS obs-transitions) diff --git a/plugins/obs-vst/CMakeLists.txt b/plugins/obs-vst/CMakeLists.txt index 683be4e77ddaff..8a2b4eb7938733 100644 --- a/plugins/obs-vst/CMakeLists.txt +++ b/plugins/obs-vst/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_VST "Enable building OBS with VST plugin" ON) diff --git a/plugins/obs-webrtc/CMakeLists.txt b/plugins/obs-webrtc/CMakeLists.txt index 5a8a325ce65087..d57ca8af5297b5 100644 --- a/plugins/obs-webrtc/CMakeLists.txt +++ b/plugins/obs-webrtc/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_WEBRTC "Enable WebRTC Output support" ON) if(NOT ENABLE_WEBRTC) diff --git a/plugins/obs-x264/CMakeLists.txt b/plugins/obs-x264/CMakeLists.txt index 010f031b5d429d..958d553c89f056 100644 --- a/plugins/obs-x264/CMakeLists.txt +++ b/plugins/obs-x264/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Libx264 REQUIRED) diff --git a/plugins/oss-audio/CMakeLists.txt b/plugins/oss-audio/CMakeLists.txt index d753466a91ae45..5ae20e2cc7394a 100644 --- a/plugins/oss-audio/CMakeLists.txt +++ b/plugins/oss-audio/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_OSS "Enable building with OSS audio support" ON) diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index 78571eb06ea489..be0364cb780f39 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_SERVICE_UPDATES "Checks for service updates" ON) diff --git a/plugins/sndio/CMakeLists.txt b/plugins/sndio/CMakeLists.txt index 0d7448a90e995c..f76e7c07e1f385 100644 --- a/plugins/sndio/CMakeLists.txt +++ b/plugins/sndio/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_SNDIO "Build OBS with sndio support" OFF) if(NOT ENABLE_SNDIO) diff --git a/plugins/text-freetype2/CMakeLists.txt b/plugins/text-freetype2/CMakeLists.txt index da768e7fd2c0f4..8643dd0b7bb6e9 100644 --- a/plugins/text-freetype2/CMakeLists.txt +++ b/plugins/text-freetype2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_FREETYPE "Enable FreeType text plugin" ON) diff --git a/plugins/vlc-video/CMakeLists.txt b/plugins/vlc-video/CMakeLists.txt index 4cce9b0275d6b1..871036c9f52b38 100644 --- a/plugins/vlc-video/CMakeLists.txt +++ b/plugins/vlc-video/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) macro(check_vlc_path) find_package(PkgConfig QUIET) diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index 3e855943e141ac..8216ba5095df0a 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") diff --git a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt index f48f02803322bf..fffb3b4e6f4f7a 100644 --- a/plugins/win-capture/get-graphics-offsets/CMakeLists.txt +++ b/plugins/win-capture/get-graphics-offsets/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT TARGET OBS::d3d8-api) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-d3d8-api" obs-d3d8-api) @@ -19,8 +19,8 @@ target_link_libraries( PRIVATE OBS::hook-config OBS::d3d8-api d3d9.lib dxgi.lib d3d11.lib ) -if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) - if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) + if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) add_custom_command( TARGET get-graphics-offsets POST_BUILD @@ -32,7 +32,7 @@ if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) add_dependencies(win-capture get-graphics-offsets) endif() -if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) set(_OUTPUT_NAME get-graphics-offsets64) else() set(_OUTPUT_NAME get-graphics-offsets32) diff --git a/plugins/win-capture/graphics-hook/CMakeLists.txt b/plugins/win-capture/graphics-hook/CMakeLists.txt index 63f0fcc6145a65..caa1bc532ec2db 100644 --- a/plugins/win-capture/graphics-hook/CMakeLists.txt +++ b/plugins/win-capture/graphics-hook/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Detours REQUIRED) find_package(Vulkan REQUIRED) @@ -54,8 +54,8 @@ if(TARGET Vulkan::Vulkan) target_compile_definitions(graphics-hook PRIVATE COMPILE_VULKAN_HOOK) endif() -if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) - if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) + if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) add_custom_command( TARGET graphics-hook POST_BUILD @@ -67,7 +67,7 @@ if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) add_dependencies(win-capture graphics-hook) endif() -if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) set(_OUTPUT_NAME graphics-hook64) else() set(_OUTPUT_NAME graphics-hook32) diff --git a/plugins/win-capture/inject-helper/CMakeLists.txt b/plugins/win-capture/inject-helper/CMakeLists.txt index b73f672ef429d7..ae62a032a6d8c2 100644 --- a/plugins/win-capture/inject-helper/CMakeLists.txt +++ b/plugins/win-capture/inject-helper/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") @@ -14,8 +14,8 @@ target_sources(inject-helper PRIVATE inject-helper.c) target_link_libraries(inject-helper PRIVATE OBS::inject-library OBS::obfuscate) -if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) - if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) + if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) add_custom_command( TARGET inject-helper POST_BUILD @@ -27,7 +27,7 @@ if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) add_dependencies(win-capture inject-helper) endif() -if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) set(_OUTPUT_NAME inject-helper64) else() set(_OUTPUT_NAME inject-helper32) diff --git a/plugins/win-dshow/CMakeLists.txt b/plugins/win-dshow/CMakeLists.txt index c04c90fb074c6e..078151f6949d97 100644 --- a/plugins/win-dshow/CMakeLists.txt +++ b/plugins/win-dshow/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(FFmpeg REQUIRED avcodec avutil) diff --git a/plugins/win-dshow/virtualcam-module/CMakeLists.txt b/plugins/win-dshow/virtualcam-module/CMakeLists.txt index ac8f46e18039b4..bc77f8efbf8904 100644 --- a/plugins/win-dshow/virtualcam-module/CMakeLists.txt +++ b/plugins/win-dshow/virtualcam-module/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_VIRTUALCAM "Enable Windows Virtual Camera" ON) if(NOT ENABLE_VIRTUALCAM) @@ -104,8 +104,8 @@ target_link_libraries( winmm ) -if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) - if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME) + if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) add_custom_command( TARGET obs-virtualcam-module POST_BUILD @@ -117,7 +117,7 @@ if(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_GENERATOR_PLATFORM) add_dependencies(win-dshow obs-virtualcam-module) endif() -if(CMAKE_GENERATOR_PLATFORM STREQUAL x64) +if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) set(_OUTPUT_NAME virtualcam-module64) else() set(_OUTPUT_NAME virtualcam-module32) diff --git a/plugins/win-wasapi/CMakeLists.txt b/plugins/win-wasapi/CMakeLists.txt index 7b7298acb6afad..4d3da03fca6dcf 100644 --- a/plugins/win-wasapi/CMakeLists.txt +++ b/plugins/win-wasapi/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(win-wasapi MODULE) add_library(OBS::wasapi ALIAS win-wasapi) diff --git a/shared/bpm/CMakeLists.txt b/shared/bpm/CMakeLists.txt index 052736c7a15651..0f16408c77b4d1 100644 --- a/shared/bpm/CMakeLists.txt +++ b/shared/bpm/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(bpm OBJECT) add_library(OBS::bpm ALIAS bpm) diff --git a/shared/file-updater/CMakeLists.txt b/shared/file-updater/CMakeLists.txt index 0151974972b74e..5308c99601c07b 100644 --- a/shared/file-updater/CMakeLists.txt +++ b/shared/file-updater/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(CURL REQUIRED) diff --git a/shared/happy-eyeballs/CMakeLists.txt b/shared/happy-eyeballs/CMakeLists.txt index efbb2629497290..9da80a18067afb 100644 --- a/shared/happy-eyeballs/CMakeLists.txt +++ b/shared/happy-eyeballs/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(happy-eyeballs OBJECT) add_library(OBS::happy-eyeballs ALIAS happy-eyeballs) diff --git a/shared/ipc-util/CMakeLists.txt b/shared/ipc-util/CMakeLists.txt index cfcdc3c71585b3..5a4ad7a086c351 100644 --- a/shared/ipc-util/CMakeLists.txt +++ b/shared/ipc-util/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(ipc-util INTERFACE) add_library(OBS::ipc-util ALIAS ipc-util) diff --git a/shared/media-playback/CMakeLists.txt b/shared/media-playback/CMakeLists.txt index 5b2d4ebba0619b..711c09b11a4bf7 100644 --- a/shared/media-playback/CMakeLists.txt +++ b/shared/media-playback/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(FFmpeg REQUIRED avcodec avdevice avutil avformat) diff --git a/shared/obs-d3d8-api/CMakeLists.txt b/shared/obs-d3d8-api/CMakeLists.txt index 35546f25537be6..e6b6acae347bf0 100644 --- a/shared/obs-d3d8-api/CMakeLists.txt +++ b/shared/obs-d3d8-api/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(d3d8-api INTERFACE) add_library(OBS::d3d8-api ALIAS d3d8-api) diff --git a/shared/obs-hook-config/CMakeLists.txt b/shared/obs-hook-config/CMakeLists.txt index 347657cc563a52..14809656538700 100644 --- a/shared/obs-hook-config/CMakeLists.txt +++ b/shared/obs-hook-config/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(hook-config INTERFACE) add_library(OBS::hook-config ALIAS hook-config) diff --git a/shared/obs-inject-library/CMakeLists.txt b/shared/obs-inject-library/CMakeLists.txt index b80f43118e4649..e7cf7d071222f8 100644 --- a/shared/obs-inject-library/CMakeLists.txt +++ b/shared/obs-inject-library/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT TARGET OBS::obfuscate) add_subdirectory("${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_BINARY_DIR}/libobs") diff --git a/shared/obs-scripting/CMakeLists.txt b/shared/obs-scripting/CMakeLists.txt index a6892194c19cbe..c609c257b395e7 100644 --- a/shared/obs-scripting/CMakeLists.txt +++ b/shared/obs-scripting/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT ENABLE_SCRIPTING) target_disable_feature(obs-scripting "Scripting support") diff --git a/shared/obs-scripting/cmake/lua.cmake b/shared/obs-scripting/cmake/lua.cmake index f46d90b15ceada..e7e8147e2f97bb 100644 --- a/shared/obs-scripting/cmake/lua.cmake +++ b/shared/obs-scripting/cmake/lua.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.22...3.25) - option(ENABLE_SCRIPTING_LUA "Enable Lua scripting support" ON) if(ENABLE_SCRIPTING_LUA) diff --git a/shared/obs-scripting/cmake/python.cmake b/shared/obs-scripting/cmake/python.cmake index 9696d2b0e9ae1b..5ea5fda7793cea 100644 --- a/shared/obs-scripting/cmake/python.cmake +++ b/shared/obs-scripting/cmake/python.cmake @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.22...3.25) - option(ENABLE_SCRIPTING_PYTHON "Enable Python scripting support" ON) if(ENABLE_SCRIPTING_PYTHON) diff --git a/shared/obs-scripting/obslua/CMakeLists.txt b/shared/obs-scripting/obslua/CMakeLists.txt index d579c00e95b471..3e617f43c5e126 100644 --- a/shared/obs-scripting/obslua/CMakeLists.txt +++ b/shared/obs-scripting/obslua/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(POLICY CMP0078) cmake_policy(SET CMP0078 NEW) diff --git a/shared/obs-scripting/obspython/CMakeLists.txt b/shared/obs-scripting/obspython/CMakeLists.txt index 072dea9209e821..837032f15db4da 100644 --- a/shared/obs-scripting/obspython/CMakeLists.txt +++ b/shared/obs-scripting/obspython/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(POLICY CMP0078) cmake_policy(SET CMP0078 NEW) diff --git a/shared/obs-shared-memory-queue/CMakeLists.txt b/shared/obs-shared-memory-queue/CMakeLists.txt index 2cc7538f021e67..c244420cbcf2db 100644 --- a/shared/obs-shared-memory-queue/CMakeLists.txt +++ b/shared/obs-shared-memory-queue/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) if(NOT TARGET OBS::tiny-nv12-scale) add_subdirectory("${CMAKE_SOURCE_DIR}/shared/obs-tiny-nv12-scale" obs-tiny-nv12-scale) diff --git a/shared/obs-tiny-nv12-scale/CMakeLists.txt b/shared/obs-tiny-nv12-scale/CMakeLists.txt index 4b25249756c5da..42f254dcdbaeba 100644 --- a/shared/obs-tiny-nv12-scale/CMakeLists.txt +++ b/shared/obs-tiny-nv12-scale/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(obs-tiny-nv12-scale INTERFACE) add_library(OBS::tiny-nv12-scale ALIAS obs-tiny-nv12-scale) diff --git a/shared/opts-parser/CMakeLists.txt b/shared/opts-parser/CMakeLists.txt index 4f74cb66df76c9..461c6d86a00d29 100644 --- a/shared/opts-parser/CMakeLists.txt +++ b/shared/opts-parser/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) add_library(opts-parser OBJECT) add_library(OBS::opts-parser ALIAS opts-parser) diff --git a/shared/properties-view/CMakeLists.txt b/shared/properties-view/CMakeLists.txt index 9769baa79c2d16..206455b95e9fb7 100644 --- a/shared/properties-view/CMakeLists.txt +++ b/shared/properties-view/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Core Widgets) diff --git a/shared/qt/icon-label/CMakeLists.txt b/shared/qt/icon-label/CMakeLists.txt index ff16e337653826..c5325d158f105f 100644 --- a/shared/qt/icon-label/CMakeLists.txt +++ b/shared/qt/icon-label/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Core Widgets) diff --git a/shared/qt/plain-text-edit/CMakeLists.txt b/shared/qt/plain-text-edit/CMakeLists.txt index 0abe0b56260073..f2977487e47827 100644 --- a/shared/qt/plain-text-edit/CMakeLists.txt +++ b/shared/qt/plain-text-edit/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Core Widgets) diff --git a/shared/qt/slider-ignorewheel/CMakeLists.txt b/shared/qt/slider-ignorewheel/CMakeLists.txt index 92dae97fb07d4f..5058f055b211f7 100644 --- a/shared/qt/slider-ignorewheel/CMakeLists.txt +++ b/shared/qt/slider-ignorewheel/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Core Widgets) diff --git a/shared/qt/vertical-scroll-area/CMakeLists.txt b/shared/qt/vertical-scroll-area/CMakeLists.txt index 16d301643be5f7..a68df5f3078982 100644 --- a/shared/qt/vertical-scroll-area/CMakeLists.txt +++ b/shared/qt/vertical-scroll-area/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Core Widgets) diff --git a/shared/qt/wrappers/CMakeLists.txt b/shared/qt/wrappers/CMakeLists.txt index e634e6f6112f20..00b6eb2a167211 100644 --- a/shared/qt/wrappers/CMakeLists.txt +++ b/shared/qt/wrappers/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) find_package(Qt6 REQUIRED Core Widgets) diff --git a/test/test-input/CMakeLists.txt b/test/test-input/CMakeLists.txt index 2e0a7f00c8d845..75e349e3518ddf 100644 --- a/test/test-input/CMakeLists.txt +++ b/test/test-input/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.22...3.25) +cmake_minimum_required(VERSION 3.28...3.30) option(ENABLE_TEST_INPUT "Build test sources" OFF) From 53853df33a9a8820798dfd177d6bb82ef1c6494a Mon Sep 17 00:00:00 2001 From: "Gale, Thy-Lan" Date: Tue, 10 Sep 2024 13:17:05 -0700 Subject: [PATCH 0539/1073] obs-qsv11: Add AV1 Screen Content Tools --- plugins/obs-qsv11/QSV_Encoder.h | 1 + plugins/obs-qsv11/QSV_Encoder_Internal.cpp | 32 +++++++++++++++++++ plugins/obs-qsv11/QSV_Encoder_Internal.h | 4 +++ .../obs-qsv11/obs-qsv-test/obs-qsv-test.cpp | 1 + 4 files changed, 38 insertions(+) diff --git a/plugins/obs-qsv11/QSV_Encoder.h b/plugins/obs-qsv11/QSV_Encoder.h index ab266774c6fe14..c69d20aba4ab18 100644 --- a/plugins/obs-qsv11/QSV_Encoder.h +++ b/plugins/obs-qsv11/QSV_Encoder.h @@ -56,6 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once +#define ONEVPL_EXPERIMENTAL #include #include #include diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp index a90af811fd6e54..17dac34a91cc23 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.cpp +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.cpp @@ -195,6 +195,22 @@ static inline bool HasOptimizedBRCSupport(const mfxPlatform &platform, UNUSED_PARAMETER(rateControl); return false; } + +static inline bool HasAV1ScreenContentSupport(const mfxPlatform &platform, + const mfxVersion &version) +{ +#if (MFX_VERSION_MAJOR >= 2 && MFX_VERSION_MINOR >= 12) || MFX_VERSION_MAJOR > 2 + // Platform enums needed are introduced in VPL version 2.12 + if ((version.Major >= 2 && version.Minor >= 12) || version.Major > 2) + if (platform.CodeName >= MFX_PLATFORM_LUNARLAKE && + platform.CodeName != MFX_PLATFORM_ALDERLAKE_N && + platform.CodeName != MFX_PLATFORM_ARROWLAKE) + return true; +#endif + UNUSED_PARAMETER(platform); + UNUSED_PARAMETER(version); + return false; +} PRAGMA_WARN_POP mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, @@ -372,6 +388,22 @@ mfxStatus QSV_Encoder_Internal::InitParams(qsv_param_t *pParams, extendedBuffers.push_back((mfxExtBuffer *)&m_ExtAv1TileParam); } + // AV1_SCREEN_CONTENT_TOOLS API is introduced in VPL version 2.11 +#if (MFX_VERSION_MAJOR >= 2 && MFX_VERSION_MINOR >= 11) || MFX_VERSION_MAJOR > 2 + if (codec == QSV_CODEC_AV1 && + HasAV1ScreenContentSupport(platform, m_ver)) { + memset(&m_ExtAV1ScreenContentTools, 0, + sizeof(m_ExtAV1ScreenContentTools)); + m_ExtAV1ScreenContentTools.Header.BufferId = + MFX_EXTBUFF_AV1_SCREEN_CONTENT_TOOLS; + m_ExtAV1ScreenContentTools.Header.BufferSz = + sizeof(m_ExtAV1ScreenContentTools); + m_ExtAV1ScreenContentTools.Palette = MFX_CODINGOPTION_ON; + extendedBuffers.push_back( + (mfxExtBuffer *)&m_ExtAV1ScreenContentTools); + } +#endif + #if defined(_WIN32) // TODO: Ask about this one on VAAPI too. memset(&m_ExtVideoSignalInfo, 0, sizeof(m_ExtVideoSignalInfo)); diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.h b/plugins/obs-qsv11/QSV_Encoder_Internal.h index 478f0b87acaf4c..c90efedf79efa5 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.h +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.h @@ -54,6 +54,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once +#define ONEVPL_EXPERIMENTAL #include #include #include "QSV_Encoder.h" @@ -121,6 +122,9 @@ class QSV_Encoder_Internal { mfxExtCodingOption m_co; mfxExtHEVCParam m_ExtHEVCParam{}; mfxExtAV1TileParam m_ExtAv1TileParam{}; +#if (MFX_VERSION_MAJOR >= 2 && MFX_VERSION_MINOR >= 11) || MFX_VERSION_MAJOR > 2 + mfxExtAV1ScreenContentTools m_ExtAV1ScreenContentTools{}; +#endif mfxExtVideoSignalInfo m_ExtVideoSignalInfo{}; mfxExtChromaLocInfo m_ExtChromaLocInfo{}; mfxExtMasteringDisplayColourVolume m_ExtMasteringDisplayColourVolume{}; diff --git a/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp b/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp index 11dafa375e5af2..14d3d07bde4597 100644 --- a/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp +++ b/plugins/obs-qsv11/obs-qsv-test/obs-qsv-test.cpp @@ -1,3 +1,4 @@ +#define ONEVPL_EXPERIMENTAL #include #include #include From 2ecf2c802c5897aa32ff988843befcea22f8b118 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Fri, 13 Sep 2024 12:31:32 -0700 Subject: [PATCH 0540/1073] obs-qsv11: Initialize allocator response We check that mids is a nullptr which may not be the case if this remains uninitialized. Earlier attempt to fix this assumed we faulted on the response itself. fixes #11221 --- plugins/obs-qsv11/QSV_Encoder_Internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-qsv11/QSV_Encoder_Internal.h b/plugins/obs-qsv11/QSV_Encoder_Internal.h index c90efedf79efa5..3fd06e6d3a71ea 100644 --- a/plugins/obs-qsv11/QSV_Encoder_Internal.h +++ b/plugins/obs-qsv11/QSV_Encoder_Internal.h @@ -105,7 +105,7 @@ class QSV_Encoder_Internal { void *m_sessionData; mfxFrameAllocator m_mfxAllocator; mfxVideoParam m_mfxEncParams; - mfxFrameAllocResponse m_mfxResponse; + mfxFrameAllocResponse m_mfxResponse{}; mfxFrameSurface1 **m_pmfxSurfaces; mfxU16 m_nSurfNum; MFXVideoENCODE *m_pmfxENC; From 2a0e61422885d56aa35dfafe29b47b00c1e1d7f9 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:50:55 -0400 Subject: [PATCH 0541/1073] build-aux: Use fallback-x11 instead of x11 --- build-aux/com.obsproject.Studio.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-aux/com.obsproject.Studio.json b/build-aux/com.obsproject.Studio.json index 2b8c10767b510b..9f578ab6c49c31 100644 --- a/build-aux/com.obsproject.Studio.json +++ b/build-aux/com.obsproject.Studio.json @@ -6,7 +6,7 @@ "command": "obs", "finish-args": [ "--socket=wayland", - "--socket=x11", + "--socket=fallback-x11", "--socket=pulseaudio", "--device=all", "--share=network", From 232e40f41ef39f15be9ae8487f320b1cfa5fd628 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 13 Sep 2024 14:52:02 -0400 Subject: [PATCH 0542/1073] CI: Update peter-evans/create-pull-request to v7.0.2 We're currently using peter-evans/create-pull-request v6.0.0 which has a known issue that affects our services-validator action when attempting to update an existing PR. This issue was fixed in v6.0.1, but we can update to v7.0.2. --- .github/actions/services-validator/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/services-validator/action.yaml b/.github/actions/services-validator/action.yaml index b82a036317c6cb..929202915ff3e5 100644 --- a/.github/actions/services-validator/action.yaml +++ b/.github/actions/services-validator/action.yaml @@ -118,7 +118,7 @@ runs: path: ${{ inputs.workingDirectory }}/other/* - name: Create pull request 🔧 - uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 + uses: peter-evans/create-pull-request@d121e62763d8cc35b5fb1710e887d6e69a52d3a4 if: fromJSON(inputs.createPullRequest) && fromJSON(inputs.runServiceChecks) && fromJSON(steps.services-check.outputs.make_pr) with: author: 'Service Checker ' From f036b0dc3f35997c58aae7e5e7893aedd1284566 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 13 Sep 2024 15:13:09 -0400 Subject: [PATCH 0543/1073] CI: Update softprops/action-gh-release to v2.0.8 --- .github/workflows/push.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 1a7c04213dc433..bbce5b8836e73b 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -308,7 +308,7 @@ jobs: - name: Create Release 🛫 if: fromJSON(steps.check.outputs.validTag) id: create_release - uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 with: draft: true prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }} From f9f974fe7b2d2ef45ca00423006253159478b7e5 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Mon, 3 Jun 2024 14:25:32 +0200 Subject: [PATCH 0544/1073] cmake: Update use of MbedTLS to support update to version 3.6.0 MbedTLS changed a lot of internals with their LTS version 3.6.0, which are incompatible with the find module currently shipped with OBS Studio. The solution requires several changes to be applied at once: * Rename the generated target name to MbedTLS::mbedtls to match the name used by MbedTLS' own CMake package * Update find module to use the updated target name(s) * Set CMAKE_FIND_PACKAGE_PREFER_CONFIG to TRUE before trying to find MbedTLS to ensure that CMake package files are used with priority (Those are shipped only with MbedTLS 3.6.0 in obs-deps). * A deprecation warning is emitted if the find module is used with MbedTLS 3.6.0 available --- UI/cmake/feature-whatsnew.cmake | 4 ++- UI/cmake/os-windows.cmake | 8 +++-- cmake/finders/FindMbedTLS.cmake | 57 +++++++++++++++++------------- plugins/obs-outputs/CMakeLists.txt | 9 +++-- 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/UI/cmake/feature-whatsnew.cmake b/UI/cmake/feature-whatsnew.cmake index d7d4d89f031b24..4ed3627059cbae 100644 --- a/UI/cmake/feature-whatsnew.cmake +++ b/UI/cmake/feature-whatsnew.cmake @@ -6,14 +6,16 @@ if(ENABLE_WHATSNEW AND TARGET OBS::browser-panels) if(OS_MACOS) include(cmake/feature-macos-update.cmake) elseif(OS_LINUX) + set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) find_package(MbedTLS REQUIRED) + set(CMAKE_FIND_PACKAGE_PREFER_CONFIG FALSE) find_package(nlohmann_json REQUIRED) if(NOT TARGET OBS::blake2) add_subdirectory("${CMAKE_SOURCE_DIR}/deps/blake2" "${CMAKE_BINARY_DIR}/deps/blake2") endif() - target_link_libraries(obs-studio PRIVATE MbedTLS::MbedTLS nlohmann_json::nlohmann_json OBS::blake2) + target_link_libraries(obs-studio PRIVATE MbedTLS::mbedtls nlohmann_json::nlohmann_json OBS::blake2) target_sources( obs-studio diff --git a/UI/cmake/os-windows.cmake b/UI/cmake/os-windows.cmake index 56e2f4ac81d2a0..7c3675e94fae07 100644 --- a/UI/cmake/os-windows.cmake +++ b/UI/cmake/os-windows.cmake @@ -6,7 +6,9 @@ if(NOT TARGET OBS::w32-pthreads) add_subdirectory("${CMAKE_SOURCE_DIR}/deps/w32-pthreads" "${CMAKE_BINARY_DIR}/deps/w32-pthreads") endif() -find_package(MbedTLS) +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) +find_package(MbedTLS REQUIRED) +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG FALSE) find_package(Detours REQUIRED) find_package(nlohmann_json REQUIRED) @@ -38,12 +40,12 @@ target_sources(obs-studio PRIVATE system-info-windows.cpp) target_link_libraries( obs-studio - PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::MbedTLS nlohmann_json::nlohmann_json Detours::Detours + PRIVATE crypt32 OBS::blake2 OBS::w32-pthreads MbedTLS::mbedtls nlohmann_json::nlohmann_json Detours::Detours ) target_compile_definitions(obs-studio PRIVATE PSAPI_VERSION=2) -target_link_options(obs-studio PRIVATE /IGNORE:4099) +target_link_options(obs-studio PRIVATE /IGNORE:4099 $<$:/NODEFAULTLIB:MSVCRT>) add_library(obs-update-helpers INTERFACE) add_library(OBS::update-helpers ALIAS obs-update-helpers) diff --git a/cmake/finders/FindMbedTLS.cmake b/cmake/finders/FindMbedTLS.cmake index 3337111b8e890c..5b9d9d95536e90 100644 --- a/cmake/finders/FindMbedTLS.cmake +++ b/cmake/finders/FindMbedTLS.cmake @@ -14,9 +14,9 @@ Components This module contains provides several components: -``MbedCrypto`` -``MbedTLS`` -``MbedX509`` +``mbedcrypto`` +``mbedtls`` +``mbedx509`` Import targets exist for each component. @@ -27,13 +27,13 @@ Imported Targets This module defines the :prop_tgt:`IMPORTED` targets: -``MbedTLS::MbedCrypto`` +``MbedTLS::mbedcrypto`` Crypto component -``MbedTLS::MbedTLS`` +``MbedTLS::mbedtls`` TLS component -``MbedTLS::MbedX509`` +``MbedTLS::mbedX509`` X509 component Result Variables @@ -78,7 +78,7 @@ macro(MbedTLS_set_soname component) ) if(_result EQUAL 0 AND _output MATCHES "^@rpath/") - set_property(TARGET MbedTLS::Mbed${component} PROPERTY IMPORTED_SONAME "${_output}") + set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_SONAME "${_output}") endif() elseif(CMAKE_HOST_SYSTEM_NAME MATCHES "Linux|FreeBSD") execute_process( @@ -89,7 +89,7 @@ macro(MbedTLS_set_soname component) if(_result EQUAL 0) string(REGEX REPLACE "[ \t]+SONAME[ \t]+([^ \t]+)" "\\1" _soname "${_output}") - set_property(TARGET MbedTLS::Mbed${component} PROPERTY IMPORTED_SONAME "${_soname}") + set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_SONAME "${_soname}") unset(_soname) endif() endif() @@ -142,8 +142,15 @@ else() set(MbedTLS_VERSION 0.0.0) endif() +if(MbedTLS_VERSION VERSION_GREATER_EQUAL 3.6.0) + message( + DEPRECATION + "Use of the custom CMake find module for MbedTLS versions >= 3.6.0 is not supported - build errors might occur!" + ) +endif() + find_library( - MbedTLS_LIBRARY + Mbedtls_LIBRARY NAMES libmbedtls mbedtls HINTS "${PC_MbedTLS_LIBRARY_DIRS}" PATHS /usr/lib /usr/local/lib @@ -151,7 +158,7 @@ find_library( ) find_library( - MbedCrypto_LIBRARY + Mbedcrypto_LIBRARY NAMES libmbedcrypto mbedcrypto HINTS "${PC_MbedTLS_LIBRARY_DIRS}" PATHS /usr/lib /usr/local/lib @@ -159,14 +166,14 @@ find_library( ) find_library( - MbedX509_LIBRARY + Mbedx509_LIBRARY NAMES libmbedx509 mbedx509 HINTS "${PC_MbedTLS_LIBRARY_DIRS}" PATHS /usr/lib /usr/local/lib DOC "MbedX509 location" ) -if(MbedTLS_LIBRARY AND NOT MbedCrypto_LIBRARY AND NOT MbedX509_LIBRARY) +if(Mbedtls_LIBRARY AND NOT Mbedcrypto_LIBRARY AND NOT Mbedx509_LIBRARY) set(CMAKE_REQUIRED_LIBRARIES "${MbedTLS_LIBRARY}") set(CMAKE_REQUIRED_INCLUDES "${MbedTLS_INCLUDE_DIR}") @@ -185,38 +192,38 @@ endif() if(MbedTLS_INCLUDES_X509 AND MbedTLS_INCLUDES_CRYPTO) find_package_handle_standard_args( MbedTLS - REQUIRED_VARS MbedTLS_LIBRARY MbedTLS_INCLUDE_DIR + REQUIRED_VARS Mbedtls_LIBRARY MbedTLS_INCLUDE_DIR VERSION_VAR MbedTLS_VERSION REASON_FAILURE_MESSAGE "${MbedTLS_ERROR_REASON}" ) - mark_as_advanced(MbedTLS_LIBRARY MbedTLS_INCLUDE_DIR) - list(APPEND _COMPONENTS TLS) + mark_as_advanced(Mbedtls_LIBRARY MbedTLS_INCLUDE_DIR) + list(APPEND _COMPONENTS tls) else() find_package_handle_standard_args( MbedTLS - REQUIRED_VARS MbedTLS_LIBRARY MbedCrypto_LIBRARY MbedX509_LIBRARY MbedTLS_INCLUDE_DIR + REQUIRED_VARS Mbedtls_LIBRARY Mbedcrypto_LIBRARY Mbedx509_LIBRARY MbedTLS_INCLUDE_DIR VERSION_VAR MbedTLS_VERSION REASON_FAILURE_MESSAGE "${MbedTLS_ERROR_REASON}" ) - mark_as_advanced(MbedTLS_LIBRARY MbedCrypto_LIBRARY MbedX509_LIBRARY MbedTLS_INCLUDE_DIR) - list(APPEND _COMPONENTS TLS Crypto X509) + mark_as_advanced(Mbedtls_LIBRARY Mbedcrypto_LIBRARY Mbedx509_LIBRARY MbedTLS_INCLUDE_DIR) + list(APPEND _COMPONENTS tls crypto x509) endif() unset(MbedTLS_ERROR_REASON) if(MbedTLS_FOUND) foreach(component IN LISTS _COMPONENTS) - if(NOT TARGET MbedTLS::Mbed${component}) + if(NOT TARGET MbedTLS::mbed${component}) if(IS_ABSOLUTE "${Mbed${component}_LIBRARY}") - add_library(MbedTLS::Mbed${component} UNKNOWN IMPORTED) - set_property(TARGET MbedTLS::Mbed${component} PROPERTY IMPORTED_LOCATION "${Mbed${component}_LIBRARY}") + add_library(MbedTLS::mbed${component} UNKNOWN IMPORTED) + set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_LOCATION "${Mbed${component}_LIBRARY}") else() - add_library(MbedTLS::Mbed${component} INTERFACE IMPORTED) - set_property(TARGET MbedTLS::Mbed${component} PROPERTY IMPORTED_LIBNAME "${Mbed${component}_LIBRARY}") + add_library(MbedTLS::mbed${component} INTERFACE IMPORTED) + set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_LIBNAME "${Mbed${component}_LIBRARY}") endif() mbedtls_set_soname(${component}) set_target_properties( - MbedTLS::MbedTLS + MbedTLS::mbedtls PROPERTIES INTERFACE_COMPILE_OPTIONS "${PC_MbedTLS_CFLAGS_OTHER}" INTERFACE_INCLUDE_DIRECTORIES "${MbedTLS_INCLUDE_DIR}" @@ -230,7 +237,7 @@ if(MbedTLS_FOUND) set(MbedTLS_LIBRARIES ${MbedTLS_LIBRARY}) else() set(MbedTLS_LIBRARIES ${MbedTLS_LIBRARY} ${MbedCrypto_LIBRARY} ${MbedX509_LIBRARY}) - set_property(TARGET MbedTLS::MbedTLS PROPERTY INTERFACE_LINK_LIBRARIES MbedTLS::MbedCrypto MbedTLS::MbedX509) + set_property(TARGET MbedTLS::mbedtls PROPERTY INTERFACE_LINK_LIBRARIES MbedTLS::mbedcrypto MbedTLS::mbedx509) endif() endif() diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index 21818d5685560d..cc0f158eadddde 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.28...3.30) +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) find_package(MbedTLS REQUIRED) +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG FALSE) find_package(ZLIB REQUIRED) if(NOT TARGET happy-eyeballs) @@ -71,7 +73,7 @@ target_link_libraries( OBS::libobs OBS::happy-eyeballs OBS::opts-parser - MbedTLS::MbedTLS + MbedTLS::mbedtls ZLIB::ZLIB $<$:OBS::w32-pthreads> $<$:crypt32> @@ -83,7 +85,10 @@ target_link_libraries( ) # Remove once jansson has been fixed on obs-deps -target_link_options(obs-outputs PRIVATE $<$:/IGNORE:4098>) +target_link_options( + obs-outputs + PRIVATE $<$:/IGNORE:4098> $<$,$>:/NODEFAULTLIB:MSVCRT> +) if(OS_WINDOWS) configure_file(cmake/windows/obs-module.rc.in obs-outputs.rc) From d7c798c60dccf1ca4171c7eea90fe22afddadc02 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 13 Sep 2024 18:16:31 -0400 Subject: [PATCH 0545/1073] CI: Use Windows 11 SDK 10.0.22621.0 Before 6c590805e88755f70ad62ffbb217ad8ace09563b, builds on Windows would automatically select a Windows SDK using CMake's rules: https://cmake.org/cmake/help/v3.25/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.html https://cmake.org/cmake/help/v3.30/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.html https://cmake.org/cmake/help/v3.30/policy/CMP0149.html This meant that CI Windows builds would end up using SDK 10.0.20348.0 because that matched the runner's OS version exactly. Locally, if a Windows SDK version that exactly matched the host system version was not installed, CMake would select the latest Windows SDK available. This caused some observed differences in build success between CI and local builds if a newer SDK was used locally. The Windows runners have the 10.0.22621.0 SDK installed, so let's specifically select that. --- CMakePresets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index 4fd2afd2c4cc2d..36820b19f1510a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -98,7 +98,7 @@ "lhs": "${hostSystemName}", "rhs": "Windows" }, - "architecture": "x64,version=10.0.20348", + "architecture": "x64,version=10.0.22621", "binaryDir": "${sourceDir}/build_x64", "generator": "Visual Studio 17 2022", "cacheVariables": { From b9808eaca82340132c56e1577ae58a1e8747a119 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Sat, 14 Sep 2024 17:31:03 +0200 Subject: [PATCH 0546/1073] UI: Fix outdated scene collection and profile method names --- UI/api-interface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index ee6dae7da87d47..82b5d16a13c0be 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -202,7 +202,7 @@ struct OBSStudioAPI : obs_frontend_callbacks { bool obs_frontend_add_scene_collection(const char *name) override { bool success = false; - QMetaObject::invokeMethod(main, "NewSceneCollection", + QMetaObject::invokeMethod(main, "CreateNewSceneCollection", WaitConnection(), Q_RETURN_ARG(bool, success), Q_ARG(QString, QT_UTF8(name))); @@ -252,13 +252,13 @@ struct OBSStudioAPI : obs_frontend_callbacks { void obs_frontend_create_profile(const char *name) override { - QMetaObject::invokeMethod(main, "NewProfile", + QMetaObject::invokeMethod(main, "CreateNewProfile", Q_ARG(QString, name)); } void obs_frontend_duplicate_profile(const char *name) override { - QMetaObject::invokeMethod(main, "DuplicateProfile", + QMetaObject::invokeMethod(main, "CreateDuplicateProfile", Q_ARG(QString, name)); } From 61d74fb4ab41370ab31a6eb185d18e946e4c8d7f Mon Sep 17 00:00:00 2001 From: Norihiro Kamae Date: Mon, 4 Dec 2023 11:55:09 +0900 Subject: [PATCH 0547/1073] libobs: Remove obs_output_signal_delay The function is never called, documented or exported. --- libobs/obs-output-delay.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/libobs/obs-output-delay.c b/libobs/obs-output-delay.c index d0b386be4259af..dd816164715959 100644 --- a/libobs/obs-output-delay.c +++ b/libobs/obs-output-delay.c @@ -146,17 +146,6 @@ void process_delay(void *data, struct encoder_packet *packet, ; } -void obs_output_signal_delay(obs_output_t *output, const char *signal) -{ - struct calldata params; - uint8_t stack[128]; - - calldata_init_fixed(¶ms, stack, sizeof(stack)); - calldata_set_ptr(¶ms, "output", output); - calldata_set_int(¶ms, "sec", output->active_delay_ns / 1000000000); - signal_handler_signal(output->context.signals, signal, ¶ms); -} - bool obs_output_delay_start(obs_output_t *output) { struct delay_data dd = { From 16b8e9c3fe48e2dadfa0723cc162e842936bbab8 Mon Sep 17 00:00:00 2001 From: gxalpha Date: Fri, 13 Sep 2024 23:06:23 +0200 Subject: [PATCH 0548/1073] libobs: Deselect scene item before removing Makes sure that a scene item that gets removed is in the "not selected" state, no matter whether it was selected before. This causes the "item_deselect" signal to be sent if the item is selected while being removed. --- libobs/obs-scene.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index c38eeb6321f252..e6d3651a937e90 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -2668,6 +2668,7 @@ static void obs_sceneitem_remove_internal(obs_sceneitem_t *item) obs_scene_t *parent = item->parent; item->removed = true; + obs_sceneitem_select(item, false); set_visibility(item, false); detach_sceneitem(item); From a289581579f9ace8c101fbc2f17553e09e4fa05c Mon Sep 17 00:00:00 2001 From: Exeldro Date: Fri, 13 Sep 2024 09:49:11 +0200 Subject: [PATCH 0549/1073] docs: Add obs_frontend_get_user_config --- docs/sphinx/reference-frontend-api.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 5e95b4c09302f0..53391aeaa7ba46 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -736,12 +736,29 @@ Functions --------------------------------------- +.. deprecated:: 31.0 .. function:: config_t *obs_frontend_get_global_config(void) :return: The config_t* associated with the global config (global.ini) --------------------------------------- +.. function:: config_t *obs_frontend_get_app_config(void) + + :return: The config_t* associated with system-wide settings (global.ini) + + .. versionadded:: 31.0 + +--------------------------------------- + +.. function:: config_t *obs_frontend_get_user_config(void) + + :return: The config_t* associated with user settings (user.ini) + + .. versionadded:: 31.0 + +--------------------------------------- + .. function:: void obs_frontend_set_streaming_service(obs_service_t *service) Sets the current streaming service to stream with. From ae90534e2e8f27d62feab1c1354723625a67af10 Mon Sep 17 00:00:00 2001 From: PatTheMav Date: Tue, 9 Jul 2024 17:33:21 +0200 Subject: [PATCH 0550/1073] CI: Update flatpak-builder-lint --- .../actions/flatpak-builder-lint/action.yaml | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/.github/actions/flatpak-builder-lint/action.yaml b/.github/actions/flatpak-builder-lint/action.yaml index 86cd7179337b36..a6fd54f496eb89 100644 --- a/.github/actions/flatpak-builder-lint/action.yaml +++ b/.github/actions/flatpak-builder-lint/action.yaml @@ -19,16 +19,10 @@ runs: working-directory: ${{ inputs.workingDirectory }} run: | : Check artifact input - case "${{ inputs.artifact }}" in - builddir);; - repo);; - manifest);; - appstream);; - *) - echo "::error::Given artifact type is incorrect" - exit 2 - ;; - esac + if ! [[ "${{ inputs.artifact }}" =~ builddir|repo|manifest|appstream ]]; then + echo "::error::Given artifact type is incorrect" + exit 2 + fi - name: Run flatpak-builder-lint id: result @@ -36,31 +30,46 @@ runs: working-directory: ${{ inputs.workingDirectory }} run: | : Run flatpak-builder-lint - exit_code=0 - ret=$(flatpak-builder-lint --exceptions ${{ inputs.artifact }} ${{ inputs.path }}) || exit_code=$? - if [[ $exit_code != 0 && -z "$ret" ]]; then + + return=0 + result="$(flatpak-builder-lint --exceptions ${{ inputs.artifact }} ${{ inputs.path }})" || return=$? + + if [[ ${return} != 0 && -z "${result}" ]]; then echo "::error::Error while running flatpak-builder-lint" exit 2 fi - if [[ ${{ inputs.artifact }} == "appstream" ]]; then - echo $ret + if [[ "${{ inputs.artifact }}" == "appstream" ]]; then + echo "${result}" - [[ $exit_code != 0 ]] && echo "::error::Flatpak appstream info is not valid" + if [[ ${return} != 0 ]]; then echo "::error::Flatpak appstream info is not valid"; fi - exit $exit_code + exit ${return} fi - n_warnings=$(echo $ret | jq '.warnings | length') - for ((i = 0 ; i < n_warnings ; i++)); do - warning=$(echo $ret | jq ".warnings[$i]") - echo "::warning::$warning found in the Flatpak ${{ inputs.artifact }}" - done + # This jq command selects any available array under the 'warnings' key in the JSON document + # or provides an empty array as a fallback if the key is not present. This array is then + # piped to the 'map' function to apply a transformation to every element in the array, + # converting it to a string prefixed with the output level, the actual element value, and + # finally the suffix string defined in 'template'. + # + # The result of this expression is concatenated with a similar expression doing the same + # but for the 'errors' key and its associated array. + # + # The second jq invocation then selects each element of the array and outputs it directly, + # which will be strings of the formats: + # + # * '::warning::